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 /services/core | |
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 'services/core')
-rw-r--r-- | services/core/java/com/android/server/AlarmManagerService.java | 147 | ||||
-rw-r--r-- | services/core/java/com/android/server/InputMethodManagerService.java | 88 |
2 files changed, 235 insertions, 0 deletions
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"; |