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