Skip to content

Commit f7f1d32

Browse files
kufikugelcodex-corp
authored andcommitted
Frameworks: Heads up meets SlimKat....SlimKat meets Google IO [1/4]
First introduced from apple, showed as android version on PA as hover and now found in android code as native implementation we want to do our interpretation of this feature. Especially thanks PA and CM for some of the UX ideas. We have basically split this feature in two parts. 1. Googles native integration. We keep it as is and use the native flags for it. Apps (in our case Dialer, Mms, Email per account) can force show a heads up notification if the notification is important. So basically same what was shown on Google IO the first time to the public. Eg the dialer on SlimKat is doing exactly the same now like google want it. Due that we use same flags like google every future app with heads up support will automatically show as heads up. 2. Give the user an ability to enable heads up mode for third party apps which do not have a native integration. Well first thought was on which apps a user would usally use it (keeping in minde the idea from google....heads up is for important notifications and for nothing more)? Right on chat, email or social apps. On all other apps it simply does not make sense at all. Which leads into the question why the current integrations are handling blacklists or whitelists and enable it per default for all apps. This is from UX point of view an absolutely no go (just imagine the user blacklists half of the 200 apps installed lol). Aside that parsing long lists is a peformance problem. So our attempt goes into another direction. Headsup for (native integration is another story) apps is disabled by default. We bind the information directly to the application package. So each app carries the information itself. The user can either enable it with a longlick in the notification drawer on the notification or in Settings->app->App info as well via a check box. As soon it is enabled for this app every notification from this app will show as heads up as long it is not an ongoing notification. In general on system signed apps the user is not allowed to change the heads up state. Thats nothing the user should decide....the system should do. Aside that it prevends user to enable heads up for eg IME selector etc etc. Thats the basic concept behind. In addition to this concept following things were done. - Introduce a new expanded notification flag. Apps can send it via extra metadata to force a expanded heads up notification is it is needed (eg dialer). - Add a user option to enable always expanded on heads up notification - Add a user option to set the timeout for the heads up view. - Introduce a snooze button. Every heads up notification has a snooze button on the top right. If the user press it heads up is disabled for a certain time (like a do not disturb mode). The time length is user configurable in settings. - We had a bunch of user which want to use heads up in a more intrusive way. Current default behaviour is that a heads up shows up if the user did not push the heads up of this app into background. This is due that the system assumes if the user put it into background he or she do not want to be disturbed by notifications of this app at the moment. Of course in the moment the user interacted with the background notification heads up is activated again. This leads like google suggested in a very good non intrusive way. Well but some user use it in another way (especially on immversive mode) and want to be notified on every notification update of this app via heads up. Due of this understandable other user behaviour we add an option for it to allow this scenario. - Detect direction of the swipe. Left swipe will dismiss the notification (if it is not a non clearable). Right swipe will just hide the heads up and the notification is still in background accessable in the notification drawer. Other then that some fixes were done as well (eg utilize the back button, correctly handle the hiding of the heads up, handle action buttons on heads up view correctly, fix several expanded desktop and immersive issues and some other issues the AOSP implementation currently has) To summarize...heads up this way is very non intrusive and especially we do not need a bunch of settings or blacklists. So it is pretty straight forward, fast, clean and user friendly. Some visual examples: https://plus.google.com/107859134357373490612/posts/bJABHs1aNMj Conflicts: core/java/android/app/Notification.java core/java/android/provider/Settings.java core/res/res/values/slim_strings.xml packages/SystemUI/res/values/slim_strings.xml packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java services/java/com/android/server/pm/Settings.java
1 parent c6eb915 commit f7f1d32

46 files changed

Lines changed: 900 additions & 113 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/java/android/app/ActivityManagerNative.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2056,6 +2056,15 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
20562056
return true;
20572057
}
20582058

2059+
case IS_HEADS_UP_ENABLED_TRANSACTION: {
2060+
data.enforceInterface(IActivityManager.descriptor);
2061+
int pid = data.readInt();
2062+
boolean res = isHeadsUpEnabledForProcess(pid);
2063+
reply.writeNoException();
2064+
reply.writeInt(res ? 1 : 0);
2065+
return true;
2066+
}
2067+
20592068
}
20602069

20612070
return super.onTransact(code, data, reply, flags);
@@ -4740,5 +4749,19 @@ public void notifySplitViewLayoutChanged() throws RemoteException {
47404749
reply.recycle();
47414750
}
47424751

