QS: Add long-press to customize prototype - part 2

 - Add info/remove drop targets that appear during dragging
 - Move drag start into NonPagedTileLayout so that both start
   and end of tile move drags are in the same place.
 - Still needs way to add tiles

Change-Id: If843ebbb86f393b461289c1407c6a82b6a5aed9d
diff --git a/packages/SystemUI/res/drawable/ic_close_white.xml b/packages/SystemUI/res/drawable/ic_close_white.xml
new file mode 100644
index 0000000..ce64047
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_close_white.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2015 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="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
+        android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 6beac31..fbd29b0 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -21,13 +21,58 @@
     android:orientation="vertical"
     android:background="?android:attr/windowBackground">
 
-    <Toolbar
-        android:id="@*android:id/action_bar"
+    <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:navigationContentDescription="@*android:string/action_bar_up_description"
-        android:background="?android:attr/colorPrimary"
-        style="?android:attr/toolbarStyle" />
+        android:background="?android:attr/colorPrimary">
+
+        <LinearLayout
+            android:id="@+id/drag_buttons"
+            android:layout_width="match_parent"
+            android:layout_height="fill_parent"
+            android:orientation="horizontal">
+            <FrameLayout
+                android:layout_width="0dp"
+                android:layout_height="fill_parent"
+                android:layout_weight="1">
+                <com.android.systemui.qs.customize.DropButton
+                    android:id="@+id/info_button"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:gravity="center"
+                    android:drawableStart="@drawable/ic_info"
+                    android:drawablePadding="10dp"
+                    android:textAppearance="?android:attr/textAppearanceMedium"
+                    android:textColor="@android:color/white"
+                    android:text="@string/qs_customize_info" />
+            </FrameLayout>
+            <FrameLayout
+                android:layout_width="0dp"
+                android:layout_height="fill_parent"
+                android:layout_weight="1">
+                <com.android.systemui.qs.customize.DropButton
+                    android:id="@+id/remove_button"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:gravity="center"
+                    android:drawableStart="@drawable/ic_close_white"
+                    android:drawablePadding="10dp"
+                    android:textAppearance="?android:attr/textAppearanceMedium"
+                    android:textColor="@android:color/white"
+                    android:text="@string/qs_customize_remove" />
+            </FrameLayout>
+        </LinearLayout>
+
+        <Toolbar
+            android:id="@*android:id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:navigationContentDescription="@*android:string/action_bar_up_description"
+            style="?android:attr/toolbarStyle"
+            android:background="?android:attr/colorPrimary" />
+    </FrameLayout>
 
     <com.android.systemui.tuner.AutoScrollView
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index da7eadc..db1e688 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1158,5 +1158,7 @@
 
     <string name="save" translatable="false">Save</string>
     <string name="qs_customize" translatable="false">Allow long-press customize in Quick Settings</string>
+    <string name="qs_customize_info" translatable="false">Info</string>
+    <string name="qs_customize_remove" translatable="false">Remove</string>
 
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
index fe75220..8866e55 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
@@ -19,26 +19,21 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnTouchListener;
 
 import com.android.systemui.R;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileView;
 import com.android.systemui.statusbar.phone.QSTileHost;
 
 /**
  * A version of QSPanel that allows tiles to be dragged around rather than
- * clicked on.  Dragging is started here, receiving is handled in the NonPagedTileLayout,
+ * clicked on.  Dragging starting and receiving is handled in the NonPagedTileLayout,
  * and the saving/ordering is handled by the CustomQSTileHost.
  */
-public class CustomQSPanel extends QSPanel implements OnTouchListener {
+public class CustomQSPanel extends QSPanel {
 
     private CustomQSTileHost mCustomHost;
-    private ClipData mCurrentClip;
-    private View mCurrentView;
 
     public CustomQSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -62,52 +57,26 @@
         }
     }
 
