Loading core/java/android/app/Notification.java +8 −13 Original line number Original line Diff line number Diff line Loading @@ -9591,21 +9591,16 @@ public class Notification implements Parcelable @NonNull @NonNull public ArrayList<Action> getActionsListWithSystemActions() { public ArrayList<Action> getActionsListWithSystemActions() { // Define the system actions we expect to see // Define the system actions we expect to see final Action negativeAction = makeNegativeAction(); final Action firstAction = makeNegativeAction(); final Action answerAction = makeAnswerAction(); final Action lastAction = makeAnswerAction(); // Sort the expected actions into the correct order: // * If there's no answer action, put the hang up / decline action at the end // * Otherwise put the answer action at the end, and put the decline action at start. final Action firstAction = answerAction == null ? null : negativeAction; final Action lastAction = answerAction == null ? negativeAction : answerAction; // Start creating the result list. // Start creating the result list. int nonContextualActionSlotsRemaining = MAX_ACTION_BUTTONS; int nonContextualActionSlotsRemaining = MAX_ACTION_BUTTONS; ArrayList<Action> resultActions = new ArrayList<>(MAX_ACTION_BUTTONS); ArrayList<Action> resultActions = new ArrayList<>(MAX_ACTION_BUTTONS); if (firstAction != null) { // Always have a first action. resultActions.add(firstAction); resultActions.add(firstAction); --nonContextualActionSlotsRemaining; --nonContextualActionSlotsRemaining; } // Copy actions into the new list, correcting system actions. // Copy actions into the new list, correcting system actions. if (mBuilder.mActions != null) { if (mBuilder.mActions != null) { Loading @@ -9621,14 +9616,14 @@ public class Notification implements Parcelable --nonContextualActionSlotsRemaining; --nonContextualActionSlotsRemaining; } } // If there's exactly one action slot left, fill it with the lastAction. // If there's exactly one action slot left, fill it with the lastAction. if (nonContextualActionSlotsRemaining == 1) { if (lastAction != null && nonContextualActionSlotsRemaining == 1) { resultActions.add(lastAction); resultActions.add(lastAction); --nonContextualActionSlotsRemaining; --nonContextualActionSlotsRemaining; } } } } } } // If there are any action slots left, the lastAction still needs to be added. // If there are any action slots left, the lastAction still needs to be added. if (nonContextualActionSlotsRemaining >= 1) { if (lastAction != null && nonContextualActionSlotsRemaining >= 1) { resultActions.add(lastAction); resultActions.add(lastAction); } } return resultActions; return resultActions; Loading core/tests/coretests/src/android/app/NotificationTest.java +113 −1 Original line number Original line Diff line number Diff line Loading @@ -59,6 +59,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy; import android.annotation.Nullable; import android.annotation.Nullable; import android.app.Notification.CallStyle; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.LocusId; import android.content.LocusId; Loading Loading @@ -92,6 +93,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import java.util.List; import java.util.function.Consumer; import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class) Loading Loading @@ -530,6 +532,108 @@ public class NotificationTest { .isGreaterThan(ContrastColorUtil.calculateLuminance(background)); .isGreaterThan(ContrastColorUtil.calculateLuminance(background)); } } @Test public void testCallStyle_getSystemActions_forIncomingCall() { PendingIntent answerIntent = createPendingIntent("answer"); PendingIntent declineIntent = createPendingIntent("decline"); Notification.CallStyle style = Notification.CallStyle.forIncomingCall( new Person.Builder().setName("A Caller").build(), declineIntent, answerIntent ); style.setBuilder(new Notification.Builder(mContext, "Channel")); List<Notification.Action> actions = style.getActionsListWithSystemActions(); assertEquals(2, actions.size()); assertEquals(declineIntent, actions.get(0).actionIntent); assertEquals(answerIntent, actions.get(1).actionIntent); } @Test public void testCallStyle_getSystemActions_forOngoingCall() { PendingIntent hangUpIntent = createPendingIntent("hangUp"); Notification.CallStyle style = Notification.CallStyle.forOngoingCall( new Person.Builder().setName("A Caller").build(), hangUpIntent ); style.setBuilder(new Notification.Builder(mContext, "Channel")); List<Notification.Action> actions = style.getActionsListWithSystemActions(); assertEquals(1, actions.size()); assertEquals(hangUpIntent, actions.get(0).actionIntent); } @Test public void testCallStyle_getSystemActions_forIncomingCallWithOtherActions() { PendingIntent answerIntent = createPendingIntent("answer"); PendingIntent declineIntent = createPendingIntent("decline"); Notification.CallStyle style = Notification.CallStyle.forIncomingCall( new Person.Builder().setName("A Caller").build(), declineIntent, answerIntent ); Notification.Action actionToKeep = makeNotificationAction(null); Notification.Action actionToDrop = makeNotificationAction(null); Notification.Builder notifBuilder = new Notification.Builder(mContext, "Channel") .addAction(actionToKeep) .addAction(actionToDrop); //expect to move this action to the end style.setBuilder(notifBuilder); //add a builder with actions List<Notification.Action> actions = style.getActionsListWithSystemActions(); assertEquals(4, actions.size()); assertEquals(declineIntent, actions.get(0).actionIntent); assertEquals(actionToKeep, actions.get(1)); assertEquals(answerIntent, actions.get(2).actionIntent); assertEquals(actionToDrop, actions.get(3)); } @Test public void testCallStyle_getSystemActions_forOngoingCallWithOtherActions() { PendingIntent hangUpIntent = createPendingIntent("hangUp"); Notification.CallStyle style = Notification.CallStyle.forOngoingCall( new Person.Builder().setName("A Caller").build(), hangUpIntent ); Notification.Action firstAction = makeNotificationAction(null); Notification.Action secondAction = makeNotificationAction(null); Notification.Builder notifBuilder = new Notification.Builder(mContext, "Channel") .addAction(firstAction) .addAction(secondAction); style.setBuilder(notifBuilder); //add a builder with actions List<Notification.Action> actions = style.getActionsListWithSystemActions(); assertEquals(3, actions.size()); assertEquals(hangUpIntent, actions.get(0).actionIntent); assertEquals(firstAction, actions.get(1)); assertEquals(secondAction, actions.get(2)); } @Test public void testCallStyle_getSystemActions_dropsOldSystemActions() { PendingIntent hangUpIntent = createPendingIntent("decline"); Notification.CallStyle style = Notification.CallStyle.forOngoingCall( new Person.Builder().setName("A Caller").build(), hangUpIntent ); Bundle actionExtras = new Bundle(); actionExtras.putBoolean("key_action_priority", true); Notification.Action oldSystemAction = makeNotificationAction( builder -> builder.addExtras(actionExtras) ); Notification.Builder notifBuilder = new Notification.Builder(mContext, "Channel") .addAction(oldSystemAction); style.setBuilder(notifBuilder); //add a builder with actions List<Notification.Action> actions = style.getActionsListWithSystemActions(); assertFalse("Old versions of system actions should be dropped.", actions.contains(oldSystemAction)); } @Test @Test public void testBuild_ensureSmallIconIsNotTooBig_resizesIcon() { public void testBuild_ensureSmallIconIsNotTooBig_resizesIcon() { Icon hugeIcon = Icon.createWithBitmap( Icon hugeIcon = Icon.createWithBitmap( Loading Loading @@ -788,7 +892,7 @@ public class NotificationTest { @Test @Test public void testRestoreFromExtras_Call_invalidExtra_noCrash() { public void testRestoreFromExtras_Call_invalidExtra_noCrash() { Notification.Style style = new Notification.CallStyle(); Notification.Style style = new CallStyle(); Bundle fakeTypes = new Bundle(); Bundle fakeTypes = new Bundle(); fakeTypes.putParcelable(EXTRA_CALL_PERSON, new Bundle()); fakeTypes.putParcelable(EXTRA_CALL_PERSON, new Bundle()); fakeTypes.putParcelable(EXTRA_ANSWER_INTENT, new Bundle()); fakeTypes.putParcelable(EXTRA_ANSWER_INTENT, new Bundle()); Loading Loading @@ -962,4 +1066,12 @@ public class NotificationTest { } } return actionBuilder.build(); return actionBuilder.build(); } } /** * Creates a PendingIntent with the given action. */ private PendingIntent createPendingIntent(String action) { return PendingIntent.getActivity(mContext, 0, new Intent(action), PendingIntent.FLAG_MUTABLE); } } } Loading
core/java/android/app/Notification.java +8 −13 Original line number Original line Diff line number Diff line Loading @@ -9591,21 +9591,16 @@ public class Notification implements Parcelable @NonNull @NonNull public ArrayList<Action> getActionsListWithSystemActions() { public ArrayList<Action> getActionsListWithSystemActions() { // Define the system actions we expect to see // Define the system actions we expect to see final Action negativeAction = makeNegativeAction(); final Action firstAction = makeNegativeAction(); final Action answerAction = makeAnswerAction(); final Action lastAction = makeAnswerAction(); // Sort the expected actions into the correct order: // * If there's no answer action, put the hang up / decline action at the end // * Otherwise put the answer action at the end, and put the decline action at start. final Action firstAction = answerAction == null ? null : negativeAction; final Action lastAction = answerAction == null ? negativeAction : answerAction; // Start creating the result list. // Start creating the result list. int nonContextualActionSlotsRemaining = MAX_ACTION_BUTTONS; int nonContextualActionSlotsRemaining = MAX_ACTION_BUTTONS; ArrayList<Action> resultActions = new ArrayList<>(MAX_ACTION_BUTTONS); ArrayList<Action> resultActions = new ArrayList<>(MAX_ACTION_BUTTONS); if (firstAction != null) { // Always have a first action. resultActions.add(firstAction); resultActions.add(firstAction); --nonContextualActionSlotsRemaining; --nonContextualActionSlotsRemaining; } // Copy actions into the new list, correcting system actions. // Copy actions into the new list, correcting system actions. if (mBuilder.mActions != null) { if (mBuilder.mActions != null) { Loading @@ -9621,14 +9616,14 @@ public class Notification implements Parcelable --nonContextualActionSlotsRemaining; --nonContextualActionSlotsRemaining; } } // If there's exactly one action slot left, fill it with the lastAction. // If there's exactly one action slot left, fill it with the lastAction. if (nonContextualActionSlotsRemaining == 1) { if (lastAction != null && nonContextualActionSlotsRemaining == 1) { resultActions.add(lastAction); resultActions.add(lastAction); --nonContextualActionSlotsRemaining; --nonContextualActionSlotsRemaining; } } } } } } // If there are any action slots left, the lastAction still needs to be added. // If there are any action slots left, the lastAction still needs to be added. if (nonContextualActionSlotsRemaining >= 1) { if (lastAction != null && nonContextualActionSlotsRemaining >= 1) { resultActions.add(lastAction); resultActions.add(lastAction); } } return resultActions; return resultActions; Loading
core/tests/coretests/src/android/app/NotificationTest.java +113 −1 Original line number Original line Diff line number Diff line Loading @@ -59,6 +59,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy; import android.annotation.Nullable; import android.annotation.Nullable; import android.app.Notification.CallStyle; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.LocusId; import android.content.LocusId; Loading Loading @@ -92,6 +93,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import java.util.List; import java.util.function.Consumer; import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class) Loading Loading @@ -530,6 +532,108 @@ public class NotificationTest { .isGreaterThan(ContrastColorUtil.calculateLuminance(background)); .isGreaterThan(ContrastColorUtil.calculateLuminance(background)); } } @Test public void testCallStyle_getSystemActions_forIncomingCall() { PendingIntent answerIntent = createPendingIntent("answer"); PendingIntent declineIntent = createPendingIntent("decline"); Notification.CallStyle style = Notification.CallStyle.forIncomingCall( new Person.Builder().setName("A Caller").build(), declineIntent, answerIntent ); style.setBuilder(new Notification.Builder(mContext, "Channel")); List<Notification.Action> actions = style.getActionsListWithSystemActions(); assertEquals(2, actions.size()); assertEquals(declineIntent, actions.get(0).actionIntent); assertEquals(answerIntent, actions.get(1).actionIntent); } @Test public void testCallStyle_getSystemActions_forOngoingCall() { PendingIntent hangUpIntent = createPendingIntent("hangUp"); Notification.CallStyle style = Notification.CallStyle.forOngoingCall( new Person.Builder().setName("A Caller").build(), hangUpIntent ); style.setBuilder(new Notification.Builder(mContext, "Channel")); List<Notification.Action> actions = style.getActionsListWithSystemActions(); assertEquals(1, actions.size()); assertEquals(hangUpIntent, actions.get(0).actionIntent); } @Test public void testCallStyle_getSystemActions_forIncomingCallWithOtherActions() { PendingIntent answerIntent = createPendingIntent("answer"); PendingIntent declineIntent = createPendingIntent("decline"); Notification.CallStyle style = Notification.CallStyle.forIncomingCall( new Person.Builder().setName("A Caller").build(), declineIntent, answerIntent ); Notification.Action actionToKeep = makeNotificationAction(null); Notification.Action actionToDrop = makeNotificationAction(null); Notification.Builder notifBuilder = new Notification.Builder(mContext, "Channel") .addAction(actionToKeep) .addAction(actionToDrop); //expect to move this action to the end style.setBuilder(notifBuilder); //add a builder with actions List<Notification.Action> actions = style.getActionsListWithSystemActions(); assertEquals(4, actions.size()); assertEquals(declineIntent, actions.get(0).actionIntent); assertEquals(actionToKeep, actions.get(1)); assertEquals(answerIntent, actions.get(2).actionIntent); assertEquals(actionToDrop, actions.get(3)); } @Test public void testCallStyle_getSystemActions_forOngoingCallWithOtherActions() { PendingIntent hangUpIntent = createPendingIntent("hangUp"); Notification.CallStyle style = Notification.CallStyle.forOngoingCall( new Person.Builder().setName("A Caller").build(), hangUpIntent ); Notification.Action firstAction = makeNotificationAction(null); Notification.Action secondAction = makeNotificationAction(null); Notification.Builder notifBuilder = new Notification.Builder(mContext, "Channel") .addAction(firstAction) .addAction(secondAction); style.setBuilder(notifBuilder); //add a builder with actions List<Notification.Action> actions = style.getActionsListWithSystemActions(); assertEquals(3, actions.size()); assertEquals(hangUpIntent, actions.get(0).actionIntent); assertEquals(firstAction, actions.get(1)); assertEquals(secondAction, actions.get(2)); } @Test public void testCallStyle_getSystemActions_dropsOldSystemActions() { PendingIntent hangUpIntent = createPendingIntent("decline"); Notification.CallStyle style = Notification.CallStyle.forOngoingCall( new Person.Builder().setName("A Caller").build(), hangUpIntent ); Bundle actionExtras = new Bundle(); actionExtras.putBoolean("key_action_priority", true); Notification.Action oldSystemAction = makeNotificationAction( builder -> builder.addExtras(actionExtras) ); Notification.Builder notifBuilder = new Notification.Builder(mContext, "Channel") .addAction(oldSystemAction); style.setBuilder(notifBuilder); //add a builder with actions List<Notification.Action> actions = style.getActionsListWithSystemActions(); assertFalse("Old versions of system actions should be dropped.", actions.contains(oldSystemAction)); } @Test @Test public void testBuild_ensureSmallIconIsNotTooBig_resizesIcon() { public void testBuild_ensureSmallIconIsNotTooBig_resizesIcon() { Icon hugeIcon = Icon.createWithBitmap( Icon hugeIcon = Icon.createWithBitmap( Loading Loading @@ -788,7 +892,7 @@ public class NotificationTest { @Test @Test public void testRestoreFromExtras_Call_invalidExtra_noCrash() { public void testRestoreFromExtras_Call_invalidExtra_noCrash() { Notification.Style style = new Notification.CallStyle(); Notification.Style style = new CallStyle(); Bundle fakeTypes = new Bundle(); Bundle fakeTypes = new Bundle(); fakeTypes.putParcelable(EXTRA_CALL_PERSON, new Bundle()); fakeTypes.putParcelable(EXTRA_CALL_PERSON, new Bundle()); fakeTypes.putParcelable(EXTRA_ANSWER_INTENT, new Bundle()); fakeTypes.putParcelable(EXTRA_ANSWER_INTENT, new Bundle()); Loading Loading @@ -962,4 +1066,12 @@ public class NotificationTest { } } return actionBuilder.build(); return actionBuilder.build(); } } /** * Creates a PendingIntent with the given action. */ private PendingIntent createPendingIntent(String action) { return PendingIntent.getActivity(mContext, 0, new Intent(action), PendingIntent.FLAG_MUTABLE); } } }