Parcelable object with enums in Android - android

In an android project i have a class with these fields:
public class TransactionHistoryDetail1 implements Parcelable, DatabaseEnabled{
private long id;
private static final String TABLE_NAME = "TransactionHistoryDetail";
private static final String EMPTY_STRING = "";
#XmlElement(name = "TxUd", required = true)
private String TxUd;
#XmlElement(name = "TxLclDtTm", required = true)
private Date TxLclDtTm;
#XmlElement(name = "CcyCd")
private CurrencyCode CcyCd;
#XmlElement(name = "Amt")
private BigDecimal Amt;
#XmlElement(name = "PmttpCd", required = true)
private PaymentTypeCode PmttpCd;
#XmlElement(name = "OprtnCd", required = true)
private OperationCode OprtnCd;
#XmlElement(name = "AppLabltpCd", required = true)
private AppLabelTypeCode AppLabltpCd;
#XmlElement(name = "PdSrl")
private String PdSrl;
#XmlElement(name = "PdBrndDsc")
private String PdBrndDsc;
#XmlElement(name = "UsrEml")
private String UsrEml;
#XmlElement(name = "CstmrAdr")
private String CstmrAdr;
#XmlElement(name = "TxDtlDsc")
private String TxDtlDsc;
#XmlElement(name = "TxRltdInd")
private boolean TxRltdInd;
#XmlElement(name = "TxSttsCd", required = true)
private TransactionStatusCode TxSttsCd;
#XmlElement(name = "UpdtDt", required = true)
private Date UpdtDt;
...
}
Im trying to Write and Read objects of this class as Parcelables but im not sure how to write and read the enums.
My writeToParcel method looks like this:
#Override
public void writeToParcel(Parcel dest, int flags) {
SimpleDateFormat sdf = new SimpleDateFormat(JsonBuilder.DateFormat);
dest.writeLong(id);
dest.writeDouble(Amt.doubleValue());
dest.writeString(PdSrl);
dest.writeString(PdBrndDsc);
dest.writeString(TxUd);
dest.writeString(PdSrl);
dest.writeString(UsrEml);
dest.writeString(CstmrAdr);
dest.writeString(TxDtlDsc);
dest.writeString((CcyCd == null) ? "" : CcyCd.name());
dest.writeString((PmttpCd == null) ? "" : PmttpCd.name());
dest.writeString((OprtnCd == null) ? "" : OprtnCd.name());
dest.writeString((AppLabltpCd == null) ? "" : AppLabltpCd.name());
dest.writeString(sdf.format(TxLclDtTm));
dest.writeString(sdf.format(UpdtDt));
dest.writeByte((byte) (TxRltdInd ? 1 : 0));
}
and my Constructor with Parcel looks like this
private TransactionHistoryDetail1(Parcel in) {
SimpleDateFormat sdf = new SimpleDateFormat(JsonBuilder.DateFormat);
try {
TxLclDtTm = sdf.parse(in.readString());
UpdtDt = sdf.parse(in.readString());
} catch (ParseException e) {
e.printStackTrace();
}
id = in.readLong();
TxUd = in.readString();
PdSrl = in.readString();
PdBrndDsc = in.readString();
UsrEml = in.readString();
CstmrAdr = in.readString();
TxDtlDsc = in.readString();
TxRltdInd = in.readByte() != 0;
Amt = new BigDecimal(in.readDouble());
CcyCd = CurrencyCode.valueOf(in.readString());
PmttpCd = PaymentTypeCode.valueOf(in.readString());
OprtnCd = OperationCode.valueOf(in.readString());
AppLabltpCd = AppLabelTypeCode.valueOf(in.readString());
TxSttsCd = TransactionStatusCode.fromValue(in.readString());
}
The writeToParcel i belive its working well, but the constructor is crashing at the "CcyCd" line.
My CurrencyCode class is a enum, (so are PaymentTypeCode,OperationCode and AppLabelTypeCode) that looks like this:
#XmlType(name = "CurrencyCode")
#XmlEnum
public enum CurrencyCode {
EUR;
public String value() {
return name();
}
public static CurrencyCode fromValue(String v) {
return valueOf(v);
}
}
Is there another way to deal with enums in Parcelables?
The exception i get is this:
java.lang.RuntimeException: Unable to start activity ComponentInfo{package/package.activities.ChildActivity}: java.lang.IllegalArgumentException: No enum constant package.data.apiClasses.CurrencyCode.��EUR����CASH������CLS����APP����2017-04-10T09:07:52.525Z������2017-04-10T09:07:52.528Z����������CHILD_12345������?

