1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.app.stubs;
18 
19 import static android.app.stubs.BubbledActivity.EXTRA_LOCUS_ID;
20 
21 import android.app.Activity;
22 import android.app.Notification;
23 import android.app.Notification.BubbleMetadata;
24 import android.app.NotificationManager;
25 import android.app.PendingIntent;
26 import android.app.Person;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.LocusId;
30 import android.graphics.drawable.Icon;
31 import android.os.Bundle;
32 import android.os.SystemClock;
33 
34 /**
35  * Used by NotificationManagerTest for testing policy around bubbles, this activity is able to
36  * send a bubble.
37  */
38 public class SendBubbleActivity extends Activity {
39     final String TAG = SendBubbleActivity.class.getSimpleName();
40 
41     // Should be same as what NotificationManagerTest is using
42     private static final String NOTIFICATION_CHANNEL_ID = "NotificationManagerTest";
43     private static final String SHARE_SHORTCUT_ID = "shareShortcut";
44 
45     public static final String BUBBLE_ACTIVITY_OPENED =
46             "android.app.stubs.BUBBLE_ACTIVITY_OPENED";
47     public static final int BUBBLE_NOTIF_ID = 1;
48 
49     private volatile boolean mIsStopped;
50 
51     @Override
onCreate(Bundle savedInstanceState)52     public void onCreate(Bundle savedInstanceState) {
53         super.onCreate(savedInstanceState);
54         setContentView(R.layout.main);
55 
56         Intent i = new Intent(BUBBLE_ACTIVITY_OPENED);
57         sendBroadcast(i);
58     }
59 
startBubbleActivity(int id)60     public void startBubbleActivity(int id) {
61         startBubbleActivity(id, true /* addLocusId */);
62     }
63 
64     /**
65      * Starts the same activity that is in the bubble produced by this activity.
66      */
startBubbleActivity(int id, boolean addLocusId)67     public void startBubbleActivity(int id, boolean addLocusId) {
68         final Intent intent = new Intent(getApplicationContext(), BubbledActivity.class);
69         // Clear any previous instance of this activity
70         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
71         if (addLocusId) {
72             intent.putExtra(EXTRA_LOCUS_ID, String.valueOf(id));
73         }
74         startActivity(intent);
75     }
76 
77     /**
78      * Sends a notification that has bubble metadata but the rest of the notification isn't
79      * configured correctly so the system won't allow it to bubble.
80      */
sendInvalidBubble(int notifId, boolean autoExpand)81     public void sendInvalidBubble(int notifId, boolean autoExpand) {
82         Context context = getApplicationContext();
83 
84         PendingIntent pendingIntent = PendingIntent.getActivity(context, notifId, new Intent(),
85                 PendingIntent.FLAG_MUTABLE);
86         Notification n = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
87                 .setSmallIcon(R.drawable.black)
88                 .setWhen(System.currentTimeMillis())
89                 .setContentTitle("notify#" + notifId)
90                 .setContentText("This is #" + notifId + "notification  ")
91                 .setContentIntent(pendingIntent)
92                 .setBubbleMetadata(getBubbleMetadata(notifId, autoExpand,
93                         false /* suppressNotification */,
94                         false /* suppressBubble */,
95                         false /* useShortcut */))
96                 .build();
97 
98         NotificationManager noMan = (NotificationManager) context.getSystemService(
99                 Context.NOTIFICATION_SERVICE);
100         noMan.notify(notifId, n);
101     }
102 
103     /** Sends a notification that is properly configured to bubble. */
sendBubble(int notifId, boolean autoExpand, boolean suppressNotification)104     public void sendBubble(int notifId, boolean autoExpand, boolean suppressNotification) {
105         sendBubble(notifId, autoExpand, suppressNotification, false /* suppressBubble */,
106                 false /* useShortcut */, true /* setLocusId */);
107     }
108 
109     /** Sends a notification that is properly configured to bubble. */
sendBubble(int notifId, boolean autoExpand, boolean suppressNotification, boolean suppressBubble)110     public void sendBubble(int notifId, boolean autoExpand, boolean suppressNotification,
111             boolean suppressBubble) {
112         sendBubble(notifId, autoExpand, suppressNotification, suppressBubble,
113                 false /* useShortcut */, true /* setLocusId */);
114     }
115 
116     /** Sends a notification that is properly configured to bubble. */
sendBubble(int notifId, boolean autoExpand, boolean suppressNotification, boolean suppressBubble, boolean useShortcut, boolean setLocusId)117     public void sendBubble(int notifId, boolean autoExpand, boolean suppressNotification,
118             boolean suppressBubble, boolean useShortcut, boolean setLocusId) {
119         Context context = getApplicationContext();
120         // Give it a person
121         Person person = new Person.Builder()
122                 .setName("bubblebot" + notifId)
123                 .build();
124         // Make it messaging style
125         Notification.Builder nb = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
126                 .setSmallIcon(R.drawable.black)
127                 .setContentTitle("Bubble Chat")
128                 .setShortcutId(SHARE_SHORTCUT_ID)
129                 .setStyle(new Notification.MessagingStyle(person)
130                         .setConversationTitle("Bubble Chat")
131                         .addMessage("Hello?",
132                                 SystemClock.currentThreadTimeMillis() - 300000, person)
133                         .addMessage("Is it me you're looking for?",
134                                 SystemClock.currentThreadTimeMillis(), person)
135                 )
136                 .setBubbleMetadata(getBubbleMetadata(notifId,
137                         autoExpand,
138                         suppressNotification,
139                         suppressBubble,
140                         useShortcut));
141 
142         if (setLocusId) {
143             nb.setLocusId(new LocusId(String.valueOf(notifId)));
144         }
145 
146         NotificationManager noMan = (NotificationManager) context.getSystemService(
147                 Context.NOTIFICATION_SERVICE);
148         noMan.notify(notifId, nb.build());
149     }
150 
getBubbleMetadata(int notifId, boolean autoExpand, boolean suppressNotification, boolean suppressBubble, boolean useShortcut)151     private BubbleMetadata getBubbleMetadata(int notifId, boolean autoExpand,
152             boolean suppressNotification,
153             boolean suppressBubble,
154             boolean useShortcut) {
155         if (useShortcut) {
156             return new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
157                     .setAutoExpandBubble(autoExpand)
158                     .setSuppressableBubble(suppressBubble)
159                     .setSuppressNotification(suppressNotification)
160                     .build();
161         } else {
162             Context context = getApplicationContext();
163             final Intent intent = new Intent(context, BubbledActivity.class);
164             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
165             intent.setAction(Intent.ACTION_MAIN);
166             final PendingIntent pendingIntent =
167                     PendingIntent.getActivity(context, notifId, intent,
168                             PendingIntent.FLAG_MUTABLE);
169 
170             return new Notification.BubbleMetadata.Builder(pendingIntent,
171                     Icon.createWithResource(context, R.drawable.black))
172                     .setAutoExpandBubble(autoExpand)
173                     .setSuppressNotification(suppressNotification)
174                     .setSuppressableBubble(suppressBubble)
175                     .build();
176         }
177     }
178 
179     /** Waits for the activity to be stopped. Do not call this method on main thread. */
waitForStopped()180     public void waitForStopped() {
181         synchronized (this) {
182             while (!mIsStopped) {
183                 try {
184                     wait(5000 /* timeout */);
185                 } catch (InterruptedException ignored) {
186                 }
187             }
188         }
189     }
190 
191     @Override
onStart()192     protected void onStart() {
193         super.onStart();
194         mIsStopped = false;
195     }
196 
197     @Override
onStop()198     protected void onStop() {
199         super.onStop();
200         synchronized (this) {
201             mIsStopped = true;
202             notifyAll();
203         }
204     }
205 }
206