Bug 1532850 - Implement the backend for prefers-color-scheme on Android. r=geckoview-reviewers,snorp
☠☠ backed out by 2eb9f5061586 ☠ ☠
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Sat, 09 Mar 2019 00:21:21 +0000
changeset 521200 d3eed8f55897
parent 521199 1823814b5dff
child 521201 16951165b363
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgeckoview-reviewers, snorp
bugs1532850
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1532850 - Implement the backend for prefers-color-scheme on Android. r=geckoview-reviewers,snorp Differential Revision: https://phabricator.services.mozilla.com/D22272
mobile/android/base/AndroidManifest.xml.in
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
widget/android/nsLookAndFeel.cpp
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -53,17 +53,17 @@
              org.mozilla.{fennec,firefox,firefox_beta}.  The internal Java
              package hierarchy inside the Android package used to have an
              org.mozilla.{fennec,firefox,firefox_beta} subtree *and* an
              org.mozilla.gecko subtree; it now only has org.mozilla.gecko. -->
         <activity android:name="@MOZ_ANDROID_BROWSER_INTENT_CLASS@"
                   android:label="@string/moz_app_displayname"
                   android:taskAffinity="@ANDROID_PACKAGE_NAME@.BROWSER"
                   android:alwaysRetainTaskState="true"
-                  android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|locale|layoutDirection|smallestScreenSize|screenLayout"
+                  android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|locale|layoutDirection|smallestScreenSize|screenLayout|uiMode"
                   android:resizeableActivity="true"
                   android:supportsPictureInPicture="true"
                   android:windowSoftInputMode="stateUnspecified|adjustResize"
                   android:launchMode="singleTask"
                   android:exported="true"
                   android:theme="@style/Gecko.App" />
 
         <!-- Bug 1256615 / Bug 1268455: We published an .App alias and we need to maintain it
@@ -330,17 +330,17 @@
                 <action android:name="android.intent.action.SEND" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <data android:mimeType="text/plain" />
             </intent-filter>
 
         </activity>
 
         <activity android:name="org.mozilla.gecko.customtabs.CustomTabsActivity"
-                  android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|locale|layoutDirection|smallestScreenSize|screenLayout"
+                  android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|locale|layoutDirection|smallestScreenSize|screenLayout|uiMode"
                   android:windowSoftInputMode="stateUnspecified|adjustResize"
                   android:theme="@style/GeckoCustomTabs" />
 
         <activity android:name="org.mozilla.gecko.webapps.WebAppActivity"
             android:theme="@style/Theme.AppCompat.NoActionBar" />
 
         <!-- Declare a predefined number of WebApp<num> activities. These are
              used so that each web app can launch in its own activity. -->
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.hardware.input.InputManager;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
 import android.support.annotation.RequiresApi;
@@ -26,16 +27,17 @@ public class GeckoSystemStateListener
     private static final String LOGTAG = "GeckoSystemStateListener";
 
     private static final GeckoSystemStateListener listenerInstance = new GeckoSystemStateListener();
 
     private boolean initialized;
     private ContentObserver mContentObserver;
     private static Context sApplicationContext;
     private InputManager mInputManager;
+    private static boolean sIsNightMode;
 
     public static GeckoSystemStateListener getInstance() {
         return listenerInstance;
     }
 
     private GeckoSystemStateListener() {
     }
 
@@ -54,16 +56,19 @@ public class GeckoSystemStateListener
         mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
             @Override
             public void onChange(boolean selfChange) {
                 onDeviceChanged();
             }
         };
         contentResolver.registerContentObserver(animationSetting, false, mContentObserver);
 
+        sIsNightMode = (sApplicationContext.getResources().getConfiguration().uiMode &
+            Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+
         initialized = true;
     }
 
     public synchronized void shutdown() {
         if (!initialized) {
             Log.w(LOGTAG, "Already shut down!");
             return;
         }
@@ -104,16 +109,34 @@ public class GeckoSystemStateListener
 
     @WrapForJNI(calledFrom = "gecko")
     private static void notifyPrefersReducedMotionChangedForTest() {
         ContentResolver contentResolver = sApplicationContext.getContentResolver();
         Uri animationSetting = Settings.System.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE);
         contentResolver.notifyChange(animationSetting, null);
     }
 