First of all you should read from the Parcel in the exact order that you wrote to it. Since Parcel just write and read data in order instead of actually serializing data you have to keep read and write in order else you gonna read wrong values and getting error..
It is the best if you treat Enum as int, write them to parcel like this:
dest.writeInt(this.CcyCd == null ? -1 : this.CurrencyCode.ordinal());
and read them like this:
int tmpCurrencyCode = in.readInt();
this.CcyCd = tmpCurrencyCode == -1 ? null : CurrencyCode.values()[tmpMState];
P.S: This code check for null values too ^^

I had problem parsing Nullable Enum. If this is your case, look up here
https://stackoverflow.com/a/58337932/5980046

Related

Realm Android aggregating on child object

public class ChallengeDB extends RealmObject {
#PrimaryKey
private int challengeId;
private String targetType;
private RealmList<ChallengeTargetDB> ChallengeTargetDB;
private boolean isTargetPerParticipant;
}
public class ChallengeTargetDB extends RealmObject {
#PrimaryKey
private int targetId;
private String targetName;
private String description;
private long targetValue;
private int targetStep;
private boolean isPassed;
}
I have these 2 tables linked as following and I need to get max "targetValue" value of a specific challengeId.
The query for which I have written is as follows
RealmResults<ChallengeDB> challengeDBs = realm.where(ChallengeDB.class)
.equalTo(WorkoutCashConstants.COLUMN_CHALLENGE_ID, challengeDB.getChallengeId()).findAll();
long max = challengeDBs.max("ChallengeTargetDB.targetValue").longValue();
I get up ending exception as follows
java.lang.IllegalArgumentException: Aggregates on child object fields are not supported: ChallengeTargetDB.targetValue
With Realm 3.5.0+
public class ChallengeTargetDB extends RealmObject {
#PrimaryKey
private int targetId;
private String targetName;
private String description;
private long targetValue;
private int targetStep;
private boolean isPassed;
#LinkingObjects("ChallengeTargetDb")
private final RealmResults<ChallengeDb> targetOfChallenge = null;
}
Then
RealmResults<ChallengeTargetDB> challengeDBs = realm.where(ChallengeTargetDB.class)
.equalTo("targetOfChallenge." + WorkoutCashConstants.COLUMN_CHALLENGE_ID, challengeDB.getChallengeId()).findAll();
long max = challengeDBs.max("targetValue").longValue();
As the exception says, aggregates on linked object's field are not supported. You need to compute it by yourself.
Try this:
Number max = null;
for (ChallengeDB item : challengeDBs) {
if (max == null) {
max = item.ChallengeTargetDB.max();
} else {
Number tmp = item.ChallengeTargetDB.max();
max = max.longValue() > tmp.longValue() ? max : tmp;
}
}

Add new object into relationship in Realm

