Merge "Add Quick Settings API"
This commit is contained in:
@@ -418,6 +418,8 @@ LOCAL_SRC_FILES += \
|
||||
packages/services/PacProcessor/com/android/net/IProxyService.aidl \
|
||||
packages/services/Proxy/com/android/net/IProxyCallback.aidl \
|
||||
packages/services/Proxy/com/android/net/IProxyPortListener.aidl \
|
||||
core/java/android/service/quicksettings/IQSService.aidl \
|
||||
core/java/android/service/quicksettings/IQSTileService.aidl \
|
||||
|
||||
# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
|
||||
LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
|
||||
@@ -626,6 +628,7 @@ aidl_files := \
|
||||
frameworks/base/core/java/android/bluetooth/le/ScanResult.aidl \
|
||||
frameworks/base/core/java/android/bluetooth/BluetoothDevice.aidl \
|
||||
frameworks/base/core/java/android/database/CursorWindow.aidl \
|
||||
frameworks/base/core/java/android/service/quicksettings/Tile.aidl \
|
||||
|
||||
gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl
|
||||
$(gen): PRIVATE_SRC_FILES := $(aidl_files)
|
||||
|
||||
@@ -30,6 +30,7 @@ package android {
|
||||
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
|
||||
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
|
||||
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
|
||||
field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
|
||||
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
|
||||
field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE";
|
||||
field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
|
||||
@@ -29051,6 +29052,35 @@ package android.service.notification {
|
||||
|
||||
}
|
||||
|
||||
package android.service.quicksettings {
|
||||
|
||||
public final class Tile implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public java.lang.CharSequence getContentDescription();
|
||||
method public android.graphics.drawable.Icon getIcon();
|
||||
method public java.lang.CharSequence getLabel();
|
||||
method public void setContentDescription(java.lang.CharSequence);
|
||||
method public void setIcon(android.graphics.drawable.Icon);
|
||||
method public void setLabel(java.lang.CharSequence);
|
||||
method public void updateTile();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
|
||||
}
|
||||
|
||||
public class TileService extends android.app.Service {
|
||||
ctor public TileService();
|
||||
method public final android.service.quicksettings.Tile getQsTile();
|
||||
method public android.os.IBinder onBind(android.content.Intent);
|
||||
method public void onClick();
|
||||
method public void onStartListening();
|
||||
method public void onStopListening();
|
||||
method public void onTileAdded();
|
||||
method public void onTileRemoved();
|
||||
field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.service.restrictions {
|
||||
|
||||
public abstract class RestrictionsReceiver extends android.content.BroadcastReceiver {
|
||||
|
||||
@@ -43,6 +43,7 @@ package android {
|
||||
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
|
||||
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
|
||||
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
|
||||
field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
|
||||
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
|
||||
field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE";
|
||||
field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
|
||||
@@ -31199,6 +31200,35 @@ package android.service.persistentdata {
|
||||
|
||||
}
|
||||
|
||||
package android.service.quicksettings {
|
||||
|
||||
public final class Tile implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public java.lang.CharSequence getContentDescription();
|
||||
method public android.graphics.drawable.Icon getIcon();
|
||||
method public java.lang.CharSequence getLabel();
|
||||
method public void setContentDescription(java.lang.CharSequence);
|
||||
method public void setIcon(android.graphics.drawable.Icon);
|
||||
method public void setLabel(java.lang.CharSequence);
|
||||
method public void updateTile();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
|
||||
}
|
||||
|
||||
public class TileService extends android.app.Service {
|
||||
ctor public TileService();
|
||||
method public final android.service.quicksettings.Tile getQsTile();
|
||||
method public android.os.IBinder onBind(android.content.Intent);
|
||||
method public void onClick();
|
||||
method public void onStartListening();
|
||||
method public void onStopListening();
|
||||
method public void onTileAdded();
|
||||
method public void onTileRemoved();
|
||||
field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.service.restrictions {
|
||||
|
||||
public abstract class RestrictionsReceiver extends android.content.BroadcastReceiver {
|
||||
|
||||
26
core/java/android/service/quicksettings/IQSService.aidl
Normal file
26
core/java/android/service/quicksettings/IQSService.aidl
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 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 android.service.quicksettings;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.service.quicksettings.Tile;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
interface IQSService {
|
||||
void updateQsTile(in Tile tile);
|
||||
}
|
||||
31
core/java/android/service/quicksettings/IQSTileService.aidl
Normal file
31
core/java/android/service/quicksettings/IQSTileService.aidl
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 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 android.service.quicksettings;
|
||||
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.IQSService;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IQSTileService {
|
||||
void setQSTile(in Tile tile);
|
||||
void onTileAdded();
|
||||
void onTileRemoved();
|
||||
void onStartListening();
|
||||
void onStopListening();
|
||||
void onClick();
|
||||
}
|
||||
19
core/java/android/service/quicksettings/Tile.aidl
Normal file
19
core/java/android/service/quicksettings/Tile.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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 android.service.quicksettings;
|
||||
|
||||
parcelable Tile;
|
||||
186
core/java/android/service/quicksettings/Tile.java
Normal file
186
core/java/android/service/quicksettings/Tile.java
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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 android.service.quicksettings;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A Tile holds the state of a tile that will be displayed
|
||||
* in Quick Settings.
|
||||
*
|
||||
* A tile in Quick Settings exists as an icon with an accompanied label.
|
||||
* It also may have content description for accessibility usability.
|
||||
* The style and layout of the tile may change to match a given
|
||||
* device.
|
||||
*/
|
||||
public final class Tile implements Parcelable {
|
||||
|
||||
private static final String TAG = "Tile";
|
||||
|
||||
private ComponentName mComponentName;
|
||||
private IQSService mService;
|
||||
private Icon mIcon;
|
||||
private CharSequence mLabel;
|
||||
private CharSequence mContentDescription;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public Tile(Parcel source) {
|
||||
readFromParcel(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public Tile(ComponentName componentName, IQSService service) {
|
||||
mComponentName = componentName;
|
||||
mService = service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public ComponentName getComponentName() {
|
||||
return mComponentName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current icon for the tile.
|
||||
*/
|
||||
public Icon getIcon() {
|
||||
return mIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current icon for the tile.
|
||||
*
|
||||
* This icon is expected to be white on alpha, and may be
|
||||
* tinted by the system to match it's theme.
|
||||
*
|
||||
* Does not take effect until {@link #updateTile()} is called.
|
||||
*
|
||||
* @param icon New icon to show.
|
||||
*/
|
||||
public void setIcon(Icon icon) {
|
||||
this.mIcon = icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current label for the tile.
|
||||
*/
|
||||
public CharSequence getLabel() {
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current label for the tile.
|
||||
*
|
||||
* Does not take effect until {@link #updateTile()} is called.
|
||||
*
|
||||
* @param label New label to show.
|
||||
*/
|
||||
public void setLabel(CharSequence label) {
|
||||
this.mLabel = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current content description for the tile.
|
||||
*/
|
||||
public CharSequence getContentDescription() {
|
||||
return mContentDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current content description for the tile.
|
||||
*
|
||||
* Does not take effect until {@link #updateTile()} is called.
|
||||
*
|
||||
* @param contentDescription New content description to use.
|
||||
*/
|
||||
public void setContentDescription(CharSequence contentDescription) {
|
||||
this.mContentDescription = contentDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes the state of the Tile to Quick Settings to be displayed.
|
||||
*/
|
||||
public void updateTile() {
|
||||
try {
|
||||
mService.updateQsTile(this);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Couldn't update tile");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeStrongInterface(mService);
|
||||
if (mComponentName != null) {
|
||||
dest.writeByte((byte) 1);
|
||||
mComponentName.writeToParcel(dest, flags);
|
||||
} else {
|
||||
dest.writeByte((byte) 0);
|
||||
}
|
||||
if (mIcon != null) {
|
||||
dest.writeByte((byte) 1);
|
||||
mIcon.writeToParcel(dest, flags);
|
||||
} else {
|
||||
dest.writeByte((byte) 0);
|
||||
}
|
||||
TextUtils.writeToParcel(mLabel, dest, flags);
|
||||
TextUtils.writeToParcel(mContentDescription, dest, flags);
|
||||
}
|
||||
|
||||
private void readFromParcel(Parcel source) {
|
||||
mService = IQSService.Stub.asInterface(source.readStrongBinder());
|
||||
if (source.readByte() != 0) {
|
||||
mComponentName = ComponentName.CREATOR.createFromParcel(source);
|
||||
} else {
|
||||
mComponentName = null;
|
||||
}
|
||||
if (source.readByte() != 0) {
|
||||
mIcon = Icon.CREATOR.createFromParcel(source);
|
||||
} else {
|
||||
mIcon = null;
|
||||
}
|
||||
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
|
||||
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
|
||||
}
|
||||
|
||||
public static final Creator<Tile> CREATOR = new Creator<Tile>() {
|
||||
@Override
|
||||
public Tile createFromParcel(Parcel source) {
|
||||
return new Tile(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile[] newArray(int size) {
|
||||
return new Tile[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
206
core/java/android/service/quicksettings/TileService.java
Normal file
206
core/java/android/service/quicksettings/TileService.java
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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 android.service.quicksettings;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
|
||||
/**
|
||||
* A QSTileService provides the user a tile that can be added to Quick Settings.
|
||||
* Quick Settings is a space provided that allows the user to change settings and
|
||||
* take quick actions without leaving the context of their current app.
|
||||
*
|
||||
* <p>The lifecycle of a QSTileService is different from some other services in
|
||||
* that it may be unbound during parts of its lifecycle. Any of the following
|
||||
* lifecycle events can happen indepently in a separate binding/creation of the
|
||||
* service.</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>When a tile is added by the user its QSTileService will be bound to and
|
||||
* {@link #onTileAdded()} will be called.</li>
|
||||
*
|
||||
* <li>When a tile should be up to date and listing will be indicated by
|
||||
* {@link #onStartListening()} and {@link #onStopListening()}.</li>
|
||||
*
|
||||
* <li>When the user removes a tile from Quick Settings {@link #onStopListening()}
|
||||
* will be called.</li>
|
||||
* </ul>
|
||||
* <p>QSTileService will be detected by tiles that match the {@value #ACTION_QS_TILE}
|
||||
* and require the permission "android.permission.BIND_QUICK_SETTINGS_TILE".
|
||||
* The label and icon for the service will be used as the default label and
|
||||
* icon for the tile. Here is an example QSTileService declaration.</p>
|
||||
* <pre class="prettyprint">
|
||||
* {@literal
|
||||
* <service
|
||||
* android:name=".MyQSTileService"
|
||||
* android:label="@string/my_default_tile_label"
|
||||
* android:icon="@drawable/my_default_icon_label"
|
||||
* android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||
* <intent-filter>
|
||||
* <action android:name="android.intent.action.QS_TILE" />
|
||||
* </intent-filter>
|
||||
* </service>}
|
||||
* </pre>
|
||||
*
|
||||
* @see Tile Tile for details about the UI of a Quick Settings Tile.
|
||||
*/
|
||||
public class TileService extends Service {
|
||||
|
||||
/**
|
||||
* Action that identifies a Service as being a QSTileService.
|
||||
*/
|
||||
public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
|
||||
|
||||
private final H mHandler = new H(Looper.getMainLooper());
|
||||
|
||||
private boolean mListening = false;
|
||||
private Tile mTile;
|
||||
|
||||
/**
|
||||
* Called when the user adds this tile to Quick Settings.
|
||||
* <p/>
|
||||
* Note that this is not guaranteed to be called between {@link #onCreate()}
|
||||
* and {@link #onStartListening()}, it will only be called when the tile is added
|
||||
* and not on subsequent binds.
|
||||
*/
|
||||
public void onTileAdded() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user removes this tile from Quick Settings.
|
||||
*/
|
||||
public void onTileRemoved() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this tile moves into a listening state.
|
||||
* <p/>
|
||||
* When this tile is in a listening state it is expected to keep the
|
||||
* UI up to date. Any listeners or callbacks needed to keep this tile
|
||||
* up to date should be registered here and unregistered in {@link #onStopListening()}.
|
||||
*
|
||||
* @see #getQsTile()
|
||||
* @see Tile#updateTile()
|
||||
*/
|
||||
public void onStartListening() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this tile moves out of the listening state.
|
||||
*/
|
||||
public void onStopListening() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user clicks on this tile.
|
||||
*/
|
||||
public void onClick() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Tile} for this service.
|
||||
* <p/>
|
||||
* This tile may be used to get or set the current state for this
|
||||
* tile. This tile is only valid for updates between {@link #onStartListening()}
|
||||
* and {@link #onStopListening()}.
|
||||
*/
|
||||
public final Tile getQsTile() {
|
||||
return mTile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new IQSTileService.Stub() {
|
||||
@Override
|
||||
public void setQSTile(Tile tile) throws RemoteException {
|
||||
mHandler.obtainMessage(H.MSG_SET_TILE, tile).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileRemoved() throws RemoteException {
|
||||
mHandler.sendEmptyMessage(H.MSG_TILE_REMOVED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileAdded() throws RemoteException {
|
||||
mHandler.sendEmptyMessage(H.MSG_TILE_ADDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopListening() throws RemoteException {
|
||||
mHandler.sendEmptyMessage(H.MSG_STOP_LISTENING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartListening() throws RemoteException {
|
||||
mHandler.sendEmptyMessage(H.MSG_START_LISTENING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() throws RemoteException {
|
||||
mHandler.sendEmptyMessage(H.MSG_TILE_CLICKED);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class H extends Handler {
|
||||
private static final int MSG_SET_TILE = 1;
|
||||
private static final int MSG_START_LISTENING = 2;
|
||||
private static final int MSG_STOP_LISTENING = 3;
|
||||
private static final int MSG_TILE_ADDED = 4;
|
||||
private static final int MSG_TILE_REMOVED = 5;
|
||||
private static final int MSG_TILE_CLICKED = 6;
|
||||
|
||||
public H(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_SET_TILE:
|
||||
mTile = (Tile) msg.obj;
|
||||
break;
|
||||
case MSG_TILE_ADDED:
|
||||
TileService.this.onTileRemoved();
|
||||
break;
|
||||
case MSG_TILE_REMOVED:
|
||||
TileService.this.onTileAdded();
|
||||
break;
|
||||
case MSG_START_LISTENING:
|
||||
if (mListening) {
|
||||
mListening = false;
|
||||
TileService.this.onStopListening();
|
||||
}
|
||||
break;
|
||||
case MSG_STOP_LISTENING:
|
||||
if (!mListening) {
|
||||
mListening = true;
|
||||
TileService.this.onStartListening();
|
||||
}
|
||||
break;
|
||||
case MSG_TILE_CLICKED:
|
||||
TileService.this.onClick();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1831,6 +1831,12 @@
|
||||
<permission android:name="android.permission.STATUS_BAR_SERVICE"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- Allows an application to bind to third party quick settings tiles.
|
||||
<p>Should only be requested by the System, should be required by
|
||||
QSTileService declarations.-->
|
||||
<permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- @SystemApi Allows an application to force a BACK operation on whatever is the
|
||||
top activity.
|
||||
<p>Not for use by third-party applications.
|
||||
|
||||
@@ -21,10 +21,6 @@
|
||||
<PreferenceScreen
|
||||
android:title="@string/quick_settings">
|
||||
|
||||
<Preference
|
||||
android:key="qs_tuner"
|
||||
android:title="@string/qs_rearrange" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/experimental">
|
||||
|
||||
|
||||
@@ -77,9 +77,9 @@ public class QSPanel extends FrameLayout implements Tunable {
|
||||
private Record mDetailRecord;
|
||||
private Callback mCallback;
|
||||
private BrightnessController mBrightnessController;
|
||||
private QSTileHost mHost;
|
||||
protected QSTileHost mHost;
|
||||
|
||||
private QSFooter mFooter;
|
||||
protected QSFooter mFooter;
|
||||
private boolean mGridContentVisible = true;
|
||||
|
||||
protected LinearLayout mQsContainer;
|
||||
|
||||
@@ -44,8 +44,8 @@ import java.util.Objects;
|
||||
* state update pass on tile looper.
|
||||
*/
|
||||
public abstract class QSTile<TState extends State> implements Listenable {
|
||||
protected final String TAG = "QSTile." + getClass().getSimpleName();
|
||||
protected static final boolean DEBUG = Log.isLoggable("QSTile", Log.DEBUG);
|
||||
protected final String TAG = "Tile." + getClass().getSimpleName();
|
||||
protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
|
||||
|
||||
protected final Host mHost;
|
||||
protected final Context mContext;
|
||||
@@ -332,7 +332,7 @@ public abstract class QSTile<TState extends State> implements Listenable {
|
||||
Looper getLooper();
|
||||
Context getContext();
|
||||
Collection<QSTile<?>> getTiles();
|
||||
void setCallback(Callback callback);
|
||||
void addCallback(Callback callback);
|
||||
BluetoothController getBluetoothController();
|
||||
LocationController getLocationController();
|
||||
RotationLockController getRotationLockController();
|
||||
@@ -453,9 +453,9 @@ public abstract class QSTile<TState extends State> implements Listenable {
|
||||
public static class State {
|
||||
public boolean visible;
|
||||
public Icon icon;
|
||||
public String label;
|
||||
public String contentDescription;
|
||||
public String dualLabelContentDescription;
|
||||
public CharSequence label;
|
||||
public CharSequence contentDescription;
|
||||
public CharSequence dualLabelContentDescription;
|
||||
public boolean autoMirrorDrawable = true;
|
||||
|
||||
public boolean copyTo(State other) {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.android.systemui.qs;
|
||||
|
||||
import android.os.IBinder;
|
||||
import android.service.quicksettings.IQSTileService;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
public class QSTileServiceWrapper implements IQSTileService {
|
||||
private static final String TAG = "IQSTileServiceWrapper";
|
||||
|
||||
private final IQSTileService mService;
|
||||
|
||||
public QSTileServiceWrapper(IQSTileService service) {
|
||||
mService = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder asBinder() {
|
||||
return mService.asBinder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQSTile(Tile tile) {
|
||||
try {
|
||||
mService.setQSTile(tile);
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Caught exception from QSTileService", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileAdded() {
|
||||
try {
|
||||
mService.onTileAdded();
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Caught exception from QSTileService", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileRemoved() {
|
||||
try {
|
||||
mService.onTileRemoved();
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Caught exception from QSTileService", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartListening() {
|
||||
try {
|
||||
mService.onStartListening();
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Caught exception from QSTileService", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopListening() {
|
||||
try {
|
||||
mService.onStopListening();
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Caught exception from QSTileService", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
try {
|
||||
mService.onClick();
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Caught exception from QSTileService", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.qs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
@@ -24,22 +25,16 @@ import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.RippleDrawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.MathUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ImageView.ScaleType;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.systemui.FontSizeUtils;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.qs.QSTile.AnimationIcon;
|
||||
import com.android.systemui.qs.QSTile.State;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -227,6 +222,7 @@ public class QSTileView extends QSTileBaseView {
|
||||
final ImageView icon = new ImageView(mContext);
|
||||
icon.setId(android.R.id.icon);
|
||||
icon.setScaleType(ScaleType.CENTER_INSIDE);
|
||||
icon.setImageTintList(ColorStateList.valueOf(getContext().getColor(android.R.color.white)));
|
||||
return icon;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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 com.android.internal.logging.MetricsLogger;
|
||||
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);
|
||||
state.visible = true;
|
||||
state.icon = new DrawableIcon(info.loadIcon(pm));
|
||||
state.label = info.loadLabel(pm).toString();
|
||||
state.contentDescription = state.label;
|
||||
} catch (Exception e) {
|
||||
state.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsLogger.QS_INTENT;
|
||||
}
|
||||
}
|
||||
@@ -15,16 +15,34 @@
|
||||
*/
|
||||
package com.android.systemui.qs.customize;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Service;
|
||||
import android.content.ClipData;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.service.quicksettings.IQSTileService;
|
||||
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.QSTileServiceWrapper;
|
||||
import com.android.systemui.qs.tiles.CustomTile;
|
||||
import com.android.systemui.statusbar.phone.QSTileHost;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A version of QSPanel that allows tiles to be dragged around rather than
|
||||
@@ -32,8 +50,14 @@ import com.android.systemui.statusbar.phone.QSTileHost;
|
||||
* and the saving/ordering is handled by the CustomQSTileHost.
|
||||
*/
|
||||
public class CustomQSPanel extends QSPanel {
|
||||
|
||||
private static final String TAG = "CustomQSPanel";
|
||||
|
||||
private CustomQSTileHost mCustomHost;
|
||||
private List<String> mSavedTiles;
|
||||
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);
|
||||
@@ -41,12 +65,9 @@ public class CustomQSPanel extends QSPanel {
|
||||
.inflate(R.layout.qs_customize_layout, mQsContainer, false);
|
||||
mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
|
||||
((NonPagedTileLayout) mTileLayout).setCustomQsPanel(this);
|
||||
}
|
||||
removeView(mFooter.getView());
|
||||
|
||||
@Override
|
||||
public void setHost(QSTileHost host) {
|
||||
super.setHost(host);
|
||||
mCustomHost = (CustomQSTileHost) host;
|
||||
TunerService.get(mContext).addTunable(this, QSTileHost.TILES_SETTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,17 +76,16 @@ public class CustomQSPanel extends QSPanel {
|
||||
// No Brightness for you.
|
||||
super.onTuningChanged(key, "0");
|
||||
}
|
||||
}
|
||||
|
||||
public CustomQSTileHost getCustomHost() {
|
||||
return mCustomHost;
|
||||
if (QSTileHost.TILES_SETTING.equals(key)) {
|
||||
mSavedTiles = QSTileHost.loadTileSpecs(mContext, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void tileSelected(QSTile<?> tile, ClipData currentClip) {
|
||||
String sourceSpec = getSpec(currentClip);
|
||||
String destSpec = tile.getTileSpec();
|
||||
if (!sourceSpec.equals(destSpec)) {
|
||||
mCustomHost.moveTo(sourceSpec, destSpec);
|
||||
moveTo(sourceSpec, destSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,4 +99,124 @@ public class CustomQSPanel extends QSPanel {
|
||||
public String getSpec(ClipData data) {
|
||||
return data.getItemAt(0).getText().toString();
|
||||
}
|
||||
|
||||
public void setSavedTiles() {
|
||||
setTiles(mSavedTiles);
|
||||
}
|
||||
|
||||
public void saveCurrentTiles() {
|
||||
for (int i = 0; i < mSavedTiles.size(); i++) {
|
||||
String tileSpec = mSavedTiles.get(i);
|
||||
if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
|
||||
if (!mTiles.contains(tileSpec)) {
|
||||
mContext.bindServiceAsUser(
|
||||
new Intent().setComponent(CustomTile.getComponentFromSpec(tileSpec)),
|
||||
new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
QSTileServiceWrapper wrapper = new QSTileServiceWrapper(
|
||||
IQSTileService.Stub.asInterface(service));
|
||||
wrapper.onStopListening();
|
||||
wrapper.onTileRemoved();
|
||||
mContext.unbindService(this);
|
||||
}
|
||||
}, Service.BIND_AUTO_CREATE,
|
||||
new UserHandle(ActivityManager.getCurrentUser()));
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < mTiles.size(); i++) {
|
||||
String tileSpec = mTiles.get(i);
|
||||
if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
|
||||
if (!mSavedTiles.contains(tileSpec)) {
|
||||
mContext.bindServiceAsUser(
|
||||
new Intent().setComponent(CustomTile.getComponentFromSpec(tileSpec)),
|
||||
new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
QSTileServiceWrapper wrapper = new QSTileServiceWrapper(
|
||||
IQSTileService.Stub.asInterface(service));
|
||||
wrapper.onTileAdded();
|
||||
mContext.unbindService(this);
|
||||
}
|
||||
}, Service.BIND_AUTO_CREATE,
|
||||
new UserHandle(ActivityManager.getCurrentUser()));
|
||||
}
|
||||
}
|
||||
Secure.putStringForUser(getContext().getContentResolver(), QSTileHost.TILES_SETTING,
|
||||
TextUtils.join(",", mTiles), ActivityManager.getCurrentUser());
|
||||
}
|
||||
|
||||
public void stashCurrentTiles() {
|
||||
mStash = new ArrayList<>(mTiles);
|
||||
}
|
||||
|
||||
public void unstashTiles() {
|
||||
setTiles(mStash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTiles(Collection<QSTile<?>> tiles) {
|
||||
setTilesInternal();
|
||||
}
|
||||
|
||||
private void setTilesInternal() {
|
||||
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)) {
|
||||
mCurrentTiles.add(BlankCustomTile.create(mHost, mTiles.get(i)));
|
||||
} else {
|
||||
mCurrentTiles.add(mHost.createTile(mTiles.get(i)));
|
||||
}
|
||||
mCurrentTiles.get(mCurrentTiles.size() - 1).setTileSpec(mTiles.get(i));
|
||||
}
|
||||
super.setTiles(mCurrentTiles);
|
||||
}
|
||||
|
||||
public void addTile(String spec) {
|
||||
mTiles.add(spec);
|
||||
setTilesInternal();
|
||||
}
|
||||
|
||||
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);
|
||||
setTilesInternal();
|
||||
}
|
||||
|
||||
public void remove(String spec) {
|
||||
if (!mTiles.remove(spec)) {
|
||||
Log.e(TAG, "Unknown remove spec " + spec);
|
||||
}
|
||||
setTilesInternal();
|
||||
}
|
||||
|
||||
public void setTiles(List<String> tiles) {
|
||||
mTiles = new ArrayList<>(tiles);
|
||||
setTilesInternal();
|
||||
}
|
||||
|
||||
public Collection<QSTile<?>> getTiles() {
|
||||
return mCurrentTiles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,194 +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.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;
|
||||
private ArrayList<String> mStash;
|
||||
|
||||
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.getUserInfoController(), host.getKeyguardMonitor(),
|
||||
new BlankSecurityController(), host.getBatteryController());
|
||||
}
|
||||
|
||||
@Override
|
||||
public 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 stashCurrentTiles() {
|
||||
mStash = new ArrayList<>(mTiles);
|
||||
}
|
||||
|
||||
public void unstashTiles() {
|
||||
setTiles(mStash);
|
||||
}
|
||||
|
||||
public void moveTo(String from, String to) {
|
||||
int fromIndex = mTiles.indexOf(from);
|
||||
if (fromIndex < 0) {
|
||||
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 remove(String spec) {
|
||||
if (!mTiles.remove(spec)) {
|
||||
Log.e(TAG, "Unknown remove spec " + spec);
|
||||
}
|
||||
super.onTuningChanged(TILES_SETTING, null);
|
||||
}
|
||||
|
||||
public void setTiles(List<String> tiles) {
|
||||
mTiles = new ArrayList<>(tiles);
|
||||
super.onTuningChanged(TILES_SETTING, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> loadTileSpecs(String tileList) {
|
||||
return mTiles;
|
||||
}
|
||||
|
||||
public void addTile(String spec) {
|
||||
mTiles.add(spec);
|
||||
super.onTuningChanged(TILES_SETTING, null);
|
||||
}
|
||||
|
||||
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 boolean isVpnRestricted() {
|
||||
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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,8 +83,8 @@ public class NonPagedTileLayout extends LinearLayout implements QSTileLayout, On
|
||||
record.tileView.setVisibility(View.VISIBLE);
|
||||
record.tileView.init(null, null, null);
|
||||
record.tileView.setOnTouchListener(this);
|
||||
if (mCurrentClip != null
|
||||
&& mCurrentClip.getItemAt(0).getText().toString().equals(record.tile.getTileSpec())) {
|
||||
if (mCurrentClip != null && mCurrentClip.getItemAt(0)
|
||||
.getText().toString().equals(record.tile.getTileSpec())) {
|
||||
record.tileView.setAlpha(.3f);
|
||||
mCurrentView = record.tileView;
|
||||
}
|
||||
@@ -180,7 +180,7 @@ public class NonPagedTileLayout extends LinearLayout implements QSTileLayout, On
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Stash the current tiles, in case the drop is on info, that we can restore
|
||||
// the previous state.
|
||||
mPanel.getCustomHost().stashCurrentTiles();
|
||||
mPanel.stashCurrentTiles();
|
||||
mCurrentView = v;
|
||||
mCurrentClip = mPanel.getClip((QSTile<?>) v.getTag());
|
||||
View.DragShadowBuilder shadow = new View.DragShadowBuilder(v);
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.content.DialogInterface.OnDismissListener;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.DragEvent;
|
||||
@@ -71,11 +72,11 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
private CustomQSPanel mQsPanel;
|
||||
|
||||
private boolean isShown;
|
||||
private CustomQSTileHost mHost;
|
||||
private DropButton mInfoButton;
|
||||
private DropButton mRemoveButton;
|
||||
private FloatingActionButton mFab;
|
||||
private SystemUIDialog mDialog;
|
||||
private QSTileHost mHost;
|
||||
|
||||
public QSCustomizer(Context context, AttributeSet attrs) {
|
||||
super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
|
||||
@@ -85,11 +86,11 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
}
|
||||
|
||||
public void setHost(QSTileHost host) {
|
||||
mHost = new CustomQSTileHost(mContext, host);
|
||||
mHost.setCallback(this);
|
||||
mHost = host;
|
||||
mHost.addCallback(this);
|
||||
mQsPanel.setTiles(mHost.getTiles());
|
||||
mQsPanel.setHost(mHost);
|
||||
mHost.setSavedTiles();
|
||||
mQsPanel.setSavedTiles();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,7 +130,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
|
||||
public void show(int x, int y) {
|
||||
isShown = true;
|
||||
mHost.setSavedTiles();
|
||||
mQsPanel.setSavedTiles();
|
||||
mPhoneStatusBar.getStatusBarWindow().addView(this);
|
||||
mQsPanel.setListening(true);
|
||||
mClipper.animateCircularClip(x, y, true, this);
|
||||
@@ -150,7 +151,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
for (String tile : QSPagingSwitch.QS_PAGE_TILES.split(",")) {
|
||||
tiles.add(tile);
|
||||
}
|
||||
mHost.setTiles(tiles);
|
||||
mQsPanel.setTiles(tiles);
|
||||
}
|
||||
|
||||
private void setDragging(boolean dragging) {
|
||||
@@ -158,7 +159,8 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
}
|
||||
|
||||
private void save() {
|
||||
mHost.saveCurrentTiles();
|
||||
Log.d("CustomQSPanel", "Save!");
|
||||
mQsPanel.saveCurrentTiles();
|
||||
// TODO: At save button.
|
||||
hide(0, 0);
|
||||
}
|
||||
@@ -167,6 +169,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENU_SAVE:
|
||||
Log.d("CustomQSPanel", "Save...");
|
||||
save();
|
||||
break;
|
||||
case MENU_RESET:
|
||||
@@ -179,7 +182,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
@Override
|
||||
public void onTileSelected(String spec) {
|
||||
if (mDialog != null) {
|
||||
mHost.addTile(spec);
|
||||
mQsPanel.addTile(spec);
|
||||
mDialog.dismiss();
|
||||
}
|
||||
}
|
||||
@@ -203,9 +206,9 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
|
||||
public void onDrop(View v, ClipData data) {
|
||||
if (v == mRemoveButton) {
|
||||
mHost.remove(mQsPanel.getSpec(data));
|
||||
mQsPanel.remove(mQsPanel.getSpec(data));
|
||||
} else if (v == mInfoButton) {
|
||||
mHost.unstashTiles();
|
||||
mQsPanel.unstashTiles();
|
||||
SystemUIDialog dialog = new SystemUIDialog(mContext);
|
||||
dialog.setTitle(mQsPanel.getSpec(data));
|
||||
dialog.setPositiveButton(R.string.ok, null);
|
||||
@@ -220,7 +223,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
|
||||
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, mHost.getTiles(), mHost);
|
||||
TileAdapter adapter = new TileAdapter(mContext, mQsPanel.getTiles(), mHost);
|
||||
adapter.setListener(this);
|
||||
listView.setDivider(null);
|
||||
listView.setDividerHeight(0);
|
||||
|
||||
@@ -27,6 +27,7 @@ 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.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -82,8 +83,10 @@ public class TileAdapter extends BaseAdapter {
|
||||
continue;
|
||||
}
|
||||
if (tileSpecs.contains(spec)) {
|
||||
Log.d(TAG, "Skipping " + spec);
|
||||
continue;
|
||||
}
|
||||
Log.d(TAG, "Trying " + spec);
|
||||
final QSTile<?> tile = host.createTile(spec);
|
||||
// Bad, bad, very bad.
|
||||
tile.setListening(true);
|
||||
@@ -156,7 +159,7 @@ public class TileAdapter extends BaseAdapter {
|
||||
Log.d(TAG, "Added " + mLabel);
|
||||
}
|
||||
|
||||
private void addTile(String spec, Drawable icon, String label) {
|
||||
private void addTile(String spec, Drawable icon, CharSequence label) {
|
||||
TileInfo info = new TileInfo();
|
||||
info.label = label;
|
||||
info.drawable = icon;
|
||||
@@ -164,7 +167,7 @@ public class TileAdapter extends BaseAdapter {
|
||||
mTiles.add(info);
|
||||
}
|
||||
|
||||
private void addTile(String spec, Icon icon, String label, Context context) {
|
||||
private void addTile(String spec, Icon icon, CharSequence label, Context context) {
|
||||
addTile(spec, icon.getDrawable(context), label);
|
||||
}
|
||||
|
||||
@@ -208,19 +211,17 @@ public class TileAdapter extends BaseAdapter {
|
||||
private static class TileInfo {
|
||||
private String spec;
|
||||
private Drawable drawable;
|
||||
private String label;
|
||||
private CharSequence label;
|
||||
}
|
||||
|
||||
private class QueryTilesTask extends AsyncTask<Void, Void, Collection<TileGroup>> {
|
||||
// TODO: Become non-prototype and an API.
|
||||
private static final String TILE_ACTION = "android.intent.action.QS_TILE";
|
||||
|
||||
@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(TILE_ACTION), 0);
|
||||
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);
|
||||
|
||||
@@ -133,7 +133,7 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> {
|
||||
R.string.accessibility_quick_settings_bluetooth_off);
|
||||
}
|
||||
|
||||
String bluetoothName = state.label;
|
||||
CharSequence bluetoothName = state.label;
|
||||
if (connected) {
|
||||
bluetoothName = state.dualLabelContentDescription = mContext.getString(
|
||||
R.string.accessibility_bluetooth_name, state.label);
|
||||
|
||||
@@ -16,36 +16,93 @@
|
||||
|
||||
package com.android.systemui.qs.tiles;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.IBinder;
|
||||
import android.os.UserHandle;
|
||||
import android.service.quicksettings.IQSTileService;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.systemui.qs.QSTile;
|
||||
import com.android.systemui.qs.QSTileServiceWrapper;
|
||||
import com.android.systemui.statusbar.phone.QSTileHost;
|
||||
|
||||
public class CustomTile extends QSTile<QSTile.State> {
|
||||
public static final String PREFIX = "custom(";
|
||||
|
||||
private final ComponentName mComponent;
|
||||
// We don't want to thrash binding and unbinding if the user opens and closes the panel a lot.
|
||||
// So instead we have a period of waiting.
|
||||
private static final long UNBIND_DELAY = 30000;
|
||||
|
||||
private CustomTile(Host host, String action) {
|
||||
private final ComponentName mComponent;
|
||||
private final Tile mTile;
|
||||
|
||||
private QSTileServiceWrapper mService;
|
||||
private boolean mListening;
|
||||
private boolean mBound;
|
||||
|
||||
private CustomTile(QSTileHost host, String action) {
|
||||
super(host);
|
||||
mComponent = ComponentName.unflattenFromString(action);
|
||||
mTile = new Tile(mComponent, host);
|
||||
try {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
ServiceInfo info = pm.getServiceInfo(mComponent, 0);
|
||||
mTile.setIcon(android.graphics.drawable.Icon
|
||||
.createWithResource(mComponent.getPackageName(), info.icon));
|
||||
mTile.setLabel(info.loadLabel(pm));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
public static QSTile<?> create(Host host, String spec) {
|
||||
if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
|
||||
throw new IllegalArgumentException("Bad intent tile spec: " + spec);
|
||||
}
|
||||
final String action = spec.substring(PREFIX.length(), spec.length() - 1);
|
||||
if (action.isEmpty()) {
|
||||
throw new IllegalArgumentException("Empty intent tile spec action");
|
||||
}
|
||||
return new CustomTile(host, action);
|
||||
public ComponentName getComponent() {
|
||||
return mComponent;
|
||||
}
|
||||
|
||||
public Tile getQsTile() {
|
||||
return mTile;
|
||||
}
|
||||
|
||||
public void updateState(Tile tile) {
|
||||
Log.d("TileService", "Setting state " + tile.getLabel());
|
||||
mTile.setIcon(tile.getIcon());
|
||||
mTile.setLabel(tile.getLabel());
|
||||
mTile.setContentDescription(tile.getContentDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListening(boolean listening) {
|
||||
if (mListening == listening) return;
|
||||
mListening = listening;
|
||||
if (listening) {
|
||||
mHandler.removeCallbacks(mUnbind);
|
||||
if (!mBound) {
|
||||
// TODO: Guarantee re-bind on user-switch.
|
||||
mContext.bindServiceAsUser(new Intent().setComponent(mComponent),
|
||||
mServiceConnection, Service.BIND_AUTO_CREATE,
|
||||
new UserHandle(ActivityManager.getCurrentUser()));
|
||||
mBound = true;
|
||||
}
|
||||
} else {
|
||||
if (mService!= null) {
|
||||
mService.onStopListening();
|
||||
}
|
||||
mHandler.postDelayed(mUnbind, UNBIND_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleDestroy() {
|
||||
super.handleDestroy();
|
||||
mHandler.removeCallbacks(mUnbind);
|
||||
mUnbind.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,6 +117,11 @@ public class CustomTile extends QSTile<QSTile.State> {
|
||||
|
||||
@Override
|
||||
protected void handleClick() {
|
||||
if (mService != null) {
|
||||
mService.onClick();
|
||||
} else {
|
||||
Log.e(TAG, "Click with no service " + getTileSpec());
|
||||
}
|
||||
MetricsLogger.action(mContext, getMetricsCategory(), mComponent.getPackageName());
|
||||
}
|
||||
|
||||
@@ -69,16 +131,13 @@ public class CustomTile extends QSTile<QSTile.State> {
|
||||
|
||||
@Override
|
||||
protected void handleUpdateState(State state, Object arg) {
|
||||
// TODO: Actual things.
|
||||
try {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
ServiceInfo info = pm.getServiceInfo(mComponent, 0);
|
||||
state.visible = true;
|
||||
state.icon = new DrawableIcon(info.loadIcon(pm));
|
||||
state.label = info.loadLabel(pm).toString();
|
||||
state.visible = true;
|
||||
state.icon = new DrawableIcon(mTile.getIcon().loadDrawable(mContext));
|
||||
state.label = mTile.getLabel();
|
||||
if (mTile.getContentDescription() != null) {
|
||||
state.contentDescription = mTile.getContentDescription();
|
||||
} else {
|
||||
state.contentDescription = state.label;
|
||||
} catch (Exception e) {
|
||||
state.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,4 +145,48 @@ public class CustomTile extends QSTile<QSTile.State> {
|
||||
public int getMetricsCategory() {
|
||||
return MetricsLogger.QS_INTENT;
|
||||
}
|
||||
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
mService = new QSTileServiceWrapper(IQSTileService.Stub.asInterface(service));
|
||||
if (mListening) {
|
||||
mService.setQSTile(mTile);
|
||||
mService.onStartListening();
|
||||
} else {
|
||||
mService.onStopListening();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mUnbind = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mContext.unbindService(mServiceConnection);
|
||||
mBound = false;
|
||||
}
|
||||
};
|
||||
|
||||
public static ComponentName getComponentFromSpec(String spec) {
|
||||
final String action = spec.substring(PREFIX.length(), spec.length() - 1);
|
||||
if (action.isEmpty()) {
|
||||
throw new IllegalArgumentException("Empty custom tile spec action");
|
||||
}
|
||||
return ComponentName.unflattenFromString(action);
|
||||
}
|
||||
|
||||
public static QSTile<?> create(QSTileHost 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 CustomTile(host, action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> {
|
||||
state.contentDescription = mContext.getString(
|
||||
R.string.accessibility_quick_settings_wifi,
|
||||
signalContentDescription);
|
||||
String wifiName = state.label;
|
||||
CharSequence wifiName = state.label;
|
||||
if (state.connected) {
|
||||
wifiName = r.getString(R.string.accessibility_wifi_name, state.label);
|
||||
}
|
||||
|
||||
@@ -927,7 +927,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
|
||||
mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
|
||||
mHeader.setQSPanel(mQSPanel);
|
||||
qsh.setCallback(new QSTileHost.Callback() {
|
||||
qsh.addCallback(new QSTileHost.Callback() {
|
||||
@Override
|
||||
public void onTilesChanged() {
|
||||
mQSPanel.setTiles(qsh.getTiles());
|
||||
|
||||
@@ -17,18 +17,56 @@
|
||||
package com.android.systemui.statusbar.phone;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Binder;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.service.quicksettings.IQSService;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.qs.QSTile;
|
||||
import com.android.systemui.qs.tiles.*;
|
||||
import com.android.systemui.statusbar.policy.*;
|
||||
import com.android.systemui.qs.tiles.AirplaneModeTile;
|
||||
import com.android.systemui.qs.tiles.BatteryTile;
|
||||
import com.android.systemui.qs.tiles.BluetoothTile;
|
||||
import com.android.systemui.qs.tiles.CastTile;
|
||||
import com.android.systemui.qs.tiles.CellularTile;
|
||||
import com.android.systemui.qs.tiles.ColorInversionTile;
|
||||
import com.android.systemui.qs.tiles.CustomTile;
|
||||
import com.android.systemui.qs.tiles.DndTile;
|
||||
import com.android.systemui.qs.tiles.FlashlightTile;
|
||||
import com.android.systemui.qs.tiles.HotspotTile;
|
||||
import com.android.systemui.qs.tiles.IntentTile;
|
||||
import com.android.systemui.qs.tiles.LocationTile;
|
||||
import com.android.systemui.qs.tiles.QAirplaneTile;
|
||||
import com.android.systemui.qs.tiles.QBluetoothTile;
|
||||
import com.android.systemui.qs.tiles.QFlashlightTile;
|
||||
import com.android.systemui.qs.tiles.QLockTile;
|
||||
import com.android.systemui.qs.tiles.QRotationLockTile;
|
||||
import com.android.systemui.qs.tiles.QWifiTile;
|
||||
import com.android.systemui.qs.tiles.RotationLockTile;
|
||||
import com.android.systemui.qs.tiles.UserTile;
|
||||
import com.android.systemui.qs.tiles.WifiTile;
|
||||
import com.android.systemui.statusbar.policy.BatteryController;
|
||||
import com.android.systemui.statusbar.policy.BluetoothController;
|
||||
import com.android.systemui.statusbar.policy.CastController;
|
||||
import com.android.systemui.statusbar.policy.FlashlightController;
|
||||
import com.android.systemui.statusbar.policy.HotspotController;
|
||||
import com.android.systemui.statusbar.policy.KeyguardMonitor;
|
||||
import com.android.systemui.statusbar.policy.LocationController;
|
||||
import com.android.systemui.statusbar.policy.NetworkController;
|
||||
import com.android.systemui.statusbar.policy.RotationLockController;
|
||||
import com.android.systemui.statusbar.policy.SecurityController;
|
||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
import com.android.systemui.statusbar.policy.ZenModeController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
import com.android.systemui.tuner.TunerService.Tunable;
|
||||
|
||||
@@ -40,7 +78,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Platform implementation of the quick settings tile host **/
|
||||
public class QSTileHost implements QSTile.Host, Tunable {
|
||||
public final class QSTileHost extends IQSService.Stub implements QSTile.Host, Tunable {
|
||||
private static final String TAG = "QSTileHost";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
@@ -65,7 +103,7 @@ public class QSTileHost implements QSTile.Host, Tunable {
|
||||
private final SecurityController mSecurity;
|
||||
private final BatteryController mBattery;
|
||||
|
||||
private Callback mCallback;
|
||||
private final List<Callback> mCallbacks = new ArrayList<>();
|
||||
|
||||
public QSTileHost(Context context, PhoneStatusBar statusBar,
|
||||
BluetoothController bluetooth, LocationController location,
|
||||
@@ -107,8 +145,8 @@ public class QSTileHost implements QSTile.Host, Tunable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCallback(Callback callback) {
|
||||
mCallback = callback;
|
||||
public void addCallback(Callback callback) {
|
||||
mCallbacks.add(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -209,14 +247,14 @@ public class QSTileHost implements QSTile.Host, Tunable {
|
||||
public SecurityController getSecurityController() {
|
||||
return mSecurity;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onTuningChanged(String key, String newValue) {
|
||||
if (!TILES_SETTING.equals(key)) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "Recreating tiles");
|
||||
final List<String> tileSpecs = loadTileSpecs(newValue);
|
||||
final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
|
||||
if (tileSpecs.equals(mTileSpecs)) return;
|
||||
for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) {
|
||||
if (!tileSpecs.contains(tile.getKey())) {
|
||||
@@ -227,11 +265,15 @@ public class QSTileHost implements QSTile.Host, Tunable {
|
||||
final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>();
|
||||
for (String tileSpec : tileSpecs) {
|
||||
if (mTiles.containsKey(tileSpec)) {
|
||||
newTiles.put(tileSpec, mTiles.get(tileSpec));
|
||||
QSTile<?> tile = mTiles.get(tileSpec);
|
||||
if (DEBUG) Log.d(TAG, "Adding " + tile);
|
||||
newTiles.put(tileSpec, tile);
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
|
||||
try {
|
||||
newTiles.put(tileSpec, createTile(tileSpec));
|
||||
QSTile<?> tile = createTile(tileSpec);
|
||||
tile.setTileSpec(tileSpec);
|
||||
newTiles.put(tileSpec, tile);
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
|
||||
}
|
||||
@@ -241,11 +283,46 @@ public class QSTileHost implements QSTile.Host, Tunable {
|
||||
mTileSpecs.addAll(tileSpecs);
|
||||
mTiles.clear();
|
||||
mTiles.putAll(newTiles);
|
||||
if (mCallback != null) {
|
||||
mCallback.onTilesChanged();
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
mCallbacks.get(i).onTilesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateQsTile(Tile tile) throws RemoteException {
|
||||
verifyCaller(tile.getComponentName().getPackageName());
|
||||
CustomTile customTile = getTileForComponent(tile.getComponentName());
|
||||
if (customTile != null) {
|
||||
Log.d("TileService", "Got tile update for " + tile.getComponentName());
|
||||
customTile.updateState(tile);
|
||||
customTile.refreshState();
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyCaller(String packageName) {
|
||||
try {
|
||||
int uid = mContext.getPackageManager().getPackageUid(packageName,
|
||||
Binder.getCallingUserHandle().getIdentifier());
|
||||
if (Binder.getCallingUid() != uid) {
|
||||
throw new SecurityException("Component outside caller's uid");
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private CustomTile getTileForComponent(ComponentName component) {
|
||||
// TODO: Build map for easier lookup.
|
||||
for (QSTile<?> qsTile : mTiles.values()) {
|
||||
if (qsTile instanceof CustomTile) {
|
||||
if (((CustomTile) qsTile).getComponent().equals(component)) {
|
||||
return (CustomTile) qsTile;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public QSTile<?> createTile(String tileSpec) {
|
||||
if (tileSpec.equals("wifi")) return new WifiTile(this, false);
|
||||
else if (tileSpec.equals("bt")) return new BluetoothTile(this, false);
|
||||
@@ -276,8 +353,8 @@ public class QSTileHost implements QSTile.Host, Tunable {
|
||||
else throw new IllegalArgumentException("Bad tile spec: " + tileSpec);
|
||||
}
|
||||
|
||||
protected List<String> loadTileSpecs(String tileList) {
|
||||
final Resources res = mContext.getResources();
|
||||
public static List<String> loadTileSpecs(Context context, String tileList) {
|
||||
final Resources res = context.getResources();
|
||||
final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
|
||||
if (tileList == null) {
|
||||
tileList = res.getString(R.string.quick_settings_tiles);
|
||||
|
||||
@@ -192,7 +192,7 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements
|
||||
host.getBatteryController());
|
||||
mHeaderQsPanel.setHost(myHost);
|
||||
mHeaderQsPanel.setTiles(myHost.getTiles());
|
||||
myHost.setCallback(new QSTile.Host.Callback() {
|
||||
myHost.addCallback(new QSTile.Host.Callback() {
|
||||
@Override
|
||||
public void onTilesChanged() {
|
||||
mHeaderQsPanel.setTiles(myHost.getTiles());
|
||||
|
||||
@@ -1,547 +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.tuner;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.DragEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnDragListener;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.qs.QSPanel;
|
||||
import com.android.systemui.qs.QSTile;
|
||||
import com.android.systemui.qs.QSTile.Host.Callback;
|
||||
import com.android.systemui.qs.QSTile.ResourceIcon;
|
||||
import com.android.systemui.qs.QSTileBaseView;
|
||||
import com.android.systemui.qs.QSTileView;
|
||||
import com.android.systemui.qs.tiles.IntentTile;
|
||||
import com.android.systemui.statusbar.phone.QSTileHost;
|
||||
import com.android.systemui.statusbar.policy.SecurityController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class QsTuner extends Fragment implements Callback {
|
||||
|
||||
private static final String TAG = "QsTuner";
|
||||
|
||||
private static final int MENU_RESET = Menu.FIRST;
|
||||
|
||||
private DraggableQsPanel mQsPanel;
|
||||
private CustomHost mTileHost;
|
||||
|
||||
private FrameLayout mDropTarget;
|
||||
|
||||
private ScrollView mScrollRoot;
|
||||
|
||||
private FrameLayout mAddTarget;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
menu.add(0, MENU_RESET, 0, com.android.internal.R.string.reset);
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
MetricsLogger.visibility(getContext(), MetricsLogger.TUNER_QS, true);
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
MetricsLogger.visibility(getContext(), MetricsLogger.TUNER_QS, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENU_RESET:
|
||||
mTileHost.reset();
|
||||
break;
|
||||
case android.R.id.home:
|
||||
getFragmentManager().popBackStack();
|
||||
break;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
mScrollRoot = (ScrollView) inflater.inflate(R.layout.tuner_qs, container, false);
|
||||
|
||||
mQsPanel = new DraggableQsPanel(getContext());
|
||||
mTileHost = new CustomHost(getContext());
|
||||
mTileHost.setCallback(this);
|
||||
mQsPanel.setTiles(mTileHost.getTiles());
|
||||
mQsPanel.setHost(mTileHost);
|
||||
mQsPanel.refreshAllTiles();
|
||||
((ViewGroup) mScrollRoot.findViewById(R.id.all_details)).addView(mQsPanel, 0);
|
||||
|
||||
mDropTarget = (FrameLayout) mScrollRoot.findViewById(R.id.remove_target);
|
||||
setupDropTarget();
|
||||
mAddTarget = (FrameLayout) mScrollRoot.findViewById(R.id.add_target);
|
||||
setupAddTarget();
|
||||
return mScrollRoot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mTileHost.destroy();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
private void setupDropTarget() {
|
||||
QSTileView tileView = new QSTileView(getContext());
|
||||
QSTile.State state = new QSTile.State();
|
||||
state.visible = true;
|
||||
state.icon = ResourceIcon.get(R.drawable.ic_delete);
|
||||
state.label = getString(com.android.internal.R.string.delete);
|
||||
tileView.onStateChanged(state);
|
||||
mDropTarget.addView(tileView);
|
||||
mDropTarget.setVisibility(View.GONE);
|
||||
new DragHelper(tileView, new DropListener() {
|
||||
@Override
|
||||
public void onDrop(String sourceText) {
|
||||
mTileHost.remove(sourceText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupAddTarget() {
|
||||
QSTileView tileView = new QSTileView(getContext());
|
||||
QSTile.State state = new QSTile.State();
|
||||
state.visible = true;
|
||||
state.icon = ResourceIcon.get(R.drawable.ic_add_circle_qs);
|
||||
state.label = getString(R.string.add_tile);
|
||||
tileView.onStateChanged(state);
|
||||
mAddTarget.addView(tileView);
|
||||
tileView.setClickable(true);
|
||||
tileView.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mTileHost.showAddDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onStartDrag() {
|
||||
mDropTarget.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDropTarget.setVisibility(View.VISIBLE);
|
||||
mAddTarget.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void stopDrag() {
|
||||
mDropTarget.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDropTarget.setVisibility(View.GONE);
|
||||
mAddTarget.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTilesChanged() {
|
||||
mQsPanel.setTiles(mTileHost.getTiles());
|
||||
}
|
||||
|
||||
private static int getLabelResource(String spec) {
|
||||
if (spec.equals("wifi")) return R.string.quick_settings_wifi_label;
|
||||
else if (spec.equals("bt")) return R.string.quick_settings_bluetooth_label;
|
||||
else if (spec.equals("inversion")) return R.string.quick_settings_inversion_label;
|
||||
else if (spec.equals("cell")) return R.string.quick_settings_cellular_detail_title;
|
||||
else if (spec.equals("airplane")) return R.string.airplane_mode;
|
||||
else if (spec.equals("dnd")) return R.string.quick_settings_dnd_label;
|
||||
else if (spec.equals("rotation")) return R.string.quick_settings_rotation_locked_label;
|
||||
else if (spec.equals("flashlight")) return R.string.quick_settings_flashlight_label;
|
||||
else if (spec.equals("location")) return R.string.quick_settings_location_label;
|
||||
else if (spec.equals("cast")) return R.string.quick_settings_cast_title;
|
||||
else if (spec.equals("hotspot")) return R.string.quick_settings_hotspot_label;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static class CustomHost extends QSTileHost {
|
||||
|
||||
public CustomHost(Context context) {
|
||||
super(context, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, new BlankSecurityController(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QSTile<?> createTile(String tileSpec) {
|
||||
return new DraggableTile(this, tileSpec);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void remove(String tile) {
|
||||
MetricsLogger.action(getContext(), MetricsLogger.TUNER_QS_REMOVE, tile);
|
||||
List<String> tiles = new ArrayList<>(mTileSpecs);
|
||||
tiles.remove(tile);
|
||||
setTiles(tiles);
|
||||
}
|
||||
|
||||
public void add(String tile) {
|
||||
MetricsLogger.action(getContext(), MetricsLogger.TUNER_QS_ADD, tile);
|
||||
List<String> tiles = new ArrayList<>(mTileSpecs);
|
||||
tiles.add(tile);
|
||||
setTiles(tiles);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
Secure.putStringForUser(getContext().getContentResolver(),
|
||||
TILES_SETTING, "default", ActivityManager.getCurrentUser());
|
||||
}
|
||||
|
||||
private void setTiles(List<String> tiles) {
|
||||
Secure.putStringForUser(getContext().getContentResolver(), TILES_SETTING,
|
||||
TextUtils.join(",", tiles), ActivityManager.getCurrentUser());
|
||||
}
|
||||
|
||||
public void showAddDialog() {
|
||||
List<String> tiles = mTileSpecs;
|
||||
int numBroadcast = 0;
|
||||
for (int i = 0; i < tiles.size(); i++) {
|
||||
if (tiles.get(i).startsWith(IntentTile.PREFIX)) {
|
||||
numBroadcast++;
|
||||
}
|
||||
}
|
||||
String[] defaults =
|
||||
getContext().getString(R.string.quick_settings_tiles_default).split(",");
|
||||
final String[] available = new String[defaults.length + 1
|
||||
- (tiles.size() - numBroadcast)];
|
||||
final String[] availableTiles = new String[available.length];
|
||||
int index = 0;
|
||||
for (int i = 0; i < defaults.length; i++) {
|
||||
if (tiles.contains(defaults[i])) {
|
||||
continue;
|
||||
}
|
||||
int resource = getLabelResource(defaults[i]);
|
||||
if (resource != 0) {
|
||||
availableTiles[index] = defaults[i];
|
||||
available[index++] = getContext().getString(resource);
|
||||
} else {
|
||||
availableTiles[index] = defaults[i];
|
||||
available[index++] = defaults[i];
|
||||
}
|
||||
}
|
||||
available[index++] = getContext().getString(R.string.broadcast_tile);
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.add_tile)
|
||||
.setItems(available, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which < available.length - 1) {
|
||||
add(availableTiles[which]);
|
||||
} else {
|
||||
showBroadcastTileDialog();
|
||||
}
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
public void showBroadcastTileDialog() {
|
||||
final EditText editText = new EditText(getContext());
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.broadcast_tile)
|
||||
.setView(editText)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
String action = editText.getText().toString();
|
||||
if (isValid(action)) {
|
||||
add(IntentTile.PREFIX + action + ')');
|
||||
}
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private boolean isValid(String action) {
|
||||
for (int i = 0; i < action.length(); i++) {
|
||||
char c = action.charAt(i);
|
||||
if (!Character.isAlphabetic(c) && !Character.isDigit(c) && c != '.') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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 boolean isVpnRestricted() {
|
||||
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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class DraggableTile extends QSTile<QSTile.State>
|
||||
implements DropListener {
|
||||
private String mSpec;
|
||||
private QSTileBaseView mView;
|
||||
|
||||
protected DraggableTile(QSTile.Host host, String tileSpec) {
|
||||
super(host);
|
||||
Log.d(TAG, "Creating tile " + tileSpec);
|
||||
mSpec = tileSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QSTileBaseView createTileView(Context context) {
|
||||
mView = super.createTileView(context);
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTileType() {
|
||||
return "wifi".equals(mSpec) || "bt".equals(mSpec) ? QSTileView.QS_TYPE_DUAL
|
||||
: QSTileView.QS_TYPE_NORMAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListening(boolean listening) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QSTile.State newTileState() {
|
||||
return new QSTile.State();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUpdateState(QSTile.State state, Object arg) {
|
||||
state.visible = true;
|
||||
state.icon = ResourceIcon.get(getIcon());
|
||||
state.label = getLabel();
|
||||
}
|
||||
|
||||
private String getLabel() {
|
||||
int resource = getLabelResource(mSpec);
|
||||
if (resource != 0) {
|
||||
return mContext.getString(resource);
|
||||
}
|
||||
if (mSpec.startsWith(IntentTile.PREFIX)) {
|
||||
int lastDot = mSpec.lastIndexOf('.');
|
||||
if (lastDot >= 0) {
|
||||
return mSpec.substring(lastDot + 1, mSpec.length() - 1);
|
||||
} else {
|
||||
return mSpec.substring(IntentTile.PREFIX.length(), mSpec.length() - 1);
|
||||
}
|
||||
}
|
||||
return mSpec;
|
||||
}
|
||||
|
||||
private int getIcon() {
|
||||
if (mSpec.equals("wifi")) return R.drawable.ic_qs_wifi_full_3;
|
||||
else if (mSpec.equals("bt")) return R.drawable.ic_qs_bluetooth_connected;
|
||||
else if (mSpec.equals("inversion")) return R.drawable.ic_invert_colors_enable;
|
||||
else if (mSpec.equals("cell")) return R.drawable.ic_qs_signal_full_3;
|
||||
else if (mSpec.equals("airplane")) return R.drawable.ic_signal_airplane_enable;
|
||||
else if (mSpec.equals("dnd")) return R.drawable.ic_qs_dnd_on;
|
||||
else if (mSpec.equals("rotation")) return R.drawable.ic_portrait_from_auto_rotate;
|
||||
else if (mSpec.equals("flashlight")) return R.drawable.ic_signal_flashlight_enable;
|
||||
else if (mSpec.equals("location")) return R.drawable.ic_signal_location_enable;
|
||||
else if (mSpec.equals("cast")) return R.drawable.ic_qs_cast_on;
|
||||
else if (mSpec.equals("hotspot")) return R.drawable.ic_hotspot_enable;
|
||||
return R.drawable.android;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return 20000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof DraggableTile) {
|
||||
return mSpec.equals(((DraggableTile) o).mSpec);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrop(String sourceText) {
|
||||
((CustomHost) mHost).replace(mSpec, sourceText);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class DragHelper implements OnDragListener {
|
||||
|
||||
private final View mView;
|
||||
private final DropListener mListener;
|
||||
|
||||
public DragHelper(View view, DropListener dropListener) {
|
||||
mView = view;
|
||||
mListener = dropListener;
|
||||
mView.setOnDragListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDrag(View v, DragEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case DragEvent.ACTION_DRAG_ENTERED:
|
||||
mView.setBackgroundColor(0x77ffffff);
|
||||
break;
|
||||
case DragEvent.ACTION_DRAG_ENDED:
|
||||
stopDrag();
|
||||
case DragEvent.ACTION_DRAG_EXITED:
|
||||
mView.setBackgroundColor(0x0);
|
||||
break;
|
||||
case DragEvent.ACTION_DROP:
|
||||
stopDrag();
|
||||
String text = event.getClipData().getItemAt(0).getText().toString();
|
||||
mListener.onDrop(text);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface DropListener {
|
||||
void onDrop(String sourceText);
|
||||
}
|
||||
|
||||
private class DraggableQsPanel extends QSPanel implements OnTouchListener {
|
||||
public DraggableQsPanel(Context context) {
|
||||
super(context);
|
||||
mBrightnessView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
for (TileRecord r : mRecords) {
|
||||
new DragHelper(r.tileView, (DraggableTile) r.tile);
|
||||
r.tileView.setTag(r.tile);
|
||||
r.tileView.setOnTouchListener(this);
|
||||
|
||||
for (int i = 0; i < r.tileView.getChildCount(); i++) {
|
||||
r.tileView.getChildAt(i).setClickable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
String tileSpec = (String) ((DraggableTile) v.getTag()).mSpec;
|
||||
ClipData data = ClipData.newPlainText(tileSpec, tileSpec);
|
||||
v.startDrag(data, new View.DragShadowBuilder(v), null, 0);
|
||||
onStartDrag();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,8 +59,6 @@ public class TunerFragment extends PreferenceFragment {
|
||||
|
||||
private SwitchPreference mBatteryPct;
|
||||
|
||||
private Preference mQsTuner;
|
||||
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
@@ -68,17 +66,6 @@ public class TunerFragment extends PreferenceFragment {
|
||||
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
mQsTuner = findPreference(KEY_QS_TUNER);
|
||||
mQsTuner.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
FragmentTransaction ft = getFragmentManager().beginTransaction();
|
||||
ft.replace(android.R.id.content, new QsTuner(), "QsTuner");
|
||||
ft.addToBackStack(null);
|
||||
ft.commit();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
findPreference(KEY_DEMO_MODE).setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
@@ -96,13 +83,6 @@ public class TunerFragment extends PreferenceFragment {
|
||||
new TunerWarningFragment().show(getFragmentManager(), WARNING_TAG);
|
||||
}
|
||||
}
|
||||
TunerService.get(getContext()).addTunable(mQsPaging, QSPanel.QS_THE_NEW_QS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
TunerService.get(getContext()).removeTunable(mQsPaging);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -175,14 +155,6 @@ public class TunerFragment extends PreferenceFragment {
|
||||
}
|
||||
};
|
||||
|
||||
private final Tunable mQsPaging = new Tunable() {
|
||||
@Override
|
||||
public void onTuningChanged(String key, String newValue) {
|
||||
// Only enable QS rearranging when paging is off, because its very broken.
|
||||
mQsTuner.setEnabled(newValue == null || Integer.parseInt(newValue) == 0);
|
||||
}
|
||||
};
|
||||
|
||||
public static class TunerWarningFragment extends DialogFragment {
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
Reference in New Issue
Block a user