Add new object into relationship in Realm - android

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 :)

Related

Livedata and many observers and instances

It's rather, a question than a problem. I have two entities with a String field called Type. Both have over twenty kinds of type: "Bills", "Transport", "Awards" etc. I want to put all them to the charts, and this is creating little problem because it totally creates over 60((!) 60 because I want to take data by date. For example day, month etc.)instances in ViewModel constructor and over 60 observers in fragment. So my question is:
There is some problem to make it more sexy for code (orderliness), performance of app, or I should not care about it?
One of entity (they are almost same):
#Entity(tableName = "expense_table")
public class Expense {
#PrimaryKey(autoGenerate = true)
private int expenseId;
private String note;
private Double value;
private String type;
private Long dateLong = System.currentTimeMillis();
private String date = new SimpleDateFormat("MM/yyyy").format(new Date(dateLong));
private static Calendar cal = Calendar.getInstance();
private int month = cal.get(Calendar.MONTH) + 1;
private int day = cal.get(Calendar.DAY_OF_MONTH);
private int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
private String weekDay = new DateFormatSymbols().getWeekdays()[dayOfWeek];
Query in Dao:
#Query("SELECT SUM(value) FROM expense_table WHERE type = :type")
MutableLiveData<List<Double>> getTotalType(String type);
One of Repository(As you can see there is many instances now, co code it will more complicated):
public class ExpenseRepository {
private ExpenseDao expenseDao;
private LiveData<List<Expense>> allExpensesDay;
private LiveData<List<Expense>> allExpensesMonth;
private LiveData<List<Expense>> allExpenses;
private LiveData<Double> totalValue;
private LiveData<Double> totalValueDay;
private LiveData<Double> totalValueMonth;
private MutableLiveData<List<Double>> totalType;
private String type = "Bills";
public ExpenseRepository(Application application) {
ExpenseIncomeDatabase database = ExpenseIncomeDatabase.getInstance(application);
expenseDao = database.expenseDao();
allExpenses = expenseDao.getAllExpenses();
allExpensesDay = expenseDao.getExpensesDay();
allExpensesMonth = expenseDao.getExpensesMonth();
totalValueDay = expenseDao.getTotalValueDay();
totalValueMonth = expenseDao.getTotalValueMonth();
totalValue = expenseDao.getTotalValue();
totalType = expenseDao.getTotalType(type);
One of Repository:
private LiveData<Double> totalExpenseValue;
private LiveData<Double> totalIncomeValue;
private MutableLiveData<List<Double>> totalType;
public TotalStatsViewModel(#NonNull Application application) {
super(application);
ExpenseRepository expenseRepository = new ExpenseRepository(application);
ExpenseRepository expenseChartsRepository = new ExpenseRepository(application);
IncomeRepository incomeRepository = new IncomeRepository(application);
totalExpenseValue = expenseChartsRepository.getTotalValue();
totalIncomeValue = incomeRepository.getTotalValue();
totalType = expenseRepository.getTotalType();
}
public LiveData<Double> getTotalExpenseValue() {
return totalExpenseValue;
}
public LiveData<Double> getTotalIncomeValue() {
return totalIncomeValue;
}
public LiveData<List<Double>> getTotalType() {
return totalType;
}
As you can see there is a lot of code now. In future it will be much more. Maybe it stupid question, but I just care about orderliness and performance.

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;
}
}

Parcelable object with enums in 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

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

Json parsing with gson not working

This is my JSON string:
[{"BranchID":1,"SecurityCode1":13,"SecurityCode2":14,"PrintHeight":10,"PrintWidth":10,"Active":true}]
This is Code I am using to parse the JSON:
Type t = new TypeToken<List<Setting>>() {
}.getClass();
String json = ServiceHelper.getJSON(response);
List<Setting> list = (List<Setting>) gson.fromJson(json, t);
//list is null which it souldnt
This is the Setting class, Entity is ORMDroid entity class:
public class Setting extends Entity {
#Column(name = "id", primaryKey = true)
#SerializedName("BranchID")
public int BranchID;
public int securityCodeLPK;
public int securityCodeSDK;
#SerializedName("PrintHeight")
public int PrintHeight;
#SerializedName("PrintWidth")
public int PrintWidth;
#SerializedName("Active")
public String Active;
#SerializedName("SecurityCode1")
public String SecurityCode1;
#SerializedName("SecurityCode2")
public String SecurityCode2;
public Setting(int BranchID, int height, int width, String active, String securityCode1, String securityCode2) {
super();
BranchID = BranchID;
PrintHeight = height;
PrintWidth = width;
Active = active;
SecurityCode1 = securityCode1;
SecurityCode2 = securityCode2;
}
public Setting () {
super();
}
}
It seems to be OK, but list after gson.FromJson is null. What's wrong with this code?
Please help
You should use getType() instead of getClass().
Type t = new TypeToken<List<Setting>>() {}.getType();

Categories

Resources