Android Realm Query - android
I love Realm, the ease of use is so good, but sometimes i struggle in finding a way of doing a specific query.
Supposing i would want to query a a table(RealmObject) and get all the results matching a column name (variable), and i want that to be unique for each id, and also the latest record for a given variable name. For instance:
realm.where(RealmMessages.thisClass)
.distinctAsync(RealmMessages.FROM_TABLE)
.addChangeListener(listener)
This will get me all the messages filtered by each unique 'from' field. But this won't work, as i want the latest by 'date' and unique by 'from'.
How would i approach to do this with realm»? Without manually querying all the objects and tho defeating the purpose of 'lazy objects' ?
You can Sort your results from the Realm using the
"realmQuery.findAll(... Sort[] ... Fields[] ...)" method. Also you can
do a multi-query by use groups and "or".
You can also use the "distinct()" method to get the "id" repeated only 1 time
I don't know if this is useful for you, but I did a class and some methods that let me do "fast & automatic queries on Realm".
These are the files:
Put this in the same package of your "Realm Utils Class" (like a "RealmHelper" in which you instantiate your "Realm" instance and where you have all your methods which queries the realm ecc...
public class RealmQueriesData<T> {
private List<QueryData> mQueries;
private QueryFind mFind;
private Class<T> mResultsClass;
/* START Factory Methods */
public static <T> RealmQueriesData<T> newInstanceFindEqualsTo(Class<T> resultsClass, String fieldName, Object value, Case mCase){
return new RealmQueriesData<>(
resultsClass,
QueryData.QUERY_EQUAL, fieldName, value, QueryFind.FIND_ALL, mCase
);
}
public static <T> RealmQueriesData<T> newInstanceCountEqualsTo(Class<T> resultsClass, String fieldName, Object value, Case mCase){
return new RealmQueriesData<>(
resultsClass,
new int[]{QueryData.QUERY_EQUAL, QueryData.QUERY_COUNT}, new String[]{fieldName, null},
new Object[]{value, null}, new Case[]{mCase, null}
);
}
public static <T> RealmQueriesData<T> newInstanceFindNotIn(Class<T> resultsClass, String fieldName, Object[] valuesIn){
return new RealmQueriesData<>(
resultsClass,
QueryData.QUERY_NOT_EQUAL, fieldName, valuesIn, QueryFind.FIND_ALL
);
}
/* END Factory Methods */
/* START Constructor Methods */
public RealmQueriesData(Class<T> resultsClass,
int[] queryTypes, String[] fieldNames, Object[] queryValues, Case[] mCases){
this.mResultsClass = resultsClass;
this.mQueries = new ArrayList<>();
for(int i = 0; i < queryTypes.length; i++){
int type = queryTypes[i];
String field = fieldNames[i];
Object value = queryValues[i];
Case mCase = mCases[i];
this.mQueries.add(
new QueryData(type, field, value, mCase)
);
}
}
public RealmQueriesData(Class<T> resultsClass, int queryType, String fieldName, Object[] queryValuesIn, int findType){
this.mResultsClass = resultsClass;
this.mQueries = new ArrayList<>();
this.mQueries.add(
new QueryData(queryType, fieldName, queryValuesIn)
);
this.mFind = new QueryFind(findType);
}
public RealmQueriesData(Class<T> resultsClass, int queryType, String fieldName, Object queryValue, int findType, Case mCase){
this.mResultsClass = resultsClass;
this.mQueries = new ArrayList<>();
this.mQueries.add(
new QueryData(queryType, fieldName, queryValue, mCase)
);
this.mFind = new QueryFind(findType);
}
public RealmQueriesData(Class<T> resultsClass){
this.mResultsClass = resultsClass;
this.mFind = new QueryFind();
}
/* END Constructor Methods */
/* START Getter & Setter Methods */
public List<QueryData> getQueries() {
return mQueries;
}
public void setQueries(List<QueryData> queries) {
this.mQueries = queries;
}
public QueryFind getFind() {
return mFind;
}
public void setFind(QueryFind find) {
this.mFind = find;
}
public Class<T> getResultsClass(){
return mResultsClass;
}
public void setResultsClass(Class<T> resultClass){
this.mResultsClass = resultClass;
}
/* END Getter & Setter Methods */
/* START Utils Methods */
public void addQuery(QueryData q){
mQueries.add(q);
}
public void addQuery(int type, String fieldName, Object value, Case mCase){
mQueries.add(new QueryData(type, fieldName, value, mCase));
}
public void addQuery(int type, String fieldName, Object value){
mQueries.add(new QueryData(type, fieldName, value));
}
public void addQuery(int type, String fieldName, Object value, Object valueTo){
mQueries.add(new QueryData(type, fieldName, value, valueTo));
}
public void addQuery(int type, String fieldName, Object[] valueIn){
mQueries.add(new QueryData(type, fieldName, valueIn));
}
public void addQuery(int type, String fieldName){
mQueries.add(new QueryData(type, fieldName));
}
/* END Utils Methods */
#Override
public String toString() {
return "RealmQueriesData{" +
"mQueries=" + mQueries.toString() +
", mFind=" + mFind.toString() +
", mResultsClass=" + mResultsClass.toString() +
'}';
}
}
class QueryData {
// QueryData Tipes
public static final int QUERY_NEGATION = 0x0;
public static final int QUERY_OR = 0x1;
public static final int QUERY_BEGIN_GROUP = 0x2;
public static final int QUERY_END_GROUP = 0x3;
public static final int QUERY_AVERAGE = 0x4;
public static final int QUERY_COUNT = 0x5;
public static final int QUERY_DISTINCT = 0x6;
public static final int QUERY_MAX = 0x7;
public static final int QUERY_MAX_DATE = 0x8;
public static final int QUERY_MIN = 0x9;
public static final int QUERY_MIN_DATE = 0xA;
public static final int QUERY_SUM = 0xB;
public static final int QUERY_EQUAL = 0xC;
public static final int QUERY_NOT_EQUAL = 0xD;
public static final int QUERY_CONTAINS = 0xE;
public static final int QUERY_IS_NULL = 0xF;
public static final int QUERY_NOT_NULL = 0x10;
public static final int QUERY_LESS = 0x11;
public static final int QUERY_GREATER = 0x12;
public static final int QUERY_GREATER_EQUAL = 0x13;
public static final int QUERY_LESS_EQUAL = 0x14;
public static final int QUERY_BETWEEN = 0x15;
public static final int QUERY_LIKE = 0x16;
public static final int QUERY_BEGINS = 0x17;
public static final int QUERY_ENDS = 0x18;
public static final int QUERY_IN = 0x19;
public static final int QUERY_IS_EMPTY = 0x1A;
public static final int QUERY_NOT_EMPTY = 0x1B;
int mType;
String mFieldName;
Object mValue;
Object mValueTo;
Object[] mValueIn;
Case mCase;
/* START Constructor Methods */
public QueryData(int type, String fieldName, Object value, Case mCase){
this.mType = type;
this.mFieldName = fieldName;
this.mValue = value;
this.mCase = mCase;
}
public QueryData(int type, String fieldName, Object value){
this.mType = type;
this.mFieldName = fieldName;
this.mValue = value;
}
public QueryData(int type, String fieldName, Object valueFrom, Object valueTo){
this.mType = type;
this.mFieldName = fieldName;
this.mValue = valueFrom;
this.mValueTo = valueTo;
}
public QueryData(int type, String fieldName, Object[] valueIn){
this.mType = type;
this.mFieldName = fieldName;
this.mValueIn = valueIn;
}
public QueryData(int type, String fieldName){
this.mType = type;
this.mFieldName = fieldName;
}
/* END Constructor Methods */
/* START Getter & Setter Methods */
public int getType() {
return mType;
}
public void setType(int type) {
this.mType = type;
}
public String getFieldName() {
return mFieldName;
}
public void setFieldName(String fieldName) {
this.mFieldName = fieldName;
}
public Object getValue(){
return mValue;
}
public void setValue(Object value){
this.mValue = value;
}
public Object getValueTo() {
return mValueTo;
}
public void setValueTo(Object valueTo) {
this.mValueTo = valueTo;
}
public Object[] getValueIn() {
return mValueIn;
}
public void setValueIn(Object[] valueIn) {
this.mValueIn = valueIn;
}
public Case getCase() {
return mCase;
}
public void setCase(Case mCase) {
this.mCase = mCase;
}
/* END Getter & Setter Methods */
#Override
public String toString() {
return "QueryData{" +
"mType=" + mType +
", mFieldName='" + mFieldName + '\'' +
", mValue=" + mValue +
", mValueTo=" + mValueTo +
", mValueIn=" + Arrays.toString(mValueIn) +
", mCase=" + mCase +
'}';
}
}
class QueryFind {
// QueryFind Types
public static final int FIND_ALL = 0x1;
public static final int FIND_ALL_SORTED_FIELD = 0x2;
public static final int FIND_ALL_SORTED_FIELD_SORT = 0x3;
public static final int FIND_ALL_SORTED_ARRAYS = 0x4;
int mType;
List<String> mSortFields;
List<Sort> mSorts;
boolean mRetAll;
/* START Constructor Methods */
public QueryFind(int type, List<String> sortFields, List<Sort> sorts, boolean retAll){
this.mType = type;
this.mSortFields = sortFields != null ? sortFields : new ArrayList<String>();
this.mSorts = sorts != null ? sorts : new ArrayList<Sort>();
this.mRetAll = retAll;
}
public QueryFind(int type, String sortField, Sort sort, boolean retAll){
this.mType = type;
this.mSortFields = new ArrayList<>();
this.mSortFields.add(sortField);
this.mSorts = new ArrayList<>();
this.mSorts.add(sort);
this.mRetAll = retAll;
}
public QueryFind(int type){
this.mType = type;
}
public QueryFind(){
this.mRetAll = true;
}
/* END Constructor Methods */
/* START Getter & Setter Methods */
public int getType() {
return mType;
}
public void setType(int type) {
this.mType = type;
}
public List<String> getSortFields() {
return mSortFields;
}
public void setSortFields(List<String> sortFields) {
this.mSortFields = sortFields;
}
public List<Sort> getSorts() {
return mSorts;
}
public void setSorts(List<Sort> sorts) {
this.mSorts = sorts;
}
public boolean getRetAll() {
return mRetAll;
}
public void setRetAll(boolean retAll) {
this.mRetAll = retAll;
}
/* END Getter & Setter Methods */
/* START Utils Methods */
public void addSort(String sortField, Sort sort){
mSortFields.add(sortField);
mSorts.add(sort);
}
/* END Utils Methods */
#Override
public String toString() {
return "QueryFind{" +
"mType=" + mType +
", mSortFields=" + mSortFields +
", mSorts=" + mSorts +
", mRetAll=" + mRetAll +
'}';
}
}
Then in you "Realm Library File" (like "RealmHelper") add these methods to do the queries by passing them a "RealmQueriesData" instance:
public class RealmHelper {
protected Realm mRealm;
// Instance to have only 1 Realm open 4 every thread in your app
protected static RealmHelper mInstance;
[....]
/* START Realm Query Methods */
// Like the one below but return a List of objects and not a RealmResults
public <T> List<T> doQueryOnRealmAsList(RealmQueriesData filters){
return (List<T>) toList(doQueryOnRealm(filters));
}
// With this you can do every type of query which return values with
// "realmQuery.findAll()" methods
public <T> RealmResults<T> doQueryOnRealm(RealmQueriesData filters){
RealmResults<T> ret = null;
if(filters != null){
List<QueryData> queries = filters.getQueries();
QueryFind find = filters.getFind();
if(queries != null && queries.size() > 0) {
RealmQuery<T> realmQuery = mRealm.where(filters.getResultsClass());
for(QueryData query : queries){
if(query.getType() == QueryData.QUERY_DISTINCT){
ret = realmQuery.distinct(query.getFieldName());
} else {
realmQuery = getRealmQueryFromQueryData(realmQuery, query);
}
}
if(find != null) {
ret = getRealmQueryResults(realmQuery, find);
}
} else {
if(find != null && find.getRetAll()){
ret = (RealmResults<T>) findAllObject(filters.getResultsClass());
}
}
}
return ret;
}
// With this you can do every type of query which return a single value
// with "realmQuery.findFirst()" method.
public <T> T doQueryOnRealmSingleResult(RealmQueriesData filters){
T ret = null;
if(filters != null){
List<QueryData> queries = filters.getQueries();
QueryFind find = filters.getFind();
if(queries != null && queries.size() > 0){
RealmQuery<T> realmQuery = mRealm.where(filters.getResultsClass());
for(QueryData query : queries){
realmQuery = getRealmQueryFromQueryData(realmQuery, query);
}
ret = realmQuery.findFirst();
}
}
return ret;
}
// For queries whose return a Numerical value using SQL built-in functions
// like "max, count, min, average ..."
public Object doMathQueryOnRealm(RealmQueriesData filters){
Object ret = null;
if(filters != null){
List<QueryData> queries = filters.getQueries();
QueryData last = queries.get(queries.size() - 1);
queries.remove(last);
if(queries.size() > 0){
RealmQuery realmQuery = mRealm.where(filters.getResultsClass());
for(QueryData query : queries){
realmQuery = getRealmQueryFromQueryData(realmQuery, query);
}
ret = getMathQueryResult(realmQuery, last);
}
}
return ret;
}
private <T> RealmQuery<T> getRealmQueryFromQueryData(RealmQuery realmQuery, QueryData query){
String field; Object value; Case mCase;
switch(query.getType()){
case QueryData.QUERY_NEGATION:
realmQuery.not();
break;
case QueryData.QUERY_OR:
realmQuery.or();
break;
case QueryData.QUERY_BEGIN_GROUP:
realmQuery.beginGroup();
break;
case QueryData.QUERY_END_GROUP:
realmQuery.endGroup();
break;
case QueryData.QUERY_EQUAL:
field = query.getFieldName();
value = query.getValue();
if(value instanceof String){
mCase = query.getCase();
realmQuery.equalTo(field, value.toString(), mCase);
} else if(value instanceof Integer){
realmQuery.equalTo(field, (Integer) value);
} else if(value instanceof Double){
realmQuery.equalTo(field, (Double) value);
} else if(value instanceof Date){
realmQuery.equalTo(field, (Date) value);
}
break;
case QueryData.QUERY_NOT_EQUAL:
field = query.getFieldName();
value = query.getValue();
if(value instanceof String){
mCase = query.getCase();
realmQuery.notEqualTo(field, value.toString(), mCase);
} else if(value instanceof Integer){
realmQuery.notEqualTo(field, (Integer) value);
} else if(value instanceof Double){
realmQuery.notEqualTo(field, (Double) value);
} else if(value instanceof Date){
realmQuery.notEqualTo(field, (Date) value);
}
break;
case QueryData.QUERY_CONTAINS:
field = query.getFieldName();
value = query.getValue();
mCase = query.getCase();
if(mCase != null){
realmQuery.contains(field, value.toString(), mCase);
} else {
realmQuery.contains(field, value.toString());
}
break;
case QueryData.QUERY_IS_NULL:
realmQuery.isNull(query.getFieldName());
break;
case QueryData.QUERY_NOT_NULL:
realmQuery.isNotNull(query.getFieldName());
break;
case QueryData.QUERY_LESS:
field = query.getFieldName();
value = query.getValue();
if(value instanceof Integer){
realmQuery.lessThan(field, (Integer) value);
} else if(value instanceof Double){
realmQuery.lessThan(field, (Double) value);
} else if(value instanceof Date){
realmQuery.lessThan(field, (Date) value);
}
break;
case QueryData.QUERY_GREATER:
field = query.getFieldName();
value = query.getValue();
if(value instanceof Integer){
realmQuery.greaterThan(field, (Integer) value);
} else if(value instanceof Double){
realmQuery.greaterThan(field, (Double) value);
} else if(value instanceof Date){
realmQuery.greaterThan(field, (Date) value);
}
break;
case QueryData.QUERY_GREATER_EQUAL:
field = query.getFieldName();
value = query.getValue();
if(value instanceof Integer){
realmQuery.greaterThanOrEqualTo(field, (Integer) value);
} else if(value instanceof Double){
realmQuery.greaterThanOrEqualTo(field, (Double) value);
} else if(value instanceof Date){
realmQuery.greaterThanOrEqualTo(field, (Date) value);
}
break;
case QueryData.QUERY_LESS_EQUAL:
field = query.getFieldName();
value = query.getValue();
if(value instanceof Integer){
realmQuery.lessThanOrEqualTo(field, (Integer) value);
} else if(value instanceof Double){
realmQuery.lessThanOrEqualTo(field, (Double) value);
} else if(value instanceof Date){
realmQuery.lessThanOrEqualTo(field, (Date) value);
}
break;
case QueryData.QUERY_BETWEEN:
field = query.getFieldName();
value = query.getValue();
if(value instanceof Integer){
realmQuery.between(field, (Integer) value, (Integer) query.getValueTo());
} else if(value instanceof Double){
realmQuery.between(field, (Double) value, (Double) query.getValueTo());
} else if(value instanceof Date){
realmQuery.between(field, (Date) value, (Date) query.getValueTo());
}
break;
case QueryData.QUERY_LIKE:
field = query.getFieldName();
value = query.getValue();
mCase = query.getCase();
if(mCase != null){
realmQuery.like(field, value.toString(), mCase);
} else {
realmQuery.like(field, value.toString());
}
break;
case QueryData.QUERY_BEGINS:
field = query.getFieldName();
value = query.getValue();
mCase = query.getCase();
if(mCase != null){
realmQuery.beginsWith(field, value.toString(), mCase);
} else {
realmQuery.beginsWith(field, value.toString());
}
break;
case QueryData.QUERY_ENDS:
field = query.getFieldName();
value = query.getValue();
mCase = query.getCase();
if(mCase != null){
realmQuery.endsWith(field, value.toString(), mCase);
} else {
realmQuery.endsWith(field, value.toString());
}
break;
case QueryData.QUERY_IN:
field = query.getFieldName();
Object[] values = query.getValueIn();
if(values instanceof String[]){
realmQuery.in(field, (String[]) values);
} else if(values instanceof Integer[]){
realmQuery.in(field, (Integer[]) values);
} else if(values instanceof Double[]){
realmQuery.in(field, (Double[]) values);
} else if(values instanceof Date[]){
realmQuery.in(field, (Date[]) values);
}
break;
case QueryData.QUERY_IS_EMPTY:
realmQuery.isEmpty(query.getFieldName());
break;
case QueryData.QUERY_NOT_EMPTY:
realmQuery.isNotEmpty(query.getFieldName());
break;
}
return realmQuery;
}
private Object getMathQueryResult(RealmQuery realmQuery, QueryData query){
Object ret = null;
switch(query.getType()){
case QueryData.QUERY_AVERAGE:
ret = realmQuery.average(query.getFieldName());
break;
case QueryData.QUERY_COUNT:
ret = realmQuery.count();
break;
case QueryData.QUERY_MAX:
ret = realmQuery.max(query.getFieldName());
break;
case QueryData.QUERY_MAX_DATE:
ret = realmQuery.maximumDate(query.getFieldName());
break;
case QueryData.QUERY_MIN:
ret = realmQuery.min(query.getFieldName());
break;
case QueryData.QUERY_MIN_DATE:
ret = realmQuery.minimumDate(query.getFieldName());
break;
case QueryData.QUERY_SUM:
ret = realmQuery.sum(query.getFieldName());
break;
}
return ret;
}
private <T> RealmResults<T> getRealmQueryResults(RealmQuery<T> realmQuery, QueryFind find){
RealmResults<T> ret = null;
switch(find.getType()){
case QueryFind.FIND_ALL:
ret = realmQuery.findAll();
break;
case QueryFind.FIND_ALL_SORTED_FIELD:
ret = realmQuery.findAllSorted(find.getSortFields().get(0));
break;
case QueryFind.FIND_ALL_SORTED_FIELD_SORT:
ret = realmQuery.findAllSorted(find.getSortFields().get(0), find.getSorts().get(0));
break;
case QueryFind.FIND_ALL_SORTED_ARRAYS:
ret = realmQuery.findAllSorted(find.getSortFields().toArray(new String[]{}), find.getSorts().toArray(new Sort[]{}));
break;
}
return ret;
}
/* END Realm Query Methods */
}
Normally to do a query I create a new "static Factory Method" in the "RealmQueriesData" class so I can do fast queries on realm just by invoking 1 method.
// GET all RealmObjects in your Realm by searching for all
// realm objects saved that have a specific value for a specific.
// Remember that the "field name" must be the "field attribute name" in the class
// and not the "SerializedName" label value!
// (As you can see in the method which do the query the "Case SENSITIVE / INSENSITIVE"
// is used only if you query a field which is a String type, if you are querying
// another type, like Integer/Double/Date, you can set the "CASE" to "null".
RealmResults<YourRealmObject> listCount = doQueryOnRealm(
RealmQueriesData.newInstanceFindEqualsTo(
YourRealmObject.class, "yourRealmObject Field Name", <field value of YourRealmObject>, <Case (SENSITIVE / INSENSITIVE)>
)
);
// Get a list of all RealmObjects whose have a specific value in a specific field
RealmResults<YourRealmObject> listObjs = doQueryOnRealm(
RealmQueriesData.newInstanceFindEqualsTo(
YourRealmObject.class, "Field Name of YourRealmObject", <Value of YourRealmObject field>, <Case (SENSITIVE / INSENSITIVE)>
)
);
// Like a COUNT query which look for all records that have a specific value
// in a specific column.
long count = (Long) doMathQueryOnRealm(
RealmQueriesData.newInstanceCountEqualsTo(
YourRealmObject.class, "Field Name of YourRealmObject", <Value of YourRealmObject field>, <Case (SENSITIVE / INSENSITIVE)>
)
);
I had to do different methods to query the realm because the realm queries can return different values, so I had to do 3 methods for every kind of return value:
multi-values return ( RealmResults / List ) (findAll method)
single-value return ( T ) (findFirst method)
numerical-value return ( Object ) ( max, min, count, ... queries)
I hope this is Helpful for you.
Bye!
(z3r0)
Related
All queries in RoomDatabase return: The columns returned by the query does not have the fields
I have a problem with queries in RoomDatabase. I have a different queries and everything works good when I use SELECT statement with *. When I want to choose only name_product or amount it gives an error: error: The columns returned by the query does not have the fields [id,amount,to_shop,cat_food,cat_package] in com.example.myapp.PojoClass even though they are annotated as non-null or primitive. Columns returned by the query: [name_product,amount] LiveData<List<Items>> getItemNameAndAmount(int category); One of my query: #Query("SELECT name_product, amount FROM Table WHERE to_shop = 1 AND food_type = :category") LiveData<List<Item_get>> getCategoryThingsToBuy(int category); Problem is similar with this: Android Room error: The columns returned by the query does not have the fields even though they are annotated as non-null or primitive But I don't understand why this won't compile. Can somebody tell me? In my PojoClass I have got a lot of different fields but only categories above are return as an error. Why Android Studio doesn't throw me all of my Pojo fields as error? Not Only those above. It throws errors in all queries statements not in one or two. If it matter I have a empty constructor in my PojoClass. EDIT: My PojoClass #Entity(tableName = "Table", indices = #Index(value = {"name_product"}, unique = true)) public class Item_get implements Parcelable { #PrimaryKey(autoGenerate = true) public int id; #ColumnInfo(name = "name_product") protected String name_product; private String date_buy; #ColumnInfo(name = "amount") private int amount; private Double price; private Double value; protected String date_exp; protected String buy_place; private String date_buy2; private String buy_place2; private Double price2; private String date_buy3; private String buy_place3; private Double price3; private int to_shop; private int amount_toShop; protected String actual_date; private int cat_food; protected int cat_package; private int cat_packageToShop; #TypeConverters(CategoriesConverter.class) public Food_type food_type; protected Item_get(Parcel in) { id = in.readInt(); name_product = in.readString(); date_buy = in.readString(); amount = in.readInt(); if (in.readByte() == 0) { price = null; } else { price = in.readDouble(); } if (in.readByte() == 0) { value = null; } else { value = in.readDouble(); } date_exp = in.readString(); buy_place = in.readString(); date_buy2 = in.readString(); buy_place2 = in.readString(); if (in.readByte() == 0) { price2 = null; } else { price2 = in.readDouble(); } date_buy3 = in.readString(); buy_place3 = in.readString(); if (in.readByte() == 0) { price3 = null; } else { price3 = in.readDouble(); } to_shop = in.readInt(); actual_date = in.readString(); cat_food = in.readInt(); cat_package = in.readInt(); amount_toShop = in.readInt(); cat_packageToShop = in.readInt(); } public static final Creator<Item_get> CREATOR = new Creator<Item_get>() { #Override public Item_get createFromParcel(Parcel in) { return new Item_get(in); } #Override public Item_get[] newArray(int size) { return new Item_get[size]; } }; #Override public int describeContents() { return 0; } #Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeString(name_product); dest.writeString(date_buy); dest.writeInt(amount); if (price == null) { dest.writeByte((byte) 0); } else { dest.writeByte((byte) 1); dest.writeDouble(price); } if (value == null) { dest.writeByte((byte) 0); } else { dest.writeByte((byte) 1); dest.writeDouble(value); } dest.writeString(date_exp); dest.writeString(buy_place); dest.writeString(date_buy2); dest.writeString(buy_place2); if (price2 == null) { dest.writeByte((byte) 0); } else { dest.writeByte((byte) 1); dest.writeDouble(price2); } dest.writeString(date_buy3); dest.writeString(buy_place3); if (price3 == null) { dest.writeByte((byte) 0); } else { dest.writeByte((byte) 1); dest.writeDouble(price3); } dest.writeInt(to_shop); dest.writeString(actual_date); dest.writeInt(cat_food); dest.writeInt(cat_package); dest.writeInt(amount_toShop); dest.writeInt(cat_packageToShop); } public enum Food_type{ SWEETS(1), VEGETABLES_FRUITS(2), DRINKS(3), OTHER(4); private int code; Food_type(int code){ this.code = code; } public int getCode(){ return code; } } #TypeConverters(PackageTypeConverter.class) public Package_type package_type; public enum Package_type{ PACKAGE_BOX(1), PIECE(2), OTHER_PACK(3); private int code; Package_type(int code){ this.code = code; } public int getCode(){ return code; } } public Item_get(){ } To short of my code I didn't paste all constructors beacuse I use different in each class. Also I skipped getters and setters. If it is necessarily I will paste it.
How to get optimized java code from kotlin source file?
I have converted below kotlin file from existing options #Entity data class DatabaseVideo constructor( #PrimaryKey val url: String, val updated: String, val title: String, val description: String, val thumbnail: String) /** * Map DatabaseVideos to domain entities */ fun List<DatabaseVideo>.asDomainModel(): List<DevByteVideo> { return map { DevByteVideo( url = it.url, title = it.title, description = it.description, updated = it.updated, thumbnail = it.thumbnail) } } The Java out put file is here as below #Metadata( mv = {1, 1, 18}, bv = {1, 0, 3}, k = 2, d1 = {"\u0000\u0010\n\u0000\n\u0002\u0010 \n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0000\u001a\u0016\u0010\u0000\u001a\b\u0012\u0004\u0012\u00020\u00020\u0001*\b\u0012\u0004\u0012\u00020\u00030\u0001¨\u0006\u0004"}, d2 = {"asDomainModel", "", "Lcom/example/android/devbyteviewer/domain/DevByteVideo;", "Lcom/example/android/devbyteviewer/database/DatabaseVideo;", "app_debug"} ) public final class DatabaseEntitiesKt { #NotNull public static final List asDomainModel(#NotNull List $this$asDomainModel) { Intrinsics.checkParameterIsNotNull($this$asDomainModel, "$this$asDomainModel"); Iterable $this$map$iv = (Iterable)$this$asDomainModel; int $i$f$map = false; Collection destination$iv$iv = (Collection)(new ArrayList(CollectionsKt.collectionSizeOrDefault($this$map$iv, 10))); int $i$f$mapTo = false; Iterator var6 = $this$map$iv.iterator(); while(var6.hasNext()) { Object item$iv$iv = var6.next(); DatabaseVideo it = (DatabaseVideo)item$iv$iv; int var9 = false; String var10002 = it.getUrl(); String var10003 = it.getTitle(); String var10004 = it.getDescription(); String var10005 = it.getUpdated(); String var10 = it.getThumbnail(); String var11 = var10005; String var12 = var10004; String var13 = var10003; String var14 = var10002; DevByteVideo var16 = new DevByteVideo(var13, var12, var14, var11, var10); destination$iv$iv.add(var16); } return (List)destination$iv$iv; } } // DatabaseVideo.java package com.example.android.devbyteviewer.database; import androidx.room.Entity; import androidx.room.PrimaryKey; import kotlin.Metadata; import kotlin.jvm.internal.Intrinsics; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; #Metadata( mv = {1, 1, 18}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\"\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0012\n\u0002\u0010\u000b\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0002\b\u0087\b\u0018\u00002\u00020\u0001B-\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0003\u0012\u0006\u0010\u0005\u001a\u00020\u0003\u0012\u0006\u0010\u0006\u001a\u00020\u0003\u0012\u0006\u0010\u0007\u001a\u00020\u0003¢\u0006\u0002\u0010\bJ\t\u0010\u000f\u001a\u00020\u0003HÆ\u0003J\t\u0010\u0010\u001a\u00020\u0003HÆ\u0003J\t\u0010\u0011\u001a\u00020\u0003HÆ\u0003J\t\u0010\u0012\u001a\u00020\u0003HÆ\u0003J\t\u0010\u0013\u001a\u00020\u0003HÆ\u0003J;\u0010\u0014\u001a\u00020\u00002\b\b\u0002\u0010\u0002\u001a\u00020\u00032\b\b\u0002\u0010\u0004\u001a\u00020\u00032\b\b\u0002\u0010\u0005\u001a\u00020\u00032\b\b\u0002\u0010\u0006\u001a\u00020\u00032\b\b\u0002\u0010\u0007\u001a\u00020\u0003HÆ\u0001J\u0013\u0010\u0015\u001a\u00020\u00162\b\u0010\u0017\u001a\u0004\u0018\u00010\u0001HÖ\u0003J\t\u0010\u0018\u001a\u00020\u0019HÖ\u0001J\t\u0010\u001a\u001a\u00020\u0003HÖ\u0001R\u0011\u0010\u0006\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\t\u0010\nR\u0011\u0010\u0007\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\u000b\u0010\nR\u0011\u0010\u0005\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\f\u0010\nR\u0011\u0010\u0004\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\r\u0010\nR\u0016\u0010\u0002\u001a\u00020\u00038\u0006X\u0087\u0004¢\u0006\b\n\u0000\u001a\u0004\b\u000e\u0010\n¨\u0006\u001b"}, d2 = {"Lcom/example/android/devbyteviewer/database/DatabaseVideo;", "", "url", "", "updated", "title", "description", "thumbnail", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", "getDescription", "()Ljava/lang/String;", "getThumbnail", "getTitle", "getUpdated", "getUrl", "component1", "component2", "component3", "component4", "component5", "copy", "equals", "", "other", "hashCode", "", "toString", "app_debug"} ) #Entity public final class DatabaseVideo { #PrimaryKey #NotNull private final String url; #NotNull private final String updated; #NotNull private final String title; #NotNull private final String description; #NotNull private final String thumbnail; #NotNull public final String getUrl() { return this.url; } #NotNull public final String getUpdated() { return this.updated; } #NotNull public final String getTitle() { return this.title; } #NotNull public final String getDescription() { return this.description; } #NotNull public final String getThumbnail() { return this.thumbnail; } public DatabaseVideo(#NotNull String url, #NotNull String updated, #NotNull String title, #NotNull String description, #NotNull String thumbnail) { Intrinsics.checkParameterIsNotNull(url, "url"); Intrinsics.checkParameterIsNotNull(updated, "updated"); Intrinsics.checkParameterIsNotNull(title, "title"); Intrinsics.checkParameterIsNotNull(description, "description"); Intrinsics.checkParameterIsNotNull(thumbnail, "thumbnail"); super(); this.url = url; this.updated = updated; this.title = title; this.description = description; this.thumbnail = thumbnail; } #NotNull public final String component1() { return this.url; } #NotNull public final String component2() { return this.updated; } #NotNull public final String component3() { return this.title; } #NotNull public final String component4() { return this.description; } #NotNull public final String component5() { return this.thumbnail; } #NotNull public final DatabaseVideo copy(#NotNull String url, #NotNull String updated, #NotNull String title, #NotNull String description, #NotNull String thumbnail) { Intrinsics.checkParameterIsNotNull(url, "url"); Intrinsics.checkParameterIsNotNull(updated, "updated"); Intrinsics.checkParameterIsNotNull(title, "title"); Intrinsics.checkParameterIsNotNull(description, "description"); Intrinsics.checkParameterIsNotNull(thumbnail, "thumbnail"); return new DatabaseVideo(url, updated, title, description, thumbnail); } // $FF: synthetic method public static DatabaseVideo copy$default(DatabaseVideo var0, String var1, String var2, String var3, String var4, String var5, int var6, Object var7) { if ((var6 & 1) != 0) { var1 = var0.url; } if ((var6 & 2) != 0) { var2 = var0.updated; } if ((var6 & 4) != 0) { var3 = var0.title; } if ((var6 & 8) != 0) { var4 = var0.description; } if ((var6 & 16) != 0) { var5 = var0.thumbnail; } return var0.copy(var1, var2, var3, var4, var5); } #NotNull public String toString() { return "DatabaseVideo(url=" + this.url + ", updated=" + this.updated + ", title=" + this.title + ", description=" + this.description + ", thumbnail=" + this.thumbnail + ")"; } public int hashCode() { String var10000 = this.url; int var1 = (var10000 != null ? var10000.hashCode() : 0) * 31; String var10001 = this.updated; var1 = (var1 + (var10001 != null ? var10001.hashCode() : 0)) * 31; var10001 = this.title; var1 = (var1 + (var10001 != null ? var10001.hashCode() : 0)) * 31; var10001 = this.description; var1 = (var1 + (var10001 != null ? var10001.hashCode() : 0)) * 31; var10001 = this.thumbnail; return var1 + (var10001 != null ? var10001.hashCode() : 0); } public boolean equals(#Nullable Object var1) { if (this != var1) { if (var1 instanceof DatabaseVideo) { DatabaseVideo var2 = (DatabaseVideo)var1; if (Intrinsics.areEqual(this.url, var2.url) && Intrinsics.areEqual(this.updated, var2.updated) && Intrinsics.areEqual(this.title, var2.title) && Intrinsics.areEqual(this.description, var2.description) && Intrinsics.areEqual(this.thumbnail, var2.thumbnail)) { return true; } } return false; } else { return true; } } } It is seems to be buggy. very difficult to understand.is there any options to get pure java code ? please let me know if we can able to perform conversion other than android studio
You have to realise that kotlin runs on JVM, hence it can do everything that Java can and nothing more. The extra features that Kotlin provides such as not null types etc are implemented using same byte code that java uses. All the extra java code that you call "buggy", is there for some specific feature requirement of kotlin. For example all the Intrinsics.checkParameterIsNotNull checks are there on variables that are declared non nullable, that is how kotlin makes sure that nulls are not introduced in such variables. So the java output you see doesn't contain any extra code, its the actual java equivalent code of your kotlin class.
android, Comparator sort wrong
I have a Comparator to sort by 3 values: ORDER_BY_Points ORDER_BY_Gdif // goals difference ORDER_BY_Goals First I get a array from a json string. I send the array to StandingsSort.ORDER_BY_RULES Arrays.sort(addressArray, StandingsSort.ORDER_BY_RULES); Here is my code: static final Comparator<Standings> ORDER_BY_Points = new Comparator<Standings>() { public int compare(Standings a1, Standings a2) { return a1.points.compareTo(a2.points); } }; static final Comparator<Standings> ORDER_BY_Gdif = new Comparator<Standings>() { public int compare(Standings a1, Standings a2) { return a1.Gdif.compareTo(a2.Gdif); } }; static final Comparator<Standings> ORDER_BY_Goals = new Comparator<Standings>() { public int compare(Standings a1, Standings a2) { return a1.goalsP.compareTo(a2.goalsP); } }; static final Comparator<Standings> ORDER_BY_RULES = new Comparator<Standings>() { public int compare(Standings a1, Standings a2) { int i = ORDER_BY_Points.compare(a1,a2); if(i == 0){ i = ORDER_BY_Gdif.compare(a1,a2); if(i == 0){ i = ORDER_BY_Goals.compare(a1,a2); } } return i; } }; class Standings { String teamName; String goalsP; String goalsM; String Gdif; String points; #Override public String toString() { return "" + teamName + "," + goalsP + ":" + goalsM + "," + Gdif + "," + points + ""; } public Standings(String teamName, String goalsP, String goalsM, String Gdif, String points) { super(); this.teamName = teamName; this.goalsP = goalsP; this.goalsM = goalsM; this.Gdif = Gdif; this.points = points; } } But the result is not OK! Here is the result Name, Goals, GDif, Points Team,11:9,2,10 Team,5:3,2,10 Team,9:2,7,11 Team,0:6,-6,2 Team,3:9,-6,2 Team,6:9,-3,3 Team,8:13,-5,3 Team,8:9,-1,5 Team,8:11,-3,5 Team,8:7,1,5 Why does the Comparator sort wrong?
Since you store the points as Strings, "10" comes before "2", "3", and "5". If you want to compare these in order by the numerical value, you need to convert them into ints first. Similarly, Gdif and Goals are compared as Strings which is probably not what you want.
Display list of time zones similar to that of timezones displayed in S Planner app
Is there a way in which I can display the list of Time zones in a format like (GMT+1.00) Windhoek like the way we see in S Planner on the Android app? Can anyone help me in sorting out this issue? Thanks in adavance
First I suggest you to create a wrapper class to manage the format of TimeZone objects as follow: public class TimeZoneWrapper { private final TimeZone timeZone; private String timeZoneID; private String timeZoneDisplay; private String timeZoneRawDisplayName; public TimeZoneWrapper(final TimeZone timeZone) { this.timeZone = timeZone; setDisplayStrings(); } private void setDisplayStrings() { this.timeZoneID = timeZone.getID().replaceAll("_", " "); int offset = timeZone.getRawOffset(); if (timeZone.inDaylightTime(new Date())) { offset = offset + timeZone.getDSTSavings(); } this.timeZoneRawDisplayName = timeZone.getDisplayName(false, TimeZone.SHORT); final int offsetHrs = offset / 1000 / 60 / 60; final int offsetMins = offset / 1000 / 60 % 60; if (offsetHrs + offsetMins == 0) { this.timeZoneDisplay = "GMT"; } else { String suffix = ""; if (offset < 0) { suffix += "-"; } else { suffix += "+"; } if (offsetMins != 0) { suffix += offsetHrs + "." + offsetMins; } else { suffix += offsetHrs; } this.timeZoneDisplay = "GMT" + suffix; } } public String getTimeZoneID() { return timeZoneID; } public String getTimeZoneDisplay() { return timeZoneDisplay; } public String getTimeZoneRawDisplayName() { return timeZoneRawDisplayName; } public TimeZone getTimeZone() { return timeZone; } #Override public final boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof TimeZoneWrapper)) { return false; } final TimeZoneWrapper that = (TimeZoneWrapper) o; return that.timeZone.equals(this.timeZone); } } Once you have created (if you want) this wrapper class you can retrieve the TimeZones from your device with the following method in your mainActivity. private List<TimeZoneWrapper> buildTimeZoneWrappers() { final List<TimeZoneWrapper> timeZoneWrappers = new ArrayList<TimeZoneWrapper>(); final List<TimeZone> timeZones = new ArrayList<TimeZone>(); final String[] timeZoneIds = TimeZone.getAvailableIDs(); for (final String id : timeZoneIds) { timeZones.add(TimeZone.getTimeZone(id)); } for (final TimeZone timeZone : timeZones) { timeZoneWrappers.add(new TimeZoneWrapper(timeZone)); } return timeZoneWrappers; } Now you have a list of "well" formatted timezone, so if you need a picker with this value you simply have to create one with them. String[] temp; for (int i=0;i<timeZoneWrappers.size();i++){ temp[i]=timeZoneWrappers.get(i).getTimeZoneDisplay(); } NumberPicker picker = new NumberPicker(getApplicationContext()); picker.setDisplayedValues(temp); Let me know;)
How can I parse price from Google Play In-app Billing
I use the follow code to parse price from Google Play In-app Billing: private static Number parsePrice(String priceFromGoogle) { Locale currencyLocale = getCurrencyLocale(priceFromGoogle); NumberFormat numberFormat = NumberFormat.getCurrencyInstance(currencyLocale); Number number = null; try { number = numberFormat.parse(priceFromGoogle); } catch (ParseException e) { e.printStackTrace(); } return number; } private Locale getCurrencyLocale(String price) { Locale locale = null; for (Locale availableLocale : Locale.getAvailableLocales()) { NumberFormat numberFormat = NumberFormat.getCurrencyInstance(availableLocale); try { numberFormat.parse(price); locale = availableLocale; break; } catch (ParseException e) { //do nothing } } return locale; } It works fine on my test devices and in my locale. But on some devices and in some countries I encounter prices like this: "Php1,337.07", "US$ 29.99", "MX$374.79". My approach doesn't work in this case. Is there an universal approach to solve this problem?
Check their In-app billing sample project and modify SkuDetails.java so that you can get that information as well: import org.json.JSONException; import org.json.JSONObject; /** * Represents an in-app product's listing details. */ public class SkuDetails { String mItemType; String mSku; String mType; int mPriceAmountMicros; String mPriceCurrencyCode; String mPrice; String mTitle; String mDescription; String mJson; public SkuDetails(String jsonSkuDetails) throws JSONException { this(IabHelper.ITEM_TYPE_INAPP, jsonSkuDetails); } public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException { mItemType = itemType; mJson = jsonSkuDetails; JSONObject o = new JSONObject(mJson); mSku = o.optString("productId"); mType = o.optString("type"); mPrice = o.optString("price"); mPriceAmountMicros = o.optInt("price_amount_micros"); mPriceCurrencyCode = o.optString("price_currency_code"); mTitle = o.optString("title"); mDescription = o.optString("description"); } public String getSku() { return mSku; } public String getType() { return mType; } public String getPrice() { return mPrice; } public String getTitle() { return mTitle; } public String getDescription() { return mDescription; } public int getPriceAmountMicros() { return mPriceAmountMicros; } public String getPriceCurrencyCode() { return mPriceCurrencyCode; } #Override public String toString() { return "SkuDetails:" + mJson; } }
You can get the price in micros in the JSON retrieved by IabHelper. This is not officially documented but here is how I've done it by editing SkuDetails.java : public class SkuDetails { ... Double mPriceMicros; public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException { ... String priceMicros = o.optString("price_amount_micros"); if (priceMicros != null) { String format = new StringBuilder(priceMicros).insert(priceMicros.length() - 6, ".").toString(); mPriceMicros = Double.parseDouble(format); } } ... public Double getPriceMicros() { return mPriceMicros; } } Hope this helps ! PS : I tried your Price class but it parsed 0.8 for 0,89 €
As Google Play may return prices in currency format which is unsupported by java.text.NumberFormat, I wrote my own implementation public class Price { private double value; private String currency; private String pattern; private DecimalFormat decimalFormat; private Price() {} private static String currencyToDecimalFormat(String value, Price price) { char decimalSeparator = '.'; char groupingSeparator = 0; if (value.length() >= 3) { char[] chars = value.toCharArray(); if (chars[chars.length - 2] == ',') { decimalSeparator = ','; chars[chars.length - 2] = '.'; } else if (chars[chars.length - 3] == ',') { decimalSeparator = ','; chars[chars.length - 3] = '.'; } value = new String(chars); } if (value.contains(",")) { groupingSeparator = ','; value = value.replaceAll(",", ""); } else if (value.contains(" ")) { groupingSeparator = ' '; value = value.replaceAll(" ", ""); } else if (value.contains("\u00A0")) { groupingSeparator = '\u00A0'; value = value.replaceAll("\u00A0", ""); } DecimalFormatSymbols symbols = new DecimalFormatSymbols(); if (groupingSeparator != 0) { price.decimalFormat = new DecimalFormat("###,###.00"); symbols.setGroupingSeparator(groupingSeparator); } else { price.decimalFormat = new DecimalFormat("######.00"); } symbols.setDecimalSeparator(decimalSeparator); price.decimalFormat.setDecimalFormatSymbols(symbols); return value.replaceAll(",", ""); } public static Price parsePrice(String priceFromGoogle) { Price price = new Price(); StringBuilder patternBuilder = new StringBuilder(); Pattern pattern = Pattern.compile("(?:[0-9]{1,3})(?:[0-9,.\\s\u00A0]+)"); Matcher matcher = pattern.matcher(priceFromGoogle); matcher.find(); String priceString = matcher.group(); if (priceFromGoogle.indexOf(priceString) == 0) { if (priceFromGoogle.length() != priceString.length()) { price.currency = priceFromGoogle.substring(priceString.length()); } else { price.currency = ""; } } else { price.currency = priceFromGoogle.substring(0, priceFromGoogle.indexOf(priceString)); } price.currency = price.currency.trim(); if (priceFromGoogle.startsWith(price.currency)) { patternBuilder.append("%1s"); char nextChar = priceFromGoogle.charAt(price.currency.length()); if (nextChar == ' ' || nextChar == 0xA0) { patternBuilder.append(' '); } patternBuilder.append("%2$s"); } else { patternBuilder.append("%2$s"); char prevChar = priceFromGoogle.charAt(priceFromGoogle.indexOf(price.currency) - 1); if (prevChar == ' ' || prevChar == 0xA0) { patternBuilder.append(' '); } patternBuilder.append("%1s"); } price.pattern = patternBuilder.toString(); priceString = trim(priceString); priceString = currencyToDecimalFormat(priceString, price); price.value = Double.parseDouble(priceString); return price; } #Override public String toString() { if (pattern != null) { return String.format(pattern, currency, decimalFormat.format(value)); } else { return ""; } } } EDIT1: Because of Google uses non-breaking space instead of usual space you need check this and use custom trim function: public static String trim(String text) { int start = 0, last = text.length() - 1; int end = last; while ((start <= end) && (text.charAt(start) <= ' ' || text.charAt(start) == 0xA0)) { start++; } while ((end >= start) && (text.charAt(end) <= ' ' || text.charAt(end) == 0xA0)) { end--; } if (start == 0 && end == last) { return text; } return text.substring(start, end); }