diff options
| author | 2015-09-03 15:46:25 -0400 | |
|---|---|---|
| committer | 2015-09-08 15:27:20 -0400 | |
| commit | bd6dbb0698eca76c4ee1337ef1a73b67c8a64ae4 (patch) | |
| tree | 539d99ad2bdaedb472aa5189df4b40e87d105ab3 | |
| parent | c6c28731e5b49d5711d5cf22dd6db0e855a7932f (diff) | |
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
14 files changed, 773 insertions, 13 deletions
diff --git a/packages/SystemUI/res/layout/horizontal_divider.xml b/packages/SystemUI/res/layout/horizontal_divider.xml new file mode 100644 index 000000000000..a060f08039e1 --- /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 000000000000..91cf89483c96 --- /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 000000000000..6beac3175636 --- /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 b732e9914f2e..da7eadcedc3f 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 59801089d1bd..07e7688d15b7 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 ece70227cf45..c612600ac82e 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 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @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 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { 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 683af97bfb82..880349e232fe 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.internal.logging.MetricsLogger; 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 class QSPanel extends FrameLayout implements Tunable { 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 @@ public class QSPanel extends FrameLayout implements Tunable { 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 @@ public class QSPanel extends FrameLayout implements Tunable { @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 @@ public class QSPanel extends FrameLayout implements Tunable { 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 @@ public class QSPanel extends FrameLayout implements Tunable { 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 @@ public class QSPanel extends FrameLayout implements Tunable { 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 @@ public class QSPanel extends FrameLayout implements Tunable { 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 class QSPanel extends FrameLayout implements Tunable { } 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 @@ public class QSPanel extends FrameLayout implements Tunable { 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 9b3372c386ac..3b3593b4c477 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 @@ public abstract class QSTile<TState extends State> implements Listenable { 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 @@ public abstract class QSTile<TState extends State> implements Listenable { 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 e0c39c5b975d..8bd05fad5699 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 @@ public class TileLayout extends ViewGroup implements QSTileLayout { 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 000000000000..fe752207ff61 --- /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 000000000000..095112912ce5 --- /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 000000000000..47c938429f96 --- /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 000000000000..7724a8759cbc --- /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 343a23149008..4387b33d0797 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 @@ import com.android.systemui.statusbar.phone.QSTileHost; 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"; |