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