summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java')
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java195
1 files changed, 195 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java
new file mode 100644
index 0000000000..8b188438a4
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java
@@ -0,0 +1,195 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.InputDevice;
+import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.util.InputDeviceUtils;
+import org.mozilla.gecko.util.ThreadUtils;
+
+public class GeckoSystemStateListener implements InputManager.InputDeviceListener {
+ private static final String LOGTAG = "SystemStateListener";
+
+ private static final GeckoSystemStateListener listenerInstance = new GeckoSystemStateListener();
+
+ private boolean mInitialized;
+ private ContentObserver mContentObserver;
+ private static Context sApplicationContext;
+ private InputManager mInputManager;
+ private boolean mIsNightMode;
+
+ public static GeckoSystemStateListener getInstance() {
+ return listenerInstance;
+ }
+
+ private GeckoSystemStateListener() {}
+
+ public synchronized void initialize(final Context context) {
+ if (mInitialized) {
+ Log.w(LOGTAG, "Already initialized!");
+ return;
+ }
+ mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+ mInputManager.registerInputDeviceListener(listenerInstance, ThreadUtils.getUiHandler());
+
+ sApplicationContext = context;
+ final ContentResolver contentResolver = sApplicationContext.getContentResolver();
+ final Uri animationSetting = Settings.System.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE);
+ mContentObserver =
+ new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(final boolean selfChange) {
+ onDeviceChanged();
+ }
+ };
+ contentResolver.registerContentObserver(animationSetting, false, mContentObserver);
+
+ final Uri invertSetting =
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+ contentResolver.registerContentObserver(invertSetting, false, mContentObserver);
+
+ final Uri textContrastSetting =
+ Settings.Secure.getUriFor(
+ /*Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED*/ "high_text_contrast_enabled");
+ contentResolver.registerContentObserver(textContrastSetting, false, mContentObserver);
+
+ mIsNightMode =
+ (sApplicationContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
+
+ mInitialized = true;
+ }
+
+ public synchronized void shutdown() {
+ if (!mInitialized) {
+ Log.w(LOGTAG, "Already shut down!");
+ return;
+ }
+
+ if (mInputManager == null) {
+ Log.e(LOGTAG, "mInputManager should be valid!");
+ return;
+ }
+
+ mInputManager.unregisterInputDeviceListener(listenerInstance);
+
+ final ContentResolver contentResolver = sApplicationContext.getContentResolver();
+ contentResolver.unregisterContentObserver(mContentObserver);
+
+ mInitialized = false;
+ mInputManager = null;
+ mContentObserver = null;
+ }
+
+ @WrapForJNI(calledFrom = "gecko")
+ /**
+ * For prefers-reduced-motion media queries feature.
+ *
+ * <p>Uses `Settings.Global` which was introduced in API version 17.
+ */
+ private static boolean prefersReducedMotion() {
+ final ContentResolver contentResolver = sApplicationContext.getContentResolver();
+
+ return Settings.Global.getFloat(contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1)
+ == 0.0f;
+ }
+
+ @WrapForJNI(calledFrom = "gecko")
+ /**
+ * For inverted-colors queries feature.
+ *
+ * <p>Uses `Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED` which was introduced in API
+ * version 21.
+ */
+ private static boolean isInvertedColors() {
+ final ContentResolver contentResolver = sApplicationContext.getContentResolver();
+
+ return Settings.Secure.getInt(
+ contentResolver, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0)
+ == 1;
+ }
+
+ @WrapForJNI(calledFrom = "gecko")
+ /**
+ * For prefers-contrast queries feature.
+ *
+ * <p>Uses `Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED` which was introduced in API
+ * version 21.
+ */
+ private static boolean prefersContrast() {
+ final ContentResolver contentResolver = sApplicationContext.getContentResolver();
+
+ return Settings.Secure.getInt(
+ contentResolver, /*Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED*/
+ "high_text_contrast_enabled",
+ 0)
+ == 1;
+ }
+
+ /** For prefers-color-scheme media queries feature. */
+ public boolean isNightMode() {
+ return mIsNightMode;
+ }
+
+ public void updateNightMode(final int newUIMode) {
+ final boolean isNightMode =
+ (newUIMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ if (isNightMode == mIsNightMode) {
+ return;
+ }
+ mIsNightMode = isNightMode;
+ onDeviceChanged();
+ }
+
+ @WrapForJNI(stubName = "OnDeviceChanged", calledFrom = "any", dispatchTo = "gecko")
+ private static native void nativeOnDeviceChanged();
+
+ public static void onDeviceChanged() {
+ if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
+ nativeOnDeviceChanged();
+ } else {
+ GeckoThread.queueNativeCallUntil(
+ GeckoThread.State.PROFILE_READY, GeckoSystemStateListener.class, "nativeOnDeviceChanged");
+ }
+ }
+
+ private void notifyDeviceChanged(final int deviceId) {
+ final InputDevice device = InputDevice.getDevice(deviceId);
+ if (device == null || !InputDeviceUtils.isPointerTypeDevice(device)) {
+ return;
+ }
+ onDeviceChanged();
+ }
+
+ @Override
+ public void onInputDeviceAdded(final int deviceId) {
+ notifyDeviceChanged(deviceId);
+ }
+
+ @Override
+ public void onInputDeviceRemoved(final int deviceId) {
+ // Call onDeviceChanged directly without checking device source types
+ // since we can no longer get a valid `InputDevice` in the case of
+ // device removal.
+ onDeviceChanged();
+ }
+
+ @Override
+ public void onInputDeviceChanged(final int deviceId) {
+ notifyDeviceChanged(deviceId);
+ }
+}