| 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();
|
| + }
|
| +}
|
|
|