Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(717)

Unified Diff: ui/android/java/src/org/chromium/ui/base/DisplayObserver.java

Issue 2300463002: Add observers for DIP scale change. (Closed)
Patch Set: Addressed comments, more questions. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/android/java/src/org/chromium/ui/base/DisplayObserver.java
diff --git a/ui/android/java/src/org/chromium/ui/base/DisplayObserver.java b/ui/android/java/src/org/chromium/ui/base/DisplayObserver.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe9e61f5e8422f5dbbb761865c8dd7047df051a5
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/base/DisplayObserver.java
@@ -0,0 +1,201 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.ui.base;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.Build;
+import android.view.WindowManager;
+
+import org.chromium.base.Log;
+import org.chromium.base.ObserverList;
+import org.chromium.base.ThreadUtils;
+import org.chromium.ui.gfx.DeviceDisplayInfo;
+
+/**
+ * Listens to changes in screen orientation.
+ */
+public interface DisplayObserver {
+ /**
+ * Called whenever the DIP scale changes.
+ *
+ * @param dipScale Density Independent Pixel scale (display density).
+ */
+ void onDIPScaleChanged(float dipScale);
+}
+
+/**
+ * DIPScaleMonitor is a class that informs its listeners when the
+ * density independent pixel (DIP) scale changes.
+ *
+ * This class is meant to monitor changes for one window, which belongs to one display.
+ */
+class DIPScaleMonitor {
+ /**
+ * DisplayListener receives configuration change signal from the system.
+ */
+ // DisplayManager.DisplayListener is only availabe at API level 17, hence making an inner class.
+ @SuppressLint("NewApi")
+ private class DisplayListener implements DisplayManager.DisplayListener {
+ @Override
+ public void onDisplayAdded(int displayId) {}
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ ThreadUtils.assertOnUiThread();
+ updateDIPScale(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {}
+ }
+
+ private static final String TAG = "cr_DIPScaleMonitor";
+
+ private static final boolean DEBUG = false;
+
+ // List of observers.
+ private ObserverList<DisplayObserver> mObservers = new ObserverList<DisplayObserver>();
+
+ // DisplayListener receives notifications from the system.
+ private DisplayListener mDisplayListener;
+
+ // Current display density.
+ private float mDIPScale;
+
+ // WindowAndroid we are attached to.
+ private WindowAndroid mWindowAndroid;
+
+ // Display ID this window is attached to.
+ private int mDisplayId;
+
+ // DisplayManager used to start listening.
+ private DisplayManager mDisplayManager;
+
+ public DIPScaleMonitor(WindowAndroid windowAndroid) {
+ ThreadUtils.assertOnUiThread();
+
+ mWindowAndroid = windowAndroid;
+ }
+
+ /**
+ * Returns current device scale factor for the window.
+ */
+ public float getDIPScale() {
+ ThreadUtils.assertOnUiThread();
+
+ // This method guarantees the up to date value only while we are monitoring, which happens
+ // only when there is at least one observer. In practice we use this method to set initial
+ // DIP scale which can be overwritten by observer any time, thus this is not a real
+ // restriction.
+ if (mDIPScale <= 0) {
+ DeviceDisplayInfo displayInfo =
+ DeviceDisplayInfo.create(getDisplayContext(mWindowAndroid));
+ mDIPScale = (float) displayInfo.getDIPScale();
+ }
+ return mDIPScale;
+ }
+
+ /**
+ * Adds |observer| in the DIPScaleListener observer list.
+ *
+ * @param listener The observer that will get notified.
+ * @param context The context associated with this observer.
+ * @return The DIP scale.
+ */
+ public void addObserver(DisplayObserver observer) {
+ ThreadUtils.assertOnUiThread();
+
+ final boolean noPriorObserver = mObservers.isEmpty();
+
+ // Prevent notifying an already attached observer.
+ if (!mObservers.addObserver(observer)) return;
+
+ if (noPriorObserver) {
+ // Update cached DIP scale.
+ Context displayContext = getDisplayContext(mWindowAndroid);
+ mDIPScale = (float) DeviceDisplayInfo.create(displayContext).getDIPScale();
+
+ startListening(displayContext);
+ }
+
+ observer.onDIPScaleChanged(mDIPScale);
+ }
+
+ /**
+ * Removes the |observer| from the DIPScaleListener observer list.
+ *
+ * @param listener The observer that will no longer receive notification.
+ */
+ public void removeObserver(DisplayObserver observer) {
+ ThreadUtils.assertOnUiThread();
+
+ final boolean wasRemoved = mObservers.removeObserver(observer);
+ if (wasRemoved && mObservers.isEmpty()) {
+ // Last observer is gone.
+ stopListening();
+ }
+ }
+
+ /**
+ * Helper methods
+ */
+
+ @SuppressLint("NewApi")
+ private void startListening(Context displayContext) {
+ if (DEBUG) Log.v(TAG, "startListening");
+
+ // DisplayManager is available at API level 17.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) return;
+
+ if (mDisplayListener == null) mDisplayListener = new DisplayListener();
+
+ WindowManager wm = (WindowManager) displayContext.getSystemService(Context.WINDOW_SERVICE);
+ mDisplayId = wm.getDefaultDisplay().getDisplayId();
+
+ mDisplayManager = (DisplayManager) displayContext.getSystemService(Context.DISPLAY_SERVICE);
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ }
+
+ @SuppressLint("NewApi")
+ private void stopListening() {
+ if (DEBUG) Log.v(TAG, "stopListening");
+
+ // DisplayManager is available at API level 17.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) return;
+
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ mDisplayManager = null;
+ }
+
+ // Get current DIP scale and notify observers if necessary.
+ private void updateDIPScale(int displayId) {
+ if (mWindowAndroid == null) return;
+
+ if (mDisplayId != displayId) return;
+
+ DeviceDisplayInfo displayInfo = DeviceDisplayInfo.create(getDisplayContext(mWindowAndroid));
+ float newDIPScale = (float) displayInfo.getDIPScale();
+
+ // Comparison of two floating point numbers is done on purpouse here: we assume
+ // the value comes from a prefedined set and is either exactly equal to the prior
+ // one or different enough.
+ if (newDIPScale != mDIPScale) {
+ if (DEBUG) Log.v(TAG, "DIP scale changed, new DIP scale:" + newDIPScale);
+ mDIPScale = newDIPScale;
+ displayInfo.updateNativeSharedDisplayInfo();
+
+ for (DisplayObserver observer : mObservers) observer.onDIPScaleChanged(newDIPScale);
+ }
+ }
+
+ private Context getDisplayContext(WindowAndroid windowAndroid) {
+ Context displayContext = windowAndroid.getContext().get();
+ // If displayContext does not exist the activity got destroyed, we do not care about
+ // DIP scale any more. Use an application context as the simplest way to prevent a crash.
+ return displayContext != null ? displayContext : windowAndroid.getApplicationContext();
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698