I am writing a client (android) /server (java) application. Server returns true or "false and error code" for every request made by client. I added every error message (string) to strings.xml in client side.
This is how i keep error codes;
public class ErrorCodes {
public static final int WRONG_USERNAME = 1;
public static final int WRONG_PASSWORD = 2;
public static final int WRONG_EMAIL= 3;
...
}
The problem is, everytime server sends an error code, i have to handle it like that (roughly);
int code = conn.receive();
if(code == ErrorCodes.WRONG_USERNAME)
print(getResources().getString(R.string.WRONG_USERNAME_ERROR));
else if(code == ErrorCodes.WRONG_PASSWORD)
print(getResources().getString(R.string.WRONG_PASSWORD_ERROR));
...
There are two approaches in my mind to ease this process but both not elegant.
Approach 1: Making ErrorCodes values equal to R.string values manually. Also with this approach i don't need to keep ErrorCodes class in client side too.
Problem: I have to update both ErrorCodes and strings.xml everytime i add a new error message.
Approach 2: Use generated R.java file in server, no need to update anything. This is the best i can think of.
Problem(s): There will be a lot of unnecessary stuff comes with R.java. I have to use it like that in server side: R.string.xyz. And if server is not coded with java, whole approach is useless.
If i use one of these, i can do this in client side;
int code = conn.receive();
print(getResources().getString(code));
I think there is a better way i can not think of. Because both way sounds painfully. How to handle this kind of situation?
I would suggest to keep your approach.
It will leads you to write some code for handling all the cases but at the end, for me, is the most robust solution because:
Your server won't be Android dependent. The logic behing your server will be valid for Android only. In case tomorrow you will develop one client for another platform it would be much better to have a platform-indipendent protocol.
Every time your edit your app, adding new resources and so on, your R file is generated again and R.string.something could have a different value than the previous build. So, in the situation you are rolling out an app update your users could experience strange errors because in the versione 1 of your app the resource 12345 could point to R.string.something and in the version 2 could point to R.string.something_else. But maybe for this point there is a method for keep a static R.string value.
The following solution seems acceptable to me:
Your server returns some code for your errors, for example:
1 for WRONG_USERNAME
2 for WRONG_PASSWORD
3 for WRONG_EMAIL
you could declare the string is this way:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="error_1">Wrong username!</string>
<string name="error_2">Wrong password!</string>
<string name="error_3">Wrong email!</string>
</resources>
And then you load the string resource by name:
int stringId = getResources().getIdentifier("error_"+errorCode,"string",getPackageName());
I use the same method you described in approach 1
If you only want to print errors, think about using json to send data from serwer, e.g.:
if(logged_on == true){
server.send("{\"status\":0}");
}else{
server.send("{\"status\":1,\"error\":"+ErrorString);
}
Then on the client side check the status field, and if equals 1 print string from the error field.
The best solution is obviously to keep error description strings on server, but that could introduce additional complexity for localization. You would need to send device's current interface language to the server to get correct error description
Otherwise I would use a short textual error identifiers on server side (e.g. "no_data_found", "db_timeout", etc) - something which makes sense and makes it easier to debug and modify string table for the android app.
On the client side, I would add corresponding error description strings, with identifiers following some standard scheme. For example: "error_no_data_found", "error_db_timeout".
Then, to retrieve error description string, you could use a reflection:
String getErrorString(String stringName)
{
try
{
Field f_a =R.string.class.getDeclaredField(stringName);
int a = f_a.getInt(null);
return getString(a);
}
catch (NoSuchFieldException x)
{
return "No error description for "+stringName;
}
catch (IllegalAccessException x)
{
return "";
}
}
...
String errorDescription = getErrorString("error_"+errorCodeReceivedFromServer);
Also, you can write a script to check if all of the server error identifiers have corresponding entries in strings.xml file.
Related
In my app, i generate and save private keys. They are encrypted using AES as described here (http://nelenkov.blogspot.com/2012/05/storing-application-secrets-in-androids.html). I've seen another entry on smiliar subject (http://nelenkov.blogspot.com/2012/05/storing-application-secrets-in-androids.html), but this solution gives no guarantee to work on all devices.
Everything is fine except one thing. Everytime usertime wants to get access to one of the encrypted files (or create new one) he needs to enter pin/password (which is very frustrating). I'm looking for a way to somehow avoid this, so as long as user don't leave aplication his password is stored.
The only solution i found is to load everything after user enters password and keep it in memory, but I don't think it's good one.
Cache your keys for a certain period of type (say 10 mins) and clear them afterwards. You can set alarms using AlarmService for this. There was a library project that does this for you, but I haven't used it personally:
https://github.com/guardianproject/cacheword
Why not create a custom Application subclass, and store the password there?
So something like:
public class App extends Application {
private static String sPassword;
public String getPassword() {
return sPassword;
}
}
Then use it:
String password = ((App) context.getApplication()).getPassword();
if(password != null && !password.isEmpty() {
// Decrypt files
} else {
// Prompt user for password, and save it to the Application
}
You'll also have to specify your custom Application in your manifest:
<application android:name="com.my.awesome.App" ....
My problem is all the texts comes from webservices. So, at the beginning, I call the webservice and I have to set the texts in strings.xml. I know writing in strings.xml it's impossible but we had the same problem on iOs and a solution has been found here http://iosapplove.com/archive/2013/01/localizable-strings-how-to-load-translations-dynamically-and-use-it-inside-your-iphone-app/.
So my question is : Is there a similar way on android ? (I think no)
It exists alternative way like using database or sharedPreferences but these solutions won't be very effective. Moreover, the application will contain many languages.
So my second question : What is the best way to do this ?
As you already know, the strings.xml files will not exist in the APK. This means that there is no way to change what getResources().getString(R.string.mystring) will return.
You say that "it's a request from customer". My suggestion is that you tell the customer "this is a bad idea". Because it really is.
If the customer persists I guess your only option is to download the texts in whatever language it is that you need and store it in your apps external file storage. The suggestion to use a properties file is good. Load it as an InputStream and get strings from it.
Have you considered what will happen if the app is in flight mode or does not have a network available when started? Will you provide default values?
Also, with this solution you will not be able to set any texts in TextViews or similar in XML since the system won't have access to your downloaded translations in the properties files.
If you still go ahead with this I'd also suggest that you wrap the Properties file into some other class and fall back to whatever texts you have in your strings.xml if a text doesn't exist in the properties file or if it wasn't downloaded for some reason:
class TextRetriever {
// the downloaded texts go here
private Properties properties;
private Context context;
public String getString(String key) {
String s = properties.getProperty(key);
// not found in properties file
if(s == null) {
// fall back to value in strings.xml
s = context.getResources().getIdentifier(key, "string", context.getPackageName())
}
return s;
}
}
i have a query regarding location files. i want to download localization files from a server (string.xml, string_ar.xml , etc) on the launch of the android application instead of declaring previously. is there any way to achieve this task..
Thanks in advance
If you want to know that the app is running for the first time, you could save a boolean value in the preferences. Something like first_run.
If you download localization files you will not be able to use the default localization support.
(Not sure why you wouldn't just place them in the apk)
What you could do is getting the localization and hitting the server for the correct translations.
You could create your own class that takes care of getting strings and if they do not exist, fail over the android one.
Something like:
public static String getString(Context ctx, int key) {
String ret = getStringFromDB(key);
if ( ret == null ) {
ret = ctx.getString(key);
}
return ret;
}
I need a solution for the really common but important issue that i am thinking of.
I have created application in which i have hard coded my server address in /res/values/strings.xml file. Suppose if my server address changes run-time due to some reasons, then i have to make changes in the file and then i have to recompile it.
So is there any way that i put my server address out of my application.. So we do not have to recomplie the application. Instead of it, it will read new server address from out side and resume its normal work...
Any type of related suggestions,links,blogs appreciated..
While I am making apps in Android , I mostly create a Utility class e.g. Constants.java , Utils.java etc. In these classes, I put all the constant numerical and String values like this:
public static String ip="74.117.153.111";
public static final String LOGIN_TOKEN_URL = "http://"+ ip + "/api/getLoginToken";
public static final String USERNAME_PARAM = "username";
public static final String PASSWORD_FIELD_EMPTY_ERROR = "Please enter password";
public static final int GRID_ICON_COUNT = 4;
public static final String FACEBOOK_KEY = "16411636362877862";
These are some type of constants which we generally use here and there. So I put them into a different dedicated class. So now when I need their value somewhere, I get it like this:
params.put(Constants.USERNAME_PARAM, username);
fbRocket = new FBRocket(this, Constants.APP_NAME,Constants.FACEBOOK_KEY);
The benefit of this approach is that if I have to change a value later on which is being used at lot of places , then I don't have to change it everywhere. I will just change the value in Constants.java and this change will replicate in the whole app.
Utils.showErrorMessage(this, Constants.PASSWORD_FIELD_EMPTY_ERROR, Constants.TOAST_VISIBLE_SHORT);
I got the answer by reading this blog.
Which says you should use shard preferences with the activity extending PreferenceActivity.
Here you can set your server address and all the dynamic stuff that will change by the time. So that you will not have to recompile the apk. Just change the settings in shared preferences.
Read this important bog that all app developer wants to know it..
hi i have found Uri as immutable reference i dont know what it is the exact meaning of immutable reference... can anyone help me?
It's a variable that cannot be changed once set. Very useful when you have multithreaded code since being able to change a variable's value might be a source of many hard to find problems in your code.
If it's immutable, it's usually good.
A good example of an immutable class within the .NET Framework is System.String. Once you create a String object, you can’t ever change it. There’s no way around it; that’s the way the class is designed. You can create copies, and those copies can be modified forms of the original, but you simply cannot change the original instance for as long as it lives, without resorting to unsafe code. If you understand that, you’re probably starting to get the gist of where I’m going here: For a referencebased object to be passed into a method, such that the client can be guaranteed that it won’t change during the method call, it must itself be immutable.
In a world such as the CLR where objects are held by reference by default, this notion of immutability becomes very important. Let’s suppose that System.String was mutable, and let’s suppose you could write a method such as the following fictitious method:
public void PrintString( string theString )
{
// Assuming following line does not create a new
// instance of String but modifies theString
theString += ": there, I printed it!";
Console.WriteLine( theString );
}
Imagine the callers’ dismay when they get further along in the code that called this method and now their string has this extra stuff appended onto the end of it. That’s what could happen if System. String were mutable. You can see that String’s immutability exists for a reason, and maybe you should consider adding the same capability to your design.
EX: string is immutable...
if u have for ex string s =" whatever" and u output it with uppercase letter..for ex
Console.Write(s.ToUpper())the console will print u WHATEVER...but the string s will still be whatever... unlike the mutable type which will change the string from whatever to WHATEVER
"immutable" means "can't change the value"
"mutable" == "changeable"
"immutable" == "not changeable"
In java , every thing is treated as String and object , Now try to think that if have created a program of 10000 lines and in this there you have added "public" 100 times so do you think that every time this public is created in storage . else what we can do , we can created something like that when ever we find something like this we will fetch it from there there ( String pool )