Get language translation in xml from external json file - android

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..

Related

Change values inside an xml file at runtime [duplicate]

Let's say, on my API call I have a parameter that's called color. Is it possible to edit or modify an existent R.colors.color to assign the color from the API result?
As an example:
I make a call to my API and it returns green, now I want to load my app with i.e (green Toolbar, green TextView color, etc.), is that possible?
My first thought was:
Create a item on colors.xml called demo then assign it a default color, then use this demo color wherever I want (Button, TextView, etc.) Then I thought it could be possible to change this value programmatically with the result from the API so I wouldn't need to create a SharedPreferences or something like that and for avoiding more code.
As #Y.S. said to me
Unfortunately, you WILL have to set the color of the text or view manually everywhere ... :(
I would like if there is other way to do it, since I don't know how many Activities my project will contain, so if is there other way to do it I'm glad to hear other guesses.
EDIT
I'm trying the #Jared Rummler answer and maybe i'm doing something wrong... I've created a simple Json and I put on my Assets I parse the Json and I put it on a GlobalConstant then I made a "simple app".
First of all I have a TextView and a Button which contains the "your_special_color", and the return of it I put the GlobalConstant int as follows :
case "your_special_color":
return GlobalConstant.color;
Then what I tried is my first Activity has 1 TextView and 1 Button as I said before and they have the color "your_special_color" that I don't want to change it, BUT I have an Intent on my Button to open the other Activity that contains the same but with the GlobalConstant.color and it doesn't change.
I tried it doing this (my second Activity):
public class Main2Activity extends AppCompatActivity {
private Res res;
#Override public Resources getResources() {
if (res == null) {
res = new Res(super.getResources());
}
return res;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
Did I miss something?
Oh.. I figured it out I guess is doing this on my MainActivity2 ?
Button btn = (Button)findViewById(R.id.button2);
btn.setBackgroundColor(res.getColor(R.color.your_special_color));
You can create a class which extends Resources and override the methods getColor(int) and getColor(int, Theme).
Example:
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="your_special_color">#FF0099CC</color>
</resources>
Res.java
public class Res extends Resources {
public Res(Resources original) {
super(original.getAssets(), original.getDisplayMetrics(), original.getConfiguration());
}
#Override public int getColor(int id) throws NotFoundException {
return getColor(id, null);
}
#Override public int getColor(int id, Theme theme) throws NotFoundException {
switch (getResourceEntryName(id)) {
case "your_special_color":
// You can change the return value to an instance field that loads from SharedPreferences.
return Color.RED; // used as an example. Change as needed.
default:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return super.getColor(id, theme);
}
return super.getColor(id);
}
}
}
BaseActivity.java
public class BaseActivity extends AppCompatActivity {
...
private Res res;
#Override public Resources getResources() {
if (res == null) {
res = new Res(super.getResources());
}
return res;
}
...
}
This is the approach I have used in one of my apps, Root Check. If you override getResources in your activities and main application class you can change the theme programmatically (even though themes are immutable). If you want, download the app and see how you can set the primary, accent, and background colors from preferences.
If you take a look at the Accessing Resources document, what it says is that ...
Once you provide a resource in your application, you can apply it by referencing its resource ID. All resource IDs are defined in your project's R class, which the aapt tool automatically generates.
Furthermore,
When your application is compiled, aapt generates the R class,
which contains resource IDs for all the resources in your res/
directory. For each type of resource, there is an R subclass (for
example, R.drawable for all drawable resources), and for each
resource of that type, there is a static integer (for example,
R.drawable.icon). This integer is the resource ID that you can use
to retrieve your resource.
What this is saying, essentially, is that pretty much everything held as a resource in the res/ directory is compiled and referenced as an unchangeable constant. It is for this reason that the values of resource elements cannot be changed programmatically/at runtime, because they are compiled. As opposed to local/global variables & SharedPreferences, resource elements are represented in program memory as fixed, unchangeable objects. They are held in a special read-only region of program memory. In this regard, see also Changing value of R.String Programmatically.
What you can do is, to avoid using the same code at a thousand places in your project, create a common function that changes the value of the color in the SharedPreferences and use this method everywhere. I'm sure you knew this already, of course.
To reduce the amount of code you need to add to the project, there is an alternative. I have previously used the calligraphy library which allowed me to fix the font style & color throughout the app. This may be of some good use to you, check it out ...
R class is not supposed to be edited. It merely contains references to your resources.
You will need to set it manually. However, to reduce the burden of setting it manually you can try to use special libraries for preference saving, for instance:
Saber - https://github.com/jug6ernaut/saber
PreferenceBinder - https://github.com/denley/preferencebinder
(full list of similar libraries https://android-arsenal.com/tag/75)
Also, you might want to think about another way of applying styles and passing parameters - consider you would want to add some other parameters like height, width etc. For that purpose, you can define custom attribute in themes.xml/styles.xml:
<attr name="demoColor" format="reference|color" />
then define styles:
<style name="BaseActivity">
</style>
<style name="GreenActivity" parent="#style/BaseActivity">
<item name="demoColor">#00cd00</item>
</style>
<style name="RedActivity" parent="#style/BaseActivity">
<item name="demoColor">#ff0000</item>
</style>
then use that color in your xml like this:
... android:background="?demoColor" ...
and switch between GreenActivity and RedActivity styles in Activity.onCreate:
setTheme(isGreenStyle() ? R.style.GreenActivity : R.style.RedActivity)
setContentView(...)
With the above approach, you will be able to easily configure your styles in xml and it should be less code and easier to refactor in future. (You will still need to have one variable in preference to save whether you have green or red style)
Another way, if you want to show demos of your app with different colors is to use build variants / flavors for loading your app with different colors and styles (it is for build time - not runtime):
app/src/main/res/colors.xml
<resources>
<color name="demoColor">#00cd00</color>
</resources>
app/src/buildVariant/res/colors.xml
<resources>
<color name="demoColor">#ff0000</color>
</resources>
Now you can quickly switch between "main" and "buildVariant" in Build Variants menu and launch your app with different "demo" colors. The same way you can customize a lot of other attributes.
Search for "Build Variants" here http://developer.android.com/tools/building/configuring-gradle.html
You can't change an app's resources, they are all constants. Instead you can save your color in SharedPrefences and use the color from there.
See How to use SharedPreferences in Android to store, fetch and edit values.
If your app already has a R.color.green defined and you just want to access it based on what API returned you use:
int resourceID = getResources().getIdentifier("green", "color", getPackageName());
store hex color codes into sharedpreferences and then use parsecolor function store your all hexcodes of colors into sessions as a string and whenever you want to change color of perticular button ,textview..just retrive that color code from session and use it as
for ex.
session.setString("white","#FFFFFF");
String colorname=session.getString("white");yourtextview.setBackgroundColor(Color.parseColor(colorname);

Android: Handling Internationalization/multi-language during runtime

I'm implementing internationalization for my application. The main part of the internationalization is supporting multi-languages.
One approach for supporting multi-language is, creating multiple values directories under the res/ directory and having strings.xml for the corresponding languages. Example here.
But my requirement is something like this:
The user enters his credentials to login to the application. Based on the language selected while creating an account on this app, the user would have selected a language.
So, on successful login, i'll be making a call to a service that will be returning all the strings in the application. And dynamically i must be associating these string to the labels in the application.
How can the above thing be done efficiently?
One approach that i have thought is, make a call to the service on successful login and store all the information on the Shared Preferences. and then use it.
Is there any other way to do this?
How do i change the text in cases of the xml layout files having android:text=""?
Please share your views regarding the same.
Take a look at Change language programmatically in Android . Whatever you do, you should use Android standard way (resources) instead of reinventing the wheel.
Update:
Due to your strange constraints, if you decide to reinvent the wheel, you could for example create derived classes using the TAG field of the views, something like:
public class LocalizableTextView extends TextView {
public LocalizableTextView(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
setText(MyLocalizableStuff.get(this.getTag());
}
}
and create a static helper class MyLocalizableStuff like this: (needs error checking, etc, just typed out of my head)
public static class MyLocalizableStuff {
private static HashMap<Integer,String> sStringTable=new HashMap<>();
public static String get(Object code) {
Integer intCode=Integer.valueOf((String)code);
String result=sStringTable.get(intCode);
return result;
}
public static void init(Context ctx) {
// read your strings and store them on the stringtable
// you will call this init from onCreate like
// MyLocalizableStuff.init(context)
}
}
This way, you can insert LocalizableTextViews in your XML and assign a (numeric) TAG code that will map to the String and in construction time, will be assigned to the TextView. You could also use Strings as the code, but bear in mind that the HashMap will then be slower.
You could also use a SparseArray to store the string table instead of a HashMap, it will be probably faster.
But again, I wouldn't go this route.

Why is this code giving me an error?

The following code gave me an error
public class SuperImage3Activity extends Activity
{
ImageView image1, image2;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
image1 = (ImageView)findViewById(R.drawable.sourceimage1); // error here
image2 = (ImageView)findViewById(R.drawable.sourceimage2); // error here
}
I indeed created a directory named drawable and placed sourceimage1.jpg and sourceimage2.jpg in the directory.
the recommendation given by Eclipse is,
"As of ADT 14, the resource fields (such as doraemon) are no longer constants when defined in library objects.
This is necessary to make library projects resuable without recompiling them.
One consequence of this is that you can no longer use the fields directinly in switch statements.
You must use an if-else chain instead
"
R.id.imageview1
R.id.imageview2
You have made Xml layout in res/layout from there you link your widgets or views or any other control or you can make your control dynamically. like this
ImageView imageView1=new ImageView(this);
imageView1.setImageResource(R.drawable.sourceimage1);
findViewById is used to find a view that is already part of the view you're calling the method on (usually an activity).
Basically the way it's done is: (in order)
First you design a layout (either in a XML layout file, or programmatically) which lists all your components and desired locations.
You allocate an id to the resource you're interested in (either using android:id="#+id/your_id_here" in your XML, or setId(XX) if your layout is done programmatically)
You set this layout to your activity or as a child of an existing component (usually with setContentView)
Now you can use findViewById using the identifier defined in 2.
findViewById is not used with a resource itself as a parameter (your drawable object).

Android: passing data between views

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

Dynamic Resource Loading Android

I'm trying to find a way to open resources whose name is determined at runtime only.
More specifically, I want to have a XML that references a bunch of other XML files in the application apk. For the purpose of explaining, let's say the main XML is main.xml and the other XML are file1.xml, file2.xml and fileX.xml. What I want is to read main.xml, extract the name of the XML I want (fileX.xml), for example, and then read fileX.xml. The problem I face is that what I extract form main.xml is a string and I can't find a way to change that to R.raw.nameOfTheFile.
Anybody has an idea?
I don't want to:
regroup everything in one huge XML file
hardcode main.xml in a huge switch case that links a number/string to the resource ID
I haven't used it with raw files or xml layout files, but for drawables I use this:
getResources().getIdentifier("fileX", "drawable","com.yourapppackage.www");
to get the identifier (R.id) of the resource. You would need to replace drawable with something else, maybe raw or layout (untested).
I wrote this handy little helper method to encapsulate this:
public static String getResourceString(String name, Context context) {
int nameResourceID = context.getResources().getIdentifier(name, "string", context.getApplicationInfo().packageName);
if (nameResourceID == 0) {
throw new IllegalArgumentException("No resource string found with name " + name);
} else {
return context.getString(nameResourceID);
}
}
There is another method:
int drawableId = R.drawable.class.getField("file1").getInt(null);
According to this blog it's 5x times faster than using getIdentifier.

Categories

Resources