I have a problem with Realm that ill my brain :(
The problem is when I try to add a new object into RealmList. The object is inserted but it is not linked with the relationship.
Now, my database has multiple relationships:
User 1-->M Trip 1-->M Vehicle 1-->M
And then:
Vehicle 1-->M VehicleInfo
VehicleInfo 1-->M Temperature
The problem reside when I try to insert a new object into Temperature class.
My classes:
Vehicle:
#RealmClass
public class Vehicle extends RealmObject
{
private String state; //R: Reservado | P: Listo para embarcar | E: Embarcado | P1: Pdte. confirmar medidas | P2: Tª no válida | R1: No embarca
private String locata = "";
private String customer = "";
private String customerCode = "";
private String originPort = ""; //Origin port
private String destinyPort = ""; //Destiny port
private int fp; //Method pay (0 = CASH | 1 = CREDIT)
//Relationship
private Trip trip; //Inverse
private RealmList<VehicleInfo> vehicleInfo = new RealmList<>(); //One-to-many
private RealmList<Observation> observations = new RealmList<>();; //One-to-many
.....
}
VehicleInfo:
#RealmClass
public class VehicleInfo extends RealmObject {
private String sv; //Vehicle type
private String licensePlate = ""; //License
private String seal = ""; //Seal
private String temperature = ""; //Temperature control
private String iv = ""; //Ida/Vuelta
private String commodityCode = "";
private int tara = 0; //TARA
private int packages = 0; //Bultos
private int weight = 0;
private double length = 0.0; //Meters
private boolean flagFT; //Flag Technical data
private boolean flagDua;
private boolean flagManifest;
private boolean flagTransport;
private boolean flagDangerCommodity;
//Relationship
private RealmList<Temperature> temperatures = new RealmList<>(); //One-to-many
....
}
My code to add new Temperature:
Temperature temp = new Temperature();
temp.setDate(appCommon.getCurrentTimeOrDate("DATE"));
temp.setTime(appCommon.getCurrentTimeOrDate("TIME"));
temp.setValue(Double.parseDouble(etTemp.getText().toString()));
VehicleInfoPersistence.updateVehicleInfoTemperature(realm, vehicle.getLocata(), selectedUnitPosition, temp);
updateRecycler(tempRecyclerAdapter, temp);
Method to find and persist in Realm:
public static VehicleInfo findVehicleInfoFromLocata(Realm realm, String locata, int position) {
RealmQuery<Vehicle> query = realm.where(Vehicle.class).equalTo("locata", locata);
Vehicle realmVehicle = query.findFirst();
return realmVehicle.getVehicleInfo().get(position);
}
public static void updateVehicleInfoTemperature(Realm realm, String locata, int position, Temperature temperature) {
Vehicle vehicle = VehiclePersistence.findVehicleFromLocata(realm, locata);
realm.beginTransaction();
Temperature realmTemp = realm.copyToRealm(temperature);
vehicle.getVehicleInfo().get(position).getTemperatures().add(realmTemp);
realm.commitTransaction();
}
How I said, object is created in database but it is not linked with the vehicle-->vehicleInfo-->Temperature.
What's wrong in my code??
Thanks in advance :)
I resolved my problem :)
By some motive, realm fails when I try to add new object into sub-array of a RealmClass.
To solve this issue I've created an intermediate object and then I've added the object to this intermediate object.
public static void updateVehicleInfoTemperature(Realm realm, String locata, int position, Temperature temperature) {
Vehicle vehicle = VehiclePersistence.findVehicleFromLocata(realm, locata);
realm.beginTransaction();
VehicleInfo vInfo = vehicle.getVehicleInfo().get(position);
VehicleInfo realmVehicleInfo = realm.copyToRealm(vInfo);
Temperature realmTemp = realm.copyToRealm(temperature);
realmVehicleInfo.getTemperatures().add(realmTemp);
realm.commitTransaction();
}
I hope to help someone :)

Android read contacts(and details) seems very slow

