Default value on Bundle.getString(String key) - android

I've just noticed that, while most of getters from a Bundle have the possibiliy of including a default value, in case the key doesn't exist in that particular bundle instance, getString does not have that possibility, returning null if that case.
Any ideas on why is that and if there is some way of easy solution to that (by easy I mean not having to check each individual value or extending the Bundle class).
As an example, right now you have only this:
bundle.getString("ITEM_TITLE");
While I would like to do:
bundle.getString("ITEM_TITLE","Unknown Title");
Thanks!

Trojanfoe has the best solution if that is what you want, though once you get into dealing with defaults for other datatypes you'll have to do the same thing for them all.
Another solution would be to check to see if the bundle contains the key:
String myString = bundle.containsKey("key") ? bundle.getString("key") : "default";
It's not as nice as a function, but you could always wrap it if you wanted.

You'll have to wrap it yourself:
public String getBundleString(Bundle b, String key, String def)
{
String value = b.getString(key);
if (value == null)
value = def;
return value;
}

Note: http://developer.android.com/reference/android/os/Bundle.html
public String getString (String key, String defaultValue)
Since: API Level 12
EDIT this function has moved to BaseBundle: here

Another solution is to check for null:
String s = bundle.getString("key");
if (s == null) s = "default";
This is better than csaunders' solution because the Bundle can contain the respective key but it can be of a different type (for example an int), in which case his solution would result myString in being null, instead of "default".

Related

Can I modify a Strings.xml file programmatically in Android? [duplicate]

