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

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());

Related

Is it possible to update string.xml file at run time in android?

I wanted to apply localization in my app dynamically so is it possible to define strings in java file and fetch that strings to our layout xml file i.e as we do #string/anystringname replacing this by the strings defined in java file to our layout file #somestring defined to java file ????
Basically, No.
You cannot manipulate resource file dynamically.
You need to implement such functionality on your own.
Here is an example:
english.json
{
"hello": "Hello"
}
french.json
{
"hello": "Bonjour"
}
public class StringManager {
private static StringManager sInstance = null;
private static String sDefaultLanguageCode = "en";
private Map<String, JSONObject> mLanguageMap = new ArrayMap<>();
private StringManager() {
}
public static StringManager getInstance() {
if (sInstance == null) {
synchronized (StringManager.class) {
if (sInstance == null)
sInstance = new StringManager();
}
}
return sInstance;
}
public static void setDefaultLanguageCode(String languageCode) {
sDefaultLanguageCode = languageCode;
}
public void addLanguage(String languageCode, JSONObject json) {
mLanguageMap.put(languageCode, json);
}
public String getString(String key) {
return mLanguageMap.get(mDefaultLanguageCode).getString(key);
}
public String getString(String languageCode, String key) {
return mLanguageMap.get(languageCode).getString(key);
}
}
public class SomeActivity extends Activity {
...
public void initStringResources() {
// assume englishJsonObject is created from english.json
StringManager.getInstance().addLanguage("en", englishJsonObject);
StringManager.getInstance().addLanguage("fr", frenchJsonObject);
StringManager.setDefaultLanguageCode("fr");
}
public void useSomeString() {
String helloString = StringManager.getInstance().getString("hello");
// helloString will be "Bonjour"
// and you can get specific language string
String englishHelloString = StringManager.getInstance().getString("en", "hello");
}
// this may be called from Button or other UI component
public void onLanguageChanged(String languageCode) {
StringManager.setDefaultLanguageCode(languageCode);
// and update UI components that display localized strings
}
}
This is not possible. It is however possible to change the language of your app at runtime. This allows you the switch between the strings.xml for the different languages.
A library I used for this is the Android localization library
All your activities have to extend the LocalizationActivity which extends the AppCompatActivity. You just have to call
setLanguage("en");
To change your apps language, and use a different strings.xml
In this thread you can read more possible solutions.
<TextView
android:id="#+id/tv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/tv"
/>
<string name="tv">old text</string>//old text
final TextView textView = (TextView) findViewById(R.id.rate);
textView.setText("new text");//new text

how to use auto increment in android realm

realm1.beginTransaction();
newuser tripnew = realm1.createObject(newuser.class);
int nid= (int)(realm.where(newuser.class).max("nid").intValue()+1);
tripnew.setNid(nid);
tripnew.setFrom(frominput.getText().toString());
tripnew.setTo(toinput.getText().toString());
tripnew.setDatejourney(dateinput.getText().toString());
realm1.commitTransaction();
updatetrip();
/// iam also use this code not working
realm.where(newuser.class).maximumInt("id_cp") + 1;
newuser.java//////
public class newuser extends RealmObject {
private String from,to,datejourney;
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getDatejourney() {
return datejourney;
}
public void setDatejourney(String datejourney) {
this.datejourney = datejourney;
}
}
It looks that your class doesn't have nid nor id_cp numeric field. Add int nid; field along with accessors and then realm.where(newuser.class).max("nid").intValue()+1 should work in most of the cases. However it will fail if there is no newuser instance in database yet. I use singleton factory for generating primary keys as a more generic solution.
There is a long discussion in Realm Git Hub if you need more context: Document how to set an auto increment id?

Difference between storing data in shared preferences and database in android

