Where to store Android preference keys? - java

When I create preference activity I define all preferences in xml file. Every preference has a key defined in this xml. But when I access preference I write:
SharedPreferences appPreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean foo_value = appPreferences.getBoolean("foo_key_defined_in_xml", false);
Is there any way to avoid referencing "foo_key_defined_in_xml" in hard-coded way?
Maybe there is a possibility to reference it in R style way (not to refer to string)?

I've found that it's possible to store keys in strings.xml and refer to them from preferences.xml just like all other values android:key="#string/preference_enable".
In code you can refer to key by typing getString(R.string.preference_enable)
You can mark the string to not be translated using a <xliff:g> tag. See Localization Checklist
<string name="preference_enable"><xliff:g id="preference_key">enable</xliff:g></string>

You could use a "keys.xml" files in "res/values", but should put something like this, this way you should dont have problem when you are using multiple languages:
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<string name="key1">key1</string>
<string name="key2">key2</string>
...
</resources>
Then you could reference it like a normal string in xml:
....
android:key="#string/key1"
....
or in your java code for example:
SwitchPreference Pref1= (SwitchPreference) getPreferenceScreen().findPreference(getString(R.string.key1));

In strings.xml mark the key as untranslatable:
<string name="screw_spacing_key" translatable="false">Screw spacing</string>
<string name="screw_spacing_title">Screw spacing</string>
<string name="screw_spacing_summary">Set screw spacing</string>
Usage:
<EditTextPreference
android:key="#string/screw_spacing_key"
android:title="#string/screw_spacing_title"
android:summary="#string/screw_spacing_summary"/>
See: Configure untranslatable rows

Try getString(R.string.key_defined_in_xml).

As far as I know there's no better way of referencing preference keys (aside from maybe using a static final String to store the string on the class).
The example given in the SDK docs does the same as what you've given in your example,

What about using a helper class to hide the getString() - instantiate the helper once in each activity or service. For example:
class Pref {
final String smsEnable_pref;
final String interval_pref;
final String sendTo_pref;
final String customTemplate_pref;
final String webUrl_pref;
Pref(Resources res) {
smsEnable_pref = res.getString(R.string.smsEnable_pref);
interval_pref = res.getString(R.string.interval_pref);
sendTo_pref = res.getString(R.string.sendTo_pref);
customTemplate_pref = res.getString(R.string.customTemplate_pref);
webUrl_pref = res.getString(R.string.webUrl_pref);
}
}

Not sure if this post need another answer, put i end up to it like this:
-Extend all the Preference needed and add this code
final static private int[] ATTR_INDEX = {android.R.attr.id};
private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
if(attrs == null) return;
AttributeReader attributes = new AttributeReader().setAttrsIndex(ATTR_INDEX).parse(attrs);
int id = attributes.asResourceID(0);
setKey(AppContext.getIdentifierName(id));
}
In the xml, don't use the key attribute but android:id="#+id/...
And finally to get back the value,
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(getContext());
String preference_1 = SP.getString(AppContext.getIdentifierName(R.id.pref_key_1), null);
This way, you don't have to create a string file who need to be maintain, just create any id on the fly. But you need to be familiar with extending a view and get read the attr to find what you want (Here the id attribute)

Related

java - Set final fields with reflection in Constructor

I'm trying to make a multi-language app with messages inside multiple *.properties files. I've started working on something like this:
public Language(#NotNull Map<String, String> info) {
Validate.notNull(info, "Language information cannot be null");
this.PLUGIN_PREFIX = info.get("PLUGIN_PREFIX");
this.ARGUMENT_CODE = info.get("ARGUMENT_CODE");
// etc...
}
Now, there's a lot of messages, and I don't feel like typing the same thing each time (plus there could me typos which could be an issue...).
The first solution I thought of was to loop through all of the fields that are like that (in caps, final, not static, etc.) and then use reflection to use the field name as a key to set it as the value. Obviously the compiler won't let me because it thinks that the final field hasn't been initialized.
Something like this:
public Language(#NotNull Map<String, String> info) {
Validate.notNull(info, "Language information cannot be null");
Field[] fields = /* TODO get fields */ new Field[0];
for (Field f : fields) f.set(f.getName(), info.get(f.getName()));
}
Is there a way this can work? Or is there a better solution?
Edit: Quick naming conventions question, should these final "constants" be in upper case?
Usually, you don't store text messages directly in constants, but rather just message keys. Then you use these keys to fetch the actual text messages in the map.
You can use a map directly, but in Java, there is ResourceBundle. A ResourceBundle can be loaded directly from a .properties file.
my-bundle_en.properties:
my.message=Hello, world!
my-bundle_fr.properties:
my.message=Bonjour tout le monde!
my-bundle_de.properties:
my.message=Hallo Welt!
Something.java:
public static final MY_MESSAGE = "my.message";
ResourceBundle bundle = ResourceBundle.getBundle("my-bundle");
String text = bundle.getMessage(MY_MESSAGE);
System.out.println(text);

