1 /*
2  * Copyright (C) 2015 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.incallui;
18 
19 import android.content.Context;
20 import android.content.pm.ActivityInfo;
21 import android.support.annotation.IntDef;
22 import android.view.OrientationEventListener;
23 import com.android.dialer.common.LogUtil;
24 import java.lang.annotation.Retention;
25 import java.lang.annotation.RetentionPolicy;
26 
27 /**
28  * This class listens to Orientation events and overrides onOrientationChanged which gets invoked
29  * when an orientation change occurs. When that happens, we notify InCallUI registrants of the
30  * change.
31  */
32 public class InCallOrientationEventListener extends OrientationEventListener {
33 
34   public static final int SCREEN_ORIENTATION_0 = 0;
35   public static final int SCREEN_ORIENTATION_90 = 90;
36   public static final int SCREEN_ORIENTATION_180 = 180;
37   public static final int SCREEN_ORIENTATION_270 = 270;
38   public static final int SCREEN_ORIENTATION_360 = 360;
39 
40   /** Screen orientation angles one of 0, 90, 180, 270, 360 in degrees. */
41   @Retention(RetentionPolicy.SOURCE)
42   @IntDef({
43     SCREEN_ORIENTATION_0,
44     SCREEN_ORIENTATION_90,
45     SCREEN_ORIENTATION_180,
46     SCREEN_ORIENTATION_270,
47     SCREEN_ORIENTATION_360,
48     SCREEN_ORIENTATION_UNKNOWN
49   })
50   public @interface ScreenOrientation {}
51 
52   // We use SCREEN_ORIENTATION_USER so that reverse-portrait is not allowed.
53   public static final int ACTIVITY_PREFERENCE_ALLOW_ROTATION = ActivityInfo.SCREEN_ORIENTATION_USER;
54 
55   public static final int ACTIVITY_PREFERENCE_DISALLOW_ROTATION =
56       ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
57 
58   /**
59    * This is to identify dead zones where we won't notify others of orientation changed. Say for e.g
60    * our threshold is x degrees. We will only notify UI when our current rotation is within x
61    * degrees right or left of the screen orientation angles. If it's not within those ranges, we
62    * return SCREEN_ORIENTATION_UNKNOWN and ignore it.
63    */
64   public static final int SCREEN_ORIENTATION_UNKNOWN = -1;
65 
66   // Rotation threshold is 10 degrees. So if the rotation angle is within 10 degrees of any of
67   // the above angles, we will notify orientation changed.
68   private static final int ROTATION_THRESHOLD = 10;
69 
70   /** Cache the current rotation of the device. */
71   @ScreenOrientation private static int currentOrientation = SCREEN_ORIENTATION_0;
72 
73   private boolean enabled = false;
74 
InCallOrientationEventListener(Context context)75   public InCallOrientationEventListener(Context context) {
76     super(context);
77   }
78 
isWithinRange(int value, int begin, int end)79   private static boolean isWithinRange(int value, int begin, int end) {
80     return value >= begin && value < end;
81   }
82 
isWithinThreshold(int value, int center, int threshold)83   private static boolean isWithinThreshold(int value, int center, int threshold) {
84     return isWithinRange(value, center - threshold, center + threshold);
85   }
86 
isInLeftRange(int value, int center, int threshold)87   private static boolean isInLeftRange(int value, int center, int threshold) {
88     return isWithinRange(value, center - threshold, center);
89   }
90 
isInRightRange(int value, int center, int threshold)91   private static boolean isInRightRange(int value, int center, int threshold) {
92     return isWithinRange(value, center, center + threshold);
93   }
94 
95   @ScreenOrientation
getCurrentOrientation()96   public static int getCurrentOrientation() {
97     return currentOrientation;
98   }
99 
100   /**
101    * Handles changes in device orientation. Notifies InCallPresenter of orientation changes.
102    *
103    * <p>Note that this API receives sensor rotation in degrees as a param and we convert that to one
104    * of our screen orientation constants - (one of: {@link #SCREEN_ORIENTATION_0}, {@link
105    * #SCREEN_ORIENTATION_90}, {@link #SCREEN_ORIENTATION_180}, {@link #SCREEN_ORIENTATION_270}).
106    *
107    * @param rotation The new device sensor rotation in degrees
108    */
109   @Override
onOrientationChanged(int rotation)110   public void onOrientationChanged(int rotation) {
111     if (rotation == OrientationEventListener.ORIENTATION_UNKNOWN) {
112       return;
113     }
114 
115     final int orientation = toScreenOrientation(rotation);
116 
117     if (orientation != SCREEN_ORIENTATION_UNKNOWN && currentOrientation != orientation) {
118       LogUtil.i(
119           "InCallOrientationEventListener.onOrientationChanged",
120           "orientation: %d -> %d",
121           currentOrientation,
122           orientation);
123       currentOrientation = orientation;
124       InCallPresenter.getInstance().onDeviceOrientationChange(currentOrientation);
125     }
126   }
127 
128   /**
129    * Enables the OrientationEventListener and optionally notifies listeners of the current
130    * orientation.
131    *
132    * @param notifyDeviceOrientationChange Whether to notify listeners that the device orientation is
133    *     changed.
134    */
enable(boolean notifyDeviceOrientationChange)135   public void enable(boolean notifyDeviceOrientationChange) {
136     if (enabled) {
137       Log.v(this, "enable: Orientation listener is already enabled. Ignoring...");
138       return;
139     }
140 
141     super.enable();
142     enabled = true;
143     if (notifyDeviceOrientationChange) {
144       InCallPresenter.getInstance().onDeviceOrientationChange(currentOrientation);
145     }
146   }
147 
148   /** Enables the OrientationEventListener. */
149   @Override
enable()150   public void enable() {
151     enable(false /* notifyDeviceOrientationChange */);
152   }
153 
154   /** Disables the OrientationEventListener. */
155   @Override
disable()156   public void disable() {
157     if (!enabled) {
158       Log.v(this, "enable: Orientation listener is already disabled. Ignoring...");
159       return;
160     }
161 
162     enabled = false;
163     super.disable();
164   }
165 
166   /** Returns true the OrientationEventListener is enabled, false otherwise. */
isEnabled()167   public boolean isEnabled() {
168     return enabled;
169   }
170 
171   /**
172    * Converts sensor rotation in degrees to screen orientation constants.
173    *
174    * @param rotation sensor rotation angle in degrees
175    * @return Screen orientation angle in degrees (0, 90, 180, 270). Returns -1 for degrees not
176    *     within threshold to identify zones where orientation change should not be trigerred.
177    */
178   @ScreenOrientation
toScreenOrientation(int rotation)179   private int toScreenOrientation(int rotation) {
180     // Sensor orientation 90 is equivalent to screen orientation 270 and vice versa. This
181     // function returns the screen orientation. So we convert sensor rotation 90 to 270 and
182     // vice versa here.
183     if (isInLeftRange(rotation, SCREEN_ORIENTATION_360, ROTATION_THRESHOLD)
184         || isInRightRange(rotation, SCREEN_ORIENTATION_0, ROTATION_THRESHOLD)) {
185       return SCREEN_ORIENTATION_0;
186     } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_90, ROTATION_THRESHOLD)) {
187       return SCREEN_ORIENTATION_270;
188     } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_180, ROTATION_THRESHOLD)) {
189       return SCREEN_ORIENTATION_180;
190     } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_270, ROTATION_THRESHOLD)) {
191       return SCREEN_ORIENTATION_90;
192     }
193     return SCREEN_ORIENTATION_UNKNOWN;
194   }
195 }
196