Y.S. Y.S. - 17 days ago 15
Android Question

Android - WebView locale changes abruptly on Android N

I have a multilingual app with primary language English and secondary language Arabic.

As described in the documentation,


  • I have added
    android:supportsRtl="true"
    in the manifest.

  • I have changed all xml properties with
    left
    and
    right
    attributes to
    start
    and
    end
    respectively.

  • I have added Arabic language strings in
    strings-ar
    (and similarly for other resources).



The above setup works properly. After changing the
Locale
to
ar-AE
, Arabic text & resources are correctly displayed in my Activities.


However, every time I navigate to an
Activity
with a
WebView

and/or a
WebViewClient
, the locale, text and layout direction
abruptly revert to the device default.


Further hints:


  • This is occurring only on a Nexus 6P with Android 7.0. Everything works properly on Android 6.0.1 and below.

  • The abrupt shift in locale happens only when I navigate to an
    Activity
    that has a
    WebView
    and/or a
    WebViewClient
    (and I have several). It does not occur on any of the other Activities.



Android 7.0 has multi-locale support, allowing the user to set more than one default locale. So if I set the primary locale to
Locale.UK
:

enter image description here


Then on navigating to the
WebView
, the locale changes from
ar-AE

to
en-GB
.


Android 7.0 API changes:

As indicated in the list of API changes, new methods pertaining to locale have been added to the following classes in API 24:

Locale
:




Configuration
:





However, I am building my app with API 23, and am not using any of
these new methods.


Furthermore ...


  • The problem occurs on the Nexus 6P emulator as well.

  • To get the default locale, I am using
    Locale.getDefault()
    .

  • To set the default locale, I am using the following code:

    public static void setLocale(Locale locale){
    Locale.setDefault(locale);
    Configuration config = new Configuration();
    config.setLocale(locale);
    Context context = MyApplication.getInstance();
    context.getResources().updateConfiguration(config,
    context.getResources().getDisplayMetrics());
    }



Has anyone encountered this problem before? What is the reason for it, and how do I resolve this?

References:

1. Native RTL support in Android 4.2.

2. Multilingual Support - Language and Locale.

3. Be wary of the default locale.

EDIT:

Here is the code in the
onCreate()
method of my
GenericWebViewActivity
:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_auto_cancel);
context = this;
webView = (WebView) findViewById(R.id.auto_cancel_webview);

Toolbar toolbar = (Toolbar) findViewById(R.id.toolBar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setElevation(5);

TextView title = (TextView) toolbar.findViewById(R.id.title_text);
title.setText(getIntent().getStringExtra("title"));
title.setVisibility(View.VISIBLE);

//toolbar.setNavigationIcon(R.drawable.back_arrow_white);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});

mProgressLoader = (RelativeLayout) findViewById(R.id.progress_rl);
mProgressLoader.setVisibility(View.VISIBLE);

url = getIntent().getStringExtra("URL");
Log.d("URL", url);

webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setPluginState(WebSettings.PluginState.ON);
webView.getSettings().setLoadWithOverviewMode(true);
webView.getSettings().setUseWideViewPort(true);
webView.getSettings().setBuiltInZoomControls(true);

// needed in case 'webView.postUrl()' is called
webView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {

// code for in-app click-to-call
if (url.startsWith("tel:")) {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(intent);
return true;
}

view.loadUrl(url);
return false; // then it is not handled by default action
}

public void onPageFinished(WebView view, String url) {
mProgressLoader.setVisibility(View.GONE);
((MarqueeLayout) mProgressLoader.findViewById(R.id.marqueeLayout)).stopProgressLoader();
webView.scrollTo(0, 0);
webView.setVisibility(View.VISIBLE);
webView.clearFocus();
}

public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
showErrorView();
}

;

@Override
public void onReceivedSslError(WebView view,
android.webkit.SslErrorHandler handler,
android.net.http.SslError error) {
if (BuildConfig.DEBUG) {
Log.e(LOG_TAG, error.toString());
}
//showErrorView();
if (handler != null) {
handler.proceed();
}
}
});


/* if user is logged in, then log them in on
the website as well with the post params. */
if (MyApplication.getBooleanValue(getString(R.string.is_logged_in))) {
webView.postUrl(getIntent().getStringExtra("Login URL"), getIntent().getByteArrayExtra("POST params"));
Log.d("Login URL", getIntent().getStringExtra("Login URL"));
try {
Log.d("POST params", new String(getIntent().getByteArrayExtra("POST params"), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else {
webView.loadUrl(url);
}
}

Answer

While Tedd Hopp's answer managed to solve the problem, he didn't address the question of why this occurs.

The reason is the changes made to the Webview class and its support package in Android 7.0.

Background:

Android's WebView is built using webkit. While it was originally a part of AOSP, from KitKat onwards a decision was made to spin off WebView into a separate component called Android System WebView. It is essentially an Android system app that comes pre-installed with Android devices. It is periodically updated, just like other system apps such as Google Play Services and the Play Store app. You can see it in your list of installed system apps:

Show System apps

and

Android System WebView

Android 7.0 changes:

Starting with Android N, the Chrome app will be used to render any/all WebViews in third-party Android apps. In phones that have Android N out-of-the-box, the Android WebView System app is not present at all. In devices that have received an OTA update to Android N, the Android System WebView is disabled:

WebView disabled

and

WebView disabled

Moreover, multi-locale support has been introduced, with devices having more than one default language.

This has an important consequence for apps that have multiple languages. If your app has WebViews, then those are rendered using the Chrome app. Because Chrome is an Android app by itself, running in its own sandboxed app process, it will not be bound to the locale set by your app. Instead, Chrome will revert by default to the device locale. For example, say your app locale is set to ar-AE, while the primary locale of the device is en-US. In this case, the locale of the Activity containing a WebView will change from ar-AE to en-US, and strings and resources from the corresponding locale folders will be displayed. You may see a mish-mash of LTR and RTL strings/resources on those Activitys that have WebViews.

The only way to solve this problem is to reset the default locale manually in every Activity, or at least every Activity that has a WebView.

As an addendum, Chrome custom tabs are now the preferred way of rendering in-app web pages.

References:

1. Android 7.0 - changes for WebView.

2. Understanding WebView and Android security patches.

3. Nougat WebView.

4. Android 7.0 Nougat.

5. WebView: From "Powered by Chrome" to straight up Chrome

Comments