QS edit drag and drop accessibility

Bug: 27667843
Change-Id: Ief0980af25d855942b0565e5a9bfcc0d266bdbe0
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 75f8fa4..6438564 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -31,7 +31,8 @@
         android:layout_height="0dp"
         android:layout_weight="1"
         android:scrollIndicators="top"
-        android:scrollbars="vertical" />
+        android:scrollbars="vertical"
+        android:importantForAccessibility="no" />
 
     <View
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 25ecd888..a523a41 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1532,4 +1532,32 @@
 
     <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity and that things might crash/not work properly [CHAR LIMIT=NONE] -->
     <string name="forced_resizable_info_text">App may not work with multi-window</string>
+
+    <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_tile_label">Position <xliff:g id="position" example="2">%1$d</xliff:g>, <xliff:g id="tile_name" example="Wi-Fi">%2$s</xliff:g>. Double tap to edit.</string>
+
+    <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_add_tile_label"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g>. Double tap to add.</string>
+
+    <!-- Accessibility description of a place to drop a tile while editing positions [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_position_label">Position <xliff:g id="position" example="2">%1$d</xliff:g>. Double tap to select.</string>
+
+    <!-- Accessibility description of option to move QS tile [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_move_tile">Move <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string>
+
+    <!-- Accessibility description of option to remove QS tile [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_remove_tile">Remove <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string>
+
+    <!-- Accessibility action when QS tile is added [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_tile_added"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> is added to position <xliff:g id="position" example="5">%2$d</xliff:g></string>
+
+    <!-- Accessibility action when QS tile is removed [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_tile_removed"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> is removed</string>
+
+    <!-- Accessibility action when QS tile is moved [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_tile_moved"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> moved to position <xliff:g id="position" example="5">%2$d</xliff:g></string>
+
+    <!-- Accessibility label for window when QS editing is happening [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_quick_settings_edit">Quick settings editor.</string>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index 5b05e84..43ebe04 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -140,6 +140,8 @@
     public void notifyCustomizeChanged() {
         // The customize state changed, so our height changed.
         updateBottom();
+        mQSPanel.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
+        mHeader.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
         // Let the panel know the position changed and it needs to update where notifications
         // and whatnot are.
         mPanelView.onQsHeightChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 42e98aa..1659888 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -294,6 +294,8 @@
         }
     }
 
+    public abstract CharSequence getTileLabel();
+
     protected final class H extends Handler {
         private static final int ADD_CALLBACK = 1;
         private static final int CLICK = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 03c2a0b..353c6b6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -14,7 +14,10 @@
 
 package com.android.systemui.qs.customize;
 
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.graphics.Canvas;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Handler;
@@ -27,7 +30,10 @@
 import android.support.v7.widget.helper.ItemTouchHelper;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 import com.android.systemui.R;
@@ -37,6 +43,7 @@
 import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
 import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
 import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -49,21 +56,27 @@
 
     private static final int TYPE_TILE = 0;
     private static final int TYPE_EDIT = 1;
+    private static final int TYPE_ACCESSIBLE_DROP = 2;
 
     private final Context mContext;
 
     private final Handler mHandler = new Handler();
     private final List<TileInfo> mTiles = new ArrayList<>();
     private final ItemTouchHelper mItemTouchHelper;
+    private final AccessibilityManager mAccessibilityManager;
     private int mDividerIndex;
+    private boolean mNeedsFocus;
     private List<String> mCurrentSpecs;
     private List<TileInfo> mOtherTiles;
     private List<TileInfo> mAllTiles;
 
     private Holder mCurrentDrag;
+    private boolean mAccessibilityMoving;
+    private int mAccessibilityFromIndex;
 
     public TileAdapter(Context context) {
         mContext = context;
+        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         mItemTouchHelper = new ItemTouchHelper(mCallbacks);
         setHasStableIds(true);
     }
@@ -130,6 +143,9 @@
 
     @Override
     public int getItemViewType(int position) {
+        if (mAccessibilityMoving && position == mDividerIndex - 1) {
+            return TYPE_ACCESSIBLE_DROP;
+        }
         if (mTiles.get(position) == null) {
             return TYPE_EDIT;
         }
@@ -140,7 +156,7 @@
     public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
         final Context context = parent.getContext();
         LayoutInflater inflater = LayoutInflater.from(context);
-        if (viewType == 1) {
+        if (viewType == TYPE_EDIT) {
             return new Holder(inflater.inflate(R.layout.qs_customize_divider, parent, false));
         }
         FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
@@ -155,22 +171,157 @@
     }
 
     @Override
-    public void onBindViewHolder(final Holder holder, int position) {
+    public void onBindViewHolder(final Holder holder, final int position) {
         if (holder.getItemViewType() == TYPE_EDIT) {
             ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(
                     mCurrentDrag != null ? R.string.drag_to_remove_tiles
                     : R.string.drag_to_add_tiles);
             return;
         }
+        if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) {
+            holder.mTileView.setClickable(true);
+            holder.mTileView.setFocusable(true);
+            holder.mTileView.setFocusableInTouchMode(true);
+            holder.mTileView.setVisibility(View.VISIBLE);
+            holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+            holder.mTileView.setContentDescription(mContext.getString(
+                    R.string.accessibility_qs_edit_position_label, position + 1));
+            holder.mTileView.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    selectPosition(position, v);
+                }
+            });
+            if (mNeedsFocus) {
+                // Wait for this to get laid out then set its focus.
+                holder.mTileView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+                    @Override
+                    public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                            int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                        holder.mTileView.removeOnLayoutChangeListener(this);
+                        holder.mTileView.requestFocus();
+                    }
+                });
+                mNeedsFocus = false;
+            }
+            return;
+        }
 
         TileInfo info = mTiles.get(position);
+
+        if (position > mDividerIndex) {
+            info.state.contentDescription = mContext.getString(
+                    R.string.accessibility_qs_edit_add_tile_label, info.state.label);
+        } else if (mAccessibilityMoving) {
+            info.state.contentDescription = mContext.getString(
+                    R.string.accessibility_qs_edit_position_label, position + 1);
+        } else {
+            info.state.contentDescription = mContext.getString(
+                    R.string.accessibility_qs_edit_tile_label, position + 1, info.state.label);
+        }
         holder.mTileView.onStateChanged(info.state);
+
+        if (mAccessibilityManager.isTouchExplorationEnabled()) {
+            final boolean selectable = !mAccessibilityMoving || position < mDividerIndex;
+            holder.mTileView.setClickable(selectable);
+            holder.mTileView.setFocusable(selectable);
+            holder.mTileView.setImportantForAccessibility(selectable
+                    ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
+                    : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+            if (selectable) {
+                holder.mTileView.setOnClickListener(new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        if (mAccessibilityMoving) {
+                            selectPosition(position, v);
+                        } else {
+                            if (position < mDividerIndex) {
+                                showAccessibilityDialog(position, v);
+                            } else {
+                                startAccessibleDrag(position);
+                            }
+                        }
+                    }
+                });
+            }
+        }
+    }
+
+    private void selectPosition(int position, View v) {
+        // Remove the placeholder.
+        mTiles.remove(mDividerIndex--);
+        mAccessibilityMoving = false;
+        move(mAccessibilityFromIndex, position, v);
+        notifyDataSetChanged();
+    }
+
+    private void showAccessibilityDialog(final int position, final View v) {
+        TileInfo info = mTiles.get(position);
+        CharSequence[] options = new CharSequence[] {
+                mContext.getString(R.string.accessibility_qs_edit_move_tile, info.state.label),
+                mContext.getString(R.string.accessibility_qs_edit_remove_tile, info.state.label),
+        };
+        AlertDialog dialog = new Builder(mContext)
+                .setItems(options, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        if (which == 0) {
+                            startAccessibleDrag(position);
+                        } else {
+                            move(position, mDividerIndex, v);
+                        }
+                    }
+                }).setNegativeButton(android.R.string.cancel, null)
+                .create();
+        SystemUIDialog.setShowForAllUsers(dialog, true);
+        SystemUIDialog.applyFlags(dialog);
+        dialog.show();
+    }
+
+    private void startAccessibleDrag(int position) {
+        mAccessibilityMoving = true;
+        mNeedsFocus = true;
+        mAccessibilityFromIndex = position;
+        // Add placeholder for last slot.
+        mTiles.add(mDividerIndex++, null);
+        notifyDataSetChanged();
     }
 
     public SpanSizeLookup getSizeLookup() {
         return mSizeLookup;
     }
 
