How to map Enum in GreenDAO - android

I've just started using greenDAO.
How do I add an Enum property?
What I've Thought of: using the addIndex property of an entity.
private static void main() {
// TODO Auto-generated method stub
static Schema blah;
Entity unicorn = blah.addEntity("Weather");
unicorn.addIdProperty();
unicorn.addIntProperty("currentAirTemp");
unicorn.addIndex("shirtSize");
}
Is this the right way to do it?
Aim: I want to have a reference to shirtSize being from the set: {XS, S, M, L, XL, XXL}

Using GreenDAO 3 we now have the option to use #convert annotation with PropertyConverter
#Entity
public class User {
#Id
private Long id;
#Convert(converter = RoleConverter.class, columnType = String.class)
private Role role;
enum Role {
DEFAULT, AUTHOR, ADMIN
}
static class RoleConverter implements PropertyConverter<Role, String> {
#Override
public Role convertToEntityProperty(String databaseValue) {
return Role.valueOf(databaseValue);
}
#Override
public String convertToDatabaseValue(Role entityProperty) {
return entityProperty.name();
}
}
}
Read more at http://greenrobot.org/objectbox/documentation/custom-types/

Latest version of GreenDao (2.x) contains functionality which ideally suits your needs. There are a Custom Types which can serve enums very easily.
Enum
public enum ShirtSize {
XS(1),
S(2),
M(3),
L(4),
XL(5),
XXL(6);
private final int value;
ShirtSize(int value) {
this.value = value;
}
public int value() {
return value;
}
}
Converter
public class ShirtSizeConverter implements PropertyConverter<ShirtSize, Integer> {
#Override
public ShirtSize convertToEntityProperty(Integer databaseValue) {
if(databaseValue == null) {
return null;
} else {
for(ShirtSize value : ShirtSize.values()) {
if(value.value() == databaseValue) {
return value;
}
}
throw new DaoException("Can't convert ShirtSize from database value: " + databaseValue.toString());
}
}
#Override
public Integer convertToDatabaseValue(ShirtSize entityProperty) {
if(entityProperty == null) {
return null;
} else {
return entityProperty.value();
}
}
}
Entity field declaration (in generator)
entity.addIntProperty("ShirtSize").customType(
"com.your_package.ShirtSize",
"com.your_package.ShirtSizeConverter"
);

As far as I know, Enums are not supported by greenDAO due to their unstable nature.
Also they are an error-prone component to add to your database logic, since the values of the enum elements can change.
One option to get around this would be to add an Int property to the database and then map Enum ordinal values to that field, like so:
// add the int property to the entity
unicorn.addIntProperty("shirtSize");
// create the enum with static values
public enum ShirtSize {
XS(1), S(2), M(3), L(4), XL(5), XXL(6);
private final int value;
private ShirtSize(int value) {
this.value = value;
}
public int value() {
return value;
}
}
// set the ordinal value of the enum
weather.setShirtSize(ShirtSize.XL.value());

Related

Embedding .NET library's properties and actions on Android native AAR

I'm adding an existing Xamarin.Android .NET library to a native Android Studio project. I'm following the indications on https://learn.microsoft.com/en-us/xamarin/tools/dotnet-embedding/ and everything works well, but I have a question not being a Java expert:
is it also possible to export to Java the C#'s properties and actions present in my libraries (like ReturnAnyText)?
namespace export_test
{
[Register("export_test.ClassToExport")]
public class ClassToExport
{
[Export("ClassToExport")]
public ClassToExport()
{
// ...
}
[Export("DoSomething")]
public void DoSomething()
{
// ...
}
public Action<string> ReturnAnyText { get; set;}
}
}
A property under the hood are just get_PropertyName() and set_PropertyName() methods. So yes, you should be able to export those too:
This would look something like:
public bool MyProp
{
[Export]
get;
[Export]
set;
}
Or if you want to name them:
public bool MyProp
{
[Export("GetMyProp")]
get;
[Export("SetMyProp")]
set;
}
The simplest solution I found is to not try to export C# delegates, and simply return an object containing the return values at the end of the method execution:
namespace export_test
{
[Register("export_test.ClassToExport")]
public class ClassToExport
{
[Export("ClassToExport")]
public ClassToExport()
{
// ...
}
[Export("DoSomething")]
public MyResult DoSomething()
{
// ...
}
}
[Register("export_test.MyResult")]
public class MyResult
{
private string _Text;
private int _Value;
[Export("MyResult")]
public MyResult(string text, int val)
{
_Text = text;
_Value = val;
}
[Export("GetText")]
public string GetText() { return _Text; }
[Export("GetValue")]
public int GetValue() { return _Value; }
}
}

Binding double to android:text

