It's happening for 0.08% of our users.
One of the crashes happening on Samsung Galaxy S10 running Android 11
It's not crashing on my Samsung Galaxy S10 running Android 11 though!
Does anyone know why?
Line where it crashes
Photo[] photoArray = (Photo[]) bundle.getParcelableArray(EXTRA_PHOTOS);
1st finding
Photo photo1 = new Photo(231, "url", "photoid", "profilid", "caption");
Parcelable[] parcelables1 = new Parcelable[]{photo1};
Photo[] array = (Photo[]) parcelables1;
Throws ClassCastException
This couple with the fact that Bundle#getParcelableArray only returns a Parcelable array, not the the concrete class Photo array explains why the crash is expected
But why is it only happening for some users only?
2nd finding
This post claims that getParcelableArray() won't work, only getParcelableArrayListExtra() or individual casting will work but the why is unclear to me
3rd finding
So I've learnt that downcasting arrays is not supposed to work in Java or Kotlin, at least not in the way Android Bundle do it.
The reason why getParcelableArrayListExtra() works is because it assume generic type and did work to handle the type.
Question remains: how come getParcelableArray() works nicely in my real device and emulator? Is there some black magic hidden in plain sight here?
Code below
Photo.java
public class Photo implements Serializable, Model, Parcelable {
private String caption;
private String id, idProfile;
private int position;
private String iphoneFullscreen;
public Photo(int position, String url, String photoId, String profileId, String caption) {
iphoneFullscreen = url;
this.position = position;
this.id = photoId;
idProfile = profileId;
this.caption = caption;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.caption);
dest.writeString(this.id);
dest.writeString(this.idProfile);
dest.writeInt(this.position);
dest.writeString(this.iphoneFullscreen);
}
protected Photo(Parcel in) {
this.caption = in.readString();
this.id = in.readString();
this.idProfile = in.readString();
this.position = in.readInt();
this.iphoneFullscreen = in.readString();
}
public static final Parcelable.Creator<Photo> CREATOR = new Parcelable.Creator<Photo>() {
#Override
public Photo createFromParcel(Parcel source) {
return new Photo(source);
}
#Override
public Photo[] newArray(int size) {
return new Photo[size];
}
};
}
How it's provided and consumed
DialogFragmentPhotoGallery.java
public static DialogFragment newInstance(
Photo[] photos) {
DialogFragmentPhotoGallery dialogFragmentPhotoGallery = new DialogFragmentPhotoGallery();
Bundle args = new Bundle();
args.putParcelableArray(DialogFragmentPhotoGallery.EXTRA_PHOTOS, photos);
dialogFragmentPhotoGallery.setArguments(args);
return dialogFragmentPhotoGallery;
}
public View onCreateView(
#NonNull LayoutInflater inflater,
ViewGroup parent,
Bundle savedInstanceState) {
if (getArguments() != null) {
Photo[] photos = (Photo[]) getArguments().getParcelableArray(EXTRA_PHOTOS); // <-- CRASH
// do things with photos
}
Related
I am working on a video player app. I have two fragments: one is AllVideolist fragment and the other one is Videos folders fragment. The AllVideoListenter code here fragment is working fine, but I don't know how to show get all videos folder.
This is my MainActivity.java code.
MainActivity.java
public ArrayList<videoFiles> getAllVideos(Context context) {
ArrayList<videoFiles> tempArrayList = new ArrayList<>();
Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
String [] projection = {
MediaStore.Video.Media._ID,
MediaStore.Video.Media.DATA,
MediaStore.Video.Media.TITLE,
MediaStore.Video.Media.DISPLAY_NAME,
MediaStore.Video.Media.SIZE,
MediaStore.Video.Media.DATE_ADDED,
MediaStore.Video.Media.DURATION
};
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
if (cursor!= null){
while (cursor.moveToNext()){
String id =cursor.getString(0);
String path =cursor.getString(1);
String title =cursor.getString(2);
String fileName =cursor.getString(3);
String size =cursor.getString(4);
String dateAdded =cursor.getString(5);
String duration =cursor.getString(6);
int durationa = Integer.parseInt(duration);
String duration_formet;
int sec = (durationa/1000)%60;
int min = (durationa/(1000*60))%60;
int hours = durationa/(1000*60*60);
if (hours == 0){
duration_formet = String.valueOf(min).concat(":" .concat(String.format(Locale.UK, "%02d",sec)));
}else {
duration_formet = String.valueOf(hours).concat(":" .concat(String.format(Locale.UK, "%02d",min).concat(":" .concat(String.format(Locale.UK, "%02d",sec)))));
}
videoFiles videoFiles = new videoFiles(id, path, title,fileName,size, dateAdded,duration_formet);
Log.d("path", path);
tempArrayList.add(videoFiles);
}
cursor.close();
}
return tempArrayList;
}
I craete this model class.
VideoFiles.java
public class videoFiles {
///------------------MODEL CLASS ---------------
private String id;
private String path;
private String title;
private String fileName;
private String size;
private String dateAdded;
private String duration;
public videoFiles(String id, String path, String title, String fileName, String size, String dateAdded, String duration) {
this.id = id;
this.path = path;
this.title = title;
this.fileName = fileName;
this.size = size;
this.dateAdded = dateAdded;
this.duration = duration;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public String getDateAdded() {
return dateAdded;
}
public void setDateAdded(String dateAdded) {
this.dateAdded = dateAdded;
}
public String getDuration() {
return duration;
}
public void setDuration(String duration) {
this.duration = duration;
}
This is folder Adapter.
FolderAdapter.java
public class FolderAdapter extends RecyclerView.Adapter<FolderAdapter.folderViewHolder> {
View view;
Context context;
private ArrayList<videoFiles> folderList;
public FolderAdapter(Context context, ArrayList<videoFiles> folderList) {
this.context = context;
this.folderList = folderList;
}
#NonNull
#Override
public FolderAdapter.folderViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
view = LayoutInflater.from(context).inflate(R.layout.foldeitems, parent, false);
return new folderViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull FolderAdapter.folderViewHolder holder, int position) {
holder.folderName.setText(folderList.get(position).getPath());
}
#Override
public int getItemCount() {
return folderList.size();
}
public class folderViewHolder extends RecyclerView.ViewHolder {
TextView folderName;
public folderViewHolder(#NonNull View itemView) {
super(itemView);
folderName = itemView.findViewById(R.id.foldername);
}
}
}
This is main foledr fragment. I want to show videos in folder by folder
FoldeFragment.java
public class FolderFragment extends Fragment {
View view;
RecyclerView recyclerView;
public FolderFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view= inflater.inflate(R.layout.fragment_folder, container, false);
recyclerView = view.findViewById(R.id.folderRecylerView);
return view;
}
}
There is no "videos folder" in Android. There are number of default locations for videos, but they vary from device to device. Devices that have expandable memory often have video storage defaults that don't exist on non-expandable devices. User are not obliged to put video files in these default locations, anyway.
If you want to get a list of all directories that contain video files, you'll either need to implement some sort of search/index operation yourself, or use the built-in media database. You should be able to enumerate the list of videos from the database, and extract the unique folder names.
For what it's worth, I answered a similar question here:
How to show only videos folder?
I have been trying to find the way to pass a ParcelableArrayList from one Fragment to Another and for some reason when I receive the ParcelableArrayList it's empty. I have checked many posts about this problem but I don't make it work.
This is my Activity that holds the Fragments in a FrameLayout:
package com.blumonpay.restaurant.customer.presentation.activities;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import android.os.Bundle;
import android.view.WindowManager;
import com.blumonpay.android.mifel.restaurante.R;
import com.blumonpay.restaurant.customer.presentation.fragments.ShowPayMethodsFragment;
public class PayMethodsActivity extends AppCompatActivity {
private Fragment showFragment;
private Fragment addFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
setContentView(R.layout.activity_pay_methods);
showFragment = ShowPayMethodsFragment.getInstance(this);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frameLayout, showFragment)
.commit();
}
public void switchFragment(Fragment fragment) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frameLayout, fragment)
.commit();
}
}
This is how I'm trying to pass the ArrayList in my first Fragment:
Bundle dataBundle = new Bundle();
dataBundle.putParcelableArrayList("cardsList", (ArrayList<? extends Parcelable>) list);
dataBundle.putInt("position",position);
ShowDetailedPayMethodFragment showDetailPayMethod = ShowDetailedPayMethodFragment.getInstance(context);
showDetailPayMethod.setArguments(dataBundle);
((PayMethodsActivity)context).switchFragment(showDetailPayMethod);
As you can see I'm also passing an int value which I can receive with no problem at all.
Then, this is the second Fragment where I'm receiving the data (ParcelableArrayList and the int value):
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_view_detailed_pay_method, container, false);
if (this.getArguments()!=null) {
cardsList = this.getArguments().getParcelableArrayList("cardsList");
positionList = this.getArguments().getInt("position");
}
cardsBannerAdapter = new CustomCardsBannerAdapter(context, cardsList);
cardsViewPager.setAdapter(cardsBannerAdapter);
initSlider();
return v;
}
For some reason the List is empty when I get to this point, the only value I'm getting is this.getArguments().getInt("position"); (int value)
And finally this is my POJO class Cards:
package com.blumonpay.restaurant.customer.domain.model;
import android.os.Parcel;
import android.os.Parcelable;
public class Cards implements Parcelable {
private String cardNumber;
private String cardType;
private String cardBrand;
private String cardExpirationDate;
private String cardHolderName;
public Cards(String cardNumber, String cardType, String cardBrand, String cardExpirationDate, String cardHolderName) {
this.cardNumber = cardNumber;
this.cardType = cardType;
this.cardBrand = cardBrand;
this.cardExpirationDate = cardExpirationDate;
this.cardHolderName = cardHolderName;
}
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
public String getCardType() {
return cardType;
}
public void setCardType(String cardType) {
this.cardType = cardType;
}
public String getCardBrand() {
return cardBrand;
}
public void setCardBrand(String cardBrand) {
this.cardBrand = cardBrand;
}
public String getCardExpirationDate() {
return cardExpirationDate;
}
public void setCardExpirationDate(String cardExpirationDate) {
this.cardExpirationDate = cardExpirationDate;
}
public String getCardHolderName() {
return cardHolderName;
}
public void setCardHolderName(String cardHolderName) {
this.cardHolderName = cardHolderName;
}
protected Cards(Parcel in) {
cardNumber = in.readString();
cardType = in.readString();
cardBrand = in.readString();
cardExpirationDate = in.readString();
cardHolderName = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(cardNumber);
dest.writeString(cardType);
dest.writeString(cardBrand);
dest.writeString(cardExpirationDate);
dest.writeString(cardHolderName);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<Cards> CREATOR = new Parcelable.Creator<Cards>() {
#Override
public Cards createFromParcel(Parcel in) {
return new Cards(in);
}
#Override
public Cards[] newArray(int size) {
return new Cards[size];
}
};
}
Any clue of what am I missing?
Tried the same in kotlin, with the Cards.java intact. The list gets passed. Here is the snippet.
Could you try and compare?
In first fragment as an example:
val item = Cards("gfhfhfhfh", "", "", "", "")
val list = ArrayList<Cards>()
list.add(item
Passing it as: (no explicit cast)
bundle.putParcelableArrayList("list",list)
In the next fragment:
val l = arguments?.getParcelableArrayList<Cards>("list")
According to this post, How to marshall and unmarshall Parcelable, Parcelable object is not stable which might well be the reason that you cannot pass it from one activity to another. I faced similar problem recently and used the same post to solve my problem. Basically in steps:
1. You need to marshall the Parcelable object in the first Activity as :
byte[] bytes = marshall(parcelableObj);
intentOpenSecondActivity.putExtra(SOME_KEY, bytes);
2. You need to unmarshall it in the second Activity as :
byte[] bytes = getIntent().getExtras().getByteArray(SOME_KEY);
parcelableObj = unmarshall(bytes, ParcelableObject.CREATOR);
I want to pass ArrayList<Palatte_Model> from one Activity to another fragment. I am using ParcelableArraylist. Here I attached my code
public class Palette_Model implements Parcelable{
public String data_format_value;
public int data_format_id;
public Palette_Model(String data_format_value, int data_format_id) {
this.data_format_value = data_format_value;
this.data_format_id = data_format_id;
}
public String getData_format_value() {
return data_format_value;
}
public void setData_format_value(String data_format_value) {
this.data_format_value = data_format_value;
}
public int getData_format_id() {
return data_format_id;
}
public void setData_format_id(int data_format_id) {
this.data_format_id = data_format_id;
}
protected Palette_Model(Parcel in) {
data_format_value = in.readString();
data_format_id = in.readInt();
}
#Override
public int describeContents() {
return this.hashCode();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(data_format_value);
dest.writeInt(data_format_id);
}
public void readfromParcel(Parcel source){
data_format_id = source.readInt();
data_format_value = source.readString();
}
public static final Creator<Palette_Model> CREATOR = new Creator<Palette_Model>() {
#Override
public Palette_Model createFromParcel(Parcel in) {
return new Palette_Model(in);
}
#Override
public Palette_Model[] newArray(int size) {
return new Palette_Model[size];
}
};
}
here i attached my activity class code. to send arraylist to fragment
Platte_fragment dFragment = new Platte_fragment();
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("arraylist", strQuestion);
dFragment.setArguments(bundle);
// dFragment.show(fragmentManager, "array list");
FragmentTransaction fts = getSupportFragmentManager().beginTransaction();
fts.replace(R.id.questContFragId, dFragment);
fts.addToBackStack(dFragment.getClass().getSimpleName());
fts.commit();
here I mentioned my fragment class code:
I fetch the ArrayList from Activity. it shows a null value
ArrayList<Palette_Model> strQuestion ;
strQuestion = new ArrayList<>();
try {
Bundle bundle = this.getArguments();
strQuestion = bundle.getParcelableArrayList("arraylist");
}catch (NullPointerException e){
Log.e("er",e.getMessage());
}
The shown method may produce NullpointerException.
I had a similar problem. After some research, I found out somewhere in my code ArrayList<Type> list is pointing to nothing(NullpointerException).
What helped me was to check if Bundle extras is null:
ArrayList<Category> categories = new ArrayList<Category>(); // you initialize it here
Bundle extras = getIntent().getExtras();
if (extras != null) {
categories = extras.getParcelableArrayList("categories");
}
So if retrieved data is null it won't attach null pointer to ArrayList.
Check this link for more info about NullPointerException
I have passed my object using Parcelable to the new activity, and also written into the writeToParcel. I believe that the object has been transferred, as i can use .toString() however it has none of its associated methods. i used this link: http://shri.blog.kraya.co.uk/2010/04/26/android-parcel-data-to-pass-between-activities-using-parcelable-classes/ my code runs, however I was expecting to be able to use a.clubName() or some such to be able to access these details, either i'm trying to access the details incorrectly or I have not quite got the set up correct.
Thanks for any help.
This is my Clubs class that associates the details to the parcel
public class Clubs implements Parcelable{
//lots of variables defining things such as clubName, address etc.
public static final Parcelable.Creator CREATOR = new Parcelable.Creator(){
public Clubs createFromParcel(Parcel in) {
return new Clubs(in); }
public Clubs[] newArray(int size) {
return new Clubs[size]; }
};
private void readFromParcel(Parcel in) {
clubName = in.readString();
address = in.readString();
postcode = in.readString();
contactName = in.readString();
contactPhone = in.readString();
date = in.readString();
eventType = in.readString();
scrutTime = in.readString();
startTime = in.readString();
eventName = in.readString();
week = in.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(clubName);
dest.writeString(address);
dest.writeString(postcode);
dest.writeString(contactName);
dest.writeString(contactPhone);
dest.writeString(date);
dest.writeString(eventType);
dest.writeString(scrutTime);
dest.writeString(startTime);
dest.writeString(eventName);
dest.writeString(week);
}
public Clubs(Parcel in) { readFromParcel(in); }
This is my onItemClick method that starts the new activity that should send the object across
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
Clubs mymeeting = db.get(map.get(position));
Intent i = new Intent(ListSample.this, DynamicEvents.class);
i.putExtra("mymeeting", mymeeting);
startActivity(i);
}
});
this is the class i want to be able to write the details into
public class DynamicEvents extends Activity
{
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle b = getIntent().getExtras();
// Create the text view
TextView textView = new TextView(this);
textView.setTextSize(20);
Object a = b.getParcelable("mymeeting");
textView.setText(a.toString());
// Set the text view as the activity layout
setContentView(textView);
}
}
You need to cast the parcelable to the correct class type.
Parcelable parcelable = b.getParcelable("mymeeting");
if(parcelable instanceof Clubs) {
Clubs clubs = (Clubs) parcelable;
}
I have a Google maps app that grabs the users lat and long values, and through the use of a Google JSON response stores a range of supermarket objects with their relative lat and long values. I use an overlay class to the place a marker onto the map dependent on the selected supermarket from the listview that shows the available supermarkets.
This all works fine, where I seem to be having a slight issue is with the accuracy of my overlay class. The map marker doesn't seem to be very accurate, in that the marker is pointing at the wrong place of the specified lat and long points passed to it from my geopoint object. (sometimes up to 11 miles away from where it should be).
I have tried declaring the LOCATION_FINE in my manifest on the uses permissions but this doesn't seem to make any difference. Do I need this as I'm using a JSON response rather than GPS?
How accurate is the map on the emulator? I may-be clutching at straws here but I have heard multiple people saying that when using the Google API's on the emulator it isn't that accurate.
No GPS is being used.
EDIT
To add to this question. I have another question that goes into more depth. I believe the issue is within my update() method as the issue is the incorrect object lat and long values are being sent to the marker.
I will post my code, just to see if anyone can find any issues.
GeoName class:
public class GeoName {
private String id;
private Geometry geometry;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Geometry getGeometry() {
return geometry;
}
public void setGeometry(Geometry geometry) {
this.geometry = geometry;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
PostalCodeAdapter class:
package com.example.restfulweb;
public class PostalCodeAdapter extends BaseAdapter {
private Context ctx = null;
Location l;
Dialog d;
Double Latt;
Double Longg;
private List<GeoName> names = new ArrayList<GeoName>();
public PostalCodeAdapter(Context ctx, List<GeoName> locations) {
this.ctx = ctx;
this.names = locations;
}
#Override
public int getCount() {
return names.size();
}
#Override
public Object getItem(int arg0) {
return null;
}
#Override
public long getItemId(int arg0) {
return 0;
}
#Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
LinearLayout layout = new LinearLayout(ctx);
AbsListView.LayoutParams params = new AbsListView.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT);
layout.setLayoutParams(params);
layout.setOrientation(LinearLayout.HORIZONTAL);
GeoName location = this.names.get(arg0);
Location l = location.getGeometry().getLocation();
Latt = l.getLat();
Longg = l.getLng();
TextView value = new TextView(this.ctx);
value.setText(location.getName());
value.setMaxHeight(100);
value.setTypeface(Typeface.DEFAULT, Typeface.NORMAL);
value.setGravity(Gravity.CENTER);
value.setOnClickListener(new CityClickListener(location));
layout.addView(value);
return layout;
}
class CityClickListener implements OnClickListener {
private GeoName geoName = null;
CityClickListener(GeoName name) {
this.geoName = name;
}
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setView(createView());
builder.setTitle("Details of " + geoName.getName());
builder.setCancelable(true);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
updateMap();
}
private void updateMap() {
MapActivity mapAct = (MapActivity)ctx;
MapView map = (MapView)mapAct.findViewById(R.id.map);
map.setScrollBarStyle(MapView.SCROLLBARS_INSIDE_INSET);
map.setBuiltInZoomControls(Boolean.TRUE);
map.displayZoomControls(Boolean.TRUE);
********** ISSUE: THE PASSED LAT AND LONG VALUES ARE NOT BEING PASSED TO THE OVERLAY **********
GeoPoint point = new GeoPoint((int)(Latt* 1E6), (int)(Longg * 1E6));
MapController mc = map.getController();
mc.setZoom(17);
mc.setCenter(point);
mc.animateTo(point);
List<Overlay> overlay = map.getOverlays();
overlay.clear();
Drawable marker = ctx.getResources().getDrawable(R.drawable.marker);
MyItemizedOverlay overlays = new MyItemizedOverlay(marker, map, ctx);
OverlayItem pointerConverted = new OverlayItem(point, geoName.getName(), null);
overlay.add(overlays);
overlays.addOverlay(pointerConverted);
}
private View createView() {
LinearLayout l = new LinearLayout(ctx);
l.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams params = new LayoutParams(100, 300);
l.setLayoutParams(params);
TextView city = new TextView(ctx);
city.setText("Supermarket: " + geoName.getName() + "");
city.setMaxHeight(100);
city.setTypeface(Typeface.DEFAULT, Typeface.NORMAL);
city.setGravity(Gravity.CENTER);
//city.setTextColor(ctx.getResources().getColor(R.color.white));
TextView orientation = new TextView(ctx);
//orientation.setText("Orientation : " + geoName.lat + " || " + geoName.lng);
orientation.setMaxHeight(100);
orientation.setTypeface(Typeface.DEFAULT, Typeface.NORMAL);
orientation.setGravity(Gravity.CENTER);
l.addView(city);
l.addView(orientation);
return l;
}
}
}
Managed to sort this.
For anyone else who is using a JSON layered response. Make sure you access you lat and long values from the correct GeoPoint object classes.
for simplicity you can use Google Maps Android API v2 with recent updates your task becomes simpler.
https://developers.google.com/maps/documentation/android/start
You just need to create GoogleMap object and add appropriate listeners.