1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
|
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.view;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.hardware.configstore.V1_1.DisplayOrientation;
import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
import android.hardware.configstore.V1_1.OptionalDisplayOrientation;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.IWindowManager;
import android.view.Surface;
import android.view.WindowManagerGlobal;
import com.android.internal.R;
import java.util.NoSuchElementException;
/**
* Provides helper functions for configuring the display rotation policy.
*/
public final class RotationPolicy {
private static final String TAG = "RotationPolicy";
private static final int CURRENT_ROTATION = -1;
private static int sNaturalRotation = -1;
private RotationPolicy() {
}
/**
* Gets whether the device supports rotation. In general such a
* device has an accelerometer and has the portrait and landscape
* features.
*
* @param context Context for accessing system resources.
* @return Whether the device supports rotation.
*/
public static boolean isRotationSupported(Context context) {
PackageManager pm = context.getPackageManager();
return pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_ACCELEROMETER)
&& pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT)
&& pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE)
&& context.getResources().getBoolean(
com.android.internal.R.bool.config_supportAutoRotation);
}
/**
* Returns the orientation that will be used when locking the orientation from system UI
* with {@link #setRotationLock}.
*
* If the device only supports locking to its natural orientation, this will be either
* Configuration.ORIENTATION_PORTRAIT or Configuration.ORIENTATION_LANDSCAPE,
* otherwise Configuration.ORIENTATION_UNDEFINED if any orientation is lockable.
*/
public static int getRotationLockOrientation(Context context) {
if (isCurrentRotationAllowed(context)) {
return Configuration.ORIENTATION_UNDEFINED;
}
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
final int rotation =
context.getResources().getConfiguration().windowConfiguration.getRotation();
final boolean rotated = rotation % 2 != 0;
final int w = rotated ? metrics.heightPixels : metrics.widthPixels;
final int h = rotated ? metrics.widthPixels : metrics.heightPixels;
return w < h ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
}
/**
* Returns true if the rotation-lock toggle should be shown in system UI.
*/
public static boolean isRotationLockToggleVisible(Context context) {
return isRotationSupported(context) &&
Settings.System.getIntForUser(context.getContentResolver(),
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
UserHandle.USER_CURRENT) == 0;
}
/**
* Returns true if rotation lock is enabled.
*/
public static boolean isRotationLocked(Context context) {
return Settings.System.getIntForUser(context.getContentResolver(),
Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
}
/**
* Enables or disables rotation lock from the system UI toggle.
*/
public static void setRotationLock(Context context, final boolean enabled) {
final int rotation = isCurrentRotationAllowed(context)
|| useCurrentRotationOnRotationLockChange(context) ? CURRENT_ROTATION
: getNaturalRotation();
setRotationLockAtAngle(context, enabled, rotation);
}
/**
* Enables or disables rotation lock at a specific rotation from system UI.
*/
public static void setRotationLockAtAngle(Context context, final boolean enabled,
final int rotation) {
Settings.System.putIntForUser(context.getContentResolver(),
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
UserHandle.USER_CURRENT);
setRotationLock(enabled, rotation);
}
/**
* Enables or disables natural rotation lock from Accessibility settings.
*
* If rotation is locked for accessibility, the system UI toggle is hidden to avoid confusion.
*/
public static void setRotationLockForAccessibility(Context context, final boolean enabled) {
Settings.System.putIntForUser(context.getContentResolver(),
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
setRotationLock(enabled, getNaturalRotation());
}
public static boolean isRotationAllowed(int rotation,
int userRotationAngles, boolean allowAllRotations) {
if (userRotationAngles < 0) {
// Not set by user so use these defaults
userRotationAngles = allowAllRotations ?
(1 | 2 | 4 | 8) : // All angles
(1 | 2 | 8); // All except 180
}
switch (rotation) {
case Surface.ROTATION_0:
return (userRotationAngles & 1) != 0;
case Surface.ROTATION_90:
return (userRotationAngles & 2) != 0;
case Surface.ROTATION_180:
return (userRotationAngles & 4) != 0;
case Surface.ROTATION_270:
return (userRotationAngles & 8) != 0;
}
return false;
}
private static boolean isCurrentRotationAllowed(Context context) {
int userRotationAngles = Settings.System.getInt(context.getContentResolver(),
Settings.System.ACCELEROMETER_ROTATION_ANGLES, -1);
boolean allowAllRotations = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowAllRotations);
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
return isRotationAllowed(wm.getDefaultDisplayRotation(), userRotationAngles,
allowAllRotations);
} catch (RemoteException exc) {
Log.w(TAG, "Unable to getWindowManagerService.getDefaultDisplayRotation()");
}
return false;
}
private static boolean useCurrentRotationOnRotationLockChange(Context context) {
return context.getResources().getBoolean(
R.bool.config_useCurrentRotationOnRotationLockChange);
}
private static void setRotationLock(final boolean enabled, final int rotation) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
if (enabled) {
wm.freezeRotation(rotation);
} else {
wm.thawRotation();
}
} catch (RemoteException exc) {
Log.w(TAG, "Unable to save auto-rotate setting");
}
}
});
}
/**
* Registers a listener for rotation policy changes affecting the caller's user
*/
public static void registerRotationPolicyListener(Context context,
RotationPolicyListener listener) {
registerRotationPolicyListener(context, listener, UserHandle.getCallingUserId());
}
/**
* Registers a listener for rotation policy changes affecting a specific user,
* or USER_ALL for all users.
*/
public static void registerRotationPolicyListener(Context context,
RotationPolicyListener listener, int userHandle) {
context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
Settings.System.ACCELEROMETER_ROTATION),
false, listener.mObserver, userHandle);
context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY),
false, listener.mObserver, userHandle);
}
/**
* Unregisters a listener for rotation policy changes.
*/
public static void unregisterRotationPolicyListener(Context context,
RotationPolicyListener listener) {
context.getContentResolver().unregisterContentObserver(listener.mObserver);
}
public static int getNaturalRotation() {
if (sNaturalRotation == -1) {
sNaturalRotation = getNaturalRotationConfig();
}
return sNaturalRotation;
}
private static int getNaturalRotationConfig() {
String primaryDisplayOrientation =
SystemProperties.get("ro.surface_flinger.primary_display_orientation");
if (primaryDisplayOrientation == "ORIENTATION_90") {
return Surface.ROTATION_90;
}
if (primaryDisplayOrientation == "ORIENTATION_180") {
return Surface.ROTATION_180;
}
if (primaryDisplayOrientation == "ORIENTATION_270") {
return Surface.ROTATION_270;
}
OptionalDisplayOrientation orientation;
try {
orientation =
ISurfaceFlingerConfigs.getService().primaryDisplayOrientation();
switch (orientation.value) {
case DisplayOrientation.ORIENTATION_90:
return Surface.ROTATION_90;
case DisplayOrientation.ORIENTATION_180:
return Surface.ROTATION_180;
case DisplayOrientation.ORIENTATION_270:
return Surface.ROTATION_270;
}
} catch (RemoteException | NoSuchElementException e) {
// do nothing
}
return Surface.ROTATION_0;
}
/**
* Listener that is invoked whenever a change occurs that might affect the rotation policy.
*/
public static abstract class RotationPolicyListener {
final ContentObserver mObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
RotationPolicyListener.this.onChange();
}
};
public abstract void onChange();
}
}
|