diff options
Diffstat (limited to 'packages/SystemUI/src')
8 files changed, 230 insertions, 45 deletions
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(); + } } } } |