summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/cm_strings.xml10
-rw-r--r--core/res/res/values/cm_symbols.xml6
-rw-r--r--packages/SystemUI/Android.mk1
-rw-r--r--packages/SystemUI/AndroidManifest_cm.xml3
-rw-r--r--packages/SystemUI/res/drawable/ic_dynamic_qs_adb.xml58
-rw-r--r--packages/SystemUI/res/drawable/ic_dynamic_qs_ime_selector.xml29
-rw-r--r--packages/SystemUI/res/drawable/ic_dynamic_qs_next_alarm.xml30
-rw-r--r--packages/SystemUI/res/drawable/ic_dynamic_qs_su.xml39
-rw-r--r--packages/SystemUI/res/values/cm_arrays.xml20
-rw-r--r--packages/SystemUI/res/values/cm_strings.xml7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CustomTileData.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java142
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SuController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SuControllerImpl.java43
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java147
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java88
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java115
21 files changed, 781 insertions, 47 deletions
diff --git a/core/res/res/values/cm_strings.xml b/core/res/res/values/cm_strings.xml
index ed573a8..6e2cabf 100644
--- a/core/res/res/values/cm_strings.xml
+++ b/core/res/res/values/cm_strings.xml
@@ -91,8 +91,18 @@
<!-- ADB notification message-->
<string name="adb_active_generic_notification_message">Touch to disable debugging.</string>
+ <!-- ADB custom tile -->
+ <string name="adb_active_custom_tile">ADB - <xliff:g id="adb_type" example="Usb">%1$s</xliff:g></string>
+ <string name="adb_active_custom_tile_both">Both</string>
+ <string name="adb_active_custom_tile_usb">USB</string>
+ <string name="adb_active_custom_tile_net">Network</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_accessThemeService">access theme service</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want the application to do this. -->
+ <string name="permlab_interceptPackageLaunch">intercept app launch</string>
+
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessThemeService">Allows an app to access the theme service. Should never be needed for normal apps.</string>
diff --git a/core/res/res/values/cm_symbols.xml b/core/res/res/values/cm_symbols.xml
index 293b951..bd38ffb 100644
--- a/core/res/res/values/cm_symbols.xml
+++ b/core/res/res/values/cm_symbols.xml
@@ -39,6 +39,12 @@
<java-symbol type="array" name="notification_light_package_mapping" />
<java-symbol type="array" name="config_notificationNoAlertsVibePattern" />
+ <!-- ADB custom tile -->
+ <java-symbol type="string" name="adb_active_custom_tile" />
+ <java-symbol type="string" name="adb_active_custom_tile_both" />
+ <java-symbol type="string" name="adb_active_custom_tile_usb" />
+ <java-symbol type="string" name="adb_active_custom_tile_net" />
+
<!-- Package Manager -->
<java-symbol type="array" name="config_disabledComponents" />
<java-symbol type="array" name="config_forceEnabledComponents" />
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 9ae27e9..d9c9a3a 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -7,7 +7,6 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) \
src/com/android/systemui/EventLogTags.logtags
LOCAL_STATIC_JAVA_LIBRARIES := Keyguard \
- org.cyanogenmod.platform.sdk \
org.cyanogenmod.platform.internal \
android-support-v7-palette \
android-support-v4
diff --git a/packages/SystemUI/AndroidManifest_cm.xml b/packages/SystemUI/AndroidManifest_cm.xml
index b869a0b..30dac81 100644
--- a/packages/SystemUI/AndroidManifest_cm.xml
+++ b/packages/SystemUI/AndroidManifest_cm.xml
@@ -32,6 +32,9 @@
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
+ <!-- Publish dynamic tiles -->
+ <uses-permission android:name="cyanogenmod.permission.PUBLISH_CUSTOM_TILE" />
+
<application>
<provider android:name=".cm.SpamMessageProvider"
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL"
diff --git a/packages/SystemUI/res/drawable/ic_dynamic_qs_adb.xml b/packages/SystemUI/res/drawable/ic_dynamic_qs_adb.xml
new file mode 100644
index 0000000..6b33a66
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_dynamic_qs_adb.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M 37.727707,9.975208 L 40.285946,3.7258741 C 40.541238,3.1009409
+40.243401,2.3882503 39.618464,2.1329586 38.993529,1.8776669 38.28084,2.1755027
+38.025548,2.8004407 L 35.374234,9.275815 C 32.449014,8.605674 28.718028,8.350381
+24.258396,8.350381 l 0,0.01596 0,0 0,-0.01862 c -0.582384,0 -1.148814,0.0053
+-1.707266,0.01596 l -0.0027,0 c -3.712371,0.06116 -6.863631,0.329751
+-9.405913,0.912136 L 10.491243,2.8004407 C 10.235951,2.1755075
+9.5232609,1.8776669 8.8983273,2.1329586 8.2733938,2.3882503 7.9755532,3.1009409
+8.2308452,3.7258741 L 10.789084,9.975208 c -3.3959157,1.284439
+-5.1989153,3.45708 -5.1989153,6.94607 l 0,7.31837 0,1.898735 0,2.084884 c
+0,6.411552 6.0738233,8.379428 16.9583033,8.557599 l 0,0.01064 0.67546,0 c
+0.340389,0.0027 0.686097,0.0053 1.034464,0.0053 l 0,-0.0053 0,0 0,0.0053 c
+0.348368,0 0.694075,-0.0027 1.034465,-0.0053 l 0.04786,0 c 11.280713,-0.11169
+17.585893,-2.031699 17.585893,-8.565578 l 0,-2.087543 0,-1.898735 0,-7.31837 C
+42.929314,13.429631 41.123615,11.259649 37.7277,9.975211 Z M 14.661013,27.973293
+c -3.084777,0 -5.5845131,-2.499733 -5.5845131,-5.584512 0,-3.084778
+2.4997361,-5.584511 5.5845131,-5.584511 3.084778,0 5.584512,2.499733
+5.584512,5.584511 0,3.084779 -2.499734,5.584512 -5.584512,5.584512 z m
+10.185086,4.050102 -1.178065,0 c -0.86693,0 -1.571642,-0.702054
+-1.571642,-1.571642 0,-0.03192 0.008,-0.06382 0.01064,-0.09573 l 4.300073,0 c
+0.0027,0.03192 0.01064,0.06116 0.01064,0.09573 0,0.869588 -0.704712,1.571642
+-1.571641,1.571642 z m 9.00968,-4.050102 c -3.084778,0 -5.584512,-2.499733
+-5.584512,-5.584512 0,-3.084778 2.499734,-5.584511 5.584512,-5.584511 3.084777,0
+5.584512,2.499733 5.584512,5.584511 -0.0027,3.084779 -2.502394,5.584512
+-5.584512,5.584512 z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:strokeWidth="16"
+ android:strokeLineJoin="round"
+ android:strokeLineCap="round"
+ android:pathData="M 23.2448199 33.152538 L 24.8907709 33.152538 Q 25.9999988 33.152538
+25.9999988 34.2617659 L 25.9999988 44.6534791 Q 25.9999988 45.762707 24.8907709 45.762707
+L 23.2448199 45.762707 Q 22.135592 45.762707 22.135592 44.6534791 L 22.135592 34.2617659
+Q 22.135592 33.152538 23.2448199 33.152538 Z" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_dynamic_qs_ime_selector.xml b/packages/SystemUI/res/drawable/ic_dynamic_qs_ime_selector.xml
new file mode 100644
index 0000000..07ee10a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_dynamic_qs_ime_selector.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M40 10H8c-2.21 0-3.98 1.79-3.98 4L4 34c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79
+4-4V14c0-2.21-1.79-4-4-4zm-18 6h4v4h-4v-4zm0 6h4v4h-4v-4zm-6-6h4v4h-4v-4zm0
+6h4v4h-4v-4zm-2 4h-4v-4h4v4zm0-6h-4v-4h4v4zm18
+14H16v-4h16v4zm0-8h-4v-4h4v4zm0-6h-4v-4h4v4zm6 6h-4v-4h4v4zm0-6h-4v-4h4v4z" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_dynamic_qs_next_alarm.xml b/packages/SystemUI/res/drawable/ic_dynamic_qs_next_alarm.xml
new file mode 100644
index 0000000..184d407
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_dynamic_qs_next_alarm.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M44 11.44l-9.19-7.71-2.57 3.06 9.19 7.71L44 11.44zM15.76 6.78l-2.57-3.06L4
+11.43l2.57 3.06 9.19-7.71zM25 16h-3v12l9.49 5.71L33
+31.24l-8-4.74V16zm-1.01-8C14.04 8 6 16.06 6 26s8.04 18 17.99 18S42 35.94 42 26
+33.94 8 23.99 8zM24 40c-7.73 0-14-6.27-14-14s6.27-14 14-14 14 6.27 14 14-6.26
+14-14 14z" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_dynamic_qs_su.xml b/packages/SystemUI/res/drawable/ic_dynamic_qs_su.xml
new file mode 100644
index 0000000..5d582d2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_dynamic_qs_su.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:strokeWidth="1"
+ android:pathData="M 28.79,43.78 L 22.26,43.78 31.33,3.52 37.88,3.52 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:strokeWidth="1"
+ android:pathData="M 15.46,43.78 L 8.54,43.78 17.69,3.52 24.41,3.52 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:strokeWidth="1"
+ android:pathData="M 7.53,15.86 L 43.53,15.86 43.53,20.34 7.53,20.34 Z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:strokeWidth="1"
+ android:pathData="M 4.68,28.68 L 41.69,28.68 41.69,33.15 4.68,33.15 Z" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/cm_arrays.xml b/packages/SystemUI/res/values/cm_arrays.xml
index 9bd31f0..687ab30 100644
--- a/packages/SystemUI/res/values/cm_arrays.xml
+++ b/packages/SystemUI/res/values/cm_arrays.xml
@@ -54,4 +54,24 @@
<item>@string/accessibility_quick_settings_perf_profile_changed_bias_perf</item>
<item>@string/accessibility_quick_settings_perf_profile_changed_perf</item>
</string-array>
+
+ <!-- Dynamic tiles -->
+ <string-array name="dynamic_qs_tiles_labels" translatable="false">
+ <item>@string/dynamic_qs_tile_next_alarm_label</item>
+ <item>@string/dynamic_qs_tile_ime_selector_label</item>
+ <item>@string/dynamic_qs_tile_su_label</item>
+ <item>@string/dynamic_qs_tile_adb_label</item>
+ </string-array>
+ <string-array name="dynamic_qs_tiles_icons_resources_ids" translatable="false">
+ <item>ic_dynamic_qs_next_alarm</item>
+ <item>ic_dynamic_qs_ime_selector</item>
+ <item>ic_dynamic_qs_su</item>
+ <item>ic_dynamic_qs_adb</item>
+ </string-array>
+ <string-array name="dynamic_qs_tiles_values" translatable="false">
+ <item>next_alarm</item>
+ <item>ime_selector</item>
+ <item>su</item>
+ <item>adb</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values/cm_strings.xml b/packages/SystemUI/res/values/cm_strings.xml
index 03b9a47..03fc080 100644
--- a/packages/SystemUI/res/values/cm_strings.xml
+++ b/packages/SystemUI/res/values/cm_strings.xml
@@ -153,4 +153,11 @@
<!-- Announcement made when the battery mode tile changes to quick (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_perf_profile_changed_bias_perf">Battery mode changed to quick mode.</string>
<string name="quick_settings_performance_profile_detail_title">Battery mode</string>
+
+ <!-- Dynamic tiles -->
+ <string name="quick_settings_dynamic_tile_detail_title">Dynamic tile</string>
+ <string name="dynamic_qs_tile_next_alarm_label">Next alarm</string>
+ <string name="dynamic_qs_tile_ime_selector_label">IME selector</string>
+ <string name="dynamic_qs_tile_su_label">Root</string>
+ <string name="dynamic_qs_tile_adb_label" translatable="false">ADB</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java
index f016dc0..9d76e4d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java
@@ -1477,7 +1477,10 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
super.handleShowDetailImpl(r, show, x, y);
if (show) {
final StatusBarPanelCustomTile customTile = r.detailAdapter.getCustomTile();
- mDetailRemoveButton.setVisibility(customTile != null ? VISIBLE : GONE);
+ mDetailRemoveButton.setVisibility(customTile != null &&
+ !(customTile.getPackage().equals(mContext.getPackageName())
+ || customTile.getUid() == android.os.Process.SYSTEM_UID)
+ ? VISIBLE : GONE);
mDetailRemoveButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java
index 4bc30a4..96cc51a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java
@@ -22,6 +22,7 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.ThemeConfig;
import android.net.Uri;
+import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -61,7 +62,7 @@ public class CustomQSTile extends QSTile<QSTile.State> {
public CustomQSTile(Host host, StatusBarPanelCustomTile tile) {
super(host);
- refreshState(tile);
+ mTile = tile;
}
@Override
@@ -109,6 +110,7 @@ public class CustomQSTile extends QSTile<QSTile.State> {
if (mOnClick != null) {
mOnClick.send();
} else if (mOnClickUri != null) {
+ mHost.collapsePanels();
final Intent intent = new Intent().setData(mOnClickUri);
mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
}
@@ -123,9 +125,9 @@ public class CustomQSTile extends QSTile<QSTile.State> {
mTile = (StatusBarPanelCustomTile) arg;
}
final CustomTile customTile = mTile.getCustomTile();
- state.visible = true;
state.contentDescription = customTile.contentDescription;
state.label = customTile.label;
+ state.visible = true;
final int iconId = customTile.icon;
if (iconId != 0 && (customTile.remoteIcon == null)) {
final String iconPackage = mTile.getResPkg();
@@ -147,12 +149,22 @@ public class CustomQSTile extends QSTile<QSTile.State> {
return MetricsLogger.DONT_TRACK_ME_BRO;
}
+ private boolean isDynamicTile() {
+ return mTile.getPackage().equals(mContext.getPackageName())
+ || mTile.getUid() == Process.SYSTEM_UID;
+ }
+
private class CustomQSDetailAdapter implements DetailAdapter, AdapterView.OnItemClickListener,
QSDetailItemsGrid.QSDetailItemsGridAdapter.OnPseudoGriditemClickListener {
private QSDetailItemsList.QSCustomDetailListAdapter mListAdapter;
private QSDetailItemsGrid.QSDetailItemsGridAdapter mGridAdapter;
public int getTitle() {
+ if (isDynamicTile()) {
+ return mContext.getResources().getIdentifier(
+ String.format("dynamic_qs_tile_%s_label", mTile.getTag()),
+ "string", mContext.getPackageName());
+ }
return R.string.quick_settings_custom_tile_detail_title;
}
@@ -199,8 +211,12 @@ public class CustomQSTile extends QSTile<QSTile.State> {
// icon is cached in state, fetch it
imageView.setImageDrawable(getState().icon.getDrawable(mContext));
customTileTitle.setText(mTile.getCustomTile().label);
- customTilePkg.setText(mTile.getPackage());
- customTileContentDesc.setText(mTile.getCustomTile().contentDescription);
+ if (isDynamicTile()) {
+ customTilePkg.setText(R.string.quick_settings_dynamic_tile_detail_title);
+ } else {
+ customTilePkg.setText(mTile.getPackage());
+ customTileContentDesc.setText(mTile.getCustomTile().contentDescription);
+ }
} else {
switch (mExpandedStyle.getStyle()) {
case CustomTile.ExpandedStyle.GRID_STYLE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CustomTileData.java b/packages/SystemUI/src/com/android/systemui/statusbar/CustomTileData.java
index 5db1f98..4be7292 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CustomTileData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CustomTileData.java
@@ -25,11 +25,12 @@ import android.util.ArrayMap;
*/
public class CustomTileData {
public static final class Entry {
- public String key;
- public StatusBarPanelCustomTile statusBarPanelCustomTile;
+ public final String key;
+ public final StatusBarPanelCustomTile sbc;
public Entry(StatusBarPanelCustomTile sbc) {
this.key = sbc.getKey();
+ this.sbc = sbc;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index bcf1c2f..3987a35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -424,6 +424,7 @@ public class NotificationPanelView extends PanelView implements
@Override
public void onAnimationEnd(Animator animation) {
mQsSizeChangeAnimator = null;
+ mQsContainer.setHeightOverride(-1);
}
});
mQsSizeChangeAnimator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index ee291f5..9c1ae95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -20,15 +20,21 @@ import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.AlarmManager.AlarmClockInfo;
import android.app.IUserSwitchObserver;
+import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
+import android.graphics.Bitmap;
import android.media.AudioManager;
import android.net.Uri;
+import android.os.Binder;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.RemoteException;
@@ -51,8 +57,16 @@ import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.SuController;
+import cyanogenmod.app.CMStatusBarManager;
+import cyanogenmod.app.CustomTile;
import cyanogenmod.providers.CMSettings;
+import org.cyanogenmod.internal.util.QSUtils;
+import org.cyanogenmod.internal.util.QSUtils.OnQSChanged;
+import org.cyanogenmod.internal.util.QSConstants;
+
+import java.util.ArrayList;
+
/**
* This class contains all of the policy about which icons are installed in the status
* bar at boot time. It goes through the normal API for icons, even though it probably
@@ -126,6 +140,13 @@ public class PhoneStatusBarPolicy implements Callback {
}
};
+ private final OnQSChanged mQSListener = new OnQSChanged() {
+ @Override
+ public void onQSChanged() {
+ processQSChangedLocked();
+ }
+ };
+
public PhoneStatusBarPolicy(Context context, CastController cast, HotspotController hotspot,
UserInfoController userInfoController, BluetoothController bluetooth, SuController su) {
mContext = context;
@@ -198,6 +219,8 @@ public class PhoneStatusBarPolicy implements Callback {
mService.setIcon(SLOT_MANAGED_PROFILE, R.drawable.stat_sys_managed_profile_status, 0,
mContext.getString(R.string.accessibility_managed_profile));
mService.setIconVisibility(SLOT_MANAGED_PROFILE, false);
+
+ QSUtils.registerObserverForQSChanges(mContext, mQSListener);
}
private ContentObserver mAlarmIconObserver = new ContentObserver(null) {
@@ -448,6 +471,12 @@ public class PhoneStatusBarPolicy implements Callback {
private void updateSu() {
mService.setIconVisibility(SLOT_SU, mSuController.hasActiveSessions());
+ final int userId = UserHandle.myUserId();
+ if (isSuEnabledForUser(userId)) {
+ publishSuCustomTile();
+ } else {
+ unpublishSuCustomTile();
+ }
}
private final CastController.Callback mCastCallback = new CastController.Callback() {
@@ -479,4 +508,117 @@ public class PhoneStatusBarPolicy implements Callback {
}
};
+ private void publishSuCustomTile() {
+ // This action should be performed as system
+ final int userId = UserHandle.myUserId();
+ long token = Binder.clearCallingIdentity();
+ try {
+ if (!QSUtils.isQSTileEnabledForUser(
+ mContext, QSConstants.DYNAMIC_TILE_SU, userId)) {
+ return;
+ }
+
+ final UserHandle user = new UserHandle(userId);
+ final int icon = QSUtils.getDynamicQSTileResIconId(mContext, userId,
+ QSConstants.DYNAMIC_TILE_SU);
+ final String contentDesc = QSUtils.getDynamicQSTileLabel(mContext, userId,
+ QSConstants.DYNAMIC_TILE_SU);
+ final Context resourceContext = QSUtils.getQSTileContext(mContext, userId);
+
+ CustomTile.ListExpandedStyle style = new CustomTile.ListExpandedStyle();
+ ArrayList<CustomTile.ExpandedListItem> items = new ArrayList<>();
+ for (String pkg : mSuController.getPackageNamesWithActiveSuSessions()) {
+ CustomTile.ExpandedListItem item = new CustomTile.ExpandedListItem();
+ int appIconIdentifier = getActiveSuApkDrawableId(pkg);
+ if (appIconIdentifier != -1) {
+ item.setExpandedListItemDrawable(appIconIdentifier);
+ } else {
+ item.setExpandedListItemDrawable(icon);
+ }
+ item.setExpandedListItemTitle(getActiveSuApkLabel(pkg));
+ item.setExpandedListItemSummary(pkg);
+ item.setExpandedListItemOnClickIntent(getCustomTilePendingIntent(pkg));
+ items.add(item);
+ }
+ style.setListItems(items);
+
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(mContext);
+ CustomTile tile = new CustomTile.Builder(resourceContext)
+ .setLabel(contentDesc)
+ .setContentDescription(contentDesc)
+ .setIcon(icon)
+ .setOnSettingsClickIntent(getCustomTileSettingsIntent())
+ .setExpandedStyle(style)
+ .build();
+ statusBarManager.publishTileAsUser(QSConstants.DYNAMIC_TILE_SU,
+ PhoneStatusBarPolicy.class.hashCode(), tile, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void unpublishSuCustomTile() {
+ // This action should be performed as system
+ final int userId = UserHandle.myUserId();
+ long token = Binder.clearCallingIdentity();
+ try {
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(mContext);
+ statusBarManager.removeTileAsUser(QSConstants.DYNAMIC_TILE_SU,
+ PhoneStatusBarPolicy.class.hashCode(), new UserHandle(userId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private PendingIntent getCustomTilePendingIntent(String pkg) {
+ Intent i = new Intent(Intent.ACTION_MAIN);
+ i.setPackage(pkg);
+ i.addCategory(Intent.CATEGORY_LAUNCHER);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return PendingIntent.getActivity(mContext, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ private Intent getCustomTileSettingsIntent() {
+ Intent i = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return i;
+ }
+
+ private String getActiveSuApkLabel(String pkg) {
+ final PackageManager pm = mContext.getPackageManager();
+ ApplicationInfo ai = null;
+ try {
+ ai = pm.getApplicationInfo(pkg, 0);
+ } catch (final NameNotFoundException e) {
+ // Ignore
+ }
+ return (String) (ai != null ? pm.getApplicationLabel(ai) : pkg);
+ }
+
+ private int getActiveSuApkDrawableId(String pkg) {
+ final PackageManager pm = mContext.getPackageManager();
+ ApplicationInfo ai;
+ try {
+ ai = pm.getApplicationInfo(pkg, 0);
+ } catch (final NameNotFoundException e) {
+ return -1;
+ }
+ return ai.icon;
+ }
+
+ private boolean isSuEnabledForUser(int userId) {
+ final boolean hasSuAccess = mSuController.hasActiveSessions();
+ final boolean isEnabledForUser = QSUtils.isQSTileEnabledForUser(mContext,
+ QSConstants.DYNAMIC_TILE_SU, userId);
+ return (userId == UserHandle.USER_OWNER) && isEnabledForUser && hasSuAccess;
+ }
+
+ private void processQSChangedLocked() {
+ final int userId = UserHandle.myUserId();
+ if (isSuEnabledForUser(userId)) {
+ publishSuCustomTile();
+ } else {
+ unpublishSuCustomTile();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index eff9b0a..be6f143 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -289,6 +289,7 @@ public class QSTileHost implements QSTile.Host, Tunable {
}
if (DEBUG) Log.d(TAG, "Recreating tiles");
final List<String> tileSpecs = loadTileSpecs(newValue);
+ removeUnusedDynamicTiles(tileSpecs);
if (tileSpecs.equals(mTileSpecs)) return;
for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) {
if (!tileSpecs.contains(tile.getKey())) {
@@ -318,6 +319,23 @@ public class QSTileHost implements QSTile.Host, Tunable {
}
}
+ private void removeUnusedDynamicTiles(List<String> tileSpecs) {
+ List<CustomTileData.Entry> tilesToRemove = new ArrayList<>();
+ for (CustomTileData.Entry entry : mCustomTileData.getEntries().values()) {
+ if (entry.sbc.getPackage().equals(mContext.getPackageName())
+ || entry.sbc.getUid() == Process.SYSTEM_UID) {
+ if (!tileSpecs.contains(entry.sbc.getTag())) {
+ tilesToRemove.add(entry);
+ }
+ }
+ }
+
+ for (CustomTileData.Entry entry : tilesToRemove) {
+ mCustomTileData.remove(entry.key);
+ removeCustomTile(entry.sbc);
+ }
+ }
+
@Override
public void goToSettingsPage() {
if (mCallback != null) {
@@ -429,29 +447,35 @@ public class QSTileHost implements QSTile.Host, Tunable {
}
void updateCustomTile(StatusBarPanelCustomTile sbc) {
- if (mTiles.containsKey(sbc.getKey())) {
- QSTile<?> tile = mTiles.get(sbc.getKey());
- if (tile instanceof CustomQSTile) {
- CustomQSTile qsTile = (CustomQSTile) tile;
- qsTile.update(sbc);
+ synchronized (mTiles) {
+ if (mTiles.containsKey(sbc.getKey())) {
+ QSTile<?> tile = mTiles.get(sbc.getKey());
+ if (tile instanceof CustomQSTile) {
+ CustomQSTile qsTile = (CustomQSTile) tile;
+ qsTile.update(sbc);
+ }
}
}
}
void addCustomTile(StatusBarPanelCustomTile sbc) {
- mCustomTileData.add(new CustomTileData.Entry(sbc));
- mTiles.put(sbc.getKey(), new CustomQSTile(this, sbc));
- if (mCallback != null) {
- mCallback.onTilesChanged();
+ synchronized (mTiles) {
+ mCustomTileData.add(new CustomTileData.Entry(sbc));
+ mTiles.put(sbc.getKey(), new CustomQSTile(this, sbc));
+ if (mCallback != null) {
+ mCallback.onTilesChanged();
+ }
}
}
void removeCustomTileSysUi(String key) {
- if (mTiles.containsKey(key)) {
- mTiles.remove(key);
- mCustomTileData.remove(key);
- if (mCallback != null) {
- mCallback.onTilesChanged();
+ synchronized (mTiles) {
+ if (mTiles.containsKey(key)) {
+ mTiles.remove(key);
+ mCustomTileData.remove(key);
+ if (mCallback != null) {
+ mCallback.onTilesChanged();
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuController.java
index 5f1e52e..de67261 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuController.java
@@ -16,10 +16,13 @@
package com.android.systemui.statusbar.policy;
+import java.util.List;
+
public interface SuController {
void addCallback(Callback callback);
void removeCallback(Callback callback);
boolean hasActiveSessions();
+ List<String> getPackageNamesWithActiveSuSessions();
public interface Callback {
void onSuSessionsChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuControllerImpl.java
index 1ba334a..c663bab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SuControllerImpl.java
@@ -16,22 +16,15 @@
package com.android.systemui.statusbar.policy;
-import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
import android.util.Log;
-import com.android.systemui.R;
-
import java.util.ArrayList;
import java.util.List;
@@ -45,15 +38,11 @@ public class SuControllerImpl implements SuController {
private ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
- private Context mContext;
-
private AppOpsManager mAppOpsManager;
- private boolean mHasActiveSuSessions;
+ private List<String> mActiveSuSessions = new ArrayList<>();
public SuControllerImpl(Context context) {
- mContext = context;
-
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
IntentFilter intentFilter = new IntentFilter();
@@ -85,7 +74,9 @@ public class SuControllerImpl implements SuController {
@Override
public boolean hasActiveSessions() {
- return mHasActiveSuSessions;
+ synchronized (mActiveSuSessions) {
+ return mActiveSuSessions.size() > 0;
+ }
}
private void fireCallback(Callback callback) {
@@ -98,10 +89,10 @@ public class SuControllerImpl implements SuController {
}
}
- /**
- * Returns true if a su session is active
- */
- private boolean hasActiveSuSessions() {
+ // Return the list of package names that currently have an active su session
+ @Override
+ public List<String> getPackageNamesWithActiveSuSessions() {
+ List<String> packageNames = new ArrayList<>();
List<AppOpsManager.PackageOps> packages
= mAppOpsManager.getPackagesForOps(mSuOpArray);
// AppOpsManager can return null when there is no requested data.
@@ -116,7 +107,8 @@ public class SuControllerImpl implements SuController {
AppOpsManager.OpEntry opEntry = opEntries.get(opInd);
if (opEntry.getOp() == AppOpsManager.OP_SU) {
if (opEntry.isRunning()) {
- return true;
+ packageNames.add(packageOp.getPackageName());
+ break;
}
}
}
@@ -124,14 +116,17 @@ public class SuControllerImpl implements SuController {
}
}
- return false;
+ return packageNames;
}
- private void updateActiveSuSessions() {
- boolean hadActiveSuSessions = mHasActiveSuSessions;
- mHasActiveSuSessions = hasActiveSuSessions();
- if (mHasActiveSuSessions != hadActiveSuSessions) {
- fireCallbacks();
+ private synchronized void updateActiveSuSessions() {
+ List<String> newList = getPackageNamesWithActiveSuSessions();
+ synchronized (mActiveSuSessions) {
+ if (!newList.equals(mActiveSuSessions)) {
+ mActiveSuSessions.clear();
+ mActiveSuSessions.addAll(newList);
+ fireCallbacks();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 27858cb..cf1340b 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -28,7 +28,9 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
@@ -42,6 +44,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.AlarmClock;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateFormat;
@@ -66,6 +69,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
+import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.TimeZone;
@@ -79,6 +83,13 @@ import static android.app.AlarmManager.RTC_POWEROFF_WAKEUP;
import com.android.internal.util.LocalLog;
+import cyanogenmod.app.CMStatusBarManager;
+import cyanogenmod.app.CustomTile;
+
+import org.cyanogenmod.internal.util.QSUtils;
+import org.cyanogenmod.internal.util.QSUtils.OnQSChanged;
+import org.cyanogenmod.internal.util.QSConstants;
+
class AlarmManagerService extends SystemService {
private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
private static final int RTC_MASK = 1 << RTC;
@@ -293,6 +304,14 @@ class AlarmManagerService extends SystemService {
final Constants mConstants;
+ private final OnQSChanged mQSListener = new OnQSChanged() {
+ @Override
+ public void onQSChanged() {
+ processQSChangedLocked();
+ }
+ };
+ private ContentObserver mQSObserver;
+
// Alarm delivery ordering bookkeeping
static final int PRIO_TICK = 0;
static final int PRIO_WAKEUP = 1;
@@ -857,6 +876,8 @@ class AlarmManagerService extends SystemService {
}
publishBinderService(Context.ALARM_SERVICE, mService);
+
+ mQSObserver = QSUtils.registerObserverForQSChanges(getContext(), mQSListener);
}
@Override
@@ -869,6 +890,12 @@ class AlarmManagerService extends SystemService {
@Override
protected void finalize() throws Throwable {
try {
+ QSUtils.unregisterObserverForQSChanges(getContext(), mQSObserver);
+ } catch (Exception ex) {
+ // Ignore
+ }
+
+ try {
close(mNativeData);
} finally {
super.finalize();
@@ -1052,6 +1079,8 @@ class AlarmManagerService extends SystemService {
if (a.alarmClock != null) {
mNextAlarmClockMayChange = true;
+ //Publish as system user
+ publishNextAlarmCustomTile(Process.SYSTEM_UID);
}
boolean needRebatch = false;
@@ -1611,6 +1640,9 @@ class AlarmManagerService extends SystemService {
updateNextAlarmInfoForUserLocked(userId, null);
}
}
+
+ // Process dynamic custom tile
+ processQSChangedLocked();
}
private void updateNextAlarmInfoForUserLocked(int userId,
@@ -2654,6 +2686,121 @@ class AlarmManagerService extends SystemService {
return bs;
}
+ private void publishNextAlarmCustomTile(int userId) {
+ // This action should be performed as system
+ long token = Binder.clearCallingIdentity();
+ try {
+ final UserHandle user = new UserHandle(userId);
+ if (!QSUtils.isQSTileEnabledForUser(
+ getContext(), QSConstants.DYNAMIC_TILE_NEXT_ALARM, user.getUserId(userId))) {
+ return;
+ }
+
+ final int icon = QSUtils.getDynamicQSTileResIconId(getContext(), userId,
+ QSConstants.DYNAMIC_TILE_NEXT_ALARM);
+ final String contentDesc = QSUtils.getDynamicQSTileLabel(getContext(), userId,
+ QSConstants.DYNAMIC_TILE_NEXT_ALARM);
+ final Context resourceContext = QSUtils.getQSTileContext(getContext(), userId);
+
+ // Create the expanded view with all the user alarms
+ AlarmManager.AlarmClockInfo nextAlarm = null;
+ CustomTile.ListExpandedStyle style = new CustomTile.ListExpandedStyle();
+ ArrayList<CustomTile.ExpandedListItem> items = new ArrayList<>();
+ for (Alarm alarm : getAllUserAlarmsLocked(userId)) {
+ if (nextAlarm == null) {
+ nextAlarm = alarm.alarmClock;
+ }
+
+ final String pkg = alarm.operation.getCreatorPackage();
+ CustomTile.ExpandedListItem item = new CustomTile.ExpandedListItem();
+ item.setExpandedListItemDrawable(icon);
+ item.setExpandedListItemTitle(formatNextAlarm(getContext(), alarm.alarmClock, userId));
+ item.setExpandedListItemSummary(getAlarmApkLabel(pkg));
+ item.setExpandedListItemOnClickIntent(getCustomTilePendingIntent(user, pkg));
+ items.add(item);
+ }
+ style.setListItems(items);
+
+ // Build the custom tile
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(getContext());
+ CustomTile tile = new CustomTile.Builder(resourceContext)
+ .setLabel(formatNextAlarm(getContext(), nextAlarm, userId))
+ .setContentDescription(contentDesc)
+ .setIcon(icon)
+ .setExpandedStyle(style)
+ .build();
+ statusBarManager.publishTileAsUser(QSConstants.DYNAMIC_TILE_NEXT_ALARM,
+ AlarmManagerService.class.hashCode(), tile, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void unpublishNextAlarmCustomTile(int userId) {
+ // This action should be performed as system
+ long token = Binder.clearCallingIdentity();
+ try {
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(getContext());
+ statusBarManager.removeTileAsUser(QSConstants.DYNAMIC_TILE_NEXT_ALARM,
+ AlarmManagerService.class.hashCode(), new UserHandle(userId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private List<Alarm> getAllUserAlarmsLocked(int userId) {
+ List<Alarm> userAlarms = new ArrayList<>();
+ synchronized (mLock) {
+ final int N = mAlarmBatches.size();
+ for (int i = 0; i < N; i++) {
+ ArrayList<Alarm> alarms = mAlarmBatches.get(i).alarms;
+ final int M = alarms.size();
+ for (int j = 0; j < M; j++) {
+ Alarm a = alarms.get(j);
+ if (a.alarmClock != null && userId == a.uid) {
+ userAlarms.add(a);
+ }
+ }
+ }
+ }
+ return userAlarms;
+ }
+
+ private String getAlarmApkLabel(String pkg) {
+ final PackageManager pm = getContext().getPackageManager();
+ ApplicationInfo ai = null;
+ try {
+ ai = pm.getApplicationInfo(pkg, 0);
+ } catch (final NameNotFoundException e) {
+ // Ignore
+ }
+ return (String) (ai != null ? pm.getApplicationLabel(ai) : pkg);
+ }
+
+ private PendingIntent getCustomTilePendingIntent(UserHandle user, String pkg) {
+ Intent i = new Intent(AlarmClock.ACTION_SHOW_ALARMS);
+ i.setPackage(pkg);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return PendingIntent.getActivityAsUser(getContext(), 0, i,
+ PendingIntent.FLAG_UPDATE_CURRENT, null, user);
+ }
+
+ private void processQSChangedLocked() {
+ synchronized (mLock) {
+ int count = mNextAlarmClockForUser.size();
+ for (int i = 0; i < count; i++) {
+ int userId = mNextAlarmClockForUser.keyAt(i);
+ boolean enabled = QSUtils.isQSTileEnabledForUser(
+ getContext(), QSConstants.DYNAMIC_TILE_NEXT_ALARM, userId);
+ if (enabled) {
+ publishNextAlarmCustomTile(userId);
+ } else {
+ unpublishNextAlarmCustomTile(userId);
+ }
+ }
+ }
+ }
+
class ResultReceiver implements PendingIntent.OnFinished {
public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
String resultData, Bundle resultExtras) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 6d6ca3c..e36d118 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -36,6 +36,9 @@ import com.android.server.pm.UserManagerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.wm.WindowManagerService;
+import cyanogenmod.app.CMStatusBarManager;
+import cyanogenmod.app.CustomTile;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -142,6 +145,9 @@ import java.util.Locale;
import cyanogenmod.providers.CMSettings;
+import org.cyanogenmod.internal.util.QSUtils;
+import org.cyanogenmod.internal.util.QSUtils.OnQSChanged;
+import org.cyanogenmod.internal.util.QSConstants;
/**
* This class provides a system service that manages input methods.
*/
@@ -199,6 +205,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1, -1);
+ private final OnQSChanged mQSListener = new OnQSChanged() {
+ @Override
+ public void onQSChanged() {
+ processQSChangedLocked();
+ }
+ };
+
// All known input methods. mMethodMap also serves as the global
// lock for this class.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<InputMethodInfo>();
@@ -949,6 +962,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}, filter);
LocalServices.addService(InputMethodManagerInternal.class, new LocalServiceImpl(mHandler));
+ QSUtils.registerObserverForQSChanges(mContext, mQSListener);
}
private void resetDefaultImeLocked(Context context) {
@@ -1822,6 +1836,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
com.android.internal.R.string.select_input_method,
mImeSwitcherNotification.build(), UserHandle.ALL);
mNotificationShown = true;
+ publishImeSelectorCustomTile(imi);
}
} else {
if (mNotificationShown && mNotificationManager != null) {
@@ -1831,6 +1846,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mNotificationManager.cancelAsUser(null,
com.android.internal.R.string.select_input_method, UserHandle.ALL);
mNotificationShown = false;
+ unpublishImeSelectorCustomTile();
}
}
} finally {
@@ -3561,6 +3577,78 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ private void publishImeSelectorCustomTile(InputMethodInfo imi) {
+ // This action should be performed as system
+ final int userId = UserHandle.myUserId();
+ long token = Binder.clearCallingIdentity();
+ try {
+ if (!QSUtils.isQSTileEnabledForUser(
+ mContext, QSConstants.DYNAMIC_TILE_IME_SELECTOR, userId)) {
+ return;
+ }
+
+ final UserHandle user = new UserHandle(userId);
+ final int icon = QSUtils.getDynamicQSTileResIconId(mContext, userId,
+ QSConstants.DYNAMIC_TILE_IME_SELECTOR);
+ final String contentDesc = QSUtils.getDynamicQSTileLabel(mContext, userId,
+ QSConstants.DYNAMIC_TILE_IME_SELECTOR);
+ final Context resourceContext = QSUtils.getQSTileContext(mContext, userId);
+ CharSequence inputMethodName = null;
+ if (mCurrentSubtype != null) {
+ inputMethodName = mCurrentSubtype.getDisplayName(mContext,
+ imi.getPackageName(), imi.getServiceInfo().applicationInfo);
+ }
+ final CharSequence label = inputMethodName == null ? contentDesc : inputMethodName;
+
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(mContext);
+ CustomTile tile = new CustomTile.Builder(resourceContext)
+ .setLabel(label.toString())
+ .setContentDescription(contentDesc)
+ .setIcon(icon)
+ .setOnClickIntent(mImeSwitchPendingIntent)
+ .build();
+ statusBarManager.publishTileAsUser(QSConstants.DYNAMIC_TILE_IME_SELECTOR,
+ InputMethodManagerService.class.hashCode(), tile, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void unpublishImeSelectorCustomTile() {
+ // This action should be performed as system
+ final int userId = UserHandle.myUserId();
+ long token = Binder.clearCallingIdentity();
+ try {
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(mContext);
+ statusBarManager.removeTileAsUser(QSConstants.DYNAMIC_TILE_IME_SELECTOR,
+ InputMethodManagerService.class.hashCode(), new UserHandle(userId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void processQSChangedLocked() {
+ final int userId = UserHandle.myUserId();
+ final boolean isIMEVisible = ((mImeWindowVis & (InputMethodService.IME_ACTIVE)) != 0)
+ && (mWindowManagerService.isHardKeyboardAvailable()
+ || (mImeWindowVis & (InputMethodService.IME_VISIBLE)) != 0);
+ InputMethodInfo imi = null;
+ synchronized (mMethodMap) {
+ if (mCurMethodId != null) {
+ imi = mMethodMap.get(mCurMethodId);
+ }
+ }
+ final boolean hasInputMethod = isIMEVisible && imi != null && mCurrentSubtype != null;
+ final boolean isEnabledForUser = QSUtils.isQSTileEnabledForUser(mContext,
+ QSConstants.DYNAMIC_TILE_NEXT_ALARM, userId);
+ boolean enabled = isEnabledForUser && hasInputMethod;
+ if (enabled) {
+ publishImeSelectorCustomTile(imi);
+ } else {
+ unpublishImeSelectorCustomTile();
+ }
+ }
+
// TODO: Cache the state for each user and reset when the cached user is removed.
private static class InputMethodFileManager {
private static final String SYSTEM_PATH = "system";
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 34a17a2..19e39e7 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -32,6 +32,7 @@ import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
+import android.os.Binder;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
@@ -51,8 +52,16 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.FgThread;
+
import cyanogenmod.providers.CMSettings;
+import cyanogenmod.app.CMStatusBarManager;
+import cyanogenmod.app.CustomTile;
+
+import org.cyanogenmod.internal.util.QSUtils;
+import org.cyanogenmod.internal.util.QSUtils.OnQSChanged;
+import org.cyanogenmod.internal.util.QSConstants;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -162,6 +171,13 @@ public class UsbDeviceManager {
}
}
+ private final OnQSChanged mQSListener = new OnQSChanged() {
+ @Override
+ public void onQSChanged() {
+ mHandler.processQSChangedLocked();
+ }
+ };
+
/*
* Listens for uevent messages from the kernel to monitor the USB state
*/
@@ -361,11 +377,13 @@ public class UsbDeviceManager {
false, adbNotificationObserver);
mContentResolver.registerContentObserver(
CMSettings.Secure.getUriFor(CMSettings.Secure.ADB_NOTIFY),
- false, adbNotificationObserver);
+ false, adbNotificationObserver);
// Watch for USB configuration changes
mUEventObserver.startObserving(USB_STATE_MATCH);
mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+
+ QSUtils.registerObserverForQSChanges(mContext, mQSListener);
} catch (Exception e) {
Slog.e(TAG, "Error initializing UsbHandler", e);
}
@@ -865,6 +883,12 @@ public class UsbDeviceManager {
}
mAdbNotificationId = id;
}
+
+ if (id > 0) {
+ publishAdbCustomTile();
+ } else {
+ unpublishAdbCustomTile();
+ }
}
private String getDefaultFunctions() {
@@ -893,6 +917,95 @@ public class UsbDeviceManager {
pw.println("IOException: " + e);
}
}
+
+ private void publishAdbCustomTile() {
+ // This action should be performed as system
+ final int userId = UserHandle.myUserId();
+ long token = Binder.clearCallingIdentity();
+ try {
+ if (!QSUtils.isQSTileEnabledForUser(
+ mContext, QSConstants.DYNAMIC_TILE_ADB, userId)) {
+ return;
+ }
+
+ final UserHandle user = new UserHandle(userId);
+ final int icon = QSUtils.getDynamicQSTileResIconId(mContext, userId,
+ QSConstants.DYNAMIC_TILE_ADB);
+ final String contentDesc = QSUtils.getDynamicQSTileLabel(mContext, userId,
+ QSConstants.DYNAMIC_TILE_ADB);
+ final Context resourceContext = QSUtils.getQSTileContext(mContext, userId);
+
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(mContext);
+ CustomTile tile = new CustomTile.Builder(resourceContext)
+ .setLabel(getAdbCustomTileLabel())
+ .setContentDescription(contentDesc)
+ .setIcon(icon)
+ .setOnClickIntent(getCustomTilePendingIntent())
+ .build();
+ statusBarManager.publishTileAsUser(QSConstants.DYNAMIC_TILE_ADB,
+ UsbDeviceManager.class.hashCode(), tile, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void unpublishAdbCustomTile() {
+ // This action should be performed as system
+ final int userId = UserHandle.myUserId();
+ long token = Binder.clearCallingIdentity();
+ try {
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(mContext);
+ statusBarManager.removeTileAsUser(QSConstants.DYNAMIC_TILE_ADB,
+ UsbDeviceManager.class.hashCode(), new UserHandle(userId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private PendingIntent getCustomTilePendingIntent() {
+ Intent i = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return PendingIntent.getActivity(mContext, 0, i, PendingIntent.FLAG_UPDATE_CURRENT, null);
+ }
+
+ private String getAdbCustomTileLabel() {
+ boolean usbAdbActive = mAdbEnabled && mConnected;
+ boolean netAdbActive = mAdbEnabled &&
+ CMSettings.Secure.getInt(mContentResolver, CMSettings.Secure.ADB_PORT, -1) > 0;
+
+ int id = 0;
+ if (usbAdbActive && netAdbActive) {
+ id = com.android.internal.R.string.adb_active_custom_tile_both;
+ } else if (usbAdbActive) {
+ id = com.android.internal.R.string.adb_active_custom_tile_usb;
+ } else if (netAdbActive) {
+ id = com.android.internal.R.string.adb_active_custom_tile_net;
+ }
+
+ Resources res = mContext.getResources();
+ return res.getString(
+ com.android.internal.R.string.adb_active_custom_tile,
+ res.getString(id));
+ }
+
+ private void processQSChangedLocked() {
+ final int userId = UserHandle.myUserId();
+ boolean usbAdbActive = mAdbEnabled && mConnected;
+ boolean netAdbActive = mAdbEnabled &&
+ CMSettings.Secure.getInt(mContentResolver, CMSettings.Secure.ADB_PORT, -1) > 0;
+ boolean notifEnabled = "1".equals(SystemProperties.get("persist.adb.notify"))
+ || CMSettings.Secure.getInt(mContext.getContentResolver(),
+ CMSettings.Secure.ADB_NOTIFY, 1) == 1;
+ boolean isActive = notifEnabled && (usbAdbActive || netAdbActive);
+ final boolean isEnabledForUser = QSUtils.isQSTileEnabledForUser(mContext,
+ QSConstants.DYNAMIC_TILE_ADB, userId);
+ boolean enabled = (userId == UserHandle.USER_OWNER) && isEnabledForUser && isActive;
+ if (enabled) {
+ publishAdbCustomTile();
+ } else {
+ unpublishAdbCustomTile();
+ }
+ }
}
/* returns the currently attached USB accessory */