Add the Allow Rotation setting to Launcher3.

This CL adds a Settings activity along with the code needed to provide
a "Allow Rotation" setting to all phones and tablets. This setting is
set to false for phones and true for tablets. On changing the setting
from unlocked to locked, the launcher (and the Settings activity)
will get locked to the orientation the user was in when he disabled
"Allow Rotation". This is consistent with how the natural rotation
feature of Android works.

Change-Id: I8a1c59d1fa0bb9262530cad96e0a9bdbab0d9344
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 1aa446e..da6430a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -221,7 +221,8 @@
     public static final String USER_HAS_MIGRATED = "launcher.user_migrated_from_old_data";
 
     /** The different states that Launcher can be in. */
-    enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED };
+    enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED }
+
     @Thunk State mState = State.WORKSPACE;
     @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation;
 
@@ -405,6 +406,24 @@
 
     FocusIndicatorView mFocusHandler;
 
+    private boolean mRotationEnabled = false;
+    private boolean mPreferenceObserverRegistered = false;
+
+    final private SharedPreferences.OnSharedPreferenceChangeListener mSettingsObserver =
+            new SharedPreferences.OnSharedPreferenceChangeListener() {
+        @Override
+        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+            if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) {
+                if (mRotationEnabled = sharedPreferences.getBoolean(
+                        Utilities.ALLOW_ROTATION_PREFERENCE_KEY, false)) {
+                    unlockScreenOrientation(true);
+                } else {
+                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
+                }
+            }
+        }
+    };
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         if (DEBUG_STRICT_MODE) {
@@ -505,7 +524,19 @@
         IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         registerReceiver(mCloseSystemDialogsReceiver, filter);
 
-        // On large interfaces, we want the screen to auto-rotate based on the current orientation
+        mRotationEnabled = Utilities.isRotationAllowedForDevice(getApplicationContext());
+        // In case we are on a device with locked rotation, we should look at preferences to check
+        // if the user has specifically allowed rotation.
+        if (!mRotationEnabled) {
+            getSharedPreferences(LauncherFiles.ROTATION_PREF_FILE,
+                    Context.MODE_MULTI_PROCESS).registerOnSharedPreferenceChangeListener(
+                    mSettingsObserver);
+            mPreferenceObserverRegistered = true;
+            mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
+        }
+
+        // On large interfaces, or on devices that a user has specifically enabled screen rotation,
+        // we want the screen to auto-rotate based on the current orientation
         unlockScreenOrientation(true);
 
         if (mLauncherCallbacks != null) {
@@ -1300,8 +1331,11 @@
     protected boolean hasSettings() {
         if (mLauncherCallbacks != null) {
             return mLauncherCallbacks.hasSettings();
+        } else {
+            // On devices with a locked orientation, we will at least have the allow rotation
+            // setting.
+            return !Utilities.isRotationAllowedForDevice(this);
         }
-        return false;
     }
 
     public void addToCustomContentPage(View customContent,
@@ -2083,6 +2117,13 @@
     public void onDestroy() {
         super.onDestroy();
 
+        if (mPreferenceObserverRegistered) {
+            getSharedPreferences(LauncherFiles.ROTATION_PREF_FILE,
+                    Context.MODE_MULTI_PROCESS).unregisterOnSharedPreferenceChangeListener(
+                    mSettingsObserver);
+            mPreferenceObserverRegistered = false;
+        }
+
         // Remove all pending runnables
         mHandler.removeMessages(ADVANCE_MSG);
         mHandler.removeMessages(0);
@@ -2865,6 +2906,8 @@
         if (LOGD) Log.d(TAG, "onClickSettingsButton");
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onClickSettingsButton(v);
+        } else {
+            showSettingsActivity();
         }
     }
 
@@ -4472,7 +4515,7 @@
     }
 
     public void lockScreenOrientation() {
-        if (Utilities.isRotationEnabled(this)) {
+        if (mRotationEnabled) {
             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
                 setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources()
                         .getConfiguration().orientation));
@@ -4481,8 +4524,9 @@
             }
         }
     }
+
     public void unlockScreenOrientation(boolean immediate) {
-        if (Utilities.isRotationEnabled(this)) {
+        if (mRotationEnabled) {
             if (immediate) {
                 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
             } else {
@@ -4587,6 +4631,10 @@
         editor.apply();
     }
 
+    private void showSettingsActivity() {
+        startActivity(new Intent(this, SettingsActivity.class));
+    }
+
     /**
      * To be overridden by subclasses to indicate that there is an in-activity full-screen intro
      * screen that must be displayed and dismissed.