4752+
4753+
public boolean isHeadsUpEnabledForProcess(int pid) throws RemoteException {
4754+
Parcel data = Parcel.obtain();
4755+
Parcel reply = Parcel.obtain();
4756+
data.writeInterfaceToken(IActivityManager.descriptor);
4757+
data.writeInt(pid);
4758+
mRemote.transact(IS_HEADS_UP_ENABLED_TRANSACTION, data, reply, 0);
4759+
reply.readException();
4760+
int res = reply.readInt();
4761+
data.recycle();
4762+
reply.recycle();
4763+
return res == 1;
4764+
}
4765+
47434766
private IBinder mRemote;
47444767
}

core/java/android/app/ApplicationPackageManager.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import android.content.res.XmlResourceParser;
5050
import android.graphics.drawable.Drawable;
5151
import android.net.Uri;
52+
import android.os.Binder;
5253
import android.os.Process;
5354
import android.os.RemoteException;
5455
import android.os.UserHandle;
@@ -1382,6 +1383,31 @@ public boolean getApplicationBlockedSettingAsUser(String packageName, UserHandle
13821383
return false;
13831384
}
13841385

1386+
/**
1387+
* @hide
1388+
*/
1389+
@Override
1390+
public void setHeadsUpSetting(String packageName, boolean enabled) {
1391+
try {
1392+
mPM.setHeadsUpSetting(packageName, enabled, mContext.getUserId());
1393+
} catch (RemoteException e) {
1394+
// Should never happen!
1395+
}
1396+
}
1397+
1398+
/**
1399+
* @hide
1400+
*/
1401+
@Override
1402+
public boolean getHeadsUpSetting(String packageName) {
1403+
try {
1404+
return mPM.getHeadsUpSetting(packageName, mContext.getUserId());
1405+
} catch (RemoteException e) {
1406+
// Should never happen!
1407+
}
1408+
return false;
1409+
}
1410+
13851411
/**
13861412
* @hide
13871413
*/

core/java/android/app/ContextImpl.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,17 @@ private WallpaperManager getWallpaperManager() {
17051705
return new DropBoxManager(service);
17061706
}
17071707

