Create downloadable custom theme and apply it during run time

后端 未结 1 2035
庸人自扰
庸人自扰 2020-12-30 10:46

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

相关标签:
1条回答
  • 2020-12-30 11:37

    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<HashMap<String, NinePatchDrawable>> ninePatchHashMaps;
        private static ArrayList<HashMap<String, Drawable>> 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<files.length; i++) {
                    if(files[i].isDirectory()) {
                        deleteDirectory(files[i]);
                    }
                    else {
                        files[i].delete();
                    }
                }
            }
            return(path.delete());
        }
    
        /**
        * Method that get's the version of assets file
        * @param url String
        */
        public static String getAssetsVersion(String url) throws IOException {
            Helper.logInfo("REQUEST URL:", url);
            OkHttpClient client = new OkHttpClient();
    
            // set connection timeut to 5min
            client.setConnectTimeout(1, TimeUnit.MINUTES);
    
            Request request = new Request.Builder()
                    .url(url)
                    .build();
    
            Response response = client.newCall(request).execute();
            return response.body().string();
        }
    
        /**
         * Method that downloads assets file from server
         * @param url String
         * @return String
         * @throws IOException
         */
        public static String getAssetsFile(String url) throws IOException {
    
            Helper.logInfo("REQUEST URL:", url);
            OkHttpClient client = new OkHttpClient();
    
            // set connection timeut to 5min
            client.setConnectTimeout(1, TimeUnit.MINUTES);
    
            Request request = new Request.Builder()
                    .url(url)
                    .header("User-Agent", MyApplication.USER_AGENT)
                    .build();
    
            Response response = client.newCall(request).execute();
            InputStream inputStreamFile = response.body().byteStream();
    
            try {
    
                // Output stream
                String outputFileName = Environment.getExternalStorageDirectory().toString() + "/assets.zip";
    
                File deleteFile = new File(outputFileName);
                deleteFile.delete();
    
                OutputStream output = new FileOutputStream(outputFileName);
    
                byte data[] = new byte[1024];
    
                int count;
    
                // writing data to file
                while ((count = inputStreamFile.read(data)) != -1)
                    output.write(data, 0, count);
    
                // flushing output
                output.flush();
    
                // closing streams
                output.close();
                inputStreamFile.close();
    
                return outputFileName;
    
            } catch (Exception e) {
                Helper.logError("Download Resursa", e);
                return "ERROR";
            }
        }
    
        public static void setStyle(View v, String styleName) {
    
            try {
                if (styleName == null || styleName.equals("")) {
                    if (v instanceof EditText)
                        processStyle(v, getStylesFile().getJSONObject("EditText"));
                } else
                    processStyle(v, getStylesFile().getJSONObject(styleName));
            } catch (Exception e) {
                Helper.logError("Setting Styles", e);
            }
    
        }
    
        private static void setBackground(View v, Drawable d) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                v.setBackgroundDrawable(d);
            } else {
                v.setBackground(d);
            }
        }
    
        public static JSONObject getStylesFile() throws JSONException {
    
            if (joStyles == null) {
                String stylesFileName = getResourcesPath() + "styles/properties.json";
                joStyles = new JSONObject(Helper.readJsonFile(stylesFileName));
            }
    
            return joStyles;
    
        }
    
        public static void processStyle(View v, JSONObject joStyle) {
    
            if(joStyle != null) {
                try {
    
                    // used for layout margins
                    LinearLayout.LayoutParams layoutParams = null;
    
                    if (Helper.isValidParameter(joStyle, "backgroundColor"))
                        v.setBackgroundColor(Color.parseColor(joStyle.getString("backgroundColor")));
    
                    if (Helper.isValidParameter(joStyle, "backgroundImage"))
                        setBackground(v, loadNinePatchFromFilesystem(getImagesPath() + joStyle.getString("backgroundImage")));
    
                    if (v instanceof TextView) {
    
                       applyTextViewParameters(v, joStyle);
    
                    } else if (v instanceof  ListView) {
    
                        if (Helper.isValidParameter(joStyle, "dividerColor")) {
                            ((ListView) v).setDivider(new ColorDrawable(Color.parseColor(joStyle.getString("dividerColor"))));
                            ((ListView) v).setDividerHeight(Helper.convertDpToPixel(1));
                        }
                        if (Helper.isValidParameter(joStyle, "dividerHeight")) {
                            ((ListView) v).setDividerHeight(Helper.convertDpToPixel(joStyle.getInt("dividerHeight")));
                        }
                    } else if (v instanceof UnderlinePageIndicator) {
                        if (Helper.isValidParameter(joStyle, "backgroundColor")) {
                            v.setBackgroundColor(Color.parseColor(joStyle.getString("backgroundColor")));
                        }
                        if (Helper.isValidParameter(joStyle, "selectedColor")) {
                            ((UnderlinePageIndicator) v).setSelectedColor(Color.parseColor(joStyle.getString("selectedColor")));
                        }
                    } else if (v instanceof StyleableBackground) {
                        if (Helper.isValidParameter(joStyle, "backgroundColor")) {
                            View background = v.findViewById(R.id.llBackground);
                            if (background != null) {
                                background.setBackgroundColor(Color.parseColor(joStyle.getString("backgroundColor")));
                            }
                        }
                        if (Helper.isValidParameter(joStyle, "borderTopColor")) {
                            View topBorder = v.findViewById(R.id.llTopBorder);
                            if (topBorder != null) {
    
                                topBorder.setBackgroundColor(Color.parseColor(joStyle.getString("borderTopColor")));
    
                                if (Helper.isValidParameter(joStyle, "borderTopHeight")) {
                                    topBorder.setMinimumHeight(Helper.convertDpToPixel(joStyle.getInt("borderTopHeight")));
                                }
                            }
                        }
                        if (Helper.isValidParameter(joStyle, "borderBottomColor")) {
                            View bottomBorder = v.findViewById(R.id.llBottomBorder);
                            if (bottomBorder != null) {
    
                                bottomBorder.setBackgroundColor(Color.parseColor(joStyle.getString("borderBottomColor")));
    
                                if (Helper.isValidParameter(joStyle, "borderBottomHeight")) {
                                    bottomBorder.setMinimumHeight(Helper.convertDpToPixel(joStyle.getInt("borderBottomHeight")));
                                }
                            }
                        }
                        if (Helper.isValidParameter(joStyle, "backgroundImage")) {
    
                            ImageView ivBackgroundImage = (ImageView) v.findViewById(R.id.ivBackgroundImage);
                            if (ivBackgroundImage != null) {
                                BitmapDrawable d = (BitmapDrawable) ResourceManager.loadImageFromFilesystem(ResourceManager.getImagesPath() + joStyle.getString("backgroundImage"));
    
                                d.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
                                d.setGravity(Gravity.FILL_HORIZONTAL | Gravity.FILL_VERTICAL);
                                setBackground(ivBackgroundImage, d);
                            }
                        }
                    }
    
                    if(Helper.isValidParameter(joStyle, "width"))
                        v.setMinimumWidth(joStyle.getInt("width"));
    
                    if(Helper.isValidParameter(joStyle, "height"))
                        v.setMinimumHeight(joStyle.getInt("height"));
    
                    if(Helper.isValidParameter(joStyle, "padding"))
                        v.setPadding(joStyle.getInt("padding"), joStyle.getInt("padding"), joStyle.getInt("padding"), joStyle.getInt("padding"));
    
                    if(Helper.isValidParameter(joStyle, "paddingLeft"))
                        v.setPadding(joStyle.getInt("paddingLeft"), v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom());
    
                    if(Helper.isValidParameter(joStyle, "paddingTop"))
                        v.setPadding(v.getPaddingLeft(), joStyle.getInt("paddingTop"), v.getPaddingRight(), v.getPaddingBottom());
    
                    if(Helper.isValidParameter(joStyle, "paddingRight"))
                        v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), joStyle.getInt("paddingRight"), v.getPaddingBottom());
    
                    if(Helper.isValidParameter(joStyle, "paddingBottom"))
                        v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(), joStyle.getInt("paddingBottom"));
    
                    if(Helper.isValidParameter(joStyle, "margin")) {
                        layoutParams = new LinearLayout.LayoutParams(v.getLayoutParams());
                        layoutParams.setMargins(joStyle.getInt("margin"), joStyle.getInt("margin"), joStyle.getInt("margin"), joStyle.getInt("margin"));
                    }
    
                    if(Helper.isValidParameter(joStyle, "marginLeft")) {
                        layoutParams = new LinearLayout.LayoutParams(v.getLayoutParams());
                        layoutParams.setMargins(joStyle.getInt("marginLeft"), layoutParams.topMargin, layoutParams.rightMargin, layoutParams.bottomMargin);
                    }
    
                    if(Helper.isValidParameter(joStyle, "marginTop")) {
                        layoutParams = new LinearLayout.LayoutParams(v.getLayoutParams());
                        layoutParams.setMargins(layoutParams.leftMargin, joStyle.getInt("marginTop"), layoutParams.rightMargin, layoutParams.bottomMargin);
                    }
    
                    if(Helper.isValidParameter(joStyle, "marginRight")) {
                        layoutParams = new LinearLayout.LayoutParams(v.getLayoutParams());
                        layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin, joStyle.getInt("marginRight"), layoutParams.bottomMargin);
                    }
    
                    if(layoutParams != null)
                        v.setLayoutParams(layoutParams);
    
    
                    RelativeLayout.LayoutParams relativeLayoutParams = null;
    
                    if (Helper.isValidParameter(joStyle, "alignParentTop") && joStyle.getBoolean("alignParentTop")) {
                        relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                        relativeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
                    }
    
                    if (Helper.isValidParameter(joStyle, "alignParentLeft") && joStyle.getBoolean("alignParentLeft")) {
                        relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                        relativeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
                    }
    
                    if (Helper.isValidParameter(joStyle, "alignParentBottom") && joStyle.getBoolean("alignParentBottom")) {
                        relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                        relativeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
                    }
    
                    if (Helper.isValidParameter(joStyle, "alignParentRight") && joStyle.getBoolean("alignParentRight")) {
                        relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                        relativeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
                    }
    
                    if(Helper.isValidParameter(joStyle, "marginLeft")) {
                        relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                        relativeLayoutParams.setMargins(joStyle.getInt("marginLeft"), relativeLayoutParams.topMargin, relativeLayoutParams.rightMargin, relativeLayoutParams.bottomMargin);
                    }
    
                    if(Helper.isValidParameter(joStyle, "marginTop")) {
                        relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                        relativeLayoutParams.setMargins(relativeLayoutParams.leftMargin, joStyle.getInt("marginTop"), relativeLayoutParams.rightMargin, relativeLayoutParams.bottomMargin);
                    }
    
                    if(Helper.isValidParameter(joStyle, "marginRight")) {
                        relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                        relativeLayoutParams.setMargins(relativeLayoutParams.leftMargin, relativeLayoutParams.topMargin, joStyle.getInt("marginRight"), relativeLayoutParams.bottomMargin);
                    }
    
                    if (relativeLayoutParams != null) {
                        v.setLayoutParams(relativeLayoutParams);
                    }
    
                } catch (Exception e) {
                    Helper.logError("", e);
                }
            }
        }
    
        public static String getSdcardPath() {
    
            if(sdcardPath == null)
                sdcardPath = ctx.getApplicationInfo().dataDir;
    
            return sdcardPath;
        }
    
        public static String getResourcesPath() {
    
            return getSdcardPath() + "/resources/";
        }
    
        public static String getCSSPath() {
    
            return getResourcesPath() + "default.css";
        }
    
        public static String getImagesPath() {
    
            return getResourcesPath() + "images/" + ResourceConstants.getScreenDPI(ctx) + "/";
        }
    
        public static String getImagesPathNoDpi() {
    
            return getResourcesPath() + "images/";
        }
    
        public static NinePatchDrawable loadNinePatchFromFilesystem(String filename) {
    
            if(ninePatchHashMaps == null)
                ninePatchHashMaps = new ArrayList<HashMap<String, NinePatchDrawable>>();
    
            // check if we already have this filename so we can reuse it
            for (int i = 0; i < ninePatchHashMaps.size(); i++) {
                HashMap<String, NinePatchDrawable> 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<String, NinePatchDrawable> drawableImage = new HashMap<String, NinePatchDrawable>();
                drawableImage.put(filename, patchy);
                ninePatchHashMaps.add(drawableImage);
            }
    
            return patchy;
    
        }
    
        public static Drawable loadImageFromFilesystem(String filename) {
    
            if(imagesHashMaps == null)
                imagesHashMaps = new ArrayList<HashMap<String, Drawable>>();
    
            // check if we already have this filename so we can reuse it
            for (int i = 0; i < imagesHashMaps.size(); i++) {
                HashMap<String, Drawable> 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<String, Drawable> drawableImage = new HashMap<String, Drawable>();
                drawableImage.put(filename, image);
                imagesHashMaps.add(drawableImage);
            }
    
            return image;
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题