I use the following code to read each contacts along with their details.
private static final String[] PROJECTION =
{
Data._ID,
Data.MIMETYPE,
Data.DATA1,
Data.DATA2,
Data.DATA3,
Data.DATA4,
Data.DATA5,
Data.DATA6,
Data.DATA7,
Data.DATA8,
Data.DATA9,
Data.DATA10,
Data.DATA11,
Data.DATA12,
Data.DATA13,
Data.DATA14,
Data.DATA15
};
private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
private String[] mSelectionArgs = { "" };
private static final String SORT_ORDER = Data.MIMETYPE;
private static final int MIME_TYPE_INDEX = 1;
private static final int DISPLAY_NAME_INDEX = 3;//data2
private static final int GIVEN_NAME_INDEX = 3;//data2
private static final int FAMILY_NAME_INDEX = 4;//data3
private static final int MIDDLE_NAME_INDEX = 6;//data5
private static final int ORGANIZATION_INDEX = 2;//data2
private static final int PHONE_TYPE_INDEX = 3;//data2
private static final int PHONE_LABEL_INDEX = 4;//data3
private static final int PHONE_NUMBER_INDEX = 2;//data1
private static final int EMAIL_TYPE_INDEX = 3;//data2
private static final int EMAIL_LABEL_INDEX = 4;//data1
private static final int EMAIL_INDEX = 2;//data1
private byte[] createJsonData(ArrayList<String> selected) throws JSONException, IOException{
Log.d("SynchContactActivity", "Time 1: " + java.text.DateFormat.getDateTimeInstance().format(Calendar.getInstance().getTime()));
int current = 0;
final String messagePrep = getResources().getString(R.string.progress_message_prep);
final String messageCompress = getResources().getString(R.string.progress_message_compress);
final String messageUpload = getResources().getString(R.string.progress_message_upload);
if(selected == null ){
selected = getContacts();
}
final int count = selected.size();
mHandler.post(new Runnable() {
#Override
public void run() {
if(mProgressDialog != null){
mProgressDialog.setMax(count);
mProgressDialog.setMessage(messagePrep);
}
}
});
updateProgress(current);
JSONObject root = new JSONObject();
JSONArray contactsArray = new JSONArray();
JSONObject contactJSON, phoneJSON, emailJSON;
JSONArray phonesArray,emailsArray;
String name, lastName, middleName,organization;
for (String key : selected) {
contactJSON = new JSONObject();
phonesArray = new JSONArray();
emailsArray = new JSONArray();
mSelectionArgs[0] = key;
//Cursor details = managedQuery(Data.CONTENT_URI, PROJECTION, SELECTION, mSelectionArgs, SORT_ORDER);
Cursor details = getApplicationContext().getContentResolver().query(Data.CONTENT_URI, PROJECTION, SELECTION, mSelectionArgs, SORT_ORDER);
//initialize null variables
name = null;
lastName = null;
middleName = null;
organization = null;
while(details.moveToNext()){
String mimeType = details.getString(MIME_TYPE_INDEX);
if(mimeType.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)){
name = details.getString(GIVEN_NAME_INDEX);
lastName = details.getString(FAMILY_NAME_INDEX);
middleName = details.getString(MIDDLE_NAME_INDEX);
}
else if(mimeType.equals(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE)){
organization = details.getString(ORGANIZATION_INDEX);
}
else if(mimeType.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)){
phoneJSON = new JSONObject();
String phoneNumber = details.getString(PHONE_NUMBER_INDEX);
int type = details.getInt(PHONE_TYPE_INDEX);
String typeLabel = phoneTypeMap.get(String.valueOf(type));
if (typeLabel == null) {
typeLabel = details.getString(PHONE_LABEL_INDEX);
}
phoneJSON.put("ptype", typeLabel);
phoneJSON.put("number", phoneNumber);
phonesArray.put(phoneJSON);
}
else if(mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)){
emailJSON = new JSONObject();
String email = details.getString(EMAIL_INDEX);
int type = details.getInt(EMAIL_TYPE_INDEX);
String typeLabel = emailTypeMap.get(String.valueOf(type));
if (typeLabel == null) {
typeLabel = details.getString(EMAIL_LABEL_INDEX);
}
emailJSON.put("etype", typeLabel);
emailJSON.put("address",email);
emailsArray.put(emailJSON);
}
}
contactJSON.put("firstname", name==null?"null":name);
contactJSON.put("middlename", middleName==null?"null":middleName);
contactJSON.put("lastname", lastName==null?"null":lastName);
contactJSON.put("organization", organization==null?"null":organization);
contactJSON.put("phones", phonesArray);
contactJSON.put("emails", emailsArray);
contactsArray.put(contactJSON);
details.close();
++current;
updateProgress(current);
}
root.put("contacts", contactsArray);
Log.d("SynchContactActivity", "Time 1: " + java.text.DateFormat.getDateTimeInstance().format(Calendar.getInstance().getTime()));
mHandler.post(new Runnable() {
#Override
public void run() {
if(mProgressDialog != null){
mProgressDialog.setMessage(messageCompress);
}
}
});
// to compress
String json_doc = root.toString();
byte[] compressed = compress(json_doc);
mHandler.post(new Runnable() {
#Override
public void run() {
if(mProgressDialog != null){
mProgressDialog.setMessage(messageUpload);
}
}
});
return compressed;
}
This code is too slow - that reads 3-4 contacts per second on average. Is this normal or can be optimized?
I think projection might be a good candidate to be optimized but I'm not sure.
Thanks in advance.
It's hard for me to tell exactly what you're trying to do, but it looks like you're trying to read data from the Contacts Provider and send it to a server using JSON. I suggest you look at the ContactsContract.RawContacts.Entity table, which contains all the data you're probably looking for without the mess of trying to figure out the MIME type of the DATA row you've just retrieved. You're certainly slowing down your app by getting the entire contents of the DATA row.
In addition, you should use a SyncAdapter to do this work. See Transferring Data Using Sync Adapters
Reading contacts can be made in 2-5 seconds. See the example app here
Source code attached

