Home | History | Annotate | Download | only in functional
      1 /*
      2  * Copyright (C) 2016 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 com.android.notification.functional;
     18 
     19 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
     20 import static android.app.NotificationManager.IMPORTANCE_LOW;
     21 
     22 import android.app.Instrumentation;
     23 import android.app.IntentService;
     24 import android.app.KeyguardManager;
     25 import android.app.Notification;
     26 import android.app.NotificationChannel;
     27 import android.app.NotificationManager;
     28 import android.app.PendingIntent;
     29 import android.app.RemoteInput;
     30 import android.content.Context;
     31 import android.content.Intent;
     32 import android.graphics.Typeface;
     33 import android.os.Handler;
     34 import android.os.RemoteException;
     35 import android.provider.Settings;
     36 import android.service.notification.StatusBarNotification;
     37 import android.support.test.uiautomator.By;
     38 import android.support.test.uiautomator.UiDevice;
     39 import android.support.test.uiautomator.UiObject2;
     40 import android.support.test.uiautomator.UiObjectNotFoundException;
     41 import android.support.test.uiautomator.UiSelector;
     42 import android.support.test.uiautomator.Until;
     43 import android.text.SpannableStringBuilder;
     44 import android.text.style.StyleSpan;
     45 import android.util.Log;
     46 import android.widget.EditText;
     47 import android.widget.ListView;
     48 import android.widget.TextView;
     49 import android.widget.Toast;
     50 
     51 import com.android.notification.functional.R;
     52 
     53 import java.lang.InterruptedException;
     54 import java.util.List;
     55 import java.util.Map;
     56 
     57 
     58 public class NotificationHelper {
     59 
     60     private static final String LOG_TAG = NotificationHelper.class.getSimpleName();
     61     private static final int LONG_TIMEOUT = 2500;
     62     private static final int SHORT_TIMEOUT = 200;
     63     private static final String KEY_QUICK_REPLY_TEXT = "quick_reply";
     64     private static final UiSelector LIST_VIEW = new UiSelector().className(ListView.class);
     65     private static final UiSelector LIST_ITEM_VALUE = new UiSelector().className(TextView.class);
     66     public static final String FIRST_ACTION = "FIRST ACTION";
     67     public static final String SECOND_ACTION = "SECOND ACTION";
     68     public static final String CONTENT_TITLE = "THIS IS A NOTIFICATION";
     69     private static final String BUZZY_CHANNEL_ID = "com.android.notification.functional.buzzy";
     70     private static final String QUIET_CHANNEL_ID = "com.android.notification.functional.quiet";
     71     private NotificationChannel mBuzzyChannel;
     72     private NotificationChannel mQuietChannel;
     73 
     74     private UiDevice mDevice;
     75     private Instrumentation mInst;
     76     private NotificationManager mNotificationManager = null;
     77     private Context mContext = null;
     78 
     79     public NotificationHelper(UiDevice device, Instrumentation inst, NotificationManager nm) {
     80         this.mDevice = device;
     81         mInst = inst;
     82         mNotificationManager = nm;
     83         mContext = inst.getContext();
     84 
     85         // create the channels we need
     86         mBuzzyChannel = getChannel(true);
     87         mQuietChannel = getChannel(false);
     88     }
     89 
     90     public void sleepAndWakeUpDevice() throws RemoteException, InterruptedException {
     91         mDevice.sleep();
     92         Thread.sleep(LONG_TIMEOUT);
     93         mDevice.wakeUp();
     94     }
     95 
     96     public static void launchSettingsPage(Context ctx, String pageName) throws Exception {
     97         Intent intent = new Intent(pageName);
     98         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     99         ctx.startActivity(intent);
    100         Thread.sleep(LONG_TIMEOUT * 2);
    101     }
    102 
    103     public void enableNotificationViaAdb(boolean isShow) {
    104         String command = String.format(" %s %s %s %s %s", "settings", "put", "secure",
    105                 "lock_screen_show_notifications",
    106                 isShow ? "1" : "0");
    107         executeAdbCommand(command);
    108     }
    109 
    110     public void executeAdbCommand(String command) {
    111         Log.i(LOG_TAG, String.format("executing - %s", command));
    112         mInst.getUiAutomation().executeShellCommand(command);
    113         mDevice.waitForIdle();
    114     }
    115 
    116     private void navigateToScreenLock() throws Exception {
    117         launchSettingsPage(mInst.getContext(), Settings.ACTION_SECURITY_SETTINGS);
    118         mDevice.wait(Until.findObject(By.text("Screen lock")), LONG_TIMEOUT).click();
    119     }
    120 
    121     public void sendNotification(int id, int visibility, String title) throws Exception {
    122         sendNotification(id, visibility, title, false);
    123     }
    124 
    125     public void sendNotification(int id, int visibility, String title, boolean buzz)
    126             throws Exception {
    127         Log.v(LOG_TAG, "Sending out notification...");
    128         PendingIntent emptyIntent = PendingIntent.getBroadcast(mContext, 0,
    129                 new Intent("an.action.that.nobody.will.be.listening.for"), 0);
    130         Intent intent = new Intent(Intent.ACTION_VIEW);
    131         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
    132         CharSequence subtitle = String.valueOf(System.currentTimeMillis());
    133         Notification.Builder notification = new Notification.Builder(mContext)
    134                 .setSmallIcon(R.drawable.stat_notify_email)
    135                 .setWhen(System.currentTimeMillis())
    136                 .setContentTitle(title)
    137                 .setContentText(subtitle)
    138                 .setContentIntent(pendingIntent)
    139                 .setVisibility(visibility)
    140                 .setChannelId(buzz ? BUZZY_CHANNEL_ID : QUIET_CHANNEL_ID)
    141                 .addAction(new Notification.Action.Builder(R.drawable.stat_notify_email,
    142                         FIRST_ACTION, emptyIntent)
    143                         .build())
    144                 .addAction(new Notification.Action.Builder(R.drawable.stat_notify_email,
    145                         SECOND_ACTION, emptyIntent)
    146                         .build())
    147                 .setAutoCancel(false);
    148         mNotificationManager.notify(id, notification.build());
    149         Thread.sleep(LONG_TIMEOUT);
    150     }
    151 
    152     public void sendNotifications(Map<Integer, String> lists, boolean withDelay) throws Exception {
    153         Log.v(LOG_TAG, "Sending out notification...");
    154         CharSequence subtitle = String.valueOf(System.currentTimeMillis());
    155         for (Map.Entry<Integer, String> l : lists.entrySet()) {
    156             Notification.Builder notification = new Notification.Builder(mContext)
    157                     .setSmallIcon(R.drawable.stat_notify_email)
    158                     .setWhen(System.currentTimeMillis()).setContentTitle(l.getValue())
    159                     .setContentTitle(CONTENT_TITLE)
    160                     .setContentText(subtitle);
    161             mNotificationManager.notify(l.getKey(), notification.build());
    162             if (withDelay) {
    163                 Thread.sleep(SHORT_TIMEOUT);
    164             }
    165         }
    166         Thread.sleep(LONG_TIMEOUT);
    167     }
    168 
    169     public void sendBundlingNotifications(List<Integer> lists, String groupKey) throws Exception {
    170         Notification childNotification = new Notification.Builder(mContext)
    171                 .setContentTitle(lists.get(1).toString())
    172                 .setSmallIcon(R.drawable.stat_notify_email)
    173                 .setGroup(groupKey)
    174                 .build();
    175         mNotificationManager.notify(lists.get(1),
    176                 childNotification);
    177         childNotification = new Notification.Builder(mContext)
    178                 .setContentText(lists.get(2).toString())
    179                 .setSmallIcon(R.drawable.stat_notify_email)
    180                 .setGroup(groupKey)
    181                 .build();
    182         mNotificationManager.notify(lists.get(2),
    183                 childNotification);
    184         Notification notification = new Notification.Builder(mContext)
    185                 .setContentTitle(lists.get(0).toString())
    186                 .setSubText(groupKey)
    187                 .setSmallIcon(R.drawable.stat_notify_email)
    188                 .setGroup(groupKey)
    189                 .setGroupSummary(true)
    190                 .build();
    191         mNotificationManager.notify(lists.get(0),
    192                 notification);
    193     }
    194 
    195     static SpannableStringBuilder BOLD(CharSequence str) {
    196         final SpannableStringBuilder ssb = new SpannableStringBuilder(str);
    197         ssb.setSpan(new StyleSpan(Typeface.BOLD), 0, ssb.length(), 0);
    198         return ssb;
    199     }
    200 
    201     public boolean checkNotificationExistence(int id, boolean exists) throws Exception {
    202         boolean isFound = false;
    203         for (int tries = 3; tries-- > 0;) {
    204             isFound = false;
    205             StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
    206             for (StatusBarNotification sbn : sbns) {
    207                 if (sbn.getId() == id) {
    208                     isFound = true;
    209                     break;
    210                 }
    211             }
    212             if (isFound == exists) {
    213                 break;
    214             }
    215             Thread.sleep(SHORT_TIMEOUT);
    216         }
    217         Log.i(LOG_TAG, "checkNotificationExistence..." + isFound);
    218         return isFound == exists;
    219     }
    220 
    221     public StatusBarNotification getStatusBarNotification(int id) {
    222         StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
    223         StatusBarNotification n = null;
    224         for (StatusBarNotification sbn : sbns) {
    225             if (sbn.getId() == id) {
    226                 n = sbn;
    227                 break;
    228             }
    229         }
    230         return n;
    231     }
    232 
    233     public void swipeUp() throws Exception {
    234         mDevice.swipe(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight()*3/4,
    235                 mDevice.getDisplayWidth() / 2, 0, 30);
    236         Thread.sleep(SHORT_TIMEOUT);
    237     }
    238 
    239     public void swipeDown() throws Exception {
    240         mDevice.swipe(mDevice.getDisplayWidth() / 2, 0, mDevice.getDisplayWidth() / 2,
    241                 mDevice.getDisplayHeight() / 2 + 50, 20);
    242         Thread.sleep(SHORT_TIMEOUT);
    243     }
    244 
    245     public void unlockScreen() throws Exception {
    246         KeyguardManager myKM = (KeyguardManager) mContext
    247                 .getSystemService(Context.KEYGUARD_SERVICE);
    248         if (myKM.inKeyguardRestrictedInputMode()) {
    249             // it is locked
    250             swipeUp();
    251         }
    252     }
    253 
    254     public void showAppNotificationSettings(Context context) throws Exception {
    255         Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
    256         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    257         intent.putExtra(Settings.EXTRA_APP_PACKAGE, mContext.getPackageName());
    258         intent.putExtra(Settings.EXTRA_APP_UID, mContext.getApplicationInfo().uid);
    259         context.startActivity(intent);
    260         Thread.sleep(LONG_TIMEOUT * 2);
    261     }
    262 
    263     public void sendNotificationsWithInlineReply(int notificationId, boolean isHeadsUp) {
    264         Notification.Action action = new Notification.Action.Builder(
    265                 R.drawable.stat_notify_email, "Reply", ToastService.getPendingIntent(mContext,
    266                         "inline reply test"))
    267                                 .addRemoteInput(new RemoteInput.Builder(KEY_QUICK_REPLY_TEXT)
    268                                         .setLabel("Quick reply").build())
    269                                 .build();
    270         Notification.Builder n = new Notification.Builder(mContext)
    271                 .setContentTitle(Integer.toString(notificationId))
    272                 .setContentText("INLINE REPLY TEST")
    273                 .setWhen(System.currentTimeMillis())
    274                 .setSmallIcon(R.drawable.stat_notify_email)
    275                 .addAction(action);
    276         if (isHeadsUp) {
    277             n.setPriority(Notification.PRIORITY_HIGH)
    278                     .setDefaults(Notification.DEFAULT_VIBRATE);
    279         }
    280         mNotificationManager.notify(notificationId, n.build());
    281     }
    282 
    283     public NotificationChannel getDefaultChannel() {
    284         return mNotificationManager.getNotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID);
    285     }
    286 
    287     public NotificationChannel getChannel(boolean buzz) {
    288         String id = (buzz ? BUZZY_CHANNEL_ID : QUIET_CHANNEL_ID);
    289         String name = (buzz ? "This channel is buzzy" : "This channel is quiet");
    290         int importance = (buzz ? IMPORTANCE_DEFAULT : IMPORTANCE_LOW);
    291 
    292         NotificationChannel channel = (buzz ? mBuzzyChannel : mQuietChannel);
    293         if (channel == null) {
    294             channel = mNotificationManager.getNotificationChannel(id);
    295         }
    296         if (channel == null){
    297             channel = new NotificationChannel(id, name, importance);
    298             if (buzz) {
    299                 channel.enableVibration(true);
    300                 channel.setSound(null, null);
    301             }
    302             mNotificationManager.createNotificationChannel(channel);
    303         }
    304         return channel;
    305     }
    306 
    307     public static class ToastService extends IntentService {
    308         private static final String TAG = "ToastService";
    309         private static final String ACTION_TOAST = "toast";
    310         private Handler handler;
    311 
    312         public ToastService() {
    313             super(TAG);
    314         }
    315 
    316         public ToastService(String name) {
    317             super(name);
    318         }
    319 
    320         @Override
    321         public int onStartCommand(Intent intent, int flags, int startId) {
    322             handler = new Handler();
    323             return super.onStartCommand(intent, flags, startId);
    324         }
    325 
    326         @Override
    327         protected void onHandleIntent(Intent intent) {
    328             if (intent.hasExtra("text")) {
    329                 final String text = intent.getStringExtra("text");
    330                 handler.post(new Runnable() {
    331                     @Override
    332                     public void run() {
    333                         Toast.makeText(ToastService.this, text, Toast.LENGTH_LONG).show();
    334                         Log.v(TAG, "toast " + text);
    335                     }
    336                 });
    337             }
    338         }
    339 
    340         public static PendingIntent getPendingIntent(Context context, String text) {
    341             Intent toastIntent = new Intent(context, ToastService.class);
    342             toastIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    343             toastIntent.setAction(ACTION_TOAST + ":" + text); // one per toast message
    344             toastIntent.putExtra("text", text);
    345             PendingIntent pi = PendingIntent.getService(
    346                     context, 58, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    347             return pi;
    348         }
    349     }
    350 }
    351