+    private boolean move(int from, int to, View v) {
+        if (to > mDividerIndex) {
+            if (from >= mDividerIndex) {
+                return false;
+            }
+        }
+        CharSequence fromLabel = mTiles.get(from).state.label;
+        move(from, to, mTiles);
+        mDividerIndex = mTiles.indexOf(null);
+        notifyItemChanged(from);
+        notifyItemMoved(from, to);
+        CharSequence announcement;
+        if (to >= mDividerIndex) {
+            announcement = mContext.getString(R.string.accessibility_qs_edit_tile_removed,
+                    fromLabel);
+        } else if (from >= mDividerIndex) {
+            announcement = mContext.getString(R.string.accessibility_qs_edit_tile_added,
+                    fromLabel, (to + 1));
+        } else {
+            announcement = mContext.getString(R.string.accessibility_qs_edit_tile_moved,
+                    fromLabel, (to + 1));
+        }
+        v.announceForAccessibility(announcement);
+        return true;
+    }
+
+    private <T> void move(int from, int to, List<T> list) {
+        list.add(from > to ? to : to + 1, list.get(from));
+        list.remove(from > to ? from + 1 : from);
+    }
+
     public class Holder extends ViewHolder {
         private QSTileView mTileView;
 
@@ -286,20 +437,7 @@
         public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
             int from = viewHolder.getAdapterPosition();
             int to = target.getAdapterPosition();
-            if (to > mDividerIndex) {
-                if (from >= mDividerIndex) {
-                    return false;
-                }
-            }
-            move(from, to, mTiles);
-            mDividerIndex = mTiles.indexOf(null);
-            notifyItemMoved(from, to);
-            return true;
-        }
-
-        private <T> void move(int from, int to, List<T> list) {
-            list.add(from > to ? to : to + 1, list.get(from));
-            list.remove(from > to ? from + 1 : from);
+            return move(from, to, target.itemView);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index d95d3ef..bbc8856 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -33,7 +33,6 @@
 import com.android.systemui.qs.QSTile.DrawableIcon;
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.statusbar.phone.QSTileHost;
-import com.android.systemui.tuner.TunerService;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -75,6 +74,8 @@
                 public void run() {
                     final QSTile.State state = tile.newTileState();
                     tile.getState().copyTo(state);
+                    // Ignore the current state and get the generic label instead.
+                    state.label = tile.getTileLabel();
                     mainHandler.post(new Runnable() {
                         @Override
                         public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 9156f3a..6114573 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -221,6 +221,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return getState().label;
+    }
+
+    @Override
     protected void handleUpdateState(State state, Object arg) {
         Drawable drawable = mTile.getIcon().loadDrawable(mContext);
         int tileState = mTile.getState();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 89f1985b..5f5a87e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -76,6 +76,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.airplane_mode);
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         final int value = arg instanceof Integer ? (Integer)arg : mSetting.getValue();
         final boolean airplaneMode = value != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index 2e87525..6ddd46e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -106,6 +106,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.battery);
+    }
+
+    @Override
     protected void handleUpdateState(State state, Object arg) {
         int level = (arg != null) ? (Integer) arg : mLevel;
         String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 80f667c..1fef8f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -96,6 +96,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_bluetooth_label);
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         final boolean enabled = mController.isBluetoothEnabled();
         final boolean connected = mController.isBluetoothConnected();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index e0ad002..bea1e15 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -108,6 +108,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_cast_title);
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         state.label = mContext.getString(R.string.quick_settings_cast_title);
         state.value = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 5f87741..55b00b5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -101,6 +101,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_cellular_detail_title);