Android How to make self-relationship in ORMLite?

I have the following object that is a self-relationship, as follows:
#DatabaseTable(tableName = "categoria")
public class Categoria implements Serializable {
#DatabaseField(generatedId = true)
public int id;
#DatabaseField(canBeNull = true, foreign = true)
public Categoria pai;
#DatabaseField(canBeNull = false, width = 50, unique = true)
public String descricao;
public Categoria() { }
}
When I do this:
Categoria cat = new Categoria();
cat.pai = null;
cat.descricao = "Comidas";
categoryDao.create(cat); //should be id = 1.
Categoria sub_cat = new Categoria();
sub_cat.pai = cat;
sub_cat.descricao = "Bebidas";
categoryDao.create(sub_cat); // should be id = 2.
So my object Categoria is null when do this:
Categoria sub_cat = categoryDao.queryForId(2);
sub_cat.descricao // is ok, return "Bebidas".
but
sub_cat.pai // is null when supposed to be sub_cat.pai.id = 1.
What I'm doing wrong?

Android Parcelable Issue

I had to pass a dataobject from one activity to another. The best way to do this is uing Parcelable.
The dataobject had some fields with setter and getter methods. After setting some fields and passing the object to another activity, wha tI observed is that the field values got interchanged to other field values.
The order of fields for writing to parcel and reading from parcel is the same.
public void writeToParcel(Parcel out, int flags) {
out.writeInt(id);
out.writeString(appNo);
out.writeString(this.policyNo);
out.writeInt((int)this.AppRcptDt.getTime());
out.writeString(this.currentStatus);
out.writeString(this.productCd);
out.writeDouble(this.sumAssured);
out.writeDouble(this.modalPremium);
out.writeDouble(this.annualPremium);
out.writeString(this.paymentMode);
out.writeString(this.branchCd);
out.writeString(this.branchName);
out.writeString(this.insuredName);
out.writeString(this.auraStatus);
out.writeString(this.ownerName);
out.writeString(this.agentCd);
out.writeString(this.billingMode);
}
private ApplicationTrackerDO(Parcel in) {
id=in.readInt();
this.appNo = in.readString();
this.policyNo = in.readString();
this.AppRcptDt = new Date(in.readLong());
this.currentStatus = in.readString();
this.productCd = in.readString();
this.sumAssured = in.readDouble();
this.modalPremium = in.readDouble();
this.annualPremium = in.readDouble();
this.paymentMode = in.readString();
this.branchCd = in.readString();
this.branchName = in.readString();
this.insuredName = in.readString();
this.auraStatus = in.readString();
this.ownerName = in.readString();
this.agentCd = in.readString();
this.billingMode = in.readString();
}
It is not the order but the data type that is not the same, from the first 4 lines you write int, string, string, int then you read int, string, string, long. I didn't check any further, you must match both order and datatype of read and write operations.
You are writing int
out.writeInt((int)this.AppRcptDt.getTime());
But reading long
this.AppRcptDt = new Date(in.readLong());
my solution to provide an date object with null
#Override
public void writeToParcel ( Parcel dest, int flags ) {
dest.writeInt((birthday != null) ? 1 : 0); // is birthday set?
dest.writeLong((birthday != null) ? birthday.getTime() : 0);
}
public void readFromParcel ( Parcel in ) {
if(in.readInt() == 1) {
birthday = new Date(in.readLong());
} else {
in.readLong(); // ignore stored value
birthday = null;
}
}
java.util.Date implements Serializable:
public class Date implements Serializable, Cloneable, Comparable<Date> {
private static final long serialVersionUID = 7523967970034938905L;
// Used by parse()
private static final int CREATION_YEAR = new Date().getYear();
private transient long milliseconds;
Use:
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeSerializable(myDateField);
}
private MyClass(Parcel in) {
myDateField = (Date)in.readSerializable();
}

Categories

Resources