• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.devicelockcontroller.activities;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import android.annotation.SuppressLint;
22 import android.app.Notification;
23 import android.app.NotificationChannel;
24 import android.app.NotificationManager;
25 import android.app.PendingIntent;
26 import android.content.Context;
27 
28 import androidx.core.app.NotificationCompat;
29 import androidx.core.app.NotificationManagerCompat;
30 
31 import com.android.devicelockcontroller.R;
32 import com.android.devicelockcontroller.storage.SetupParametersClient;
33 import com.android.devicelockcontroller.util.LogUtil;
34 import com.android.devicelockcontroller.util.StringUtil;
35 
36 import com.google.common.util.concurrent.FutureCallback;
37 import com.google.common.util.concurrent.Futures;
38 import com.google.common.util.concurrent.ListenableFuture;
39 
40 import java.time.Instant;
41 import java.time.format.DateTimeFormatter;
42 import java.time.format.FormatStyle;
43 
44 /**
45  * A utility class used to send notification.
46  */
47 public final class DeviceLockNotificationManager {
48 
49     private static final String TAG = "DeviceLockNotificationManager";
50 
51     private static final String PROVISION_NOTIFICATION_CHANNEL_ID = "devicelock-provision";
52     private static final String DEVICE_RESET_NOTIFICATION_TAG = "devicelock-device-reset";
53     private static final int DEVICE_RESET_NOTIFICATION_ID = 0;
54     private static final int DEFER_ENROLLMENT_NOTIFICATION_ID = 1;
55 
56     /**
57      * Similar to {@link #sendDeviceResetNotification(Context, int)}, except that:
58      * 1. The number of days to reset is always one.
59      * 2. The notification is ongoing.
60      */
sendDeviceResetInOneDayOngoingNotification(Context context)61     public static void sendDeviceResetInOneDayOngoingNotification(Context context) {
62         sendDeviceResetNotification(context, /* days= */ 1, /* ongoing= */ true);
63     }
64 
65     /**
66      * Send the device reset notification. The call is thread safe and can be called from any
67      * thread.
68      *
69      * @param context the context where the notification will be sent out
70      * @param days    the number of days the reset will happen
71      */
sendDeviceResetNotification(Context context, int days)72     public static void sendDeviceResetNotification(Context context, int days) {
73         sendDeviceResetNotification(context, days, /* ongoing= */ false);
74     }
75 
sendDeviceResetNotification(Context context, int days, boolean ongoing)76     private static void sendDeviceResetNotification(Context context, int days, boolean ongoing) {
77         // TODO: check/request permission first
78 
79         // re-creating the same notification channel is essentially no-op
80         createNotificationChannel(context);
81         NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
82         Futures.addCallback(createDeviceResetNotification(context, days, ongoing),
83                 new FutureCallback<>() {
84                     @Override
85                     public void onSuccess(Notification notification) {
86                         LogUtil.d(TAG, "send device reset notification");
87                         notificationManager.notify(DEVICE_RESET_NOTIFICATION_TAG,
88                                 DEVICE_RESET_NOTIFICATION_ID, notification);
89                     }
90 
91                     @Override
92                     public void onFailure(Throwable t) {
93                         LogUtil.e(TAG, "Failed to create device reset notification", t);
94                     }
95                 }, context.getMainExecutor());
96     }
97 
98     // Already requested POST_NOTIFICATION permission in ProvisionInfoFragment
99     @SuppressLint("MissingPermission")
sendDeferredEnrollmentNotification(Context context, Instant resumeTime, PendingIntent pendingIntent)100     public static void sendDeferredEnrollmentNotification(Context context,
101             Instant resumeTime, PendingIntent pendingIntent) {
102         createNotificationChannel(context);
103         DateTimeFormatter timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);
104         String enrollmentResumeTime = timeFormatter.format(resumeTime);
105         NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
106                 context, PROVISION_NOTIFICATION_CHANNEL_ID)
107                 .setContentTitle(context.getString(R.string.device_enrollment_header_text))
108                 .setContentText(context.getString(R.string.device_enrollment_notification_body_text,
109                         enrollmentResumeTime))
110                 .setSmallIcon(R.drawable.ic_action_lock)
111                 .setContentIntent(pendingIntent)
112                 .setOngoing(true);
113         NotificationManagerCompat notificationManager =
114                 NotificationManagerCompat.from(context);
115         notificationManager.notify(DEFER_ENROLLMENT_NOTIFICATION_ID, notificationBuilder.build());
116     }
117 
createDeviceResetNotification(Context context, int days, boolean onging)118     private static ListenableFuture<Notification> createDeviceResetNotification(Context context,
119             int days, boolean onging) {
120         return Futures.transform(SetupParametersClient.getInstance().getKioskAppProviderName(),
121                 providerName ->
122                         // TODO: update the icon
123                         new NotificationCompat.Builder(context, PROVISION_NOTIFICATION_CHANNEL_ID)
124                                 .setSmallIcon(R.drawable.ic_action_lock)
125                                 .setOngoing(onging)
126                                 .setContentTitle(StringUtil.getPluralString(context, days,
127                                         R.string.device_reset_in_days_notification_title))
128                                 .setContentText(context.getString(
129                                         R.string.device_reset_notification_content,
130                                         providerName)).build(),
131                 context.getMainExecutor());
132     }
133 
createNotificationChannel(Context context)134     private static void createNotificationChannel(Context context) {
135         NotificationManager notificationManager = context.getSystemService(
136                 NotificationManager.class);
137         checkNotNull(notificationManager);
138         NotificationChannel notificationChannel = new NotificationChannel(
139                 PROVISION_NOTIFICATION_CHANNEL_ID,
140                 context.getString(R.string.provision_notification_channel_name),
141                 NotificationManager.IMPORTANCE_HIGH);
142         notificationManager.createNotificationChannel(notificationChannel);
143     }
144 }
145