Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a WebView inherit colors from the current Android theme?

This question was initially posted on Google Moderator for the AndroidDev Office Hours Hangout that took place last week. The question was actually answered live and you can watch it here if you like. I'm posting it here cause they seemed intrigued by it and here I have a little bit more room to elaborate.

A WebView is normally used to display web content as the author intended (as they state in the hangout). But I use a WebView to display, not exactly content per se, but formatted text (mainly, bolds, italics, bulleted lists and text alignment). It's much easier with a WebView (in other words, much easier with HTML/CSS) than to use a bunch of TextViews and keep it all perfectly formatted.

The problem is that I'm using this specific WebView with a transparent background on an AlertDialog. The WebView text color comes from the loaded content CSS but it's important that this color contrasts with the background color of the AlertDialog. And that's my problem.

Up until Android 2.3, the AlertDialog background was always dark. It didn't matter if the app theme was the default dark one or the light one, the AlertDialog was still a dark gray. This on vanilla Android. But even on skinned Android (Sense, TouchWiz, MotoBlur, etc...) the AlertDialog has always been a dark color (for both the default/dark and light themes).

This all changed with ICS (it probably changed on Honeycomb but I didn't confirm that). The default/dark theme now has a dark AlertDialog while the light theme actually has a light AlertDialog.

Since I'm exclusively using the light theme on my app, I could easily solve my problem by loading the WebView content with different CSS files. One with a dark text color for versions below ICS and anothr light text color for ICS and above. This would mostly solve my issue if it weren't for OEMs skins.

On their skin versions for ICS, they might provide dark/light themes for the AlertDialog. Or not. They are more likely to do exactly has they been doing it, provide dark and light themes as usual but for the AlertDialog only a dark version, no matter the app's theme.

I could force Holo on my app and have the problem solved that way but I would prefer not to interfere with how the overall system looks on the user's devices. For instance, if they have Sense and really like it, I don't want to display a Holo themed AlertDialog when using my app.

Ultimately, my question is this:

So, how do I cope with this? How do I make sure the text on my WebView is readable against the AlertDialog background? No matter the Android version, the theme being used or if it's skinned by OEMs or not...

I don't know how feasible is this but one alternative to solve this problem would be to, somehow, extract the text color from the AlertDialog theme in the device. But not the default theme or the Holo theme, but the DeviceDefault theme I believe.

Is this extraction easily done? Could it be a good solution to my problem? Do you have any other alternatives?

One last detail... If you watched the hangout and for the guys that actually answered me, one of them suggested injecting CSS. If I wasn't clear before, I don't need to do that. I just need to build the WebView content String with the exact CSS I want, which can take the text color extracted from the theme.

like image 346
rfgamaral Avatar asked Feb 01 '12 00:02

rfgamaral


1 Answers

I think in order to find a solution we must be able to determine what theme is used at runtime by your dialogs. I assume that you do not set a theme explicity. That means that the default dialog theme will be used. The following code is from the Dialog class and it shows how the default theme is in fact applied to a dialog if a theme was not explicitely set.

Dialog(Context context, int theme, boolean createContextWrapper) {
    if (theme == 0) {
        TypedValue outValue = new TypedValue();
        context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
                outValue, true);
        theme = outValue.resourceId;
    }

    mContext = createContextWrapper ? new ContextThemeWrapper(context, theme) : context;
    mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
    Window w = PolicyManager.makeNewWindow(mContext);
    mWindow = w;
    w.setCallback(this);
    w.setWindowManager(mWindowManager, null, null);
    w.setGravity(Gravity.CENTER);
    mUiThread = Thread.currentThread();
    mListenersHandler = new ListenersHandler(this);
    }

Now we need to find out what the primary text color of that theme is. We can do it like this( the variable dialog is a reference to your dialog):

    TypedValue tv = new TypedValue();
    dialog.getContext().getTheme().resolveAttribute(android.R.attr.textColorPrimary, tv, true);
    int textColor = getResources().getColor(tv.resourceId);

Now that we have the text color it is a matter of injecting that value into your webview. I tested this code with the following devices

Samsung Galaxy Tab 10.1 running Honeycomb

  • Theme.Holo.Light gave the text color pure black
  • Theme.Holo gave the text color pure white

HTC Sensation running Gingerbread

  • Theme.Light gave a text color of pure white (which is correct since the dialog has a dark background
  • Theme.Black also gave a text color of pure white

Samsung Galaxy S2 running Gingerbread

  • Theme.Black gave a textcolor of light gray (ffc8c8c8)
  • Theme.Light gave the same textcolor (ffc8c8c8)

ICS Emulator

  • Theme.Holo.Light gave a text color of pure black
  • Theme.Holo gave a very light gray (fff3f3f3)

So this method worked fine across all tested devices.

Cheers Renard

like image 99
Renard Avatar answered Sep 18 '22 06:09

Renard