blob: 29c37bbd0fd7d038484d87efc72a093b6d634971 [file] [log] [blame]
Angus Kong9f1db522013-11-09 16:25:59 -08001/*
2 * Copyright (C) 2013 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.camera.app;
18
19import android.app.Activity;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.pm.ActivityInfo;
23import android.content.res.Configuration;
24import android.os.Handler;
25import android.provider.Settings;
26import android.util.Log;
27import android.view.OrientationEventListener;
28import android.view.Surface;
29
30import com.android.camera.util.ApiHelper;
31
32import java.util.ArrayList;
33import java.util.List;
34
35/**
36 * The implementation of {@link com.android.camera.app.OrientationManager}
37 * by {@link android.view.OrientationEventListener}.
38 * TODO: make this class package-private
39 */
40public class OrientationManagerImpl implements OrientationManager {
Sascha Haeberling58501152014-01-06 11:02:35 -080041 private static final String TAG = "OrientationManagerImpl";
Angus Kong9f1db522013-11-09 16:25:59 -080042
43 // Orientation hysteresis amount used in rounding, in degrees
44 private static final int ORIENTATION_HYSTERESIS = 5;
45
Sascha Haeberling58501152014-01-06 11:02:35 -080046 private final Activity mActivity;
47 private final MyOrientationEventListener mOrientationListener;
Angus Kong9f1db522013-11-09 16:25:59 -080048 // If the framework orientation is locked.
49 private boolean mOrientationLocked = false;
50
51 // This is true if "Settings -> Display -> Rotation Lock" is checked. We
52 // don't allow the orientation to be unlocked if the value is true.
53 private boolean mRotationLockedSetting = false;
54
55 private final List<OrientationChangeCallback> mListeners =
56 new ArrayList<OrientationChangeCallback>();
57
58 private static class OrientationChangeCallback {
59 private final Handler mHandler;
60 private final OnOrientationChangeListener mListener;
61
62 OrientationChangeCallback(Handler handler, OnOrientationChangeListener listener) {
63 mHandler = handler;
64 mListener = listener;
65 }
66
67 public void postOrientationChangeCallback(final int orientation) {
68 mHandler.post(new Runnable() {
69 @Override
70 public void run() {
71 mListener.onOrientationChanged(orientation);
72 }
73 });
74 }
75
76 @Override
77 public boolean equals(Object o) {
78 if (o != null && o instanceof OrientationChangeCallback) {
79 OrientationChangeCallback c = (OrientationChangeCallback) o;
80 if (mHandler == c.mHandler && mListener == c.mListener) {
81 return true;
82 }
83 return false;
84 }
85 return false;
86 }
87 }
88
89 public OrientationManagerImpl(Activity activity) {
90 mActivity = activity;
91 mOrientationListener = new MyOrientationEventListener(activity);
92 }
93
94 public void resume() {
95 ContentResolver resolver = mActivity.getContentResolver();
96 mRotationLockedSetting = Settings.System.getInt(
97 resolver, Settings.System.ACCELEROMETER_ROTATION, 0) != 1;
98 mOrientationListener.enable();
99 }
100
101 public void pause() {
102 mOrientationListener.disable();
103 }
104
105 ////////////////////////////////////////////////////////////////////////////
106 // Orientation handling
107 //
108 // We can choose to lock the framework orientation or not. If we lock the
109 // framework orientation, we calculate a a compensation value according to
110 // current device orientation and send it to listeners. If we don't lock
111 // the framework orientation, we always set the compensation value to 0.
112 ////////////////////////////////////////////////////////////////////////////
113
114 @Override
115 public void addOnOrientationChangeListener(Handler handler,
116 OnOrientationChangeListener listener) {
117 OrientationChangeCallback callback = new OrientationChangeCallback(handler, listener);
118 if (mListeners.contains(callback)) {
119 return;
120 }
121 mListeners.add(callback);
122 }
123
124 @Override
125 public void removeOnOrientationChangeListener(Handler handler,
126 OnOrientationChangeListener listener) {
127 OrientationChangeCallback callback = new OrientationChangeCallback(handler, listener);
128 if (!mListeners.remove(callback)) {
129 Log.v(TAG, "Removing non-existing listener.");
130 }
131 }
132
133 @Override
134 public void lockOrientation() {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100135 if (mOrientationLocked || mRotationLockedSetting) {
136 return;
137 }
Angus Kong9f1db522013-11-09 16:25:59 -0800138 mOrientationLocked = true;
139 if (ApiHelper.HAS_ORIENTATION_LOCK) {
140 mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
141 } else {
142 mActivity.setRequestedOrientation(calculateCurrentScreenOrientation());
143 }
144 }
145
146 @Override
147 public void unlockOrientation() {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100148 if (!mOrientationLocked || mRotationLockedSetting) {
149 return;
150 }
Angus Kong9f1db522013-11-09 16:25:59 -0800151 mOrientationLocked = false;
152 Log.d(TAG, "unlock orientation");
153 mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
154 }
155
156 private int calculateCurrentScreenOrientation() {
157 int displayRotation = getDisplayRotation();
158 // Display rotation >= 180 means we need to use the REVERSE landscape/portrait
159 boolean standard = displayRotation < 180;
160 if (mActivity.getResources().getConfiguration().orientation
161 == Configuration.ORIENTATION_LANDSCAPE) {
162 return standard
163 ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
164 : ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
165 } else {
166 if (displayRotation == 90 || displayRotation == 270) {
167 // If displayRotation = 90 or 270 then we are on a landscape
168 // device. On landscape devices, portrait is a 90 degree
169 // clockwise rotation from landscape, so we need
170 // to flip which portrait we pick as display rotation is counter clockwise
171 standard = !standard;
172 }
173 return standard
174 ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
175 : ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
176 }
177 }
178
179 // This listens to the device orientation, so we can update the compensation.
180 private class MyOrientationEventListener extends OrientationEventListener {
181 public MyOrientationEventListener(Context context) {
182 super(context);
183 }
184
185 @Override
186 public void onOrientationChanged(int orientation) {
187 // We keep the last known orientation. So if the user first orient
188 // the camera then point the camera to floor or sky, we still have
189 // the correct orientation.
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100190 if (orientation == ORIENTATION_UNKNOWN) {
191 return;
192 }
Angus Kong9f1db522013-11-09 16:25:59 -0800193 final int roundedOrientation = roundOrientation(orientation, 0);
194
195 for (OrientationChangeCallback l : mListeners) {
196 l.postOrientationChangeCallback(roundedOrientation);
197 }
198 }
199 }
200
201 @Override
202 public int getDisplayRotation() {
203 return getDisplayRotation(mActivity);
204 }
205
206 private static int roundOrientation(int orientation, int orientationHistory) {
207 boolean changeOrientation = false;
208 if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) {
209 changeOrientation = true;
210 } else {
211 int dist = Math.abs(orientation - orientationHistory);
212 dist = Math.min(dist, 360 - dist);
213 changeOrientation = (dist >= 45 + ORIENTATION_HYSTERESIS);
214 }
215 if (changeOrientation) {
216 return ((orientation + 45) / 90 * 90) % 360;
217 }
218 return orientationHistory;
219 }
220
221 private static int getDisplayRotation(Activity activity) {
222 int rotation = activity.getWindowManager().getDefaultDisplay()
223 .getRotation();
224 switch (rotation) {
225 case Surface.ROTATION_0: return 0;
226 case Surface.ROTATION_90: return 90;
227 case Surface.ROTATION_180: return 180;
228 case Surface.ROTATION_270: return 270;
229 }
230 return 0;
231 }
232}