1708+
@Override
1709+
public boolean isHeadsUpEnabled() {
1710+
try {
1711+
return ActivityManagerNative.getDefault()
1712+
.isHeadsUpEnabledForProcess(Binder.getCallingPid());
1713+
} catch (RemoteException e) {
1714+
Log.e(TAG, e.getMessage(), e);
1715+
}
1716+
return false;
1717+
}
1718+
17081719
@Override
17091720
public int checkPermission(String permission, int pid, int uid) {
17101721
if (permission == null) {

core/java/android/app/IActivityManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ public int handleIncomingUser(int callingPid, int callingUid, int userId, boolea
206206
public void setProcessForeground(IBinder token, int pid,
207207
boolean isForeground) throws RemoteException;
208208

209+
public boolean isHeadsUpEnabledForProcess(int pid) throws RemoteException;
210+
209211
public int checkPermission(String permission, int pid, int uid)
210212
throws RemoteException;
211213

@@ -700,6 +702,7 @@ private WaitResult(Parcel source) {
700702
int GET_PERSISTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+181;
701703
int APP_NOT_RESPONDING_VIA_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+182;
702704
int GET_CALLING_PACKAGE_FOR_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+183;
705+
int IS_HEADS_UP_ENABLED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+184;
703706
/* SPLIT VIEW */
704707
int GET_ACTIVITY_FOR_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+200;
705708
int NOTIFY_SPLIT_VIEW_LAYOUT_CHANGED = IBinder.FIRST_CALL_TRANSACTION+201;

core/java/android/app/Notification.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ public class Notification implements Parcelable
568568
public static final String EXTRA_FORCE_SHOW_LIGHTS = "android.forceShowLights";
569569

570570
/**
571-
* Value for {@link #EXTRA_AS_HEADS_UP}.
571+
* Default alue for {@link #EXTRA_AS_HEADS_UP}.
572572
* @hide
573573
*/
574574
public static final int HEADS_UP_NEVER = 0;
@@ -581,11 +581,29 @@ public class Notification implements Parcelable
581581

582582

583583
/**
584-
* Value for {@link #EXTRA_AS_HEADS_UP}.
584+
* Default value for {@link #EXTRA_AS_HEADS_UP}.
585585
* @hide
586586
*/
587587
public static final int HEADS_UP_REQUESTED = 2;
588588

589+
/**
590+
* Not used.
591+
* @hide
592+
*/
593+
public static final String EXTRA_HEADS_UP_EXPANDED = "headsupExpanded";
594+
595+
/**
596+
* Value for {@link #EXTRA_HEADS_UP_EXPANDED}.
597+
* @hide
598+
*/
599+
public static final int HEADS_UP_EXPANDED = 0;
600+
601+
/**
602+
* Default value for {@link #EXTRA_HEADS_UP_EXPANDED}.
603+
* @hide
604+
*/
605+
public static final int HEADS_UP_NOT_EXPANDED = 1;
606+
589607
/**
590608
* Structure to encapsulate a named action that can be shown as part of this notification.
591609
* It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
@@ -1788,9 +1806,7 @@ private RemoteViews generateActionButton(Action action) {
17881806
: R.layout.notification_action);
17891807
button.setTextViewCompoundDrawablesRelative(R.id.action0, action.icon, 0, 0, 0);
17901808
button.setTextViewText(R.id.action0, action.title);
1791-
if (!tombstone) {
1792-
button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
1793-
}
1809+
button.setOnClickPendingIntent(R.id.action0, action.actionIntent, tombstone);
17941810
button.setContentDescription(R.id.action0, action.title);
17951811
return button;
17961812
}

core/java/android/content/Context.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2439,6 +2439,15 @@ public abstract boolean startInstrumentation(ComponentName className,
24392439
*/
24402440
public static final String USER_SERVICE = "user";
24412441

2442+
/**
2443+
* Determine whether the application or calling application has
2444+
* heads up notification enabled. Non system applications and content providers
2445+
* can check this value if they wish to honor the heads up feature.
2446+
*
2447+
* @hide
2448+
*/
2449+
public abstract boolean isHeadsUpEnabled();
2450+
24422451
/**
24432452
* Use with {@link #getSystemService} to retrieve a
24442453
* {@link android.app.AppOpsManager} for tracking application operations

core/java/android/content/ContextWrapper.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,12 @@ public int checkPermission(String permission, int pid, int uid) {
545545
return mBase.checkPermission(permission, pid, uid);
546546
}
547547

548+
/** @hide */
549+
@Override
550+
public boolean isHeadsUpEnabled() {
551+
return mBase.isHeadsUpEnabled();
552+
}
553+
548554
@Override
549555
public int checkCallingPermission(String permission) {
550556
return mBase.checkCallingPermission(permission);

core/java/android/content/pm/IPackageManager.aidl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,10 @@ interface IPackageManager {
238238
int getPreferredActivities(out List<IntentFilter> outFilters,
239239
out List<ComponentName> outActivities, String packageName);
240240

241+
boolean getHeadsUpSetting(in String packageName, int userId);
242+
243+
void setHeadsUpSetting(in String packageName, boolean enabled, int userId);
244+
241245
/**
242246
* Report the set of 'Home' activity candidates, plus (if any) which of them
243247
* is the current "always use this one" setting.

core/java/android/content/pm/PackageManager.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3221,6 +3221,22 @@ public abstract void setApplicationEnabledSetting(String packageName,
32213221
*/
32223222
public abstract int getApplicationEnabledSetting(String packageName);
32233223

3224+
/**
3225+
* @param packageName
3226+
* @return
3227+
*
3228+
* @hide
3229+
*/
3230+
public abstract boolean getHeadsUpSetting(String packageName);
3231+
3232+
/**
3233+
* @param packageName
3234+
* @param enabled
3235+
*
3236+
* @hide
3237+
*/
3238+
public abstract void setHeadsUpSetting(String packageName, boolean enabled);
3239+
32243240
/**
32253241
* Puts the package in a blocked state, which is almost like an uninstalled state,
32263242
* making the package unavailable, but it doesn't remove the data or the actual

core/java/android/content/pm/PackageUserState.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public class PackageUserState {
3030
public boolean installed;
3131
public boolean blocked; // Is the app restricted by owner / admin
3232
public int enabled;
33+
public boolean headsUp;
3334

3435
public String lastDisableAppCaller;
3536

@@ -42,13 +43,15 @@ public PackageUserState() {
4243
installed = true;
4344
blocked = false;
4445
enabled = COMPONENT_ENABLED_STATE_DEFAULT;
46+
headsUp = false;
4547
}
4648

4749
public PackageUserState(PackageUserState o) {
4850
installed = o.installed;
4951
stopped = o.stopped;
5052
notLaunched = o.notLaunched;
5153
enabled = o.enabled;
54+
headsUp = o.headsUp;
5255
blocked = o.blocked;
5356
lastDisableAppCaller = o.lastDisableAppCaller;
5457
disabledComponents = o.disabledComponents != null
@@ -60,4 +63,4 @@ public PackageUserState(PackageUserState o) {
6063
visibleComponents = o.visibleComponents != null
6164
? new HashSet<String>(o.visibleComponents) : null;
6265
}
63-
}
66+
}

0 commit comments

Comments
 (0)