Setting tag to a dynamically generated view in android [duplicate]

Why do I get this Exception?
05-18 20:29:38.044: ERROR/AndroidRuntime(5453): java.lang.IllegalArgumentException: The key must be an application-specific resource id.
05-18 20:29:38.044: ERROR/AndroidRuntime(5453): at android.view.View.setTag(View.java:7704)
05-18 20:29:38.044: ERROR/AndroidRuntime(5453): at com.mypkg.viewP.inflateRow(viewP.java:518)
the line in question is:
((Button) row.findViewById(R.id.btnPickContact)).setTag(TAG_ONLINE_ID,objContact.onlineid);
and I have it defined as:
private static final int TAG_ONLINE_ID = 1;
The reason you're not able to use setTag(int, Object) is because android require a pre-compiled unique id in the 'int' argument.
Try creating two unique entry in String.xml xml say, "firstname" & "secondname" & use them as below
imageView.setTag(R.string.firstname, "Abhishek");
imageView.setTag(R.string.lastname, "Gondalia");
I'm a little late to the party but I stumbled on this problem myself today and thought I'd give an answer as well. This answer will be a bit of a compilation of the other answers, but with a twist. First of all, the id, as has been pointed out by others, can NOT be a constant defined in your code (such as private static final int MYID = 123) or any other int that you define as a field somewhere.
The id has to be a precompiled unique id, just like the ones you get for strings that you put in values/strings.xml (ie R.string.mystring). Refer to http://developer.android.com/guide/topics/resources/available-resources.html and http://developer.android.com/guide/topics/resources/more-resources.html for more information.
My suggestion is that you create a new file called values/tags.xml and write:
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<item name="TAG_ONLINE_ID" type="id"/>
</resources>
I think it's better to create a separate file instead of putting it in strings.xml as EtienneSky suggested.
THIS WILL DO THE JOB...
If you just have 1 setTag in your class, you could use any int, maybe static final declared in the top.
The problem comes when you had 2 or more setTag's with different keys.
I mean:
public static final int KEY_1 = 1;
public static final int KEY_2 = 2;
...
setTag(KEY_1, VALUE_1)
setTag(KEY_2, VALUE_2)
...
That scenario is wrong. You then need to add a value file called maybe ids.xml with the following:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="resourceDrawable" />
<item type="id" name="imageURI" />
</resources>
Then, in your class, call:
...
setTag(R.id.resourceDrawable, VALUE_1)
setTag(R.id.imageURI, VALUE_2)
...
The tag id must be unique so it wants it to be an id created in a resources file to guarantee uniqueness.
If the view will only contain one tag though you can just do
setTag(objContact.onlineid);
private static final int TAG_ONLINE_ID = 1 + 2 << 24;
should work. More info from ceph3us:
The specified key should be an id declared in the resources of the
application to ensure it is unique Keys identified as belonging to the
Android framework or not associated with any package will cause an
IllegalArgumentException to be thrown.
from source:
public void setTag(int key, final Object tag) {
// If the package id is 0x00 or 0x01, it's either an undefined package
// or a framework id
if ((key >>> 24) < 2) {
throw new IllegalArgumentException("The key must be an application-specific "
+ "resource id.");
}
setKeyedTag(key, tag);
}
I've used viewHolder.itemTitleTextView.getId(). But you can also declare in your resources:
<item type="id" name="conversation_thread_id"/>
you can use this :
private static final int TAG_ONLINE_ID = View.generateViewId() + 2 << 24;
for uniqness application-specific resource id
This works for me:
setTag(0xffffffff,objContact.onlineid);
The reason why you want to save the value by an id is, that you want to cover more than one value in this tag, right?
Here a more simple solution:
Let's say you want to save two values (Strings) into this tag: "firstname" and "lastname". You can save them both in one string, separated by semicolon:
v.setTag(firstname + ";" + lastname);
... and access them by splitting them into an string array:
String[] data = v.getTag().toString().split(";");
System.out.println(data[0]) //firstname
System.out.println(data[1]) //lastname
Here is a simple workaround that works for me:
int tagKey = "YourSimpleKey".hashCode();
myView.setTag(tagKey, "MyTagObject");
the important clue here is to call .hashCode(); on the String

Is it possible to use resource IDs instead of their values for xml preferences in Android?

