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