Home | History | Annotate | Download | only in app
      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 
     17 package com.android.gallery3d.app;
     18 
     19 import android.app.Activity;
     20 import android.content.ContentResolver;
     21 import android.content.Context;
     22 import android.content.pm.ActivityInfo;
     23 import android.content.res.Configuration;
     24 import android.provider.Settings;
     25 import android.view.OrientationEventListener;
     26 import android.view.Surface;
     27 import android.view.ViewConfiguration;
     28 
     29 import com.android.gallery3d.ui.OrientationSource;
     30 
     31 import java.util.ArrayList;
     32 
     33 public class OrientationManager implements OrientationSource {
     34     private static final String TAG = "OrientationManager";
     35 
     36     public interface Listener {
     37         public void onOrientationCompensationChanged();
     38     }
     39 
     40     // Orientation hysteresis amount used in rounding, in degrees
     41     private static final int ORIENTATION_HYSTERESIS = 5;
     42 
     43     private Activity mActivity;
     44     private ArrayList<Listener> mListeners;
     45     private MyOrientationEventListener mOrientationListener;
     46     // The degrees of the device rotated clockwise from its natural orientation.
     47     private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
     48     // If the framework orientation is locked.
     49     private boolean mOrientationLocked = false;
     50     // The orientation compensation: if the framwork orientation is locked, the
     51     // device orientation and the framework orientation may be different, so we
     52     // need to rotate the UI. For example, if this value is 90, the UI
     53     // components should be rotated 90 degrees counter-clockwise.
     54     private int mOrientationCompensation = 0;
     55 
     56     // This is true if "Settings -> Display -> Rotation Lock" is checked. We
     57     // don't allow the orientation to be unlocked if the value is true.
     58     private boolean mRotationLockedSetting = false;
     59 
     60     public OrientationManager(Activity activity) {
     61         mActivity = activity;
     62         mListeners = new ArrayList<Listener>();
     63         mOrientationListener = new MyOrientationEventListener(activity);
     64     }
     65 
     66     public void resume() {
     67         ContentResolver resolver = mActivity.getContentResolver();
     68         mRotationLockedSetting = Settings.System.getInt(
     69                 resolver, Settings.System.ACCELEROMETER_ROTATION, 0) != 1;
     70         mOrientationListener.enable();
     71     }
     72 
     73     public void pause() {
     74         mOrientationListener.disable();
     75     }
     76 
     77     public void addListener(Listener listener) {
     78         synchronized (mListeners) {
     79             mListeners.add(listener);
     80         }
     81     }
     82 
     83     public void removeListener(Listener listener) {
     84         synchronized (mListeners) {
     85             mListeners.remove(listener);
     86         }
     87     }
     88 
     89     ////////////////////////////////////////////////////////////////////////////
     90     //  Orientation handling
     91     //
     92     //  We can choose to lock the framework orientation or not. If we lock the
     93     //  framework orientation, we calculate a a compensation value according to
     94     //  current device orientation and send it to listeners. If we don't lock
     95     //  the framework orientation, we always set the compensation value to 0.
     96     ////////////////////////////////////////////////////////////////////////////
     97 
     98     // Lock the framework orientation to the current device orientation
     99     public void lockOrientation() {
    100         if (mOrientationLocked) return;
    101         mOrientationLocked = true;
    102         if (mActivity.getResources().getConfiguration().orientation
    103                 == Configuration.ORIENTATION_LANDSCAPE) {
    104             Log.d(TAG, "lock orientation to landscape");
    105             mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    106         } else {
    107             Log.d(TAG, "lock orientation to portrait");
    108             mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    109         }
    110         updateCompensation();
    111     }
    112 
    113     // Unlock the framework orientation, so it can change when the device
    114     // rotates.
    115     public void unlockOrientation() {
    116         if (!mOrientationLocked) return;
    117         if (mRotationLockedSetting) return;
    118         mOrientationLocked = false;
    119         Log.d(TAG, "unlock orientation");
    120         mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
    121         disableCompensation();
    122     }
    123 
    124     // Calculate the compensation value and send it to listeners.
    125     private void updateCompensation() {
    126         if (mOrientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
    127             return;
    128         }
    129 
    130         int orientationCompensation =
    131             (mOrientation + getDisplayRotation(mActivity)) % 360;
    132 
    133         if (mOrientationCompensation != orientationCompensation) {
    134             mOrientationCompensation = orientationCompensation;
    135             notifyListeners();
    136         }
    137     }
    138 
    139     // Make the compensation value 0 and send it to listeners.
    140     private void disableCompensation() {
    141         if (mOrientationCompensation != 0) {
    142             mOrientationCompensation = 0;
    143             notifyListeners();
    144         }
    145     }
    146 
    147     private void notifyListeners() {
    148         synchronized (mListeners) {
    149             for (int i = 0, n = mListeners.size(); i < n; i++) {
    150                 mListeners.get(i).onOrientationCompensationChanged();
    151             }
    152         }
    153     }
    154 
    155     // This listens to the device orientation, so we can update the compensation.
    156     private class MyOrientationEventListener extends OrientationEventListener {
    157         public MyOrientationEventListener(Context context) {
    158             super(context);
    159         }
    160 
    161         @Override
    162         public void onOrientationChanged(int orientation) {
    163             // We keep the last known orientation. So if the user first orient
    164             // the camera then point the camera to floor or sky, we still have
    165             // the correct orientation.
    166             if (orientation == ORIENTATION_UNKNOWN) return;
    167             mOrientation = roundOrientation(orientation, mOrientation);
    168             // If the framework orientation is locked, we update the
    169             // compensation value and notify the listeners.
    170             if (mOrientationLocked) updateCompensation();
    171         }
    172     }
    173 
    174     @Override
    175     public int getDisplayRotation() {
    176         return getDisplayRotation(mActivity);
    177     }
    178 
    179     @Override
    180     public int getCompensation() {
    181         return mOrientationCompensation;
    182     }
    183 
    184     private static int roundOrientation(int orientation, int orientationHistory) {
    185         boolean changeOrientation = false;
    186         if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) {
    187             changeOrientation = true;
    188         } else {
    189             int dist = Math.abs(orientation - orientationHistory);
    190             dist = Math.min(dist, 360 - dist);
    191             changeOrientation = (dist >= 45 + ORIENTATION_HYSTERESIS);
    192         }
    193         if (changeOrientation) {
    194             return ((orientation + 45) / 90 * 90) % 360;
    195         }
    196         return orientationHistory;
    197     }
    198 
    199     private static int getDisplayRotation(Activity activity) {
    200         int rotation = activity.getWindowManager().getDefaultDisplay()
    201                 .getRotation();
    202         switch (rotation) {
    203             case Surface.ROTATION_0: return 0;
    204             case Surface.ROTATION_90: return 90;
    205             case Surface.ROTATION_180: return 180;
    206             case Surface.ROTATION_270: return 270;
    207         }
    208         return 0;
    209     }
    210 }
    211