The Goal
In my preferences.xml I would like to store the ID of certain string elements (like) e.g. ...
<string-array name="preference_values">
<item>#string/nav_start</item>
<item>#string/nav_overview</item>
<item>#string/nav_someelse</item>
</string-array>
... meanwhile in xml/pref_general.xml ...
<ListPreference
android:key="#string/preference_key"
android:entryValues="#array/preference_values"
... />
... where the different strings will be localized:
<string name="nav_start">Startbildschirm</string>
<string name="nav_overview">Übersicht</string>
<string name="nav_someelse">Sonstige</string>
Since I don't want to store the localized strings (as those xml definitions do right now), but rather their integer keys, I would really like to know if there is a way of doing this.
This would make the preferences still available after a language change for example.
More Background
I really like being able to access/compare resources in code, which are not subjective to typos or name changes (or at least the IDE checks/handles those for you) e.g.:
switch( getMyPreferenceAsInt(R.string.preferenece_key) ) {
case R.string.nav_start: doSome(); break;
case R.string.nav_someelse: doSomeElse(); break;
...
}
I use something similar for my NavigationDrawer, and find it really nice to work with. This is also where does string values actually come from (it's like a "default start page" preference).
<string-array name="navigation_keys">
<item>#string/nav_start</item>
<item>#string/nav_overview</item>
<item>#string/nav_someelse</item>
...
</string-array>
But at least here I am able to get the keys with obtainTypedArray(...):
void onCreate(Bundle savedInstanceState) {
...
mNavKeys = getResources().obtainTypedArray(R.array.navigation_keys);
}
void onNavigationDrawerItemSelected(int position) {
Fragment fragment;
int selectedResource = mNavKeys.getResourceId(position, R.string.nav_default);
if (selectedResource == R.string.nav_start) {
fragment = FancyStartFragment.newinstance();
}
else {
fragment = ...;
}
// Fancy methods to do a fragment transaction
...
}

Is there any way to hide some chars from a string?

For example I have Hello123456 string and I want to show it as just Hello in a TextView.
I need those numbers in my code and I can't erase them.
EDIT:
All answer are providing a way that I erase the numbers and then set it to textview, But I want them to be there.
I get the "hello" part from user and I add the number part to it and this will be the name of a listview Item. but I want to show just the hello part in listview and also if I checked if that listview clicked item ends with "123456" it returns true.
I want to show it as just "Hello" in a TextView.
Then just put Hello in your TextView without cutting your string or create an temporary string to hold the "Hello" String.
If your string is like this "HELLOHI12345" then you need a regex to eliminate all the number string within it.
sample:
textview.setText(s.replaceAll("[0-9]+", ""));
Also take note that string are immutable so the original String wont get replaced after executing replaceAll
Use this dude ! :) You can use contextDescription to access the actual text
textView.setText(yourText.replaceAll("[0-9]",""));
textView.setContentDescription(yourText);
textView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
String str = ((TextView) v.getContentDescription()).toString();
}
});
A TextView can have a tag attached to it i.e. some object that is stored as extra data to the view itself. Check out the documentation here:
View.setTag(Object)
In your case, you can assign the "Hello" to the displayed text (using the method below), and set the tag to the full text.
textView.setText(getDisplayText(helloString));
textView.setTag("TAG", helloString);
When the user clicks on your view, you can get the tag from the view and do what you need with it.
This way, there is more data stored than what you see.
(original answer before the edit)
How about a method that does what you need:
private String getDisplayText(String input)
{
return input.substring(0, 5);
}
And then just use that method when you set the value of your TextView.
textView.setText(getDisplayText(helloString));
use \\w+ regex with Pattern and Matcher classes. This will also work if you don't know the String length. The String can be hello123 or olo123
Its not possible to hide the characters in a string. You have to solve your problem in a different way. My suggestion is to implement a separate class which contains the string:
I would write an class which contains the string. Then add two methods to this class which returns the string for display and for internal use. E.g.
class DataObject {
private String data;
public String forTextView() {
//code from one of the answers above, may be the regex version
}
public Stringg getStringWithNumbers() {
return data;
}
}
This allows you to show the string without the numbers and still have them ready when you use them.

How to add a string in string.xml using Java code, in Android?

I have created two EditText objects in the Java code of My android application, as
final EditText et1=(EditText)findViewById(R.id.editText1);
final EditText et2=(EditText)findViewById(R.id.editText2);
then on the onClick() event of a button, called a method with parameters as-
addStringToXmlFile(et1.getText(),et2.getText());
now in the definition of this method below, i have written-
private void addStringToXmlFile(Editable editable1,Editable editable2){
String s1=new String();
s1=editable1.toString();
String s2=new String();
s2=editable2.toString();
}
The problem is that, now i want to use these two String objects s1,s2, to make two entries in the res/values/Strings.xml file of the database, & I don't know how to do it.
please guide me further.
This is not possible - an app's apk (including it's resources) can not be changed at runtime. I'm not entirely sure of all of the reasons why, but one obvious thing I can think of is that R.java needs to contain a reference to your String in order for you to access it, and this file is generated by the compiler when you create the APK.
If you need to persist a String across sessions, you should look into using one of several data storage mechanisms that Android provides, such as SharedPreferences.
Take a look into using SharedPreferences to store your string values instead.
//Saving your strings
SharedPreferences prefs = this.getSharedPreferences("myPrefsKey", Context.MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putString("s1", s1);
editor.putString("s2", s2);
editor.commit();
//retrieving your strings from preferences
SharedPreferences prefs = this.getSharedPreferences("myPrefsKey", Context.MODE_PRIVATE);
String s1 = prefs.getString("s1", ""); //empty string is the default value
String s2 = prefs.getString("s2", ""); //empty string is the default value

Categories