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.internal.view;
18
19import android.content.Context;
20import android.database.ContentObserver;
21import android.net.Uri;
22import android.os.AsyncTask;
23import android.os.Handler;
24import android.os.RemoteException;
25import android.os.ServiceManager;
26import android.os.UserHandle;
27import android.provider.Settings;
28import android.util.Log;
29import android.view.IWindowManager;
30import android.view.Surface;
31import android.view.WindowManagerGlobal;
32
33/**
34 * Provides helper functions for configuring the display rotation policy.
35 */
36public final class RotationPolicy {
37    private static final String TAG = "RotationPolicy";
38
39    private RotationPolicy() {
40    }
41
42    /**
43     * Returns true if the device supports the rotation-lock toggle feature
44     * in the system UI or system bar.
45     *
46     * When the rotation-lock toggle is supported, the "auto-rotate screen" option in
47     * Display settings should be hidden, but it should remain available in Accessibility
48     * settings.
49     */
50    public static boolean isRotationLockToggleSupported(Context context) {
51        return context.getResources().getConfiguration().smallestScreenWidthDp >= 600;
52    }
53
54    /**
55     * Returns true if the rotation-lock toggle should be shown in the UI.
56     */
57    public static boolean isRotationLockToggleVisible(Context context) {
58        return isRotationLockToggleSupported(context) &&
59                Settings.System.getIntForUser(context.getContentResolver(),
60                        Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
61                        UserHandle.USER_CURRENT) == 0;
62    }
63
64    /**
65     * Returns true if rotation lock is enabled.
66     */
67    public static boolean isRotationLocked(Context context) {
68        return Settings.System.getIntForUser(context.getContentResolver(),
69                Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
70    }
71
72    /**
73     * Enables or disables rotation lock.
74     *
75     * Should be used by the rotation lock toggle.
76     */
77    public static void setRotationLock(Context context, final boolean enabled) {
78        Settings.System.putIntForUser(context.getContentResolver(),
79                Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
80                UserHandle.USER_CURRENT);
81
82        AsyncTask.execute(new Runnable() {
83            @Override
84            public void run() {
85                try {
86                    IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
87                    if (enabled) {
88                        wm.freezeRotation(-1);
89                    } else {
90                        wm.thawRotation();
91                    }
92                } catch (RemoteException exc) {
93                    Log.w(TAG, "Unable to save auto-rotate setting");
94                }
95            }
96        });
97    }
98
99    /**
100     * Enables or disables rotation lock and adjusts whether the rotation lock toggle
101     * should be hidden for accessibility purposes.
102     *
103     * Should be used by Display settings and Accessibility settings.
104     */
105    public static void setRotationLockForAccessibility(Context context, final boolean enabled) {
106        Settings.System.putIntForUser(context.getContentResolver(),
107                Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0,
108                        UserHandle.USER_CURRENT);
109
110        AsyncTask.execute(new Runnable() {
111            @Override
112            public void run() {
113                try {
114                    IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
115                    if (enabled) {
116                        wm.freezeRotation(Surface.ROTATION_0);
117                    } else {
118                        wm.thawRotation();
119                    }
120                } catch (RemoteException exc) {
121                    Log.w(TAG, "Unable to save auto-rotate setting");
122                }
123            }
124        });
125    }
126
127    /**
128     * Registers a listener for rotation policy changes affecting the caller's user
129     */
130    public static void registerRotationPolicyListener(Context context,
131            RotationPolicyListener listener) {
132        registerRotationPolicyListener(context, listener, UserHandle.getCallingUserId());
133    }
134
135    /**
136     * Registers a listener for rotation policy changes affecting a specific user,
137     * or USER_ALL for all users.
138     */
139    public static void registerRotationPolicyListener(Context context,
140            RotationPolicyListener listener, int userHandle) {
141        context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
142                Settings.System.ACCELEROMETER_ROTATION),
143                false, listener.mObserver, userHandle);
144        context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
145                Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY),
146                false, listener.mObserver, userHandle);
147    }
148
149    /**
150     * Unregisters a listener for rotation policy changes.
151     */
152    public static void unregisterRotationPolicyListener(Context context,
153            RotationPolicyListener listener) {
154        context.getContentResolver().unregisterContentObserver(listener.mObserver);
155    }
156
157    /**
158     * Listener that is invoked whenever a change occurs that might affect the rotation policy.
159     */
160    public static abstract class RotationPolicyListener {
161        final ContentObserver mObserver = new ContentObserver(new Handler()) {
162            public void onChange(boolean selfChange, Uri uri) {
163                RotationPolicyListener.this.onChange();
164            }
165        };
166
167        public abstract void onChange();
168    }
169}