+    }
+
+    @Override
     protected void handleUpdateState(SignalState state, Object arg) {
         CallbackInfo cb = (CallbackInfo) arg;
         if (cb == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index a608316..416132e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -85,6 +85,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_inversion_label);
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue();
         final boolean enabled = value != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 74b3fdc..35aff21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -57,6 +57,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.data_saver);
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         state.value = arg instanceof Boolean ? (Boolean) arg
                 : mDataSaverController.isDataSaverEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 8b22868..11efd56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -128,6 +128,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_dnd_label);
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen();
         final boolean newValue = zen != Global.ZEN_MODE_OFF;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index a01a9a5..6887bec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -87,6 +87,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_flashlight_label);
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
         if (!mFlashlightController.isAvailable()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index da93120..bf5b22c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -74,6 +74,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_hotspot_label);
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         state.label = mContext.getString(R.string.quick_settings_hotspot_label);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index bb5ff8e..2a2cc46 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -119,6 +119,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return getState().label;
+    }
+
+    @Override
     protected void handleUpdateState(State state, Object arg) {
         Intent intent = (Intent) arg;
         if (intent == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index b1d1c77..6286f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -89,6 +89,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_location_label);
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         final boolean locationEnabled =  mController.isLocationEnabled();
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index d80ca10..38b3706 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -80,6 +80,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return getState().label;
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         if (mController == null) return;
         final boolean rotationLocked = arg != null ? (Boolean) arg
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index f1066c1..5b4279c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -73,6 +73,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return getState().label;
+    }
+
+    @Override
     protected void handleUpdateState(State state, Object arg) {
         final Pair<String, Drawable> p = arg != null ? (Pair<String, Drawable>) arg : mLastUpdate;
         if (p != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 7ee795f..65154f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -119,6 +119,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_wifi_label);
+    }
+
+    @Override
     protected void handleUpdateState(SignalState state, Object arg) {
         if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg);
         CallbackInfo cb = (CallbackInfo) arg;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 421a2cf..2c5f7d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -82,6 +82,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_work_mode_label);
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         if (arg instanceof Boolean) {
             state.value = (Boolean) arg;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 7486519..12035b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1208,7 +1208,9 @@
     }
 
     private String getKeyguardOrLockScreenString() {
-        if (mStatusBarState == StatusBarState.KEYGUARD) {
+        if (mQsContainer.isCustomizing()) {
+            return getContext().getString(R.string.accessibility_desc_quick_settings_edit);
+        } else if (mStatusBarState == StatusBarState.KEYGUARD) {
             return getContext().getString(R.string.accessibility_desc_lock_screen);
         } else {
             return getContext().getString(R.string.accessibility_desc_notification_shade);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 0442ac3..2783ec5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -37,22 +37,14 @@
         super(context, theme);
         mContext = context;
 
-        getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
-                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+        applyFlags(this);
         WindowManager.LayoutParams attrs = getWindow().getAttributes();
         attrs.setTitle(getClass().getSimpleName());
         getWindow().setAttributes(attrs);
     }
 
     public void setShowForAllUsers(boolean show) {
-        if (show) {
-            getWindow().getAttributes().privateFlags |=
-                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-        } else {
-            getWindow().getAttributes().privateFlags &=
-                    ~WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-        }
+        setShowForAllUsers(this, show);
     }
 
     public void setMessage(int resId) {
@@ -66,4 +58,20 @@
     public void setNegativeButton(int resId, OnClickListener onClick) {
         setButton(BUTTON_NEGATIVE, mContext.getString(resId), onClick);
     }
+
+    public static void setShowForAllUsers(AlertDialog dialog, boolean show) {
+        if (show) {
+            dialog.getWindow().getAttributes().privateFlags |=
+                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        } else {
+            dialog.getWindow().getAttributes().privateFlags &=
+                    ~WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        }
+    }
+
+    public static void applyFlags(AlertDialog dialog) {
+        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
+        dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java b/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
index 26e1d46..fe44502 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
@@ -15,10 +15,7 @@
  */
 package com.android.systemui.tuner;
 
-import android.app.ActivityManager;
 import android.content.Intent;
-import android.provider.Settings;
-
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.systemui.Prefs;
 import com.android.systemui.Prefs.Key;
@@ -26,8 +23,6 @@
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.NightModeController;
 
-import java.util.Objects;
-
 
 public class NightModeTile extends QSTile<QSTile.State> implements NightModeController.Listener {
 
@@ -81,6 +76,11 @@
     }
 
     @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.night_mode);
+    }
+
+    @Override
     protected void handleUpdateState(State state, Object arg) {
         // TODO: Right now this is just a dropper, needs an actual night icon.
         boolean enabled = mNightModeController.isEnabled();