+    @WrapForJNI(calledFrom = "gecko")
+    /**
+     * For prefers-color-scheme media queries feature.
+     */
+    private static boolean isNightMode() {
+        return sIsNightMode;
+    }
+
+    public void updateNightMode(int newUIMode) {
+        boolean isNightMode = (newUIMode & Configuration.UI_MODE_NIGHT_MASK)
+            == Configuration.UI_MODE_NIGHT_YES;
+        if (isNightMode == sIsNightMode) {
+          return;
+        }
+        sIsNightMode = isNightMode;
+        onDeviceChanged();
+    }
+
     @WrapForJNI(stubName = "OnDeviceChanged", calledFrom = "ui", dispatchTo = "gecko")
     private static native void nativeOnDeviceChanged();
 
     private static void onDeviceChanged() {
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
             nativeOnDeviceChanged();
         } else {
             GeckoThread.queueNativeCallUntil(
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
@@ -6,16 +6,17 @@
 
 package org.mozilla.geckoview;
 
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.content.Context;
 import android.os.Process;
 import android.support.annotation.AnyThread;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
@@ -432,24 +433,25 @@ public final class GeckoRuntime implemen
      */
     @UiThread
     public void orientationChanged() {
         ThreadUtils.assertOnUiThread();
         GeckoScreenOrientation.getInstance().update();
     }
 
     /**
-     * Notify Gecko that the screen orientation has changed.
-     * @param newOrientation The new screen orientation, as retrieved e.g. from the current
-     *                       {@link android.content.res.Configuration}.
+     * Notify Gecko that the device configuration such as screen orientation has changed.
+     * @param newConfig The new Configuration object,
+     *                  {@link android.content.res.Configuration}.
      */
     @UiThread
-    public void orientationChanged(int newOrientation) {
+    public void configurationChanged(Configuration newConfig) {
         ThreadUtils.assertOnUiThread();
-        GeckoScreenOrientation.getInstance().update(newOrientation);
+        GeckoScreenOrientation.getInstance().update(newConfig.orientation);
+        GeckoSystemStateListener.getInstance().updateNightMode(newConfig.uiMode);
     }
 
     @Override // Parcelable
     @AnyThread
     public int describeContents() {
         return 0;
     }
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
@@ -460,17 +460,17 @@ public class GeckoView extends FrameLayo
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
         if (mRuntime != null) {
             // onConfigurationChanged is not called for 180 degree orientation changes,
             // we will miss such rotations and the screen orientation will not be
             // updated.
-            mRuntime.orientationChanged(newConfig.orientation);
+            mRuntime.configurationChanged(newConfig);
         }
     }
 
     @Override
     public boolean gatherTransparentRegion(final Region region) {
         // For detecting changes in SurfaceView layout, we take a shortcut here and
         // override gatherTransparentRegion, instead of registering a layout listener,
         // which is more expensive.
--- a/widget/android/nsLookAndFeel.cpp
+++ b/widget/android/nsLookAndFeel.cpp
@@ -395,16 +395,26 @@ nsresult nsLookAndFeel::GetIntImpl(IntID
 
     case eIntID_PrimaryPointerCapabilities:
       aResult = java::GeckoAppShell::GetPrimaryPointerCapabilities();
       break;
     case eIntID_AllPointerCapabilities:
       aResult = java::GeckoAppShell::GetAllPointerCapabilities();
       break;
 
+    case eIntID_SystemUsesDarkTheme:
+      // Bail out if AndroidBridge hasn't initialized since we try to query
+      // this vailue via nsMediaFeatures::InitSystemMetrics without initializing
+      // AndroidBridge on xpcshell tests.
+      if (!jni::IsAvailable()) {
+        return NS_ERROR_FAILURE;
+      }
+      aResult = java::GeckoSystemStateListener::IsNightMode() ? 1 : 0;
+      break;
+
     default:
       aResult = 0;
       rv = NS_ERROR_FAILURE;
   }
 
   return rv;
 }