-    @Override
-    protected void addTile(QSTile<?> tile) {
-        super.addTile(tile);
-        if (tile.getTileType() != QSTileView.QS_TYPE_QUICK) {
-            TileRecord record = mRecords.get(mRecords.size() - 1);
-            if (record.tileView.getTag() == record.tile) {
-                return;
-            }
-            record.tileView.setTag(record.tile);
-            record.tileView.setVisibility(View.VISIBLE);
-            record.tileView.init(null, null, null);
-            record.tileView.setOnTouchListener(this);
-            if (mCurrentClip != null
-                    && mCurrentClip.getItemAt(0).getText().toString().equals(tile.getTileSpec())) {
-                record.tileView.setAlpha(.3f);
-                mCurrentView = record.tileView;
-            }
-        }
+    public CustomQSTileHost getCustomHost() {
+        return mCustomHost;
     }
 
-    public void tileSelected(View v) {
-        String sourceSpec = mCurrentClip.getItemAt(0).getText().toString();
-        String destSpec = ((QSTile<?>) v.getTag()).getTileSpec();
+    public void tileSelected(QSTile<?> tile, ClipData currentClip) {
+        String sourceSpec = getSpec(currentClip);
+        String destSpec = tile.getTileSpec();
         if (!sourceSpec.equals(destSpec)) {
             mCustomHost.moveTo(sourceSpec, destSpec);
         }
     }
 
-    @Override
-    public boolean onTouch(View v, MotionEvent event) {
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-                String tileSpec = (String) ((QSTile<?>) v.getTag()).getTileSpec();
-                mCurrentView = v;
-                mCurrentClip = ClipData.newPlainText(tileSpec, tileSpec);
-                View.DragShadowBuilder shadow = new View.DragShadowBuilder(v);
-                ((View) getParent().getParent()).startDrag(mCurrentClip, shadow, null, 0);
-                v.setAlpha(.3f);
-                return true;
-        }
-        return false;
+    public ClipData getClip(QSTile<?> tile) {
+        String tileSpec = tile.getTileSpec();
+        // TODO: Something better than plain text.
+        // TODO: Once using something better than plain text, stop listening to non-QS drag events.
+        return ClipData.newPlainText(tileSpec, tileSpec);
     }
 
-    public void onDragEnded() {
-        mCurrentView.setAlpha(1f);
-        mCurrentView = null;
-        mCurrentClip = null;
+    public String getSpec(ClipData data) {
+        return data.getItemAt(0).getText().toString();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
index 0951129..84b05d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
@@ -37,6 +37,7 @@
     private static final String TAG = "CustomHost";
     private List<String> mTiles;
     private List<String> mSavedTiles;
+    private ArrayList<String> mStash;
 
     public CustomQSTileHost(Context context, QSTileHost host) {
         super(context, null, host.getBluetoothController(), host.getLocationController(),
@@ -70,6 +71,14 @@
                 TextUtils.join(",", mTiles), ActivityManager.getCurrentUser());
     }
 
+    public void stashCurrentTiles() {
+        mStash = new ArrayList<>(mTiles);
+    }
+
+    public void unstashTiles() {
+        setTiles(mStash);
+    }
+
     public void moveTo(String from, String to) {
         int fromIndex = mTiles.indexOf(from);
         if (fromIndex < 0) {
@@ -86,6 +95,13 @@
         super.onTuningChanged(TILES_SETTING, null);
     }
 
+    public void remove(String spec) {
+        if (!mTiles.remove(spec)) {
+            Log.e(TAG, "Unknown remove spec " + spec);
+        }
+        super.onTuningChanged(TILES_SETTING, null);
+    }
+
     public void setTiles(List<String> tiles) {
         mTiles = new ArrayList<>(tiles);
         super.onTuningChanged(TILES_SETTING, null);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java b/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java
new file mode 100644
index 0000000..0e15f2b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 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.systemui.qs.customize;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.DragEvent;
+import android.view.View;
+import android.view.View.OnDragListener;
+import android.widget.TextView;
+
+public class DropButton extends TextView implements OnDragListener {
+
+    private OnDropListener mListener;
+
+    public DropButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        // TODO: Don't do this, instead make this view the right size...
+        ((View) getParent()).setOnDragListener(this);
+    }
+
+    public void setOnDropListener(OnDropListener listener) {
+        mListener = listener;
+    }
+
+    private void setHovering(boolean hovering) {
+        setAlpha(hovering ? .5f : 1);
+    }
+
+    @Override
+    public boolean onDrag(View v, DragEvent event) {
+        switch (event.getAction()) {
+            case DragEvent.ACTION_DRAG_ENTERED:
+                setHovering(true);
+                break;
+            case DragEvent.ACTION_DROP:
+                if (mListener != null) {
+                    mListener.onDrop(this, event.getClipData());
+                }
+            case DragEvent.ACTION_DRAG_EXITED:
+            case DragEvent.ACTION_DRAG_ENDED:
+                setHovering(false);
+                break;
+        }
+        return true;
+    }
+
+    public interface OnDropListener {
+        void onDrop(View v, ClipData data);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
index 47c9384..fdcff1d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
@@ -15,12 +15,15 @@
  */
 package com.android.systemui.qs.customize;
 
+import android.content.ClipData;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.DragEvent;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.OnTouchListener;
 import android.widget.LinearLayout;
 
 import com.android.systemui.R;
@@ -28,6 +31,7 @@
 import com.android.systemui.qs.PagedTileLayout.TilePage;
 import com.android.systemui.qs.QSPanel.QSTileLayout;
 import com.android.systemui.qs.QSPanel.TileRecord;
+import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.QSTileView;
 import com.android.systemui.qs.QuickTileLayout;
 
@@ -38,7 +42,7 @@
  * vertically and expects to be inside a ScrollView.
  * @see CustomQSPanel
  */
-public class NonPagedTileLayout extends LinearLayout implements QSTileLayout {
+public class NonPagedTileLayout extends LinearLayout implements QSTileLayout, OnTouchListener {
 
     private QuickTileLayout mQuickTiles;
     private final ArrayList<TilePage> mPages = new ArrayList<>();
@@ -46,6 +50,9 @@
     private CustomQSPanel mPanel;
     private final Rect mHitRect = new Rect();
 
+    private ClipData mCurrentClip;
+    private View mCurrentView;
+
     public NonPagedTileLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -63,17 +70,23 @@
         mPanel = qsPanel;
     }
 
-    private void clear() {
-        mQuickTiles.removeAllViews();
-        for (int i = 0; i < mPages.size(); i++) {
-            mPages.get(i).removeAllViews();
-        }
-    }
-
     @Override
-    public void addTile(TileRecord tile) {
-        mTiles.add(tile);
+    public void addTile(TileRecord record) {
+        mTiles.add(record);
         distributeTiles();
+        if (record.tile.getTileType() == QSTileView.QS_TYPE_QUICK
+                || record.tileView.getTag() == record.tile) {
+            return;
+        }
+        record.tileView.setTag(record.tile);
+        record.tileView.setVisibility(View.VISIBLE);
+        record.tileView.init(null, null, null);
+        record.tileView.setOnTouchListener(this);
+        if (mCurrentClip != null
+                && mCurrentClip.getItemAt(0).getText().toString().equals(record.tile.getTileSpec())) {
+            record.tileView.setAlpha(.3f);
+            mCurrentView = record.tileView;
+        }
     }
 
     @Override
@@ -118,8 +131,8 @@
 
     @Override
     public int getOffsetTop(TileRecord tile) {
-        // TODO: Fix this.
-        return getTop();
+        // No touch feedback, so this isn't required.
+        return 0;
     }
 
     @Override
@@ -145,7 +158,7 @@
                             for (int j = 0; j < NC; j++) {
                                 View child = page.getChildAt(j);
                                 if (contains(child, x, y)) {
-                                    mPanel.tileSelected(child);
+                                    mPanel.tileSelected((QSTile<?>) child.getTag(), mCurrentClip);
                                 }
                             }
                             break;
@@ -154,12 +167,35 @@
                 }
                 break;
             case DragEvent.ACTION_DRAG_ENDED:
-                mPanel.onDragEnded();
+                onDragEnded();
                 break;
         }
         return true;
     }
 
+    @Override
+    public boolean onTouch(View v, MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                // Stash the current tiles, in case the drop is on info, that we we can restore
+                // the previous state.
+                mPanel.getCustomHost().stashCurrentTiles();
+                mCurrentView = v;
+                mCurrentClip = mPanel.getClip((QSTile<?>) v.getTag());
+                View.DragShadowBuilder shadow = new View.DragShadowBuilder(v);
+                ((View) getParent().getParent()).startDrag(mCurrentClip, shadow, null, 0);
+                v.setAlpha(.3f);
+                return true;
+        }
+        return false;
+    }
+
+    public void onDragEnded() {
+        mCurrentView.setAlpha(1f);
+        mCurrentView = null;
+        mCurrentClip = null;
+    }
+
     private boolean contains(View v, float x, float y) {
         v.getHitRect(mHitRect);
         return mHitRect.contains((int) x, (int) y);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 7724a87..13a8dba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -15,13 +15,17 @@
  */
 package com.android.systemui.qs.customize;
 
+import android.app.AlertDialog;
+import android.content.ClipData;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
+import android.view.DragEvent;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import android.widget.Toolbar;
 import android.widget.Toolbar.OnMenuItemClickListener;
@@ -29,8 +33,10 @@
 import com.android.systemui.R;
 import com.android.systemui.SystemUIApplication;
 import com.android.systemui.qs.QSTile.Host.Callback;
+import com.android.systemui.qs.customize.DropButton.OnDropListener;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.tuner.QSPagingSwitch;
 
 import java.util.ArrayList;
@@ -41,7 +47,8 @@
  * This adds itself to the status bar window, so it can appear on top of quick settings and
  * *someday* do fancy animations to get into/out of it.
  */
-public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener, Callback {
+public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener, Callback,
+        OnDropListener {
 
     private static final int MENU_SAVE = Menu.FIRST;
     private static final int MENU_RESET = Menu.FIRST + 1;
@@ -49,10 +56,13 @@
     private PhoneStatusBar mPhoneStatusBar;
 
     private Toolbar mToolbar;
+    private ViewGroup mDragButtons;
     private CustomQSPanel mQsPanel;
 
     private boolean isShown;
     private CustomQSTileHost mHost;
+    private DropButton mInfoButton;
+    private DropButton mRemoveButton;
 
     public QSCustomizer(Context context, AttributeSet attrs) {
         super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
@@ -90,6 +100,14 @@
                 mContext.getString(com.android.internal.R.string.reset));
 
         mQsPanel = (CustomQSPanel) findViewById(R.id.quick_settings_panel);
+
+        mDragButtons = (ViewGroup) findViewById(R.id.drag_buttons);
+        setDragging(false);
+
+        mInfoButton = (DropButton) findViewById(R.id.info_button);
+        mInfoButton.setOnDropListener(this);
+        mRemoveButton = (DropButton) findViewById(R.id.remove_button);
+        mRemoveButton.setOnDropListener(this);
     }
 
     public void show() {
@@ -117,6 +135,10 @@
         mHost.setTiles(tiles);
     }
 
+    private void setDragging(boolean dragging) {
+        mToolbar.setVisibility(!dragging ? View.VISIBLE : View.INVISIBLE);
+    }
+
     private void save() {
         mHost.saveCurrentTiles();
         hide();
@@ -139,4 +161,28 @@
     public void onTilesChanged() {
         mQsPanel.setTiles(mHost.getTiles());
     }
+
+    public boolean onDragEvent(DragEvent event) {
+        switch (event.getAction()) {
+            case DragEvent.ACTION_DRAG_STARTED:
+                setDragging(true);
+                break;
+            case DragEvent.ACTION_DRAG_ENDED:
+                setDragging(false);
+                break;
+        }
+        return true;
+    }
+
+    public void onDrop(View v, ClipData data) {
+        if (v == mRemoveButton) {
+            mHost.remove(mQsPanel.getSpec(data));
+        } else if (v == mInfoButton) {
+            mHost.unstashTiles();
+            SystemUIDialog dialog = new SystemUIDialog(mContext);
+            dialog.setTitle(mQsPanel.getSpec(data));
+            dialog.setPositiveButton(R.string.ok, null);
+            dialog.show();
+        }
+    }
 }
\ No newline at end of file