I am creating an application in android and I want to store data of places user selected on the google map. I am currently storing all the places by adding them all in an array and then serialize them by Gson library and it works fine and coding is very simple and easy but if i use data base instead of that that then the coding will be more complex and because implantation of data base is more complex then simply string the array of places to shared preferences. below is the class whose objects are i am storing and saving in the shared preferences but if want to store them on the data base then i have to go through more complex I have to create queries for insert, delete update etc. so suggest me that should i use db or shred preference is good for saving list of places.
package com.example.googlemapstext;
import java.util.ArrayList;
import android.location.Address;
public class MyPlace {
private int id;
private String placeName;
private Address placeAddress;
private int ringerState;
private int brightnessState;
private int wifiState;
private int gpsState;
private int bluetoothState;
private int radiusValueIndex;
private ArrayList<Contact> contactArrayList;
private String message;
private double radiusValue;
private boolean notificationCheck;
public MyPlace(int id,String placeName, Address placeAddress, String radiusValue,
int ringerState, int brightnessState, int wifiState, int gpsState,
int bluetoothState, int radiusValueIndex, ArrayList<Contact> contactArrayList,
String message, boolean notificationCheck) {
this.id=id;
this.placeName = placeName;
this.placeAddress = placeAddress;
this.radiusValue = getTrimedRadiusValue(radiusValue);
this.ringerState = ringerState;
this.brightnessState = brightnessState;
this.wifiState = wifiState;
this.gpsState = gpsState;
this.bluetoothState = bluetoothState;
this.contactArrayList = contactArrayList;
this.message = message;
this.radiusValueIndex = radiusValueIndex;
this.notificationCheck = notificationCheck;
}
private double getTrimedRadiusValue(String radiusValue)
{
radiusValue=radiusValue.replace("Radius ", "");
radiusValue=radiusValue.replace(" Meters", "");
return Double.parseDouble(radiusValue);
}
public boolean getNotificationCheck() {
return notificationCheck;
}
public void setNotificationCheck(boolean notificationCheck) {
this.notificationCheck = notificationCheck;
}
public int getRadiusValueIndex() {
return radiusValueIndex;
}
public void setRadiusValueIndex(int radiusValueIndex) {
this.radiusValueIndex = radiusValueIndex;
}
public int getRingerState() {
return ringerState;
}
public void setRingerState(int ringerState) {
this.ringerState = ringerState;
}
public int getBrightnessState() {
return brightnessState;
}
public void setBrightnessState(int brightnessState) {
this.brightnessState = brightnessState;
}
public int getWifiState() {
return wifiState;
}
public void setWifiState(int wifiState) {
this.wifiState = wifiState;
}
public int getGpsState() {
return gpsState;
}
public void setGpsState(int gpsState) {
this.gpsState = gpsState;
}
public int getBluetoothState() {
return bluetoothState;
}
public void setBluetoothState(int bluetoothState) {
this.bluetoothState = bluetoothState;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public double getRadiusValue() {
return radiusValue;
}
public void setRadiusValue(String radiusValue) {
this.radiusValue = getTrimedRadiusValue(radiusValue);
}
public String getPlaceName() {
return placeName;
}
public void setPlaceName(String placeName) {
this.placeName = placeName;
}
public Address getPlaceAddress() {
return placeAddress;
}
public void setPlaceAddress(Address placeAddress) {
this.placeAddress = placeAddress;
}
public ArrayList<Contact> getContactArrayList() {
return contactArrayList;
}
public void setContactArrayList(ArrayList<Contact> contactArrayList) {
this.contactArrayList = contactArrayList;
}
public int getId() {
return id`enter code here`;
}
public void setId(int id) {
this.id = id;
}
}
The main difference between SharedPreferences and DataBase is like you mentioned :
SharedPreferences works on an Key-Value pair basis. you simply provide the Key and get back the Value you stored. that's great.
DataBase creates an SQLite Tables and you need to use queries to pull them out.
I think that if you are good with the JSON mechanism that you built, then storing a string in SharedPreferences is all you need.
But when the Data get more and more complex, and you would like quick access to any part of it, I think DB would be easier than parsing and seaching a JSON string all the time.
Yes, it might make you write more code for handling the DB queries..
I think SQLite will be better for you. I only use SharePreferences for small, simple and "key - value" structured data. (and it should be like that)
You have a lot of data, so SQLite is the way to go.
Read this for more information : Pros and Cons of SQLite and Shared Preferences
I think answer depends on how many places you want to save and what do you plan to do with them but I consider DB as hte best way to go.
With a DB you will be able to create queries to get only places you want and not load all places in a list and search in it.
To simplify DB creation (and use) you can try orm for Android like OrmLite and GreenDao. I think OrmLite is easier to use than GreenDao (but second one seems to have better performance...) and you can find multiple examples there.
In my opinion, SharedPreferences should only be used for saving user preferences data.

How to map Enum in GreenDAO

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());

How to declare global variables without using context?

From several classes in my app I want to get String global values without using Context.
If I declare this variables in Application class, SharedPreferences or Strings.xml, then I must use Context for get/set it values.
Using Singleton is not a good practice as I understand from this post.
Is there any good way for using global variables without using context?
Create a global class or store the string in a place that "makes sense". For instance if you have the class Shoes you could do this:
public class Shoes {
public static class BRAND {
public static final String NIKE = "nike";
public static final String REBOK = "rebok";
public static final String ADDIDAS = "addidas";
}
private String brand;
public Shoes() {}
public void setBrand(String brand) {
this.brand = brand;
}
public String getBrand() {
return this.brand;
}
}
Now you can do this:
public static void main(String[] args) {
Shoes myShoes = new Shoes();
myShoes.setBrand(Shoes.BRAND.NIKE);
}
You will find there are many things like this in Android. It would be even better if you used enums instead. Hope this helps.
UPDATE
If you would like to use setters and getters then there are 2 solutions:
The first you would need an instance of the object and since you only want a single instance a singleton design pattern would be required. Really if you synchronize correctly and the design makes sense they can be very good and useful. In your situation I don't think it would be worth the work.
You can take advantage of the static initializer and static methods. You could just remove the final declaration and do what you want with the Strings, like this:
public class Shoes {
public static class BRAND {
public static String NIKE;
public static String REBOK;
public static String ADDIDAS;
static {
NIKE = "nike";
REBOK = "rebok";
ADDIDAS = "addidas";
}
}
private String brand;
public Shoes() {}
public void setBrand(String brand) {
this.brand = brand;
}
public String getBrand() {
return this.brand;
}
}
Or use good encapsulation practices and do this:
public class Shoes {
public static class BRAND {
private static String NIKE;
private static String REBOK;
private static String ADDIDAS;
static {
NIKE = "nike";
REBOK = "rebok";
ADDIDAS = "addidas";
}
public static String getNIKE() {
return NIKE;
}
public static void setNIKE(String name) {
NIKE = name;
}
public static String getREBOK() {
return REBOK;
}
public static void setREBOK(String name) {
REBOK = name;
}
public static String getADDIDAS() {
return ADDIDAS;
}
public static void setADDIDAS(String name) {
ADDIDAS = name;
}
}
private String brand;
public Shoes() {}
public void setBrand(String brand) {
this.brand = brand;
}
public String getBrand() {
return this.brand;
}
}
However I must note: If you are doing this because you cannot get a Context then you are missing something. A context can be obtained from anywhere - objects instantiated by the system are given a context as a parameter. If you have your own custom object you can just pass the ApplicationContext as a parameter or the class using the object itself (this).
Store the values in your strings.xml file. Then call it like this.
String yourString = Context.getResources().getString(R.string.your_string);

Categories

Resources