tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GeckoScreenOrientation.java (8067B)


      1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
      2 * This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 package org.mozilla.gecko;
      7 
      8 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
      9 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
     10 
     11 import android.content.Context;
     12 import android.graphics.Rect;
     13 import android.util.Log;
     14 import android.view.Surface;
     15 import java.util.ArrayList;
     16 import java.util.List;
     17 import org.mozilla.gecko.util.ThreadUtils;
     18 
     19 /*
     20 * Updates, locks and unlocks the screen orientation.
     21 *
     22 * Note: Replaces the OnOrientationChangeListener to avoid redundant rotation
     23 * event handling.
     24 */
     25 public class GeckoScreenOrientation {
     26  private static final String LOGTAG = "GeckoScreenOrientation";
     27 
     28  // Make sure that any change in hal/HalScreenConfiguration.h happens here too.
     29  public enum ScreenOrientation {
     30    NONE(0),
     31    PORTRAIT_PRIMARY(1 << 0),
     32    PORTRAIT_SECONDARY(1 << 1),
     33    PORTRAIT(PORTRAIT_PRIMARY.value | PORTRAIT_SECONDARY.value),
     34    LANDSCAPE_PRIMARY(1 << 2),
     35    LANDSCAPE_SECONDARY(1 << 3),
     36    LANDSCAPE(LANDSCAPE_PRIMARY.value | LANDSCAPE_SECONDARY.value),
     37    ANY(
     38        PORTRAIT_PRIMARY.value
     39            | PORTRAIT_SECONDARY.value
     40            | LANDSCAPE_PRIMARY.value
     41            | LANDSCAPE_SECONDARY.value),
     42    DEFAULT(1 << 4);
     43 
     44    public final short value;
     45 
     46    ScreenOrientation(final int value) {
     47      this.value = (short) value;
     48    }
     49 
     50    private static final ScreenOrientation[] sValues = ScreenOrientation.values();
     51 
     52    public static ScreenOrientation get(final int value) {
     53      for (final ScreenOrientation orient : sValues) {
     54        if (orient.value == value) {
     55          return orient;
     56        }
     57      }
     58      return NONE;
     59    }
     60  }
     61 
     62  // Singleton instance.
     63  private static GeckoScreenOrientation sInstance;
     64  // Default rotation, used when device rotation is unknown.
     65  private static final int DEFAULT_ROTATION = Surface.ROTATION_0;
     66  // Last updated screen orientation with Gecko value space.
     67  private ScreenOrientation mScreenOrientation = ScreenOrientation.PORTRAIT_PRIMARY;
     68 
     69  public interface OrientationChangeListener {
     70    void onScreenOrientationChanged(ScreenOrientation newOrientation);
     71  }
     72 
     73  private final List<OrientationChangeListener> mListeners;
     74 
     75  public static GeckoScreenOrientation getInstance() {
     76    if (sInstance == null) {
     77      sInstance = new GeckoScreenOrientation();
     78    }
     79    return sInstance;
     80  }
     81 
     82  private GeckoScreenOrientation() {
     83    mListeners = new ArrayList<>();
     84    update();
     85  }
     86 
     87  /** Add a listener that will be notified when the screen orientation has changed. */
     88  public void addListener(final OrientationChangeListener aListener) {
     89    ThreadUtils.assertOnUiThread();
     90    mListeners.add(aListener);
     91  }
     92 
     93  /** Remove a OrientationChangeListener again. */
     94  public void removeListener(final OrientationChangeListener aListener) {
     95    ThreadUtils.assertOnUiThread();
     96    mListeners.remove(aListener);
     97  }
     98 
     99  /**
    100   * Update screen orientation by retrieving orientation and rotation via GeckoAppShell.
    101   *
    102   * @return Whether the screen orientation has changed.
    103   */
    104  public boolean update() {
    105    // Check whether we have the application context for fenix/a-c unit test.
    106    final Context appContext = GeckoAppShell.getApplicationContext();
    107    if (appContext == null) {
    108      return false;
    109    }
    110    final Rect rect = GeckoAppShell.getScreenSizeIgnoreOverride();
    111    final int orientation =
    112        rect.width() >= rect.height() ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
    113    return update(getScreenOrientation(orientation, getRotation()));
    114  }
    115 
    116  /**
    117   * Update screen orientation given the Android orientation by retrieving rotation via
    118   * GeckoAppShell.
    119   *
    120   * @param aAndroidOrientation Android screen orientation from Configuration.orientation.
    121   * @return Whether the screen orientation has changed.
    122   */
    123  public boolean update(final int aAndroidOrientation) {
    124    return update(getScreenOrientation(aAndroidOrientation, getRotation()));
    125  }
    126 
    127  /**
    128   * Update screen orientation given the screen orientation.
    129   *
    130   * @param aScreenOrientation Gecko screen orientation based on Android orientation and rotation.
    131   * @return Whether the screen orientation has changed.
    132   */
    133  public synchronized boolean update(final ScreenOrientation aScreenOrientation) {
    134    // Gecko expects a definite screen orientation, so we default to the
    135    // primary orientations.
    136    final ScreenOrientation screenOrientation;
    137    if ((aScreenOrientation.value & ScreenOrientation.PORTRAIT_PRIMARY.value) != 0) {
    138      screenOrientation = ScreenOrientation.PORTRAIT_PRIMARY;
    139    } else if ((aScreenOrientation.value & ScreenOrientation.PORTRAIT_SECONDARY.value) != 0) {
    140      screenOrientation = ScreenOrientation.PORTRAIT_SECONDARY;
    141    } else if ((aScreenOrientation.value & ScreenOrientation.LANDSCAPE_PRIMARY.value) != 0) {
    142      screenOrientation = ScreenOrientation.LANDSCAPE_PRIMARY;
    143    } else if ((aScreenOrientation.value & ScreenOrientation.LANDSCAPE_SECONDARY.value) != 0) {
    144      screenOrientation = ScreenOrientation.LANDSCAPE_SECONDARY;
    145    } else {
    146      screenOrientation = ScreenOrientation.PORTRAIT_PRIMARY;
    147    }
    148    if (mScreenOrientation == screenOrientation) {
    149      return false;
    150    }
    151    mScreenOrientation = screenOrientation;
    152    Log.d(LOGTAG, "updating to new orientation " + mScreenOrientation);
    153    notifyListeners(mScreenOrientation);
    154    ScreenManagerHelper.refreshScreenInfo();
    155    return true;
    156  }
    157 
    158  private void notifyListeners(final ScreenOrientation newOrientation) {
    159    final Runnable notifier =
    160        new Runnable() {
    161          @Override
    162          public void run() {
    163            for (final OrientationChangeListener listener : mListeners) {
    164              listener.onScreenOrientationChanged(newOrientation);
    165            }
    166          }
    167        };
    168 
    169    if (ThreadUtils.isOnUiThread()) {
    170      notifier.run();
    171    } else {
    172      ThreadUtils.runOnUiThread(notifier);
    173    }
    174  }
    175 
    176  /**
    177   * @return The Gecko screen orientation derived from Android orientation and rotation.
    178   */
    179  public ScreenOrientation getScreenOrientation() {
    180    return mScreenOrientation;
    181  }
    182 
    183  /**
    184   * Combine the Android orientation and rotation to the Gecko orientation.
    185   *
    186   * @param aAndroidOrientation Android orientation from Configuration.orientation.
    187   * @param aRotation Device rotation from Display.getRotation().
    188   * @return Gecko screen orientation.
    189   */
    190  private ScreenOrientation getScreenOrientation(
    191      final int aAndroidOrientation, final int aRotation) {
    192    final boolean isPrimary = aRotation == Surface.ROTATION_0 || aRotation == Surface.ROTATION_90;
    193    if (aAndroidOrientation == ORIENTATION_PORTRAIT) {
    194      if (isPrimary) {
    195        // Non-rotated portrait device or landscape device rotated
    196        // to primary portrait mode counter-clockwise.
    197        return ScreenOrientation.PORTRAIT_PRIMARY;
    198      }
    199      return ScreenOrientation.PORTRAIT_SECONDARY;
    200    }
    201    if (aAndroidOrientation == ORIENTATION_LANDSCAPE) {
    202      if (isPrimary) {
    203        // Non-rotated landscape device or portrait device rotated
    204        // to primary landscape mode counter-clockwise.
    205        return ScreenOrientation.LANDSCAPE_PRIMARY;
    206      }
    207      return ScreenOrientation.LANDSCAPE_SECONDARY;
    208    }
    209    return ScreenOrientation.NONE;
    210  }
    211 
    212  /**
    213   * @return Device rotation converted to an angle.
    214   */
    215  public short getAngle() {
    216    switch (getRotation()) {
    217      case Surface.ROTATION_0:
    218        return 0;
    219      case Surface.ROTATION_90:
    220        return 90;
    221      case Surface.ROTATION_180:
    222        return 180;
    223      case Surface.ROTATION_270:
    224        return 270;
    225      default:
    226        Log.w(LOGTAG, "getAngle: unexpected rotation value");
    227        return 0;
    228    }
    229  }
    230 
    231  /**
    232   * @return Device rotation.
    233   */
    234  private int getRotation() {
    235    return GeckoAppShell.getRotation();
    236  }
    237 }