Using two way data binding I'm trying to fit a double(Double) inside a EditText type field.
I have tried with converter functions (with #InverseMethod) and also tried to write a #BindingAdapter with #InverseBindingAdapter.
I think I may be missing something crucial cause:
"#={`` + muObject.myDecimal}"
reveals 'null' in the EditText field.
The #InverseFunction method crashes, and the binding adapter way did not work either...
Could someone please point me in the right direction?
Thanks
ViewModel code:
Note that the BaseObservableViewModel extends ViewModel from architecture components and contains the contents of the BaseObservable class (tip from Yigit Boyar).
Also note that none of the fields in the QualityControl class are observable.
At last: Note that the getter/setter for measurementKm are a test. measurementKm is Double field in the QualityControl class and I would prefer to bind directly to that field.
public final class QualityControlViewModel extends BaseObservableViewModel {
#SuppressWarnings("unused")
private static final String TAG = "QualityControlVM";
private QualityControl qualityControl;
private int position;
public String measurementKm = "";
QualityControlViewModel(Application application) {
super(application);
}
public QualityControl getQualityControl() {
return qualityControl;
}
public void setQualityControl(QualityControl qualityControl) {
this.qualityControl = qualityControl;
// Initialize massMeasurementPlaceSelected
if (isValidMassMeasurementPlace()) massMeasurementPlaceSelected = true;
setMeasurementKm(qualityControl.getMeasurementKm());
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
private Double getMeasurementKm() {
if (this.measurementKm.length() > 0) {
return Double.parseDouble(this.measurementKm);
} else {
return 0.0;
}
}
private void setMeasurementKm(Double measurementKm) {
if (qualityControl.getMeasurementKm() != null) {
this.measurementKm = String.valueOf(qualityControl.getMeasurementKm());
}
notifyChange();
}
}

Android Rxjava subscribe to a variable change

I am learning Observer pattern, I want my observable to keep track of a certain variable when it changes it's value and do some operations, I've done something like :
public class Test extends MyChildActivity {
private int VARIABLE_TO_OBSERVE = 0;
Observable<Integer> mObservable = Observable.just(VARIABLE_TO_OBSERVE);
protected void onCreate() {/*onCreate method*/
super();
setContentView();
method();
changeVariable();
}
public void changeVariable() {
VARIABLE_TO_OBSERVE = 1;
}
public void method() {
mObservable.map(value -> {
if (value == 1) doMethod2();
return String.valueOf(value);
}).subScribe(string -> System.out.println(string));
}
public void doMethod2() {/*Do additional operations*/}
}
But doMethod2() doesn't get called
Nothing is magic in the life : if you update a value, your Observable won't be notified. You have to do it by yourself. For example using a PublishSubject.
public class Test extends MyChildActivity {
private int VARIABLE_TO_OBSERVE = 0;
Subject<Integer> mObservable = PublishSubject.create();
protected void onCreate() {/*onCreate method*/
super();
setContentView();
method();
changeVariable();
}
public void changeVariable() {
VARIABLE_TO_OBSERVE = 1;
// notify the Observable that the value just change
mObservable.onNext(VARIABLE_TO_OBSERVE);
}
public void method() {
mObservable.map(value -> {
if (value == 1) doMethod2();
return String.valueOf(value);
}).subScribe(string -> System.out.println(string));
}
public void doMethod2() {/*Do additional operations*/}
}
If interested here a Kotlin version of Variable class, which lets subscribers to be updated after every variable change.
class Variable<T>(private val defaultValue: T) {
var value: T = defaultValue
set(value) {
field = value
observable.onNext(value)
}
val observable = BehaviorSubject.createDefault(value)
}
Usage:
val greeting = Variable("Hello!")
greeting.observable.subscribe { Log.i("RxKotlin", it) }
greeting.value = "Ciao!"
greeting.value = "Hola!"
This will print:
"Hello!"
"Ciao!"
"Hola!"
#dwursteisen Nothing is magic, no, but I think we can get it a little more magic than that... 😊
How about using an Rx BehaviourSubject in this way:
import rx.functions.Action1;
import rx.subjects.BehaviorSubject;
public class BehaviourSubjectExample {
public BehaviourSubjectExample() {
subject.skip(1).subscribe(new Action1<Integer>() {
#Override
public void call(Integer integer) {
System.out.println("The value changed to " + integer );
}
});
}
public final BehaviorSubject<Integer> subject = BehaviorSubject.create(0);
public int getValue() { return subject.getValue(); }
public void setValue(int value) { subject.onNext(value); }
}
Remove the .skip(1) if you want the observing code to see the initial value.
The variable backing remains with the BehaviourSubject and can be accessed through conventional Java Getter/Setter. This is a toy example of course: If your use case were really this simple there'd be no excuse for not just writing:
private int value = 0;
public int getValue() { return value; }
public void setValue(int value) {
this.value = value;
System.out.println("The value changed to " + value );
}
...but the use of BehaviourSubject lets you bridge changes to other Rx data-streams inside your class for composing more advanced behaviours.

Android: enums with values which can be translated to other locales

Suppose I have below enum:
public enum Feelings {
happy("label1"),sad("label2")
private final String name;
private Feelings(String s) {
name = s;
}
public boolean equalsName(String otherName) {
return (otherName == null) ? false : name.equals(otherName);
}
public String toString() {
return name;
}
}
when I call its .toString() it returns labels defined for different enumeration. I use these labels on UI and they will be displayed to users.
When I consider to publish my app with different locales, It comes to my mindhow can I define these labels such that they can be translated to other languages?
Shouldn't be much different than how you handle localization outside of an enum. Just need to pass in reference to Resources. So something like:
public enum Feelings {
happy(R.string.happy),
sad(R.string.sad)
private final int nameId;
private Feelings(int nameId) {
this.nameId = nameId;
}
public String toString(Resources res) {
return res.getString(nameId);
}
}
Instead of setting the actual value of the label in the enum you could use the string resource id.
public enum Feelings {
happy(R.string.happy_label), sad(R.string.sad_label);
private final int strId;
private Feelings(int strId) {
this.strId = strId;
}
#StringRes public int getStringId() {
return strId;
}
}
This way Android will pick the correct translation of your strings given the device's locale
Your usage could then look like this:
textView.setText(Feelings.happy.getStringId());

Android databinding with Firebase [duplicate]

I have data in my firebase DB, everything works fine until I try to De-serialize the data.
Error: argument 1 has type io.realm.RealmList, got java.util.ArrayList
Here's my code:
DatabaseReference root = FirebaseDatabase.getInstance().
getReferenceFromUrl("https://swing-8792d.firebaseio.com/playlist");
Query playlistQuery = root.orderByKey().equalTo(key);
playlistQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
Log.d("Child", child + "");
Playlist receivedPlaylist = child.getValue(Playlist.class);
Playlist playlist = new Playlist();
playlist.setCreatedBy(receivedPlaylist.getCreatedBy());
playlist.setName(receivedPlaylist.getName());
playlist.setMyMap(receivedPlaylist.getMyMap());
playlist.setQrKey(receivedPlaylist.getQrKey());
playlist.setCount(receivedPlaylist.getCount());
playlist.setId(receivedPlaylist.getId());
playlist.setTracks(receivedPlaylist.getTracks());
mPlaylist.add(playlist);
}
This is my POJO class:
#RealmClass
public class Playlist extends RealmObject {
String name;
Long id;
RealmList<Track> tracks;
Integer count;
String createdBy;
RealmList<UserMap> myMap;
String qrKey;
public RealmList<UserMap> getMyMap() {
return myMap;
}
public void setMyMap(RealmList<UserMap> myMap) {
this.myMap = myMap;
}
public Playlist(){}
public String getQrKey() {
return qrKey;
}
public void setQrKey(String qrKey) {
this.qrKey = qrKey;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public RealmList<Track> getTracks() {
return tracks;
}
public void setTracks(RealmList<Track> tracks) {
this.tracks = tracks;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}
If I try to de-serialize with Normal POJO class (i.e Removing Realm) it works fine.
Firebase won't work with classes that do not have default constructor or private variables i.e no public getter/setter.
A easier solution in your case would be to make a middleware class that is the same pojo just not extending RealmObject. Next initialise your RealmObject subclass using the values of the pojo.
Pseudo code
class SimplePojoPlaylist {
public String variable;
}
class Playlist extends RealmObject {
public String variable;
}
Then first cast into SimplePojoPlaylist
for (DataSnapshot child : dataSnapshot.getChildren()) {
SimplePojoPlaylist receivedPlaylist = child.getValue(SimplePojoPlaylist.class);
Playlist playList = new Playlist();
playList.variable = receivedPlaylist.variable;
}
RealmList is not a supported type for deserialization. Your database checks its structure and deduces that tracks should be an ArrayList. Then, when it tries to convert it, it finds that the types do not match.
Check this link from the docs:
Also, it is a good practice to make your objects immutable to avoid unwanted access and/or modifications.
Creating an empty object from scratch and then calling setter methods to define its state is not a very good pattern, because it can create a situation where an object is accessed before when its state is "broken".
If you need to create an object that is flexible, has a few mandatory fields and some optional, consider using the Builder pattern, although to do it you'd have to redesign your model.
wikipedia - Builder
If you don't need/want to use a builder, my advice is:
1) Make the empty constructor private and create another public one that requires all the fields.
2) Change your tracks field to be of type "List". Then, if you need the object to return a RealmList create another getter method such as tracksAsRealmList() that makes a RealmList out of the member list and returns it.
3) Make sure that the "Track" model has an empty private constructor, a public one with all of its parameters and that all of its fields are supported by firebase deserialization.
4) Unless strictly necessary, make your object fields private and set its value through a setter method.
I hope this helps you.

Categories

Resources