I have declared a string in my strings.xml file , and using it in my activity as R.string.compose_title. (setting it as title i.e. setTitle(R.id.compose_title)). Now in some case I want to edit the string and then use it to set the title . How can I do this ?
P.S. I need to change value of a single string only , So declaring a new strings.xml for each case(which are variable depending upon the user) using localization seems to be a lil inefficient .
One thing what you have to understand here is that, when you provide a data as a Resource, it can't be modified during run time. For example, the drawables what you have in your drawable folder can't be modified at run time. To be precise, the "res" folder can't be modified programatically.
This applies to Strings.xml also, i.e "Values" folder. If at all you want a String which has to be modified at runtime, create a separate class and have your strings placed in this Class and access during run time. This is the best solution what I have found.
example howto:
how? by changing one variable reference to other reference
usage:
setRColor(pl.mylib.R.class,"endColor",pl.myapp.R.color.startColor);
// override app_name in lib R class
setRString(pl.mylib.R.class,"app_name",pl.myapp.R.string.app_name);
base methods:
public static void setRColor(Class rClass, String rFieldName, Object newValue) {
setR(rClass, "color", rFieldName, newValue);
}
public static void setRString(Class rClass, String rFieldName, Object newValue) {
setR(rClass, "string", rFieldName, newValue);
}
// AsciiStrings.STRING_DOLAR = "$";
public static void setR(Class rClass, String innerClassName, String rFieldName, Object newValue) {
setStatic(rClass.getName() + AsciiStrings.STRING_DOLAR + innerClassName, rFieldName, newValue);
}
helper methods :
public static boolean setStatic(String aClassName, String staticFieldName, Object toSet) {
try {
return setStatic(Class.forName(aClassName), staticFieldName, toSet);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
}
}
public static boolean setStatic(Class<?> aClass, String staticFieldName, Object toSet) {
try {
Field declaredField = aClass.getDeclaredField(staticFieldName);
declaredField.setAccessible(true);
declaredField.set(null, toSet);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
#bradenV2 My app is supporting many languages , so I wanted to take a
string from my strings.xml that's currently in use and change that ,
and then use that one – atuljangra Mar 12 '12 at 22:04
ps the above solution is good for example when u want to inject some data in already compiled lib/jar. But if u want localize strings just make folder under res per LANG CODE like values-CC where cc is lang code (values-de,values-cs) etc
then u have 2 choices:
"build in" system dependent language selection - based on device selected lang
via create resources for configuration - you decide which lang show
like this:
configuration = new Configuration(resources.getConfiguration());
configuration.setLocale(targetLocale);
String localized = Context.createConfigurationContext(configuration)
.getResources()
.getString(resourceId);
I don't think you can programmatically customize the R class as it is built by ADT automatically.
I had a situation like this, where one of my strings.xml values had some dynamic piece of it. I set up the strings.xml with a "replacement text" (something like %%REPLACEMENT_EMAIL%%), and when I wanted to use that string programatically, I retrieved the string value of the resource, and replaced instances of that replacement text with the dynamic value (e.g. input by the user).
To be honest, my app has not been localized yet, but I'm still attempting to follow best practices w.r.t. not hardcoding any strings.
Use SharedPreferences instead of a Java class. It will give you more versatility if you decide to take values from the outside (web). Filling Java class in runtime can be useless offline. In case of SharedPreferences you have to ensure they are loaded only once, during app's first start, and then updated only by manual request, as previous import will be used.
myActivity.getSharedPreferences("com.example.imported",0)
.edit()
.putString("The news",getTheNews())
.apply();
Maybe you want to "modify" the string.xml so when it is required by the activity again it uses the new value, for example to keep a new dynamic title after screen rotation.
First, you can't modify the resource. It's already compiled. You can't modify the R class (what for?) all it's atributes are "final".
So, for the example above you can use onSaveInstanceState() and onRestoreInstanceState() for those properties you wanna keep on display.
According to my knowledge, you can't change resource value(R class value) while app running. why don't try to store on shared preference? I recommend you to use shared preference
I used below method to get the key-value pairs from the API and storing it in HashMap globally. If the key value is not found in HashMap then I will search that key in strings.xml file. It will achieve the purpose of dynamically changing the value of key.
public String getAppropriateLangText(String key) {
String value = "";
try {
HashMap<String, String> HashMapLanguageData HashMapLanguageData = gv.getHashMapLanguageData();
value = HashMapLanguageData.get(key);//Fetching the value of key from API
if (value == null || value.length() == 0) { //If Key value not found, search in strings.xml file
String packageName = getPackageName();
int resId = getResources().getIdentifier(key, "string", packageName);
value = getString(resId);
}
} catch (Exception e) {
value = "";
}
return value;
}

Firestore - How to update a field that contains period(.) in it's key from Android?

Updating a field contains period (.) is not working as expected.
In docs, nested fields can be updated by providing dot-seperated filed path strings or by providing FieldPath objects.
So if I have a field and it's key is "com.example.android" how I can update this field (from Android)?
In my scenario I've to set the document if it's not exists otherwise update the document. So first set is creating filed contains periods like above and then trying update same field it's creating new field with nested fields because it contains periods.
db.collection(id).document(uid).update(pkg, score)
What you want to do is possible:
FieldPath field = FieldPath.of("com.example.android");
db.collection(collection).document(id).update(field, value);
This is happening because the . (dot) symbol is used as a separator between objects that exist within Cloud Firestore documents. That's why you have this behaviour. To solve this, please avoid using the . symbol inside the key of the object. So in order to solve this, you need to change the way you are setting that key. So please change the following key:
com.example.android
with
com_example_android
And you'll be able to update your property without any issue. This can be done in a very simple way, by encoding the key when you are adding data to the database. So please use the following method to encode the key:
private String encodeKey(String key) {
return key.replace(".", "_");
}
And this method, to decode the key:
private String decodeKey(String key) {
return key.replace("_", ".");
}
Edit:
Acording to your comment, if you have a key that looks like this:
com.social.game_1
This case can be solved in a very simple way, by encoding/decoding the key twice. First econde the _ to #, second encode . to _. When decoding, first decode _ to . and second, decode # to _. Let's take a very simple example:
String s = "com.social.game_1";
String s1 = encodeKeyOne(s);
String s2 = encodeKeyTwo(s1);
System.out.println(s2);
String s3 = decodeKeyOne(s2);
String s4 = decodeKeyTwo(s3);
System.out.println(s4);
Here are the corresponding methods:
private static String encodeKeyOne(String key) {
return key.replace("_", "#");
}
private static String encodeKeyTwo(String key) {
return key.replace(".", "_");
}
private static String decodeKeyOne(String key) {
return key.replace("_", ".");
}
private static String decodeKeyTwo(String key) {
return key.replace("#", "_");
}
The output will be:
com_social_game#1
com.social.game_1 //The exact same String as the initial one
But note, this is only an example, you can encode/decode this key according to the use-case of your app. This a very common practice when it comes to encoding/decoding strings.
Best way to overcome this behavior is to use the set method with a merge: true parameter.
Example:
db.collection(id).document(uid).set(new HashMap<>() {{
put(pkg, score);
}}, SetOptions.merge())
for the js version
firestore schema:
cars: {
toyota.rav4: $25k
}
js code
const price = '$25k'
const model = 'toyota.rav4'
const field = new firebase.firestore.FieldPath('cars', model)
return await firebase
.firestore()
.collection('teams')
.doc(teamId)
.update(field, price)
Key should not contains periods (.), since it's conflicting with nested fields. An ideal solution is don't make keys are dynamic, those can not be determined. Then you have full control over how the keys should be.

multiple check or,and in if statement

hi i'm wonder why my if always Toast me : "names Successfully saved!"
i'm try every thing.
public void btnSave_Clicked(View view) {
TextView txtOname = (TextView)findViewById(R.id.txtOname);
TextView txtXname = (TextView)findViewById(R.id.txtXname);
String X = txtXname.getText().toString();
String O = txtOname.getText().toString();
if((X!="") && (O!="")){
DatabaseHelper.insertName(getBaseContext(),((TextView)findViewById(R.id.txtOname))
.getText().toString());
DatabaseHelper.insertName(getBaseContext(),((TextView)findViewById(R.id.txtXname))
.getText().toString());
Toast.makeText(this,"names Successfully saved!",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this,"E",Toast.LENGTH_SHORT).show();
}
}
}
Strings are reference types in Java, and thus the reference of a dynamically created empty string will be different from your variables. Another option to isEmpty is equals.
if (!x.equals("") && !o.equals(")) {
//code
}
Though I'd probably go with isEmpty
Replace your if statement with:
if (!x.isEmpty() && !o.isEmpty()) {
//code
}
operator == compares Object reference.
.equals() compares String value.
.isEmpty() return true if String length is 0.
Strings are objects. Object instances (the value behind them) have to be compared manually with a method to assure that the content is the same.
The == operator just compares the string references ("adresses"). So when you create 2 object instances at runtime, they have different adresses even if the content is the same. Compile-time strings on the other hand are internalized, they are put into special memory and duplicates are sorted out.
System.out.println(new String("test") == new String("test"));
This will print false, because those 2 objects get created at runtime. The new keyword in the first example mandates that a new object with a new adress is created.
System.out.println("test" == "test");
This will print true, because they are String literals, which are known at runtime, you are not explicitly stating the new keyword here either. You are simply specifying that you want those literals represented in the code somehow, so the compiler internalizes them.

integer value comparison not working properly

I've a string value String str="Success"
How to check the "str" value is not equal to "Success". If I try if(str!="Success"). It is not working properly.
I've a integer value int a=250 If I use If(a!=250) this is also not working properly.
How to code for these conditional statements ? I'm using Android and Eclipse version 2.1.
Any help would be appreciable.
a!=250 should work.
For string try str.equals("Success")
As every one said, String values should compare with .equals method.
like
if(str.equals("Success"))
{
System.out.println("My String is Success");
}
Next coming to integer comparison.
As you have said, you have the integer value
int a=250
if(a!=250)
{
// Some code
}
Here already your a value is 250. then you are executing some lines of code if a value is not equal to 250. Then how the condition will execute. if you want to test then
change the a value and then check again as
int a=50
if(a!=250)
{
// Some code===============> Now this code will execute
}
For string comparison, use the standard String.equals() method.
str.equals("Success"); - For Equals
if(!str.equals("Success")) - For Not Equals(your case)
And for int, what is being used, is proper. a!=250 will return false in your case. Hence, it'll not enter the if block.
You should try to study Java first ;)
This is not the way to do a String comparison. For String comparison, you must use .equals(String) method.
String str1 = "str1";
String str2 = "str2";
if(str1.equals(str2)) {
//do something
}
You should not compare strings with != or =. That won't work in most cases.
new String("test").equals("test")
Should be used for comparison.
!= compares the value and is true if they are NOT equal.
you could also use something like if(a == 250){...}
In Java you cannot use == to compare Strings, you must use:
if(string.equals("example"))
So let's use equals() in your conditional and optimize it:
if(!str.equals("Success"))
this will work

Can you call a string resource with a string?

I have a method that returns one of about 20 possible strings from an EditText. Each of these strings has a corresponding response to be printed in a TextView from strings.xml. Is there a way to call a string from strings.xml using something like context.getResources().getString(R.strings."stringFromMethod")? Is there another way to call a string from a large list like that?
The only methods I can think of is converting each string to an int, and use that to find a string in a string array, or a switch statement. Both of which involve a huge amount if-else if statements to convert the string to an int, and would take just enough steps to change if any strings were added or taken away that I'd be more likely to miss one and have fun bug hunting. Any ideas to do this cleanly?
Edit: Forgot to add, another method I tried was using was to get the resourceID from
int ID = context.getResources().getIdentifier("stringFromMethod", "String", context.getPackageName())
and taking that integer and putting it in
context.getResources().getString(ID)
That doesn't appear to be working either.
No, you can't. The getString() requires the resource id in integer format, so you can't append a string to it.
You can, however, try this:
String packageName = context.getPackageName();
int resId = context.getResources().getIdentifier("stringFromMethod", "string", packageName);
if (resId == 0) {
throw new IllegalException("Unknown string resource!"; // can't find the string resource!
}
string stringVal = context.getString(resId);
The above statements will return string value of resource R.string.stringFromMethod.
You need to use reflection (pretty ugly but only solution) load the R class, and get the relevant field by you string and get the value of it.
this is what I used to do in these kind of situations, I will made a Array like
int[] stringIds = { R.string.firstCase,
R.string.secondCase, R.string.thridCase,
R.string.fourthCase,... };
int caseFromServer=getCaseofServerResponse();
here caseFromServer varies from 0 to wahtever
and then simply
context.getResources().getString(stringIds[caseFromServer]);

Categories

Resources