Developer tile for toggling desktop mode

Create a developer tile for toggling desktop windowing mode.
Relies on desktop windowing prototype flag, captions in shell flag and
freeform to be enabled.

Bug: 241944030
Test: manually verified tile is available in developer tiles
Change-Id: I0e5904ac896405ac166590972a83872cdbabb5a4
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c8c33a8..7c44a8f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4054,6 +4054,20 @@
                        android:value="true"/>
         </service>
 
+        <service
+            android:name=".development.qstile.DevelopmentTiles$DesktopMode"
+            android:label="@string/desktop_mode"
+            android:icon="@drawable/tile_icon_desktop_mode"
+            android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
+            android:exported="true"
+            android:enabled="false">
+            <intent-filter>
+                <action android:name="android.service.quicksettings.action.QS_TILE" />
+            </intent-filter>
+            <meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE"
+                android:value="true"/>
+        </service>
+
         <activity
             android:name=".HelpTrampoline"
             android:exported="true"
diff --git a/res/drawable/tile_icon_desktop_mode.xml b/res/drawable/tile_icon_desktop_mode.xml
new file mode 100644
index 0000000..2ccd81f
--- /dev/null
+++ b/res/drawable/tile_icon_desktop_mode.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2022 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M18,15V20Q18,20.825 17.413,21.413Q16.825,22 16,22H4Q3.175,22 2.588,21.413Q2,20.825 2,20V11Q2,10.175 2.588,9.587Q3.175,9 4,9H6V4Q6,3.175 6.588,2.587Q7.175,2 8,2H20Q20.825,2 21.413,2.587Q22,3.175 22,4V13Q22,13.825 21.413,14.412Q20.825,15 20,15ZM4,13V20Q4,20 4,20Q4,20 4,20H16Q16,20 16,20Q16,20 16,20V13ZM18,13H20Q20,13 20,13Q20,13 20,13V6H8V9H16Q16.825,9 17.413,9.587Q18,10.175 18,11Z" />
+</vector>
diff --git a/src/com/android/settings/development/qstile/DevelopmentTiles.java b/src/com/android/settings/development/qstile/DevelopmentTiles.java
index 0483aea..343ed24 100644
--- a/src/com/android/settings/development/qstile/DevelopmentTiles.java
+++ b/src/com/android/settings/development/qstile/DevelopmentTiles.java
@@ -476,4 +476,77 @@
                 Settings.System.SHOW_TOUCHES, isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
         }
     }
+
+    /**
+     * Tile to enable desktop mode
+     */
+    public static class DesktopMode extends DevelopmentTiles {
+
+        private static final int SETTING_VALUE_ON = 1;
+        private static final int SETTING_VALUE_OFF = 0;
+        private Context mContext;
+
+        @Override
+        public void onCreate() {
+            super.onCreate();
+            mContext = getApplicationContext();
+        }
+
+        @Override
+        protected boolean isEnabled() {
+            return Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.DESKTOP_MODE, SETTING_VALUE_OFF) == SETTING_VALUE_ON;
+        }
+
+        private boolean isDesktopModeFlagEnabled() {
+            return SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false);
+        }
+
+        private boolean isFreeformFlagEnabled() {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, SETTING_VALUE_OFF)
+                    == SETTING_VALUE_ON;
+        }
+
+        private boolean isCaptionOnShellEnabled() {
+            return SystemProperties.getBoolean("persist.wm.debug.caption_on_shell", false);
+        }
+
+        @Override
+        protected void setIsEnabled(boolean isEnabled) {
+            if (isEnabled) {
+                // Check that all required features are enabled
+                if (!isDesktopModeFlagEnabled()) {
+                    closeShade();
+                    showMessage(
+                            "Enable 'Desktop Windowing Proto 1' from the Flag Flipper app");
+                    return;
+                }
+                if (!isCaptionOnShellEnabled()) {
+                    closeShade();
+                    showMessage("Enable 'Captions in Shell' from the Flag Flipper app");
+                    return;
+                }
+                if (!isFreeformFlagEnabled()) {
+                    closeShade();
+                    showMessage(
+                            "Enable freeform windows from developer settings");
+                    return;
+                }
+            }
+
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.DESKTOP_MODE,
+                    isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
+            closeShade();
+        }
+
+        private void closeShade() {
+            sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+        }
+
+        private void showMessage(String message) {
+            Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
+        }
+    }
 }