1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// found in the LICENSE file. 4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)package org.chromium.content.browser; 6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.annotation.SuppressLint; 8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.content.ComponentCallbacks; 9a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.content.Context; 10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.content.res.Configuration; 11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.hardware.display.DisplayManager; 12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.hardware.display.DisplayManager.DisplayListener; 13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.os.Build; 14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.util.Log; 15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.view.Surface; 16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.view.WindowManager; 17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import org.chromium.base.ObserverList; 19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import org.chromium.base.ThreadUtils; 201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport org.chromium.base.VisibleForTesting; 21e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport org.chromium.ui.gfx.DeviceDisplayInfo; 22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)/** 24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * ScreenOrientationListener is a class that informs its observers when the 25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * screen orientation changes. 26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)@VisibleForTesting 28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)public class ScreenOrientationListener { 29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Observes changes in screen orientation. 32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public interface ScreenOrientationObserver { 34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Called whenever the screen orientation changes. 36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param orientation The orientation angle of the screen. 38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) void onScreenOrientationChanged(int orientation); 40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * ScreenOrientationListenerBackend is an interface that abstract the 44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * mechanism used for the actual screen orientation listening. The reason 45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * being that from Android API Level 17 DisplayListener will be used. Before 46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * that, an unreliable solution based on onConfigurationChanged has to be 47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * used. 48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private interface ScreenOrientationListenerBackend { 50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Starts to listen for screen orientation changes. This will be called 53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * when the first observer is added. 54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) void startListening(); 56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Stops to listen for screen orientation changes. This will be called 59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * when the last observer is removed. 60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) void stopListening(); 626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) /** 646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Toggle the accurate mode if it wasn't already doing so. The backend 656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * will keep track of the number of times this has been called. 666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) void startAccurateListening(); 686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) /** 706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Request to stop the accurate mode. It will effectively be stopped 716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * only if this method is called as many times as 726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * startAccurateListening(). 736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) void stopAccurateListening(); 75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * ScreenOrientationConfigurationListener implements ScreenOrientationListenerBackend 79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * to use ComponentCallbacks in order to listen for screen orientation 80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * changes. 81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * This method is known to not correctly detect 180 degrees changes but it 83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * is the only method that will work before API Level 17 (excluding polling). 846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * When toggleAccurateMode() is called, it will start polling in order to 856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * find out if the display has changed. 86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private class ScreenOrientationConfigurationListener 88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) implements ScreenOrientationListenerBackend, ComponentCallbacks { 89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private static final long POLLING_DELAY = 500; 916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private int mAccurateCount = 0; 936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // ScreenOrientationListenerBackend implementation: 95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void startListening() { 98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) mAppContext.registerComponentCallbacks(this); 99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void stopListening() { 103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) mAppContext.unregisterComponentCallbacks(this); 104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 1066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) @Override 1076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public void startAccurateListening() { 1086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ++mAccurateCount; 1096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (mAccurateCount > 1) 1116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return; 1126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Start polling if we went from 0 to 1. The polling will 1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // automatically stop when mAccurateCount reaches 0. 1156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) final ScreenOrientationConfigurationListener self = this; 1166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ThreadUtils.postOnUiThreadDelayed(new Runnable() { 1176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) @Override 1186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public void run() { 1196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) self.onConfigurationChanged(null); 1206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (self.mAccurateCount < 1) 1226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return; 1236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ThreadUtils.postOnUiThreadDelayed(this, 1256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ScreenOrientationConfigurationListener.POLLING_DELAY); 1266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) }, POLLING_DELAY); 1286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) @Override 1316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public void stopAccurateListening() { 1326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) --mAccurateCount; 1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) assert mAccurateCount >= 0; 1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // ComponentCallbacks implementation: 137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void onConfigurationChanged(Configuration newConfig) { 140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) notifyObservers(); 141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void onLowMemory() { 145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * ScreenOrientationDisplayListener implements ScreenOrientationListenerBackend 150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * to use DisplayListener in order to listen for screen orientation changes. 151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * This method is reliable but DisplayListener is only available for API Level 17+. 153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @SuppressLint("NewApi") 155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private class ScreenOrientationDisplayListener 156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) implements ScreenOrientationListenerBackend, DisplayListener { 157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // ScreenOrientationListenerBackend implementation: 159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void startListening() { 162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DisplayManager displayManager = 163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) (DisplayManager) mAppContext.getSystemService(Context.DISPLAY_SERVICE); 164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) displayManager.registerDisplayListener(this, null); 165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void stopListening() { 169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DisplayManager displayManager = 170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) (DisplayManager) mAppContext.getSystemService(Context.DISPLAY_SERVICE); 171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) displayManager.unregisterDisplayListener(this); 172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) @Override 1756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public void startAccurateListening() { 1766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Always accurate. Do nothing. 1776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) @Override 1806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public void stopAccurateListening() { 1816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Always accurate. Do nothing. 1826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 184a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // DisplayListener implementation: 185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void onDisplayAdded(int displayId) { 188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void onDisplayRemoved(int displayId) { 192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 193a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void onDisplayChanged(int displayId) { 196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) notifyObservers(); 197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private static final String TAG = "ScreenOrientationListener"; 202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // List of observers to notify when the screen orientation changes. 204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private final ObserverList<ScreenOrientationObserver> mObservers = 205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) new ObserverList<ScreenOrientationObserver>(); 206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // mOrientation will be updated every time the orientation changes. When not 208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // listening for changes, the value will be invalid and will be updated when 209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // starting to listen again. 210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private int mOrientation; 211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Current application context derived from the first context being received. 213a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private Context mAppContext; 214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private ScreenOrientationListenerBackend mBackend; 216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private static ScreenOrientationListener sInstance; 218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Returns a ScreenOrientationListener implementation based on the device's 221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * supported API level. 222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 223a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public static ScreenOrientationListener getInstance() { 224a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ThreadUtils.assertOnUiThread(); 225a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (sInstance == null) { 227a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) sInstance = new ScreenOrientationListener(); 228a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 229a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 230a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return sInstance; 231a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 232a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 233a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private ScreenOrientationListener() { 234a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) mBackend = Build.VERSION.SDK_INT >= 17 ? 235a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) new ScreenOrientationDisplayListener() : 236a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) new ScreenOrientationConfigurationListener(); 237a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 238a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 239a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 240a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Add |observer| in the ScreenOrientationListener observer list and 241a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * immediately call |onScreenOrientationChanged| on it with the current 242a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * orientation value. 243a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 244a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param observer The observer that will get notified. 245a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param context The context associated with this observer. 246a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 247a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void addObserver(ScreenOrientationObserver observer, Context context) { 248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (mAppContext == null) { 249a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) mAppContext = context.getApplicationContext(); 250a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 251a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 252a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) assert mAppContext == context.getApplicationContext(); 253a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) assert mAppContext != null; 254a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 255a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!mObservers.addObserver(observer)) { 256a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Log.w(TAG, "Adding an observer that is already present!"); 257a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return; 258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 259a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // If we got our first observer, we should start listening. 261a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (mObservers.size() == 1) { 262a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) updateOrientation(); 263a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) mBackend.startListening(); 264a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 265a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 266a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // We need to send the current value to the added observer as soon as 267a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // possible but outside of the current stack. 268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) final ScreenOrientationObserver obs = observer; 269a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ThreadUtils.assertOnUiThread(); 270a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ThreadUtils.postOnUiThread(new Runnable() { 271a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @Override 272a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void run() { 273a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) obs.onScreenOrientationChanged(mOrientation); 274a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 275a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) }); 276a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 277a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 278a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 279a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Remove the |observer| from the ScreenOrientationListener observer list. 280a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 281a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param observer The observer that will no longer receive notification. 282a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 283a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public void removeObserver(ScreenOrientationObserver observer) { 284a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!mObservers.removeObserver(observer)) { 285a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Log.w(TAG, "Removing an inexistent observer!"); 286a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return; 287a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 288a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 289a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (mObservers.isEmpty()) { 290a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // The last observer was removed, we should just stop listening. 291a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) mBackend.stopListening(); 292a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 293a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 294a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 295a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 2966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Toggle the accurate mode if it wasn't already doing so. The backend will 2976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * keep track of the number of times this has been called. 2986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 2996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public void startAccurateListening() { 3006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) mBackend.startAccurateListening(); 3016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) /** 3046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Request to stop the accurate mode. It will effectively be stopped only if 3056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * this method is called as many times as startAccurateListening(). 3066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 3076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public void stopAccurateListening() { 3086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) mBackend.stopAccurateListening(); 3096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) /** 312a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * This should be called by classes extending ScreenOrientationListener when 313a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * it is possible that there is a screen orientation change. If there is an 314a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * actual change, the observers will get notified. 315a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 316a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private void notifyObservers() { 317a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) int previousOrientation = mOrientation; 318a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) updateOrientation(); 319a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 320a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (mOrientation == previousOrientation) { 321a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return; 322a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 323a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 3246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DeviceDisplayInfo.create(mAppContext).updateNativeSharedDisplayInfo(); 3256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 326a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for (ScreenOrientationObserver observer : mObservers) { 327a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) observer.onScreenOrientationChanged(mOrientation); 328a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 329a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 330a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 331a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) /** 332a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Updates |mOrientation| based on the default display rotation. 333a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */ 334a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private void updateOrientation() { 335a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) WindowManager windowManager = 336a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) (WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE); 337a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 338a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) switch (windowManager.getDefaultDisplay().getRotation()) { 339a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) case Surface.ROTATION_0: 340a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) mOrientation = 0; 341a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) break; 342a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) case Surface.ROTATION_90: 343a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) mOrientation = 90; 344a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) break; 345a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) case Surface.ROTATION_180: 346a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) mOrientation = 180; 347a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) break; 348a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) case Surface.ROTATION_270: 349a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) mOrientation = -90; 350a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) break; 351a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) default: 352a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) throw new IllegalStateException( 353a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) "Display.getRotation() shouldn't return that value"); 354a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 355a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 356a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 357