Merge "Rewrite QS customizer"
This commit is contained in:
@@ -1,43 +0,0 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingStart="7dp"
|
||||
android:paddingEnd="7dp">
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="80dp"
|
||||
android:orientation="vertical">
|
||||
<ImageView
|
||||
android:id="@+id/tile_icon"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="@dimen/qs_tile_icon_size"
|
||||
android:layout_height="@dimen/qs_tile_icon_size" />
|
||||
<TextView
|
||||
android:id="@+id/tile_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center|bottom"
|
||||
android:paddingTop="10dp"
|
||||
android:gravity="center"
|
||||
android:textSize="@dimen/qs_tile_text_size" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 The Android Open Source Project
|
||||
Copyright (C) 2016 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.
|
||||
@@ -14,18 +14,16 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.systemui.qs.customize.NonPagedTileLayout
|
||||
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tiles_container"
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<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>
|
||||
|
||||
android:paddingTop="20dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="13dp"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Body2"
|
||||
android:textColor="?android:attr/colorAccent"
|
||||
android:text="@string/drag_to_add_tiles" />
|
||||
@@ -22,85 +22,53 @@
|
||||
android:background="@drawable/qs_customizer_background"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<FrameLayout
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/notification_header_bg">
|
||||
android:paddingTop="28dp"
|
||||
android:paddingEnd="8dp">
|
||||
|
||||
<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>
|
||||
<ImageView
|
||||
android:id="@+id/close"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_close_white" />
|
||||
|
||||
<Toolbar
|
||||
android:id="@*android:id/action_bar"
|
||||
android:layout_width="match_parent"
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/save"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:navigationContentDescription="@*android:string/action_bar_up_description"
|
||||
android:background="@drawable/notification_header_bg"
|
||||
style="?android:attr/toolbarStyle" />
|
||||
</FrameLayout>
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Widget.Button.Inverse"
|
||||
android:textColor="?android:attr/colorAccent"
|
||||
android:text="@string/save" />
|
||||
|
||||
<com.android.systemui.tuner.AutoScrollView
|
||||
<Button
|
||||
android:id="@+id/reset"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:textAppearance="@android:style/TextAppearance.Material.Widget.Button.Inverse"
|
||||
android:textColor="?android:attr/colorAccent"
|
||||
android:text="@*android:string/reset" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="@dimen/notification_panel_width"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:paddingTop="12dp"
|
||||
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.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:clickable="true"
|
||||
android:layout_width="@dimen/fab_size"
|
||||
android:layout_height="@dimen/fab_size"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="@dimen/fab_margin"
|
||||
android:layout_marginBottom="@dimen/fab_margin"
|
||||
android:elevation="@dimen/fab_elevation"
|
||||
android:background="@drawable/fab_background" />
|
||||
android:layout_weight="1" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
|
||||
24
packages/SystemUI/res/layout/qs_customize_tile_frame.xml
Normal file
24
packages/SystemUI/res/layout/qs_customize_tile_frame.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2016 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.
|
||||
-->
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="16dp" />
|
||||
@@ -1400,4 +1400,7 @@
|
||||
<!-- SysUI Tuner: Label for preview area in navigation bar tuner [CHAR LIMIT=NONE] -->
|
||||
<string name="preview">Preview</string>
|
||||
|
||||
<!-- Label for area where tiles can be dragged out of [CHAR LIMIT=60] -->
|
||||
<string name="drag_to_add_tiles">Drag to add tiles</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -76,7 +76,7 @@ public abstract class QSTile<TState extends State> implements Listenable {
|
||||
|
||||
private String mTileSpec;
|
||||
|
||||
abstract protected TState newTileState();
|
||||
public abstract TState newTileState();
|
||||
abstract protected void handleClick();
|
||||
abstract protected void handleUpdateState(TState state, Object arg);
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* 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.ComponentName;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.MetricsProto.MetricsEvent;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.qs.QSTile;
|
||||
|
||||
public class BlankCustomTile extends QSTile<QSTile.State> {
|
||||
public static final String PREFIX = "custom(";
|
||||
|
||||
private final ComponentName mComponent;
|
||||
|
||||
private BlankCustomTile(Host host, String action) {
|
||||
super(host);
|
||||
mComponent = ComponentName.unflattenFromString(action);
|
||||
}
|
||||
|
||||
public static QSTile<?> create(Host host, String spec) {
|
||||
if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
|
||||
throw new IllegalArgumentException("Bad custom tile spec: " + spec);
|
||||
}
|
||||
final String action = spec.substring(PREFIX.length(), spec.length() - 1);
|
||||
if (action.isEmpty()) {
|
||||
throw new IllegalArgumentException("Empty custom tile spec action");
|
||||
}
|
||||
return new BlankCustomTile(host, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListening(boolean listening) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State newTileState() {
|
||||
return new State();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUserSwitch(int newUserId) {
|
||||
super.handleUserSwitch(newUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClick() {
|
||||
MetricsLogger.action(mContext, getMetricsCategory(), mComponent.getPackageName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleLongClick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUpdateState(State state, Object arg) {
|
||||
try {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
ServiceInfo info = pm.getServiceInfo(mComponent, 0);
|
||||
Drawable drawable = info.loadIcon(pm);
|
||||
drawable.setTint(mContext.getColor(R.color.qs_tile_tint_active));
|
||||
state.icon = new DrawableIcon(drawable);
|
||||
state.label = info.loadLabel(pm).toString();
|
||||
state.contentDescription = state.label;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsEvent.QS_INTENT;
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* 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.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.qs.QSPanel;
|
||||
import com.android.systemui.qs.QSTile;
|
||||
import com.android.systemui.qs.external.CustomTile;
|
||||
import com.android.systemui.qs.external.TileLifecycleManager;
|
||||
import com.android.systemui.statusbar.phone.QSTileHost;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A version of QSPanel that allows tiles to be dragged around rather than
|
||||
* 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 {
|
||||
|
||||
private static final String TAG = "CustomQSPanel";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private List<String> mSavedTiles = Collections.emptyList();
|
||||
private ArrayList<String> mStash;
|
||||
private List<String> mTiles = new ArrayList<>();
|
||||
|
||||
private ArrayList<QSTile<?>> mCurrentTiles = new ArrayList<>();
|
||||
|
||||
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);
|
||||
removeView(mFooter.getView());
|
||||
|
||||
if (DEBUG) Log.d(TAG, "new CustomQSPanel", new Throwable());
|
||||
TunerService.get(mContext).addTunable(this, QSTileHost.TILES_SETTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showDetail(boolean show, Record r) {
|
||||
// No detail here.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
// Don't allow the super to unregister the tunable.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTuningChanged(String key, String newValue) {
|
||||
if (key.equals(QS_SHOW_BRIGHTNESS)) {
|
||||
// No Brightness for you.
|
||||
super.onTuningChanged(key, "0");
|
||||
}
|
||||
if (QSTileHost.TILES_SETTING.equals(key)) {
|
||||
mSavedTiles = Collections.unmodifiableList(
|
||||
QSTileHost.loadTileSpecs(mContext, newValue));
|
||||
if (DEBUG) Log.d(TAG, "New saved tiles " + TextUtils.join(",", mSavedTiles));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createCustomizePanel() {
|
||||
// Already in CustomizePanel.
|
||||
}
|
||||
|
||||
public void tileSelected(QSTile<?> tile, ClipData currentClip) {
|
||||
String sourceSpec = getSpec(currentClip);
|
||||
String destSpec = tile.getTileSpec();
|
||||
if (!sourceSpec.equals(destSpec)) {
|
||||
moveTo(sourceSpec, destSpec);
|
||||
}
|
||||
}
|
||||
|
||||
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 String getSpec(ClipData data) {
|
||||
return data.getItemAt(0).getText().toString();
|
||||
}
|
||||
|
||||
public void setSavedTiles() {
|
||||
if (DEBUG) Log.d(TAG, "setSavedTiles " + TextUtils.join(",", mSavedTiles));
|
||||
setTiles(mSavedTiles);
|
||||
}
|
||||
|
||||
public void saveCurrentTiles() {
|
||||
mHost.changeTiles(mSavedTiles, mTiles);
|
||||
}
|
||||
|
||||
public void stashCurrentTiles() {
|
||||
mStash = new ArrayList<>(mTiles);
|
||||
}
|
||||
|
||||
public void unstashTiles() {
|
||||
setTiles(mStash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTiles(Collection<QSTile<?>> tiles) {
|
||||
setTilesInternal();
|
||||
}
|
||||
|
||||
private void setTilesInternal() {
|
||||
if (DEBUG) Log.d(TAG, "Set tiles internal");
|
||||
for (int i = 0; i < mCurrentTiles.size(); i++) {
|
||||
mCurrentTiles.get(i).destroy();
|
||||
}
|
||||
mCurrentTiles.clear();
|
||||
for (int i = 0; i < mTiles.size(); i++) {
|
||||
if (mTiles.get(i).startsWith(CustomTile.PREFIX)) {
|
||||
QSTile<?> tile = BlankCustomTile.create(mHost, mTiles.get(i));
|
||||
tile.setTileSpec(mTiles.get(i));
|
||||
mCurrentTiles.add(tile);
|
||||
} else {
|
||||
QSTile<?> tile = mHost.createTile(mTiles.get(i));
|
||||
if (tile != null) {
|
||||
tile.setTileSpec(mTiles.get(i));
|
||||
mCurrentTiles.add(tile);
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "Skipping " + mTiles.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
super.setTiles(mCurrentTiles);
|
||||
}
|
||||
|
||||
public void addTile(String spec) {
|
||||
if (DEBUG) Log.d(TAG, "addTile " + spec);
|
||||
mTiles.add(spec);
|
||||
setTilesInternal();
|
||||
}
|
||||
|
||||
public void moveTo(String from, String to) {
|
||||
if (DEBUG) Log.d(TAG, "moveTo " + from + " " + 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);
|
||||
setTilesInternal();
|
||||
}
|
||||
|
||||
public void remove(String spec) {
|
||||
if (!mTiles.remove(spec)) {
|
||||
Log.e(TAG, "Unknown remove spec " + spec);
|
||||
}
|
||||
setTilesInternal();
|
||||
}
|
||||
|
||||
public void setTiles(List<String> tiles) {
|
||||
if (DEBUG) Log.d(TAG, "Set tiles " + TextUtils.join(",", tiles));
|
||||
mTiles = new ArrayList<>(tiles);
|
||||
setTilesInternal();
|
||||
}
|
||||
|
||||
public Collection<QSTile<?>> getTiles() {
|
||||
return mCurrentTiles;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* 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 ? .3f : 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);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* 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.animation.AnimatorInflater;
|
||||
import android.content.Context;
|
||||
import android.graphics.Outline;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
public class FloatingActionButton extends ImageView {
|
||||
|
||||
public FloatingActionButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setScaleType(ScaleType.CENTER);
|
||||
setStateListAnimator(AnimatorInflater.loadStateListAnimator(context, R.anim.fab_elevation));
|
||||
setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
outline.setOval(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
});
|
||||
setClipToOutline(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
invalidateOutline();
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* 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.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;
|
||||
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.QSTile;
|
||||
|
||||
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, OnTouchListener {
|
||||
|
||||
private final ArrayList<TilePage> mPages = new ArrayList<>();
|
||||
private final ArrayList<TileRecord> mTiles = new ArrayList<TileRecord>();
|
||||
private CustomQSPanel mPanel;
|
||||
private final Rect mHitRect = new Rect();
|
||||
|
||||
private ClipData mCurrentClip;
|
||||
private View mCurrentView;
|
||||
|
||||
public NonPagedTileLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTile(TileRecord record) {
|
||||
mTiles.add(record);
|
||||
distributeTiles();
|
||||
if (record.tileView.getTag() == record.tile) {
|
||||
return;
|
||||
}
|
||||
record.tileView.setTag(record.tile);
|
||||
record.tileView.setVisibility(View.VISIBLE);
|
||||
record.tileView.init(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
|
||||
public void removeTile(TileRecord tile) {
|
||||
if (mTiles.remove(tile)) {
|
||||
distributeTiles();
|
||||
}
|
||||
}
|
||||
|
||||
private void distributeTiles() {
|
||||
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);
|
||||
mPages.get(index).addTile(tile);
|
||||
// Keep everything in one layout for now.
|
||||
if (false && 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 int getOffsetTop(TileRecord tile) {
|
||||
// No touch feedback, so this isn't required.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateResources() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDragEvent(DragEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case DragEvent.ACTION_DRAG_LOCATION:
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
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((QSTile<?>) child.getTag(), mCurrentClip);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DragEvent.ACTION_DRAG_ENDED:
|
||||
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 can restore
|
||||
// the previous state.
|
||||
mPanel.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);
|
||||
}
|
||||
}
|
||||
@@ -16,38 +16,25 @@
|
||||
package com.android.systemui.qs.customize;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.content.ClipData;
|
||||
import android.animation.Animator.AnimatorListener;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.content.DialogInterface.OnDismissListener;
|
||||
import android.support.v7.widget.DefaultItemAnimator;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.DragEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toolbar;
|
||||
import android.widget.Toolbar.OnMenuItemClickListener;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.qs.QSDetailClipper;
|
||||
import com.android.systemui.qs.QSTile.Host.Callback;
|
||||
import com.android.systemui.qs.customize.DropButton.OnDropListener;
|
||||
import com.android.systemui.qs.customize.TileAdapter.TileSelectedListener;
|
||||
import com.android.systemui.qs.QSTile;
|
||||
import com.android.systemui.statusbar.phone.PhoneStatusBar;
|
||||
import com.android.systemui.statusbar.phone.QSTileHost;
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Allows full-screen customization of QS, through show() and hide().
|
||||
@@ -55,26 +42,19 @@ import java.util.ArrayList;
|
||||
* 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,
|
||||
OnDropListener, OnClickListener, Animator.AnimatorListener, TileSelectedListener,
|
||||
OnCancelListener, OnDismissListener {
|
||||
public class QSCustomizer extends LinearLayout implements AnimatorListener, OnClickListener {
|
||||
|
||||
private static final int MENU_SAVE = Menu.FIRST;
|
||||
private static final int MENU_RESET = Menu.FIRST + 1;
|
||||
private final QSDetailClipper mClipper;
|
||||
|
||||
private PhoneStatusBar mPhoneStatusBar;
|
||||
|
||||
private Toolbar mToolbar;
|
||||
private ViewGroup mDragButtons;
|
||||
private CustomQSPanel mQsPanel;
|
||||
|
||||
private boolean isShown;
|
||||
private DropButton mInfoButton;
|
||||
private DropButton mRemoveButton;
|
||||
private FloatingActionButton mFab;
|
||||
private SystemUIDialog mDialog;
|
||||
private QSTileHost mHost;
|
||||
private RecyclerView mRecyclerView;
|
||||
private TileAdapter mTileAdapter;
|
||||
private View mClose;
|
||||
private View mSave;
|
||||
private View mReset;
|
||||
|
||||
public QSCustomizer(Context context, AttributeSet attrs) {
|
||||
super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
|
||||
@@ -83,59 +63,42 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
|
||||
public void setHost(QSTileHost host) {
|
||||
mHost = host;
|
||||
mHost.addCallback(this);
|
||||
mPhoneStatusBar = host.getPhoneStatusBar();
|
||||
mQsPanel.setTiles(mHost.getTiles());
|
||||
mQsPanel.setHost(mHost);
|
||||
mQsPanel.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(R.drawable.ic_close_white, mContext.getTheme()));
|
||||
mToolbar.setNavigationOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
hide(0, 0);
|
||||
}
|
||||
});
|
||||
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));
|
||||
mClose = findViewById(R.id.close);
|
||||
mSave = findViewById(R.id.save);
|
||||
mReset = findViewById(R.id.reset);
|
||||
mClose.setOnClickListener(this);
|
||||
mSave.setOnClickListener(this);
|
||||
mReset.setOnClickListener(this);
|
||||
|
||||
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);
|
||||
|
||||
mFab = (FloatingActionButton) findViewById(R.id.fab);
|
||||
mFab.setImageResource(R.drawable.ic_add);
|
||||
mFab.setOnClickListener(this);
|
||||
mRecyclerView = (RecyclerView) findViewById(android.R.id.list);
|
||||
mTileAdapter = new TileAdapter(getContext());
|
||||
mRecyclerView.setAdapter(mTileAdapter);
|
||||
new ItemTouchHelper(mTileAdapter.getCallback()).attachToRecyclerView(mRecyclerView);
|
||||
GridLayoutManager layout = new GridLayoutManager(getContext(), 3);
|
||||
layout.setSpanSizeLookup(mTileAdapter.getSizeLookup());
|
||||
mRecyclerView.setLayoutManager(layout);
|
||||
mRecyclerView.addItemDecoration(mTileAdapter.getItemDecoration());
|
||||
DefaultItemAnimator animator = new DefaultItemAnimator();
|
||||
animator.setMoveDuration(TileAdapter.MOVE_DURATION);
|
||||
mRecyclerView.setItemAnimator(animator);
|
||||
}
|
||||
|
||||
public void show(int x, int y) {
|
||||
isShown = true;
|
||||
mQsPanel.setSavedTiles();
|
||||
mPhoneStatusBar.getStatusBarWindow().addView(this);
|
||||
mQsPanel.setListening(true);
|
||||
setTileSpecs();
|
||||
mClipper.animateCircularClip(x, y, true, this);
|
||||
new TileQueryHelper(mContext, mHost).setListener(mTileAdapter);
|
||||
}
|
||||
|
||||
public void hide(int x, int y) {
|
||||
isShown = false;
|
||||
mQsPanel.setListening(false);
|
||||
mClipper.animateCircularClip(x, y, false, this);
|
||||
}
|
||||
|
||||
@@ -149,108 +112,34 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
for (String tile : defTiles.split(",")) {
|
||||
tiles.add(tile);
|
||||
}
|
||||
mQsPanel.setTiles(tiles);
|
||||
mTileAdapter.setTileSpecs(tiles);
|
||||
}
|
||||
|
||||
private void setDragging(boolean dragging) {
|
||||
mToolbar.setVisibility(!dragging ? View.VISIBLE : View.INVISIBLE);
|
||||
private void setTileSpecs() {
|
||||
List<String> specs = new ArrayList<>();
|
||||
for (QSTile tile : mHost.getTiles()) {
|
||||
specs.add(tile.getTileSpec());
|
||||
}
|
||||
mTileAdapter.setTileSpecs(specs);
|
||||
}
|
||||
|
||||
private void save() {
|
||||
Log.d("CustomQSPanel", "Save!");
|
||||
mQsPanel.saveCurrentTiles();
|
||||
// TODO: At save button.
|
||||
hide(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENU_SAVE:
|
||||
Log.d("CustomQSPanel", "Save...");
|
||||
save();
|
||||
break;
|
||||
case MENU_RESET:
|
||||
reset();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileSelected(String spec) {
|
||||
if (mDialog != null) {
|
||||
mQsPanel.addTile(spec);
|
||||
mDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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) {
|
||||
mQsPanel.remove(mQsPanel.getSpec(data));
|
||||
} else if (v == mInfoButton) {
|
||||
mQsPanel.unstashTiles();
|
||||
SystemUIDialog dialog = new SystemUIDialog(mContext);
|
||||
dialog.setTitle(mQsPanel.getSpec(data));
|
||||
dialog.setPositiveButton(R.string.ok, null);
|
||||
dialog.show();
|
||||
}
|
||||
mTileAdapter.saveSpecs(mHost);
|
||||
hide((int) mSave.getX() + mSave.getWidth() / 2, (int) mSave.getY() + mSave.getHeight() / 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mFab == v) {
|
||||
mDialog = new SystemUIDialog(mContext,
|
||||
android.R.style.Theme_Material_Dialog);
|
||||
View view = LayoutInflater.from(mContext).inflate(R.layout.qs_add_tiles_list, null);
|
||||
ListView listView = (ListView) view.findViewById(android.R.id.list);
|
||||
TileAdapter adapter = new TileAdapter(mContext, mQsPanel.getTiles(), mHost);
|
||||
adapter.setListener(this);
|
||||
listView.setDivider(null);
|
||||
listView.setDividerHeight(0);
|
||||
listView.setAdapter(adapter);
|
||||
listView.setEmptyView(view.findViewById(R.id.empty_text));
|
||||
mDialog.setView(view);
|
||||
mDialog.setOnDismissListener(this);
|
||||
mDialog.setOnCancelListener(this);
|
||||
mDialog.show();
|
||||
// Too lazy to figure out what this will be now, but it should probably be something
|
||||
// besides just a dialog.
|
||||
// For now, just make it big.
|
||||
WindowManager.LayoutParams params = mDialog.getWindow().getAttributes();
|
||||
params.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
mDialog.getWindow().setAttributes(params);
|
||||
if (v == mClose) {
|
||||
hide((int) mClose.getX() + mClose.getWidth() / 2,
|
||||
(int) mClose.getY() + mClose.getHeight() / 2);
|
||||
} else if (v == mSave) {
|
||||
save();
|
||||
} else if (v == mReset) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
mDialog = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
mDialog = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!isShown) {
|
||||
@@ -274,4 +163,4 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
// Don't care.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,262 +1,293 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* Copyright (C) 2016 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
|
||||
* 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.
|
||||
* 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.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.service.quicksettings.TileService;
|
||||
import android.util.Log;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.RecyclerView.ItemDecoration;
|
||||
import android.support.v7.widget.RecyclerView.State;
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper.Callback;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.GridLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import android.widget.FrameLayout;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.qs.QSTile;
|
||||
import com.android.systemui.qs.QSTile.Icon;
|
||||
import com.android.systemui.qs.external.CustomTile;
|
||||
import com.android.systemui.qs.QSIconView;
|
||||
import com.android.systemui.qs.QSTileView;
|
||||
import com.android.systemui.qs.customize.TileAdapter.Holder;
|
||||
import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
|
||||
import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
|
||||
import com.android.systemui.statusbar.phone.QSTileHost;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class TileAdapter extends BaseAdapter {
|
||||
public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener {
|
||||
|
||||
private static final String TAG = "TileAdapter";
|
||||
private static final long DRAG_LENGTH = 100;
|
||||
private static final float DRAG_SCALE = 1.2f;
|
||||
public static final long MOVE_DURATION = 150;
|
||||
|
||||
private static final int TYPE_TILE = 0;
|
||||
private static final int TYPE_EDIT = 1;
|
||||
|
||||
private final ArrayList<TileGroup> mGroups = new ArrayList<>();
|
||||
private final Context mContext;
|
||||
|
||||
private TileSelectedListener mListener;
|
||||
private ArrayList<String> mCurrentTiles;
|
||||
private final List<TileInfo> mTiles = new ArrayList<>();
|
||||
private int mDividerIndex;
|
||||
private List<String> mCurrentSpecs;
|
||||
private List<TileInfo> mOtherTiles;
|
||||
private List<TileInfo> mAllTiles;
|
||||
|
||||
public TileAdapter(Context context, Collection<QSTile<?>> currentTiles, QSTileHost host) {
|
||||
private Holder mCurrentDrag;
|
||||
|
||||
public TileAdapter(Context context) {
|
||||
mContext = context;
|
||||
addSystemTiles(currentTiles, host);
|
||||
// TODO: Live?
|
||||
}
|
||||
|
||||
private void addSystemTiles(Collection<QSTile<?>> currentTiles, QSTileHost host) {
|
||||
try {
|
||||
ArrayList<String> tileSpecs = new ArrayList<>();
|
||||
for (QSTile<?> tile : currentTiles) {
|
||||
tileSpecs.add(tile.getTileSpec());
|
||||
}
|
||||
mCurrentTiles = tileSpecs;
|
||||
final TileGroup group = new TileGroup("com.android.settings", mContext);
|
||||
boolean hasColorMod = host.getDisplayController().isEnabled();
|
||||
String possible = mContext.getString(R.string.quick_settings_tiles_default)
|
||||
+ ",hotspot,inversion,saver" + (hasColorMod ? ",colors" : "");
|
||||
String[] possibleTiles = possible.split(",");
|
||||
for (int i = 0; i < possibleTiles.length; i++) {
|
||||
final String spec = possibleTiles[i];
|
||||
if (spec.startsWith("q")) {
|
||||
// Quick tiles can't be customized.
|
||||
continue;
|
||||
}
|
||||
if (tileSpecs.contains(spec)) {
|
||||
Log.d(TAG, "Skipping " + spec);
|
||||
continue;
|
||||
}
|
||||
Log.d(TAG, "Trying " + spec);
|
||||
final QSTile<?> tile = host.createTile(spec);
|
||||
if (tile == null) {
|
||||
continue;
|
||||
}
|
||||
// Bad, bad, very bad.
|
||||
tile.setListening(true);
|
||||
tile.clearState();
|
||||
tile.refreshState();
|
||||
tile.setListening(false);
|
||||
new Handler(host.getLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
group.addTile(spec, tile.getState().icon, tile.getState().label, mContext);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Error: Badness (10000).
|
||||
// Serialize this work after the host's looper's queue is empty.
|
||||
new Handler(host.getLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (group.mTiles.size() > 0) {
|
||||
mGroups.add(group);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
new QueryTilesTask().execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(TAG, "Couldn't load system tiles", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener(TileSelectedListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mGroups.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return mGroups.get(position);
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
return mTiles.get(position) != null ? mAllTiles.indexOf(mTiles.get(position)) : -1;
|
||||
}
|
||||
|
||||
public Callback getCallback() {
|
||||
return mCallbacks;
|
||||
}
|
||||
|
||||
public ItemDecoration getItemDecoration() {
|
||||
return mDecoration;
|
||||
}
|
||||
|
||||
public void saveSpecs(QSTileHost host) {
|
||||
List<String> newSpecs = new ArrayList<>();
|
||||
for (int i = 0; mTiles.get(i) != null; i++) {
|
||||
newSpecs.add(mTiles.get(i).spec);
|
||||
}
|
||||
host.changeTiles(mCurrentSpecs, newSpecs);
|
||||
setTileSpecs(newSpecs);
|
||||
}
|
||||
|
||||
public void setTileSpecs(List<String> currentSpecs) {
|
||||
mCurrentSpecs = currentSpecs;
|
||||
recalcSpecs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
return mGroups.get(position).getView(mContext, convertView, parent, mListener);
|
||||
public void onTilesChanged(List<TileInfo> tiles) {
|
||||
mAllTiles = tiles;
|
||||
recalcSpecs();
|
||||
}
|
||||
|
||||
private static class TileGroup {
|
||||
private final ArrayList<TileInfo> mTiles = new ArrayList<>();
|
||||
private CharSequence mLabel;
|
||||
private Drawable mIcon;
|
||||
|
||||
public TileGroup(String pkg, Context context) throws NameNotFoundException {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
ApplicationInfo info = pm.getApplicationInfo(pkg, 0);
|
||||
mLabel = info.loadLabel(pm);
|
||||
mIcon = info.loadIcon(pm);
|
||||
Log.d(TAG, "Added " + mLabel);
|
||||
private void recalcSpecs() {
|
||||
if (mCurrentSpecs == null || mAllTiles == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
private void addTile(String spec, Drawable icon, CharSequence label) {
|
||||
TileInfo info = new TileInfo();
|
||||
info.label = label;
|
||||
info.drawable = icon;
|
||||
info.spec = spec;
|
||||
mTiles.add(info);
|
||||
mOtherTiles = new ArrayList<TileInfo>(mAllTiles);
|
||||
mTiles.clear();
|
||||
for (int i = 0; i < mCurrentSpecs.size(); i++) {
|
||||
mTiles.add(getAndRemoveOther(mCurrentSpecs.get(i)));
|
||||
}
|
||||
mTiles.add(null);
|
||||
mTiles.addAll(mOtherTiles);
|
||||
mDividerIndex = mTiles.indexOf(null);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void addTile(String spec, Icon icon, CharSequence label, Context context) {
|
||||
addTile(spec, icon != null ? icon.getDrawable(context) : null, label);
|
||||
}
|
||||
|
||||
private View getView(Context context, View convertView, ViewGroup parent,
|
||||
final TileSelectedListener listener) {
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(context).inflate(R.layout.tile_listing, parent,
|
||||
false);
|
||||
private TileInfo getAndRemoveOther(String s) {
|
||||
for (int i = 0; i < mOtherTiles.size(); i++) {
|
||||
if (mOtherTiles.get(i).spec.equals(s)) {
|
||||
return mOtherTiles.remove(i);
|
||||
}
|
||||
((TextView) convertView.findViewById(android.R.id.title)).setText(mLabel);
|
||||
((ImageView) convertView.findViewById(android.R.id.icon)).setImageDrawable(mIcon);
|
||||
GridLayout grid = (GridLayout) convertView.findViewById(R.id.tile_grid);
|
||||
final int N = mTiles.size();
|
||||
if (grid.getChildCount() != N) {
|
||||
grid.removeAllViews();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (mTiles.get(position) == null) {
|
||||
return TYPE_EDIT;
|
||||
}
|
||||
return TYPE_TILE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
final Context context = parent.getContext();
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
if (viewType == 1) {
|
||||
return new Holder(inflater.inflate(R.layout.qs_customize_divider, parent, false));
|
||||
}
|
||||
FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
|
||||
false);
|
||||
frame.addView(new QSTileView(context, new QSIconView(context)));
|
||||
return new Holder(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mTiles.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(Holder holder, int position) {
|
||||
if (holder.getItemViewType() == TYPE_EDIT) return;
|
||||
|
||||
TileInfo info = mTiles.get(position);
|
||||
holder.mTileView.onStateChanged(info.state);
|
||||
}
|
||||
|
||||
public SpanSizeLookup getSizeLookup() {
|
||||
return mSizeLookup;
|
||||
}
|
||||
|
||||
public class Holder extends ViewHolder {
|
||||
private QSTileView mTileView;
|
||||
|
||||
public Holder(View itemView) {
|
||||
super(itemView);
|
||||
if (itemView instanceof FrameLayout) {
|
||||
mTileView = (QSTileView) ((FrameLayout) itemView).getChildAt(0);
|
||||
}
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (grid.getChildCount() <= i) {
|
||||
grid.addView(createTile(context));
|
||||
}
|
||||
View view = grid.getChildAt(i);
|
||||
final TileInfo tileInfo = mTiles.get(i);
|
||||
((ImageView) view.findViewById(R.id.tile_icon)).setImageDrawable(tileInfo.drawable);
|
||||
((TextView) view.findViewById(R.id.tile_label)).setText(tileInfo.label);
|
||||
view.setClickable(true);
|
||||
view.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
listener.onTileSelected(tileInfo.spec);
|
||||
}
|
||||
});
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
|
||||
private View createTile(Context context) {
|
||||
return LayoutInflater.from(context).inflate(R.layout.qs_add_tile_layout, null);
|
||||
public void startDrag() {
|
||||
itemView.animate()
|
||||
.setDuration(DRAG_LENGTH)
|
||||
.scaleX(DRAG_SCALE)
|
||||
.scaleY(DRAG_SCALE);
|
||||
mTileView.findViewById(R.id.tile_label).animate()
|
||||
.setDuration(DRAG_LENGTH)
|
||||
.alpha(0);
|
||||
}
|
||||
|
||||
public void stopDrag() {
|
||||
itemView.animate()
|
||||
.setDuration(DRAG_LENGTH)
|
||||
.scaleX(1)
|
||||
.scaleY(1);
|
||||
mTileView.findViewById(R.id.tile_label).animate()
|
||||
.setDuration(DRAG_LENGTH)
|
||||
.alpha(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TileInfo {
|
||||
private String spec;
|
||||
private Drawable drawable;
|
||||
private CharSequence label;
|
||||
}
|
||||
|
||||
private class QueryTilesTask extends AsyncTask<Void, Void, Collection<TileGroup>> {
|
||||
private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() {
|
||||
@Override
|
||||
protected Collection<TileGroup> doInBackground(Void... params) {
|
||||
HashMap<String, TileGroup> pkgMap = new HashMap<>();
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
// TODO: Handle userness.
|
||||
List<ResolveInfo> services = pm.queryIntentServices(
|
||||
new Intent(TileService.ACTION_QS_TILE), 0);
|
||||
for (ResolveInfo info : services) {
|
||||
String packageName = info.serviceInfo.packageName;
|
||||
ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);
|
||||
String spec = CustomTile.toSpec(componentName);
|
||||
if (mCurrentTiles.contains(spec)) {
|
||||
public int getSpanSize(int position) {
|
||||
return getItemViewType(position) == TYPE_EDIT ? 3 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
private final ItemDecoration mDecoration = new ItemDecoration() {
|
||||
// TODO: Move this to resource.
|
||||
private final ColorDrawable mDrawable = new ColorDrawable(0xff384248);
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, State state) {
|
||||
super.onDraw(c, parent, state);
|
||||
|
||||
final int childCount = parent.getChildCount();
|
||||
final int width = parent.getWidth();
|
||||
final int bottom = parent.getBottom();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = parent.getChildAt(i);
|
||||
final ViewHolder holder = parent.getChildViewHolder(child);
|
||||
if (holder.getAdapterPosition() < mDividerIndex) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
TileGroup group = pkgMap.get(packageName);
|
||||
if (group == null) {
|
||||
group = new TileGroup(packageName, mContext);
|
||||
pkgMap.put(packageName, group);
|
||||
}
|
||||
Drawable icon = info.serviceInfo.loadIcon(pm);
|
||||
CharSequence label = info.serviceInfo.loadLabel(pm);
|
||||
group.addTile(spec, icon, label != null ? label.toString() : "null");
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w(TAG, "Couldn't find resolved package... " + packageName, e);
|
||||
}
|
||||
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
|
||||
.getLayoutParams();
|
||||
final int top = child.getTop() + params.topMargin +
|
||||
Math.round(ViewCompat.getTranslationY(child));
|
||||
// Draw full width, in case there aren't tiles all the way across.
|
||||
mDrawable.setBounds(0, top, width, bottom);
|
||||
mDrawable.draw(c);
|
||||
break;
|
||||
}
|
||||
return pkgMap.values();
|
||||
}
|
||||
};
|
||||
|
||||
private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
|
||||
|
||||
@Override
|
||||
public boolean isLongPressDragEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Collection<TileGroup> result) {
|
||||
mGroups.addAll(result);
|
||||
notifyDataSetChanged();
|
||||
public boolean isItemViewSwipeEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public interface TileSelectedListener {
|
||||
void onTileSelected(String spec);
|
||||
}
|
||||
@Override
|
||||
public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
|
||||
super.onSelectedChanged(viewHolder, actionState);
|
||||
if (mCurrentDrag != null) {
|
||||
mCurrentDrag.stopDrag();
|
||||
}
|
||||
if (viewHolder != null) {
|
||||
mCurrentDrag = (Holder) viewHolder;
|
||||
mCurrentDrag.startDrag();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
|
||||
if (viewHolder.getItemViewType() == TYPE_EDIT) {
|
||||
return makeMovementFlags(0, 0);
|
||||
}
|
||||
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT
|
||||
| ItemTouchHelper.LEFT;
|
||||
return makeMovementFlags(dragFlags, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
|
||||
int from = viewHolder.getAdapterPosition();
|
||||
int to = target.getAdapterPosition();
|
||||
if (to > mDividerIndex) {
|
||||
if (from < mDividerIndex) {
|
||||
to = mDividerIndex;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (target.getItemViewType() == TYPE_EDIT && from < mDividerIndex) {
|
||||
to++;
|
||||
}
|
||||
move(from, to, mTiles);
|
||||
mDividerIndex = mTiles.indexOf(null);
|
||||
notifyItemMoved(from, to);
|
||||
return true;
|
||||
}
|
||||
|
||||
private <T> void move(int from, int to, List<T> list) {
|
||||
list.add(from > to ? to : to + 1, list.get(from));
|
||||
list.remove(from > to ? from + 1 : from);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(ViewHolder viewHolder, int direction) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.service.quicksettings.TileService;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.qs.QSTile;
|
||||
import com.android.systemui.qs.QSTile.DrawableIcon;
|
||||
import com.android.systemui.qs.external.CustomTile;
|
||||
import com.android.systemui.statusbar.phone.QSTileHost;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class TileQueryHelper {
|
||||
|
||||
private static final String TAG = "TileQueryHelper";
|
||||
|
||||
private final ArrayList<TileInfo> mTiles = new ArrayList<>();
|
||||
private final Context mContext;
|
||||
private TileStateListener mListener;
|
||||
|
||||
public TileQueryHelper(Context context, QSTileHost host) {
|
||||
mContext = context;
|
||||
addSystemTiles(host);
|
||||
// TODO: Live?
|
||||
}
|
||||
|
||||
private void addSystemTiles(QSTileHost host) {
|
||||
boolean hasColorMod = host.getDisplayController().isEnabled();
|
||||
String possible = mContext.getString(R.string.quick_settings_tiles_default)
|
||||
+ ",hotspot,inversion,saver" + (hasColorMod ? ",colors" : "");
|
||||
String[] possibleTiles = possible.split(",");
|
||||
final Handler qsHandler = new Handler(host.getLooper());
|
||||
final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
for (int i = 0; i < possibleTiles.length; i++) {
|
||||
final String spec = possibleTiles[i];
|
||||
final QSTile<?> tile = host.createTile(spec);
|
||||
if (tile == null) {
|
||||
continue;
|
||||
}
|
||||
tile.setListening(true);
|
||||
tile.clearState();
|
||||
tile.refreshState();
|
||||
tile.setListening(false);
|
||||
qsHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final QSTile.State state = tile.newTileState();
|
||||
tile.getState().copyTo(state);
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
addTile(spec, state, mTiles);
|
||||
mListener.onTilesChanged(mTiles);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
qsHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new QueryTilesTask().execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setListener(TileStateListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
private static void addTile(String spec, QSTile.State state, List<TileInfo> tiles) {
|
||||
TileInfo info = new TileInfo();
|
||||
info.state = state;
|
||||
info.spec = spec;
|
||||
tiles.add(info);
|
||||
}
|
||||
|
||||
private static void addTile(String spec, Drawable drawable, CharSequence label, Context context,
|
||||
List<TileInfo> tiles) {
|
||||
QSTile.State state = new QSTile.State();
|
||||
state.label = label;
|
||||
state.contentDescription = label;
|
||||
state.icon = new DrawableIcon(drawable);
|
||||
addTile(spec, state, tiles);
|
||||
}
|
||||
|
||||
public static class TileInfo {
|
||||
public String spec;
|
||||
public QSTile.State state;
|
||||
}
|
||||
|
||||
private class QueryTilesTask extends AsyncTask<Void, Void, Collection<TileInfo>> {
|
||||
@Override
|
||||
protected Collection<TileInfo> doInBackground(Void... params) {
|
||||
List<TileInfo> tiles = new ArrayList<>();
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
List<ResolveInfo> services = pm.queryIntentServicesAsUser(
|
||||
new Intent(TileService.ACTION_QS_TILE), 0, ActivityManager.getCurrentUser());
|
||||
for (ResolveInfo info : services) {
|
||||
String packageName = info.serviceInfo.packageName;
|
||||
ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);
|
||||
String spec = CustomTile.toSpec(componentName);
|
||||
Drawable icon = info.serviceInfo.loadIcon(pm);
|
||||
if (icon != null) {
|
||||
icon.mutate();
|
||||
icon.setTint(mContext.getColor(android.R.color.white));
|
||||
}
|
||||
CharSequence label = info.serviceInfo.loadLabel(pm);
|
||||
addTile(spec, icon, label != null ? label.toString() : "null", mContext, tiles);
|
||||
}
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Collection<TileInfo> result) {
|
||||
mTiles.addAll(result);
|
||||
mListener.onTilesChanged(mTiles);
|
||||
}
|
||||
}
|
||||
|
||||
public interface TileStateListener {
|
||||
void onTilesChanged(List<TileInfo> tiles);
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@ public class CustomTile extends QSTile<QSTile.State> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State newTileState() {
|
||||
public State newTileState() {
|
||||
return new State();
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ public class AirplaneModeTile extends QSTile<QSTile.BooleanState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanState newTileState() {
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State newTileState() {
|
||||
public State newTileState() {
|
||||
return new QSTile.State();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanState newTileState() {
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ public class CastTile extends QSTile<QSTile.BooleanState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanState newTileState() {
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SignalState newTileState() {
|
||||
public SignalState newTileState() {
|
||||
return new SignalState();
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanState newTileState() {
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public class DataSaverTile extends QSTile<QSTile.BooleanState> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanState newTileState() {
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ public class DndTile extends QSTile<QSTile.BooleanState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanState newTileState() {
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanState newTileState() {
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanState newTileState() {
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ public class IntentTile extends QSTile<QSTile.State> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State newTileState() {
|
||||
public State newTileState() {
|
||||
return new State();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public class LocationTile extends QSTile<QSTile.BooleanState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanState newTileState() {
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public class RotationLockTile extends QSTile<QSTile.BooleanState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanState newTileState() {
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public class UserTile extends QSTile<QSTile.State> implements UserInfoController
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State newTileState() {
|
||||
public State newTileState() {
|
||||
return new QSTile.State();
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SignalState newTileState() {
|
||||
public SignalState newTileState() {
|
||||
return new SignalState();
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ public class WorkModeTile extends QSTile<QSTile.BooleanState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BooleanState newTileState() {
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class ColorMatrixTile extends QSTile<QSTile.State> implements DisplayCont
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State newTileState() {
|
||||
public State newTileState() {
|
||||
return new State();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user