Is there any way I can add items in code to a String-Array resource? For instance, if I want to create a spinner that shows the user values, and I want to allow the user to add their own custom values.
No. this is not supported because resources are packaged in the binary .apk and as such cannot be changed.
Don't follow this design pattern, change your approach.
probably JoxTraex has no idea on android framework or as they are saying:
when someone says this can't be done - there always is someone who doesn't know that and will do it :)
so to the point:
Resources is an open class is just a wrapper around ResourcesImpl class
(with depreciated constructor - but is available)
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config)
in app every call to it is made via Context
so:
1) you always can provide your own Resources implementation
2) or (when its enough) your own Context implementation (so sky is the limit)
... i now feel eyes on "Context implementation" - yeah you can even replace top most context in LoadedApk, Activity, etc.. yeah cool? but when, how? how: via reflections, when: good app designer knows that this place is where (before) first/any call to such object is made ...
But there is one dangerous catch here - in one case android straight unwrap Context to ContextImp and then you need to return those Context instead yours - where is the place that i'll keep as secret for those who like riddles :)
there is also in Resources
a) a hidden constructor (best entry point as it is not making any ReourceImpl (which can be set by method mentioned bellow)
public Resources(#Nullable ClassLoader classLoader)
b) hidden method
setResImpl(ResourcesImpl)
sum up:
so by calling
Resources.getStringArray(**not existing in APK resource id here**);
on our own implementation we could get whatcha want :)
even better we can add to our implementation
addResourceById(int,Object)
method and add new resources :) then with assign ability check on resource we will use and do a up casting to our implementation :) to use our newly added method :)
btw: if someone says to you "you shouldn't do it - blablabla" this is the best reason to do it! - if not permitted by law :)
enough the theory go to a practice:
example Context implementation forwards calls to getString:
public class MyContext extends Context {
....
// override context get resources method
#Override
public android.content.res.Resources getResources() {
// get super resources
android.content.res.Resources resources = super.getResources();
// pull assets
android.content.res.AssetManager assets = resources.getAssets();
// pull metrics
android.util.DisplayMetrics displayMetrics = resources.getDisplayMetrics();
// pull configuration
android.content.res.Configuration configuration = resources.getConfiguration();
// construct new anon resource implementation
return new android.content.res.Resources(assets, displayMetrics, configuration) {
// overrride interesting method
#android.support.annotation.NonNull
#Override
public String getString(int id) throws android.content.res.Resources.NotFoundException {
return id == pl.ceph3us.base.common.R.string.my_sweet_google
? "fck_you_google";
: super.getString(id);
}
};
}
}
Related
As a part of an app that I am developing there is request that it has to download JSON file that contains language translation that needs to be used in app instead of strings.xml file that is commonly used because this way any translation in app can be changed by updating external JSON file on some web portal and it avoid the need to make new build every time you want to change language translation.
I've already done this, and everything is working fine in a following way:
For example If I have button in xml once the activity starts I can reference the button and set the it's text from JSON that I've downloaded at the startup.
btnLogin = (Button) v.findViewById(R.id.btnLogin);
// reads translation from json that is stored in external application directory
btnLogin.setText(ResourceManager.getString("btnLogin"));
But my question is is there any way that I can avoid setting this text always from activity, can I somehow do it from XML file where this button is defined?
<Button
android:id="#+id/btnLogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="{ResourceManager.getString("btnLogin")}"/>
Is there any possibility that let's me call some function from xml and bind it's result to text attribute or is there any other way which avoids referencing all buttons/textviews and other controls from activity and setting text from there?
Have you tried overriding getResources() in you Application class and then
returning an extended version of Resources.
Ok, I tried this and it seemed to work for most of the resources in my xml file.
You may have to override this in more than one place. I just overrode it inside my Application class.
private MyResources resources;
#Override
public Resources getResources() {
if (resources == null) {
resources = new MyResources(super.getResources());
}
return resources;
}
class MyResources extends Resources {
public MyResources(Resources resource) {
super(resource.getAssets(), resource.getDisplayMetrics(), resource.getConfiguration());
}
public String getString(int resId) {
return "Bob"; // Use your ResourceManger here
}
}
This worked for me..
Everything I've read about Intents talks about using them to push data, or to start one Activity from another Activity. I want to pull data from an Activity that's already running.
The Tab Layout tutorial at http://developer.android.com/resources/tutorials/views/hello-tabwidget.html illustrates what I want to do. (My app is doing some engineering calculations instead, but the tutorial code provides a good analogy to my app.) The tutorial creates an app with three tabs, and each tab hosts a separate activity.
To expand on the example in the tutorial, suppose I select an artist in the Artists tab/activity. I want to be able to select the Albums tab/activity and have it display all the albums featuring that artist.
It seems to me that I need to use an Intent to do this. All of the tutorials I've found assume that I would create a "See albums" Button in the Artists tab/activity, and that pressing the Button would execute an Intent that starts the Albums activity and passes artistName.
I DO NOT want to create that Button. Real estate on the Artists layout is precious, and I have a perfectly good Albums tab, AND the HelloTabWidget activity already contains an intent to create the Albums tab.
Besides, a user will want to skip back and forth between Album and Artist in order to change artist selections, and the tabs are a perfectly good way to do this. There's no need to complicate the UI with another button.
So how can I have the Albums activity PULL artistName from the Artists activity when the Albums tab is selected (or the Albums layout is displayed), rather than have the Artists activity START Albums and PUSH the artistName?
Equivalents I can think of from other programming worlds:
Global variables. Discouraged in Android devt, right? And if they do exist, what are they called?
A getter, like artistName = Artists.getArtistName(); . I get the feeling that it's not that easy.
Writing to, and reading from, a file - that is, mass storage or non-volatile memory. I don't need the artistName value to be permanent. It will be reset to null every time the user launches the application.
So how is it done in the Android world? Do I use an Intent - and if so, how?
Global variables were the right answer.
I thought Java discouraged their use, but a couple of links that appeared in the "Related" links on the right margin of this window mentioned them directly. One was "Android: How to declare global variables?" and the other was "how to pass value betweeen two tab in android". Both pointed to the Application Class as the place to define global variables and methods. Armed with this new knowledge, I found an article called "Android Application Class" on the Xoriant blog that expanded on the StackOverflow answers.
It's best to review those three links first. I need to add some tips to what those authors have said.
Your Application class has to be in its own separate file. (That might be a "duh" to some people, but not to everybody.) Here's a good framework for an example called Something.java:
public class Something extends Application {
// Put application wide (global) variables here
// Constants are final, so they don't have to be private
// But other variables should be declared private;
// use getters/setters to access them
public final boolean FEET = false;
public final boolean METERS = true;
private boolean units = FEET;
#Override
public void onCreate() {
super.onCreate();
// Put any application wide (global) initialization here
}
// Put application wide (global) methods here
public boolean getUnits() {
return units;
}
public void setUnits(boolean whichOne) {
units = whichOne;
}
}
I'm using Eclipse with the ADT plug-in, in Windows XP. Eclipse doesn't always behave properly if you edit XML code directly, so it's best to open AndroidManifest.xml, then select the Application tab and enter your application name in the Name field. You don't need to put a dot or period in front of the name. Just type in the name of your class, like "Globals" or "MyApplication" or whatever. (Note that this is the default application in your Manifest. You don't have to create a separate <application></application> tag.
This step may not be necessary on an actual Android device, but it was necessary for the emulator: you need to use the getApplicationContext() command in every onCreate() and every method that will be accessing the global variables and methods. I tried to put it outside of onCreate() with the rest of my activity wide variables, and it didn't work. Putting it inside every method seems wasteful, but both the emulator and the Android device work fine with it that way. Here's a sample showing how I used it:
public void fooBar() {
// Access to global variables and methods
final Something s = (Something)getApplicationContext();
// ...
// This next line demonstrates both a global method and a global variable
if (s.getUnits() == s.FEET) {
// do something with feet
} else {
// do something with meters instead
}
// ...
}
Those were the only hiccups I encountered. The three references that I have listed, taken together, are otherwise pretty complete.
Background
I'm trying to keep an app which is as modular as possible.
The app will have have tasks which it performs on different intervals. My goal is to make it as easy possible to add new tasks with minimal understanding of the underlying architecture and without having to modify other files but at the same time not over complicating the code.
It would be perfect if all you needed to do to add a new task is to create the file and that's it.
This will require loading the tasks in runtime which I don't really like, I could live with a single place where all the registration is done (this also enabled toggling of the task)
Right now I've have an abstract task class which has a piece of static code which registers all tasks (basically adds them to a list).
Problem
Each task will have their own set of preferences and possibly resources.
Dividing strings and array is pretty easy by using prefixes for the names but the main issue comes with the preferences.
Right now I'm using a PreferenceActivity to display my preferences.
The general settings are loaded from an XML file. Each task's preferences are located in a separate PreferenceScreen. All tasks have only one thing common and that is an "Enabled" checkbox.
I don't want to store all the preferences in one file as it has the possibility to get quite messy that way.
Current solution
Right now each task have a method setupPreferences(PreferenceScreen) in which they can add whatever options they want. This however has the downside of being programmatically which is not all that bad but I'd like to avoid that if possible.
Desired solution
The optimal solution would be if each task could have their own XML file which is loaded and added to the root PreferenceScreen, as far as I know however this is not possible, the only way to load it is into a PreferenceActivity.
Other notes
If anyone has any other suggestions on dividing resources in android feel free to share them :)
Thanks
Nicklas
Clarification
The tasks I'm talking about are never third party, they will internal only. This is more of a way to early on get a good structure of this app.
By using reflection I'm calling PreferenceManager.inflateFromResource(Context, int, PreferenceScreen) to create a PreferenceScreen from my XML files.
String resources are separated in separate files and prefixed with taskname_
Here is the code for inflating the PreferenceScreen, it should be placed in a PreferenceActivity:
/**
* Inflates a {#link android.preference.PreferenceScreen PreferenceScreen} from the specified
* resource.<br>
* <br>
* The resource should come from {#code R.xml}
*
* #param resId The ID of the XML file
* #return The preference screen or null on failure.
*/
private PreferenceScreen inflatePreferenceScreenFromResource(int resId) {
try {
Class<PreferenceManager> cls = PreferenceManager.class;
Method method = cls.getDeclaredMethod("inflateFromResource", Context.class, int.class, PreferenceScreen.class);
return (PreferenceScreen) method.invoke(getPreferenceManager(), this, resId, null);
} catch(Exception e) {
Log.w(LOG_TAG, "Could not inflate preference screen from XML", e);
}
return null;
}
Here is an example of how to use it:
package com.example;
import java.lang.reflect.Method;
import com.example.R;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.util.Log;
public class ExamplePreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "ExamplePreferences";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Sets the preference name
PreferenceManager pm = getPreferenceManager();
pm.setSharedPreferencesName(PREFERENCE_NAME);
// Adds default values and the root preference screen
PreferenceManager.setDefaultValues(this, PREFERENCE_NAME, MODE_PRIVATE, R.xml.preferences_layout, false);
addPreferencesFromResource(R.xml.preferences_layout);
PreferenceScreen root = getPreferenceScreen();
// Includes R.xml.other_preferences_layout and adds it to the bottom of the root preference screen
PreferenceScreen otherPreferenceScreen = inflatePreferenceScreenFromResource(R.xml.other_preferences_layout);
root.addPreference(otherPreferenceScreen);
PreferenceManager.setDefaultValues(this, PREFERENCE_NAME, MODE_PRIVATE, R.xml.other_preferences_layout, false);
}
/**
* Inflates a {#link android.preference.PreferenceScreen PreferenceScreen} from the specified
* resource.<br>
* <br>
* The resource should come from {#code R.xml}
*
* #param resId The ID of the XML file
* #return The preference screen or null on failure.
*/
private PreferenceScreen inflatePreferenceScreenFromResource(int resId) {
try {
Class<PreferenceManager> cls = PreferenceManager.class;
Method method = cls.getDeclaredMethod("inflateFromResource", Context.class, int.class, PreferenceScreen.class);
return (PreferenceScreen) method.invoke(getPreferenceManager(), this, resId, null);
} catch(Exception e) {
Log.w(LOG_TAG, "Could not inflate preference screen from XML", e);
}
return null;
}
}
This example would use res/xml/preferences_layout.xml as the root and then add res/xml/other_preferences_layout.xml to the bottom of the root.
Not exactly an answer to your question, but might be interesting anyway: have a look at plugin API for Locale and Tasker: http://www.twofortyfouram.com/developer.html
Locale and Tasker are phone automation apps. Both are highly configurable and modular, they accept third party plugins to extend their functionality. Similar to your case, each plugin has unique preferences. Their solution to the problem is, each plugin brings its own preferences activity, accessible using specific intent action. There are UI guidelines so that preferences screens look consistent.
On subclasses of View there is a getTag() method, which returns the android:tag attribute's value from .xml.
I would like the same for a MenuItem... is it okay to just cast it to a View?
Because item elements also allow a tag attribute in .xml...
Update: My goal with this is setting a tag in .xml, i.e. "notranslate", and querying it at runtime (we localize by hand at runtime, don't ask...)
It is always alright to cast, however, casting any Interface cannot be checked at compile time, only runtime. This is normally the reason many do not recommend casting an Interface that you have no control over. Having the proper error checking code is the best way to insure that such a cast does not break your code.
For the casting, it doesn't really matter whether the MenuItem is an Interface or a View, but the object it references must be one of View's subclasses, if not a View itself. If you are going to cast it, try the cast and catch a ClassCastException just in case as this is the error that will be thrown in runtime.
Another option is that since the MenuItem is simply an interface, you can easily just create a View subclass that utilizes MenuItem allowing you to do the cast. If you are doing a custom ContextMenu as many launchers do, then chances are your answer is nearly complete.
Hope this helps,
FuzzicalLogic
MenuItem is an interface. Any class can implement this interface and so it will not always be safe to cast the MenuItem to a View. You can use the "instanceOf" operator to test to see if the object that implements the MenuItem interface is indeed a View or not.
I understand that you want to define a flag in the XML definition of the menu and then at run time interrogate that flag to make a programmatic decision.
The Menu Resource Documentation records what attributes can be set in the XML. You can consider using (abusing) one of those settings such as the "android:alphabeticShortcut" to encode the flag and use the MenuItem::getAlphabeticShortcut() method to get the value. This does not require casting - it just uses the existing fields in the MenuItem XML construct/class for your own purposes.
Perhaps a less hacky way to do this is to keep a simple table in a separate assets file that lists the menu item identifiers and the special behavior associated with that identifier such as to translate or not to translate.
Alternatively create a simple class that has a table with this configuration information hard coded using the logical "#[+][package:]id/resource_name" resource identifier as the keys to the table. While this doesn't keep it all in one place (in the XML) it does it in a manner that is not encoding information in unused attributes, or relying on the ids not changing. The "table" could be implemented as a static method with an embedded switch statement allowing code such as "if (TranslationTable.shouldTranslate(menuItem.getItemId())) { do translation }"
I had a similar problem in that I wanted to associate some arbitrary data with each menu item so that I could handle menu items in a generic way without having to use hardcoded checks for individual item ids in code.
What I did was for a particular menu item (e.g. #+id/foo) There was an a TypedArray that was defined using the same name as the menu item ID. You could do this with other types of resources as well.
So to do the association, you get the resouce entry name (foo in my example) and then use that to look up the id of the other resource of a different type (#array/foo in my example).
In my handler for menu I had code like this:
Resources resources = getResources();
String name = resources.getResourceEntryName(item.getItemId());
int id = resources.getIdentifier(name, "array", "com.example");
if(id != 0)
{
TypedArray data = resources.obtainTypedArray(id);
// Use the typed array to get associated data
}
EDIT:
Actually it is even easier than that. There is nothing special about the ids on menu items other than you don't want multiple menu items with the same id. The id does not have to be of the form #+id/foo. It can actually also refer to other resources. So in my example above, instead of having the menu have an id of #+id/foo and using the resource manager to use that to find #array/foo, I changed to actually have the menu item have the id of #array/foo.
Now in my onOptionsItemSelected I have this:
Resources resources = getResources();
if("array".equals(resources.getResourceTypeName(item.getItemId())))
{
TypedArray data = resources.obtainTypedArray(item.getItemId());
// Use the typed array
}
In my Android app, I have two custom view classes - PortraitClass and LandscapeClass. They both do the same thing. On running the app, the view class fetches some pictures from an SDCard and then maniputlates (skew etc) and displays them. The only difference between the two classes is that the layout of pictures is slightly different on the screen.
I have two display.xml files (one under layout folder and the other under layout-land). The one under the layout folder adds Portrait and the other adds the Landscape class.
On orientation change, I would like to send information (picture numbers and few bitmaps) from one class to another so I won't have to fetch all the bitmaps again and also so that I display the ones that were being displayed.
I find the parcelable thing kind of confusing. I tried following this_example, but I noticed that in onRestoreInstance, the Parcelable has null for the "mSuperState" and I get a classCastException # "SavedState ss = (SavedState)state". The integer (picture number) that I am trying to pass is there. I am not sure what I am doing wrong.
You could use a global singleton in the Application class. For example an "AssetManager" is available here:
public class YourApplication extends Application {
public AssetManager assetManager = new AssetManager();
#Override
public void onCreate()
{
super.onCreate();
}
}
You can then call it from another activity:
YourApplication application = ((YourApplication ) this.getApplication());
application.assetManager.someFunction();
Not sure if this is what you are looking for but my stuff always works in orientation change with onCreate(Bundle savedInstanceState). Mine is all text in edit text boxes though so I am not sure how it would work for you.
Also check this out about midway down under "Persisting State Information During Configuration Change"
http://www.devx.com/wireless/Article/40792/1954