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