Search code examples
androidandroid-studiofontsandroid-webview

Determine which Flipfont package is active (and use it on WebViews)


First I should explain what my ultimate goal is. I develop Android Apps, mostly using WebViews. They are great in various aspects, but one thing they don't do very well is "matching the native UI", especially fonts. Some phones (such as Samsung's) support using Flipfont to switch the default font for the entire system, but for some reasons, no browsers (as far as I know) adapt that setting and display webpages with the Flipfont settings. The same is true for WebViews, and thus creating inconsistent user experience.

But I think there should be a way to let WebViews using the Flipfont font. By studying the decompiled source code of iFont, I think I've figured out how to extract the .ttf file from the assets of a given Flipfont package, of which the package name always begins with com.monotype.android.font. And after that, I supposedly can let the WebView use that .ttf file as the default font. The problem is, I don't know which package I should extract, that is, I don't know which Flipfont package is currently in use, if any. It appears that iFont cannot determine that either; there's no place in that app that tells me explicitly "You're using font xxx".

But obviously there must be a way to determine that, because the Flipfont setting dialog shows me exactly that. However, I failed to decompile the setting dialog to study how it is done. From Logcat, it appears that the setting dialog has something to do with the package com.sec.android.easysettings and com.android.settings, but decompiling the corresponding apk's (which are under /system/app/easysettings and /system/priv-app/SecSettings, respectively) both result in no source code at all, only resources (can someone also explain why this happens?).

So does anyone know how to determine the current Flipfont package?


Solution

  • After more digging I finally found a solution that works.

    For those system that uses Flipfont, the Typeface class has additional methods that allows one to obtain information regarding the Flipfont setting (I figured that out here). However since those methods are not defined on the standard Typeface class, one would need to use reflection to call those methods, with exception catching of course.

    I came up with the following FontUtil class, of which getFlipFont method returns the File object of the current Flipfont .ttf file if there's one, or null if there's none.

    import android.content.Context;
    import android.graphics.Typeface;
    
    import java.io.File;
    import java.lang.reflect.Method;
    
    public class FontUtil {
    
        @SuppressWarnings("JavaReflectionMemberAccess")
        public static File getFlipFont(Context context) {
            try {
                Method m = Typeface.class.getMethod("getFontPathFlipFont", Context.class, int.class);
                String path = m.invoke(null, context, 1).toString();
                if (!path.equals("default")) {
                    File file = new File(path + "/DroidSansFallback.ttf");
                    if (file.exists()) return file;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    After that, my goal was achieved by the following, should anyone cares. Add the following CSS on the webpages of my app:

    @font-face {
        font-family: Flipfont;
        src: url(flipfont.ttf);
    }
    
    body {
        font-family: Flipfont;
    }
    

    Next, use something like this in my activities:

    
    private File flipFont;
    private WebView webView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        flipFont = FontUtil.getFlipFont(this);
        ...
        webView.setWebViewClient(new AppWebViewClients());
        ...
    }
    
    private class AppWebViewClients extends WebViewClient {
        
        @Nullable
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
            String url = request.getUrl().toString();
            if (url.endsWith("flipfont.ttf") && flipFont != null) {
                try {
                    InputStream stream = new FileInputStream(flipFont);
                    return new WebResourceResponse("application/x-font-ttf", "UTF-8", stream);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
            return super.shouldInterceptRequest(view, request);
        }
    }
    

    Now my WebView apps uses exactly the same font as any other native apps.

    Update 2021.4.25:

    Today I realize that on Samsung Note 9, the method name is semGetFontPathOfCurrentFontStyle() instead, so one should also check if this method is available. Also, in some methods of changing the font, the font filename might end up being DroidSans.ttf instead of DroidSansFallback.ttf, so it is also necessary that one should check the former as a "fallback" (in the opposite order to the filenames).