diff options
author | Jorge Ruesga <jorge@ruesga.com> | 2015-06-11 02:39:36 +0200 |
---|---|---|
committer | Adnan Begovic <adnan@cyngn.com> | 2015-12-10 18:23:11 -0800 |
commit | 68c510802aed593bc7bf0526724538c2ef489780 (patch) | |
tree | 5f1e60c0bde95c8fb908e12ee51a2fdbe189b3af /packages | |
parent | 699d345030efb8b8d081ea68e000edc19c1a4860 (diff) | |
download | frameworks_base-68c510802aed593bc7bf0526724538c2ef489780.zip frameworks_base-68c510802aed593bc7bf0526724538c2ef489780.tar.gz frameworks_base-68c510802aed593bc7bf0526724538c2ef489780.tar.bz2 |
base: dynamic tiles
Add support for dynamic tiles
Video: https://cloud.ruesga.com/f/71a12e4801/
Required: topic:customtiles_respkg
topic:readd_customtiles
JIRA: CML-22
Change-Id: Ie17ebf09ee88bc0c53561decc27430e174814543
Signed-off-by: Jorge Ruesga <jorge@ruesga.com>
Diffstat (limited to 'packages')
16 files changed, 416 insertions, 46 deletions
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(); + } } } } |