OrientationManager.java revision ad08811a71e246d45ecdf97402f08cf7bd68e83b
1ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang/* 2ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * Copyright (C) 2012 The Android Open Source Project 3ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * 4ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * Licensed under the Apache License, Version 2.0 (the "License"); 5ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * you may not use this file except in compliance with the License. 6ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * You may obtain a copy of the License at 7ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * 8ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * http://www.apache.org/licenses/LICENSE-2.0 9ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * 10ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * Unless required by applicable law or agreed to in writing, software 11ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * distributed under the License is distributed on an "AS IS" BASIS, 12ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * See the License for the specific language governing permissions and 14ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang * limitations under the License. 15ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang */ 16ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 17ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Changpackage com.android.gallery3d.app; 18ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 19ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Changimport android.app.Activity; 20ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Changimport android.content.Context; 21ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Changimport android.content.pm.ActivityInfo; 22ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Changimport android.content.res.Configuration; 23ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Changimport android.view.OrientationEventListener; 24ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Changimport android.view.Surface; 25ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Changimport android.view.ViewConfiguration; 26ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 27ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Changimport java.util.ArrayList; 28ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 29ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Changpublic class OrientationManager { 30ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private static final String TAG = "OrientationManager"; 31ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 32ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public interface Listener { 33ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public void onOrientationCompensationChanged(int degrees); 34ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 35ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 36ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // Orientation hysteresis amount used in rounding, in degrees 37ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private static final int ORIENTATION_HYSTERESIS = 5; 38ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 39ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private Activity mActivity; 40ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private ArrayList<Listener> mListeners; 41ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private MyOrientationEventListener mOrientationListener; 42ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // The degrees of the device rotated clockwise from its natural orientation. 43ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; 44ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // If the framework orientation is locked. 45ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private boolean mOrientationLocked = false; 46ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // The orientation compensation: if the framwork orientation is locked, the 47ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // device orientation and the framework orientation may be different, so we 48ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // need to rotate the UI. For example, if this value is 90, the UI 49ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // components should be rotated 90 degrees counter-clockwise. 50ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private int mOrientationCompensation = 0; 51ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 52ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public OrientationManager(Activity activity) { 53ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mActivity = activity; 54ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mListeners = new ArrayList<Listener>(); 55ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mOrientationListener = new MyOrientationEventListener(activity); 56ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 57ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 58ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public void resume() { 59ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mOrientationListener.enable(); 60ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 61ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 62ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public void pause() { 63ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mOrientationListener.disable(); 64ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 65ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 66ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public void addListener(Listener listener) { 67ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang synchronized (mListeners) { 68ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mListeners.add(listener); 69ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 70ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 71ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 72ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public void removeListener(Listener listener) { 73ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang synchronized (mListeners) { 74ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mListeners.remove(listener); 75ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 76ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 77ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 78ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 79ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // Orientation handling 80ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // 81ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // We can choose to lock the framework orientation or not. If we lock the 82ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // framework orientation, we calculate a a compensation value according to 83ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // current device orientation and send it to listeners. If we don't lock 84ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // the framework orientation, we always set the compensation value to 0. 85ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang //////////////////////////////////////////////////////////////////////////// 86ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 87ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // Lock the framework orientation to the current device orientation 88ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public void lockOrientation() { 89ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang if (mOrientationLocked) return; 90ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mOrientationLocked = true; 91ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang if (mActivity.getResources().getConfiguration().orientation 92ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang == Configuration.ORIENTATION_LANDSCAPE) { 93ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang Log.d(TAG, "lock orientation to landscape"); 94ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 95ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } else { 96ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang Log.d(TAG, "lock orientation to portrait"); 97ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 98ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 99ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang updateCompensation(); 100ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 101ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 102ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // Unlock the framework orientation, so it can change when the device 103ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // rotates. 104ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public void unlockOrientation() { 105ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang if (!mOrientationLocked) return; 106ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mOrientationLocked = false; 107ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang Log.d(TAG, "unlock orientation"); 108ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 109ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang disableCompensation(); 110ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 111ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 112ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // Calculate the compensation value and send it to listeners. 113ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private void updateCompensation() { 114ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang if (mOrientation == OrientationEventListener.ORIENTATION_UNKNOWN) { 115ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang return; 116ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 117ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 118ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang int orientationCompensation = 119ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang (mOrientation + getDisplayRotation(mActivity)) % 360; 120ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 121ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang if (mOrientationCompensation != orientationCompensation) { 122ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mOrientationCompensation = orientationCompensation; 123ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang notifyListeners(); 124ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 125ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 126ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 127ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // Make the compensation value 0 and send it to listeners. 128ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private void disableCompensation() { 129ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang if (mOrientationCompensation != 0) { 130ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mOrientationCompensation = 0; 131ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang notifyListeners(); 132ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 133ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 134ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 135ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private void notifyListeners() { 136ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang synchronized (mListeners) { 137ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang for (int i = 0, n = mListeners.size(); i < n; i++) { 138ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mListeners.get(i).onOrientationCompensationChanged( 139ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mOrientationCompensation); 140ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 141ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 142ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 143ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 144ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // This listens to the device orientation, so we can update the compensation. 145ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private class MyOrientationEventListener extends OrientationEventListener { 146ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public MyOrientationEventListener(Context context) { 147ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang super(context); 148ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 149ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 150ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang @Override 151ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public void onOrientationChanged(int orientation) { 152ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // We keep the last known orientation. So if the user first orient 153ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // the camera then point the camera to floor or sky, we still have 154ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // the correct orientation. 155ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang if (orientation == ORIENTATION_UNKNOWN) return; 156ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang mOrientation = roundOrientation(orientation, mOrientation); 157ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // If the framework orientation is locked, we update the 158ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang // compensation value and notify the listeners. 159ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang if (mOrientationLocked) updateCompensation(); 160ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 161ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 162ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 163ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang public int getDisplayRotation() { 164ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang return getDisplayRotation(mActivity); 165ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 166ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 167ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private static int roundOrientation(int orientation, int orientationHistory) { 168ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang boolean changeOrientation = false; 169ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) { 170ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang changeOrientation = true; 171ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } else { 172ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang int dist = Math.abs(orientation - orientationHistory); 173ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang dist = Math.min(dist, 360 - dist); 174ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang changeOrientation = (dist >= 45 + ORIENTATION_HYSTERESIS); 175ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 176ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang if (changeOrientation) { 177ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang return ((orientation + 45) / 90 * 90) % 360; 178ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 179ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang return orientationHistory; 180ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 181ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang 182ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang private static int getDisplayRotation(Activity activity) { 183ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang int rotation = activity.getWindowManager().getDefaultDisplay() 184ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang .getRotation(); 185ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang switch (rotation) { 186ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang case Surface.ROTATION_0: return 0; 187ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang case Surface.ROTATION_90: return 90; 188ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang case Surface.ROTATION_180: return 180; 189ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang case Surface.ROTATION_270: return 270; 190ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 191ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang return 0; 192ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang } 193ad08811a71e246d45ecdf97402f08cf7bd68e83bChih-Chung Chang} 194