I\'m making an Android app that needs to allow client to maintain the resources from their server which would include strings, drawables etc.
I\'ve already created a
I've finally decided to solve this by making a custom system for handling drawables, strings etc so now i have a custom class called "ResourceManager" that handles what needs to be loadad and how, and themes are distributed as a zip file which app downloads, extracts and later uses.
I had to compile nine patch images by myself before putting them in zip file, and I did that using "abrc" from here: http://forum.xda-developers.com/showthread.php?t=785012
I've also created a simple bash script that goes recursively through custom folder and compiles all nine patch images with abrc.
I've also created a simple helper in the ResourceManager that checks and tells me the screen density so i can normally support images in hdpi, xhdpi etc densities, and finally i don't re-create the images every time i need them, i save them in a static list of HashMap so i can reuse the ones I've already created and that way i hope to prevent wasting too much phone's memory :).
OK that's all in short lines, if anyone has any questions please let me know, i'll be glad to share this experience with anyone.
Cheers!
============ EDIT ============
Here's the class I ended up writing for this purpose (it downloads the file, checks for it's version, loads strings from JSON file rather than strings.xml etc)
NOTE: This is not a full class so some parts are missing, but I think it's more than enough to get the idea how I solved all this :)
/**
* Created by bojank on 7/28/2014.
* Class that handles custom resources downloaded from server
*/
public class ResourceManager {
// List of ninePatchImages in the application
private static ArrayList> ninePatchHashMaps;
private static ArrayList> imagesHashMaps;
private static ImageLoader imageLoader;
// Context for methods
public static Context ctx;
// JSONObject with all strings
private static JSONObject joString;
// JSONObject with all styles
private static JSONObject joStyles;
// String with current active lang code
private static String currentLanguage;
private static String sdcardPath;
// Private consturctor to prevent creating a class instance
private ResourceManager() {
}
/**
* Method that returns a translated string for given key
*
* @param key String
* @return String
*/
public static String getString(String module, String key) {
String output = ""; //String.format("[%s - %s]", module, key);
try {
if (getStringsFile() != null && getStringsFile().getJSONObject(module).has(key))
output = getStringsFile().getJSONObject(module).getString(key);
} catch (Exception e) {
// Force some default language if proper json file is missing for newly added language
currentLanguage = "en-US";
Helper.saveLocale(currentLanguage, ctx);
Helper.logError("ErrorFetchingString", e);
}
return output;
}
/**
* Method that returns JSONObject with string resources
* @return JSONObject
* @throws JSONException
*/
public static JSONObject getStringsFile() throws JSONException {
if (joString == null) {
String stringFileName = getResourcesPath() + "languages/" + getCurrentLanguage() + "/values.json";
String languageFile = Helper.readJsonFile(stringFileName);
if (languageFile != null) {
joString = new JSONObject(Helper.readJsonFile(stringFileName));
} else {
return null;
}
}
return joString.getJSONObject("strings");
}
/**
* Method that returns current language ("sr", "en"...)
* @return String
*/
public static String getCurrentLanguage() {
if (currentLanguage == null)
currentLanguage = Helper.getCurrentLanguage(ctx);
return currentLanguage;
}
/**
* Method that resets joString object and currentLanguage on language change
*/
public static void resetLanguage() {
joString = null;
currentLanguage = null;
}
/**
* Method that resets joStyles object on theme change
*/
public static void resetStyle() {
joStyles = null;
}
/**
* Method that deletes a directory from filesystem
* @param path File
* @return boolean
*/
public static boolean deleteDirectory(File path) {
if( path.exists() ) {
File[] files = path.listFiles();
for(int i=0; i>();
// check if we already have this filename so we can reuse it
for (int i = 0; i < ninePatchHashMaps.size(); i++) {
HashMap row = ninePatchHashMaps.get(i);
if(row.containsKey(filename))
return row.get(filename);
}
NinePatchDrawable patchy = null;
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(filename, options);
byte[] chunk = bitmap.getNinePatchChunk();
boolean result = NinePatch.isNinePatchChunk(chunk);
if (result)
patchy = new NinePatchDrawable(bitmap, chunk, new Rect(), null);
} catch (Exception e){
Helper.logError("NinePatchLoading",e);
}
if(patchy != null) {
HashMap drawableImage = new HashMap();
drawableImage.put(filename, patchy);
ninePatchHashMaps.add(drawableImage);
}
return patchy;
}
public static Drawable loadImageFromFilesystem(String filename) {
if(imagesHashMaps == null)
imagesHashMaps = new ArrayList>();
// check if we already have this filename so we can reuse it
for (int i = 0; i < imagesHashMaps.size(); i++) {
HashMap row = imagesHashMaps.get(i);
if(row.containsKey(filename))
return row.get(filename);
}
Drawable image = null;
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(filename, options);
if(bitmap == null)
bitmap = BitmapFactory.decodeFile(filename.replace(ResourceConstants.getScreenDPI(ctx) + "/", ""), options);
image = new BitmapDrawable(bitmap);
} catch (Exception e){
Helper.logError("ImageLoadingError",e);
}
if(image != null) {
HashMap drawableImage = new HashMap();
drawableImage.put(filename, image);
imagesHashMaps.add(drawableImage);
}
return image;
}
}