QS: Add long-press to customize prototype - part 1
Start adding prototype to long-press to go to customization view
for QS. Currently it allows re-arranging and resetting. Later
it will have more.
Change-Id: Ib2ba0f93ac2f4cced4f146d39771a8a17ac05bc2
diff --git a/packages/SystemUI/res/layout/horizontal_divider.xml b/packages/SystemUI/res/layout/horizontal_divider.xml
new file mode 100644
index 0000000..a060f08
--- /dev/null
+++ b/packages/SystemUI/res/layout/horizontal_divider.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<View
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp"
+ android:layout_marginStart="40dp"
+ android:layout_marginEnd="40dp"
+ android:background="#4dffffff" />
diff --git a/packages/SystemUI/res/layout/qs_customize_layout.xml b/packages/SystemUI/res/layout/qs_customize_layout.xml
new file mode 100644
index 0000000..91cf894
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_customize_layout.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<com.android.systemui.qs.customize.NonPagedTileLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tiles_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <com.android.systemui.qs.QuickTileLayout
+ android:id="@+id/quick_tile_layout"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_quick_actions_height"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/qs_quick_actions_padding"
+ android:paddingEnd="@dimen/qs_quick_actions_padding" />
+
+ <view
+ class="com.android.systemui.qs.PagedTileLayout$TilePage"
+ android:id="@+id/tile_page"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</com.android.systemui.qs.customize.NonPagedTileLayout>
+
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
new file mode 100644
index 0000000..6beac31
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<com.android.systemui.qs.customize.QSCustomizer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="?android:attr/windowBackground">
+
+ <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"
+ android:background="?android:attr/colorPrimary"
+ style="?android:attr/toolbarStyle" />
+
+ <com.android.systemui.tuner.AutoScrollView
+ android:layout_width="match_parent"
+ android:layout_height="fill_parent"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:elevation="2dp">
+
+ <com.android.systemui.qs.customize.CustomQSPanel
+ android:id="@+id/quick_settings_panel"
+ android:background="#0000"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </com.android.systemui.tuner.AutoScrollView>
+
+</com.android.systemui.qs.customize.QSCustomizer>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b732e99..da7eadc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1156,4 +1156,7 @@
settings are -->
<string name="experimental">Experimental</string>
+ <string name="save" translatable="false">Save</string>
+ <string name="qs_customize" translatable="false">Allow long-press customize in Quick Settings</string>
+
</resources>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 5980108..07e7688d 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -37,6 +37,10 @@
android:key="qs_paged_panel"
android:title="@string/qs_paging" />
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="qs_allow_customize"
+ android:title="@string/qs_customize" />
+
</PreferenceCategory>
</PreferenceScreen>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index ece7022..c612600 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -75,8 +75,8 @@
@Override
public void setTileVisibility(TileRecord tile, int visibility) {
tile.tileView.setVisibility(visibility);
- // TODO: Do something smarter here.
- distributeTiles();
+// // TODO: Do something smarter here.
+// distributeTiles();
}
@Override
@@ -183,13 +183,17 @@
mAllowDual = false;
}
+ public void setMaxRows(int maxRows) {
+ mMaxRows = maxRows;
+ }
+
private void clear() {
if (DEBUG) Log.d(TAG, "Clearing page");
removeAllViews();
mRecords.clear();
}
- private boolean isFull() {
+ public boolean isFull() {
return mRecords.size() >= mColumns * mMaxRows;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 683af97..880349e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -39,6 +39,7 @@
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile.DetailAdapter;
+import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSlider;
import com.android.systemui.statusbar.phone.QSTileHost;
@@ -54,8 +55,9 @@
public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
public static final String QS_PAGED_PANEL = "qs_paged_panel";
+ public static final String QS_ALLOW_CUSTOMIZE = "qs_allow_customize";
- private final Context mContext;
+ protected final Context mContext;
protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
private final View mDetail;
private final ViewGroup mDetailContent;
@@ -79,8 +81,10 @@
private QSFooter mFooter;
private boolean mGridContentVisible = true;
- private LinearLayout mQsContainer;
- private QSTileLayout mTileLayout;
+ protected LinearLayout mQsContainer;
+ protected QSTileLayout mTileLayout;
+
+ private QSCustomizer mCustomizePanel;
public QSPanel(Context context) {
this(context, null);
@@ -131,7 +135,8 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- TunerService.get(mContext).addTunable(this, QS_SHOW_BRIGHTNESS, QS_PAGED_PANEL);
+ TunerService.get(mContext).addTunable(this,
+ QS_SHOW_BRIGHTNESS, QS_PAGED_PANEL, QS_ALLOW_CUSTOMIZE);
}
@Override
@@ -160,6 +165,17 @@
for (int i = 0; i < mRecords.size(); i++) {
mTileLayout.addTile(mRecords.get(i));
}
+ } else if (QS_ALLOW_CUSTOMIZE.equals(key)) {
+ if (newValue != null && Integer.parseInt(newValue) != 0) {
+ mCustomizePanel = (QSCustomizer) LayoutInflater.from(mContext)
+ .inflate(R.layout.qs_customize_panel, null);
+ mCustomizePanel.setHost(mHost);
+ } else {
+ if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
+ mCustomizePanel.hide();
+ }
+ mCustomizePanel = null;
+ }
}
}
@@ -224,6 +240,12 @@
mFooter.onConfigurationChanged();
}
+ public void onCollapse() {
+ if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
+ mCustomizePanel.hide();
+ }
+ }
+
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
@@ -307,7 +329,7 @@
r.tileView.onStateChanged(state);
}
- private void addTile(final QSTile<?> tile) {
+ protected void addTile(final QSTile<?> tile) {
final TileRecord r = new TileRecord();
r.tile = tile;
r.tileView = tile.createTileView(mContext);
@@ -358,7 +380,13 @@
final View.OnLongClickListener longClick = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
- r.tile.longClick();
+ if (mCustomizePanel != null) {
+ if (!mCustomizePanel.isCustomizing()) {
+ mCustomizePanel.show();
+ }
+ } else {
+ r.tile.longClick();
+ }
return true;
}
};
@@ -374,10 +402,16 @@
}
public boolean isShowingDetail() {
- return mDetailRecord != null;
+ return mDetailRecord != null
+ || (mCustomizePanel != null && mCustomizePanel.isCustomizing());
}
public void closeDetail() {
+ if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
+ // Treat this as a detail panel for now, to make things easy.
+ mCustomizePanel.hide();
+ return;
+ }
showDetail(false, mDetailRecord);
}
@@ -527,7 +561,7 @@
int y;
}
- protected static final class TileRecord extends Record {
+ public static final class TileRecord extends Record {
public QSTile<?> tile;
public QSTileView tileView;
public int row;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 9b3372c3..3b3593b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -65,6 +65,8 @@
private TState mTmpState = newTileState();
private boolean mAnnounceNextStateChange;
+ private String mTileSpec;
+
abstract protected TState newTileState();
abstract protected void handleClick();
abstract protected void handleUpdateState(TState state, Object arg);
@@ -83,7 +85,15 @@
mContext = host.getContext();
mHandler = new H(host.getLooper());
}
-
+
+ public String getTileSpec() {
+ return mTileSpec;
+ }
+
+ public void setTileSpec(String tileSpec) {
+ mTileSpec = tileSpec;
+ }
+
public int getTileType() {
return QSTileView.QS_TYPE_NORMAL;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index e0c39c5..8bd05fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -54,6 +54,11 @@
removeView(tile.tileView);
}
+ public void removeAllViews() {
+ mRecords.clear();
+ super.removeAllViews();
+ }
+
@Override
public void setTileVisibility(TileRecord tile, int visibility) {
tile.tileView.setVisibility(visibility);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
new file mode 100644
index 0000000..fe75220
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
@@ -0,0 +1,113 @@
+/*
+ * 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.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,
+ * and the saving/ordering is handled by the CustomQSTileHost.
+ */
+public class CustomQSPanel extends QSPanel implements OnTouchListener {
+
+ private CustomQSTileHost mCustomHost;
+ private ClipData mCurrentClip;
+ private View mCurrentView;
+
+ public CustomQSPanel(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mTileLayout = (QSTileLayout) LayoutInflater.from(mContext)
+ .inflate(R.layout.qs_customize_layout, mQsContainer, false);
+ mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
+ ((NonPagedTileLayout) mTileLayout).setCustomQsPanel(this);
+ }
+
+ @Override
+ public void setHost(QSTileHost host) {
+ super.setHost(host);
+ mCustomHost = (CustomQSTileHost) host;
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (key.equals(QS_SHOW_BRIGHTNESS)) {
+ // No Brightness for you.
+ super.onTuningChanged(key, "0");
+ }
+ }
+
+ @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 void tileSelected(View v) {
+ String sourceSpec = mCurrentClip.getItemAt(0).getText().toString();
+ String destSpec = ((QSTile<?>) v.getTag()).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 void onDragEnded() {
+ mCurrentView.setAlpha(1f);
+ mCurrentView = null;
+ mCurrentClip = null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
new file mode 100644
index 0000000..0951129
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
@@ -0,0 +1,167 @@
+/*
+ * 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.app.ActivityManager;
+import android.content.Context;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.policy.SecurityController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @see CustomQSPanel
+ */
+public class CustomQSTileHost extends QSTileHost {
+
+ private static final String TAG = "CustomHost";
+ private List<String> mTiles;
+ private List<String> mSavedTiles;
+
+ public CustomQSTileHost(Context context, QSTileHost host) {
+ super(context, null, host.getBluetoothController(), host.getLocationController(),
+ host.getRotationLockController(), host.getNetworkController(),
+ host.getZenModeController(), host.getHotspotController(), host.getCastController(),
+ host.getFlashlightController(), host.getUserSwitcherController(),
+ host.getKeyguardMonitor(), new BlankSecurityController());
+ }
+
+ @Override
+ protected QSTile<?> createTile(String tileSpec) {
+ QSTile<?> tile = super.createTile(tileSpec);
+ tile.setTileSpec(tileSpec);
+ return tile;
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ // No Tunings For You.
+ if (TILES_SETTING.equals(key)) {
+ mSavedTiles = super.loadTileSpecs(newValue);
+ }
+ }
+
+ public void setSavedTiles() {
+ setTiles(mSavedTiles);
+ }
+
+ public void saveCurrentTiles() {
+ Secure.putStringForUser(getContext().getContentResolver(), TILES_SETTING,
+ TextUtils.join(",", mTiles), ActivityManager.getCurrentUser());
+ }
+
+ public void moveTo(String from, String to) {
+ int fromIndex = mTiles.indexOf(from);
+ if (fromIndex < 0) {
+ Log.e(TAG, "Unknown from tile " + from);
+ return;
+ }
+ int index = mTiles.indexOf(to);
+ if (index < 0) {
+ Log.e(TAG, "Unknown to tile " + to);
+ return;
+ }
+ mTiles.remove(fromIndex);
+ mTiles.add(index, from);
+ super.onTuningChanged(TILES_SETTING, null);
+ }
+
+ public void setTiles(List<String> tiles) {
+ mTiles = new ArrayList<>(tiles);
+ super.onTuningChanged(TILES_SETTING, null);
+ }
+
+ @Override
+ protected List<String> loadTileSpecs(String tileList) {
+ return mTiles;
+ }
+
+ public void replace(String oldTile, String newTile) {
+ if (oldTile.equals(newTile)) {
+ return;
+ }
+ MetricsLogger.action(getContext(), MetricsLogger.TUNER_QS_REORDER, oldTile + ","
+ + newTile);
+ List<String> order = new ArrayList<>(mTileSpecs);
+ int index = order.indexOf(oldTile);
+ if (index < 0) {
+ Log.e(TAG, "Can't find " + oldTile);
+ return;
+ }
+ order.remove(newTile);
+ order.add(index, newTile);
+ setTiles(order);
+ }
+
+ /**
+ * Blank so that the customizing QS view doesn't show any security messages in the footer.
+ */
+ private static class BlankSecurityController implements SecurityController {
+ @Override
+ public boolean hasDeviceOwner() {
+ return false;
+ }
+
+ @Override
+ public boolean hasProfileOwner() {
+ return false;
+ }
+
+ @Override
+ public String getDeviceOwnerName() {
+ return null;
+ }
+
+ @Override
+ public String getProfileOwnerName() {
+ return null;
+ }
+
+ @Override
+ public boolean isVpnEnabled() {
+ return false;
+ }
+
+ @Override
+ public String getPrimaryVpnName() {
+ return null;
+ }
+
+ @Override
+ public String getProfileVpnName() {
+ return null;
+ }
+
+ @Override
+ public void onUserSwitched(int newUserId) {
+ }
+
+ @Override
+ public void addCallback(SecurityControllerCallback callback) {
+ }
+
+ @Override
+ public void removeCallback(SecurityControllerCallback callback) {
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
new file mode 100644
index 0000000..47c9384
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
@@ -0,0 +1,167 @@
+/*
+ * 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.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.DragEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.PagedTileLayout;
+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.QSTileView;
+import com.android.systemui.qs.QuickTileLayout;
+
+import java.util.ArrayList;
+
+/**
+ * Similar to PagedTileLayout, except that instead of pages it lays them out
+ * vertically and expects to be inside a ScrollView.
+ * @see CustomQSPanel
+ */
+public class NonPagedTileLayout extends LinearLayout implements QSTileLayout {
+
+ private QuickTileLayout mQuickTiles;
+ private final ArrayList<TilePage> mPages = new ArrayList<>();
+ private final ArrayList<TileRecord> mTiles = new ArrayList<TileRecord>();
+ private CustomQSPanel mPanel;
+ private final Rect mHitRect = new Rect();
+
+ public NonPagedTileLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mQuickTiles = (QuickTileLayout) findViewById(R.id.quick_tile_layout);
+ TilePage page = (PagedTileLayout.TilePage) findViewById(R.id.tile_page);
+ page.setMaxRows(3 /* First page only gets 3 */);
+ mPages.add(page);
+ }
+
+ public void setCustomQsPanel(CustomQSPanel qsPanel) {
+ 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);
+ distributeTiles();
+ }
+
+ @Override
+ public void removeTile(TileRecord tile) {
+ if (mTiles.remove(tile)) {
+ distributeTiles();
+ }
+ }
+
+ private void distributeTiles() {
+ mQuickTiles.removeAllViews();
+ final int NP = mPages.size();
+ for (int i = 0; i < NP; i++) {
+ mPages.get(i).removeAllViews();
+ }
+ int index = 0;
+ final int NT = mTiles.size();
+ for (int i = 0; i < NT; i++) {
+ TileRecord tile = mTiles.get(i);
+ if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
+ tile.tileView.setType(QSTileView.QS_TYPE_QUICK);
+ mQuickTiles.addView(tile.tileView);
+ continue;
+ }
+ mPages.get(index).addTile(tile);
+ if (mPages.get(index).isFull()) {
+ if (++index == mPages.size()) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ inflater.inflate(R.layout.horizontal_divider, this);
+ mPages.add((TilePage) inflater.inflate(R.layout.qs_paged_page, this, false));
+ addView(mPages.get(mPages.size() - 1));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setTileVisibility(TileRecord tile, int visibility) {
+ // All tiles visible here, so that they can be re-arranged.
+ tile.tileView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public int getOffsetTop(TileRecord tile) {
+ // TODO: Fix this.
+ return getTop();
+ }
+
+ @Override
+ public void updateResources() {
+ }
+
+ @Override
+ public boolean onDragEvent(DragEvent event) {
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_LOCATION:
+ float x = event.getX();
+ float y = event.getY();
+ if (contains(mQuickTiles, x, y)) {
+ // TODO: Reset to pre-drag state.
+ } else {
+ final int NP = mPages.size();
+ for (int i = 0; i < NP; i++) {
+ TilePage page = mPages.get(i);
+ if (contains(page, x, y)) {
+ x -= page.getLeft();
+ y -= page.getTop();
+ final int NC = page.getChildCount();
+ for (int j = 0; j < NC; j++) {
+ View child = page.getChildAt(j);
+ if (contains(child, x, y)) {
+ mPanel.tileSelected(child);
+ }
+ }
+ break;
+ }
+ }
+ }
+ break;
+ case DragEvent.ACTION_DRAG_ENDED:
+ mPanel.onDragEnded();
+ break;
+ }
+ return true;
+ }
+
+ 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
new file mode 100644
index 0000000..7724a87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -0,0 +1,142 @@
+/*
+ * 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.Context;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.Toolbar;
+import android.widget.Toolbar.OnMenuItemClickListener;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUIApplication;
+import com.android.systemui.qs.QSTile.Host.Callback;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.tuner.QSPagingSwitch;
+
+import java.util.ArrayList;
+
+/**
+ * Allows full-screen customization of QS, through show() and hide().
+ *
+ * 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 {
+
+ private static final int MENU_SAVE = Menu.FIRST;
+ private static final int MENU_RESET = Menu.FIRST + 1;
+
+ private PhoneStatusBar mPhoneStatusBar;
+
+ private Toolbar mToolbar;
+ private CustomQSPanel mQsPanel;
+
+ private boolean isShown;
+ private CustomQSTileHost mHost;
+
+ public QSCustomizer(Context context, AttributeSet attrs) {
+ super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
+ mPhoneStatusBar = ((SystemUIApplication) mContext.getApplicationContext())
+ .getComponent(PhoneStatusBar.class);
+ }
+
+ public void setHost(QSTileHost host) {
+ mHost = new CustomQSTileHost(mContext, host);
+ mHost.setCallback(this);
+ mQsPanel.setTiles(mHost.getTiles());
+ mQsPanel.setHost(mHost);
+ mHost.setSavedTiles();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mToolbar = (Toolbar) findViewById(com.android.internal.R.id.action_bar);
+ TypedValue value = new TypedValue();
+ mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
+ mToolbar.setNavigationIcon(
+ getResources().getDrawable(value.resourceId, mContext.getTheme()));
+ mToolbar.setNavigationOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // TODO: Is this all we want...?
+ hide();
+ }
+ });
+ mToolbar.setOnMenuItemClickListener(this);
+ mToolbar.getMenu().add(Menu.NONE, MENU_SAVE, 0, mContext.getString(R.string.save))
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
+ mContext.getString(com.android.internal.R.string.reset));
+
+ mQsPanel = (CustomQSPanel) findViewById(R.id.quick_settings_panel);
+ }
+
+ public void show() {
+ isShown = true;
+ mHost.setSavedTiles();
+ // TODO: Fancy shmancy reveal.
+ mPhoneStatusBar.getStatusBarWindow().addView(this);
+ }
+
+ public void hide() {
+ isShown = false;
+ // TODO: Similarly awesome or better hide.
+ mPhoneStatusBar.getStatusBarWindow().removeView(this);
+ }
+
+ public boolean isCustomizing() {
+ return isShown;
+ }
+
+ private void reset() {
+ ArrayList<String> tiles = new ArrayList<>();
+ for (String tile : QSPagingSwitch.QS_PAGE_TILES.split(",")) {
+ tiles.add(tile);
+ }
+ mHost.setTiles(tiles);
+ }
+
+ private void save() {
+ mHost.saveCurrentTiles();
+ hide();
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ switch (item.getItemId()) {
+ case MENU_SAVE:
+ save();
+ break;
+ case MENU_RESET:
+ reset();
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ public void onTilesChanged() {
+ mQsPanel.setTiles(mHost.getTiles());
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
index 343a231..4387b33 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
@@ -8,7 +8,7 @@
public class QSPagingSwitch extends TunerSwitch {
- private static final String QS_PAGE_TILES =
+ public static final String QS_PAGE_TILES =
"dwifi,dbt,inversion,dnd,cell,airplane,rotation,flashlight,location,"
+ "hotspot,qwifi,qbt,qrotation,qflashlight,qairplane,cast";