• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.adservices.ui.notifications;
18 
19 import static com.android.adservices.ui.notifications.ConsentNotificationFragment.IS_EU_DEVICE_ARGUMENT_KEY;
20 
21 import android.app.Notification;
22 import android.app.NotificationChannel;
23 import android.app.NotificationManager;
24 import android.app.PendingIntent;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.os.Build;
28 
29 import androidx.annotation.NonNull;
30 import androidx.annotation.RequiresApi;
31 import androidx.core.app.NotificationCompat;
32 import androidx.core.app.NotificationManagerCompat;
33 
34 import com.android.adservices.api.R;
35 import com.android.adservices.service.FlagsFactory;
36 import com.android.adservices.service.consent.AdServicesApiType;
37 import com.android.adservices.service.consent.ConsentManager;
38 import com.android.adservices.service.stats.UiStatsLogger;
39 import com.android.adservices.ui.OTAResourcesManager;
40 
41 /** Provides methods which can be used to display Privacy Sandbox consent notification. */
42 // TODO(b/269798827): Enable for R.
43 @RequiresApi(Build.VERSION_CODES.S)
44 public class ConsentNotificationTrigger {
45     /* Random integer for NotificationCompat purposes. */
46     public static final int NOTIFICATION_ID = 67920;
47     private static final String CHANNEL_ID = "PRIVACY_SANDBOX_CHANNEL";
48     private static final int NOTIFICATION_PRIORITY = NotificationCompat.PRIORITY_MAX;
49 
50     /**
51      * Shows consent notification as the highest priority notification to the user.
52      *
53      * @param context Context which is used to display {@link NotificationCompat}
54      */
showConsentNotification(@onNull Context context, boolean isEuDevice)55     public static void showConsentNotification(@NonNull Context context, boolean isEuDevice) {
56         UiStatsLogger.logRequestedNotification(context);
57 
58         boolean gaUxFeatureEnabled = FlagsFactory.getFlags().getGaUxFeatureEnabled();
59 
60         NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
61         ConsentManager consentManager = ConsentManager.getInstance(context);
62         if (!notificationManager.areNotificationsEnabled()) {
63             recordNotificationDisplayed(gaUxFeatureEnabled, consentManager);
64             UiStatsLogger.logNotificationDisabled(context);
65             return;
66         }
67 
68         // Set OTA resources if it exists.
69         if (FlagsFactory.getFlags().getUiOtaStringsFeatureEnabled()) {
70             OTAResourcesManager.applyOTAResources(context.getApplicationContext(), true);
71         }
72 
73         setupConsents(context, isEuDevice, gaUxFeatureEnabled, consentManager);
74 
75         createNotificationChannel(context);
76         Notification notification = getNotification(context, isEuDevice, gaUxFeatureEnabled);
77         notificationManager.notify(NOTIFICATION_ID, notification);
78 
79         UiStatsLogger.logNotificationDisplayed(context);
80         recordNotificationDisplayed(gaUxFeatureEnabled, consentManager);
81     }
82 
recordNotificationDisplayed( boolean gaUxFeatureEnabled, ConsentManager consentManager)83     private static void recordNotificationDisplayed(
84             boolean gaUxFeatureEnabled, ConsentManager consentManager) {
85         if (FlagsFactory.getFlags().getRecordManualInteractionEnabled()
86                 && consentManager.getUserManualInteractionWithConsent()
87                         != ConsentManager.MANUAL_INTERACTIONS_RECORDED) {
88             consentManager.recordUserManualInteractionWithConsent(
89                     ConsentManager.NO_MANUAL_INTERACTIONS_RECORDED);
90         }
91         if (gaUxFeatureEnabled) {
92             consentManager.recordGaUxNotificationDisplayed();
93         }
94         consentManager.recordNotificationDisplayed();
95     }
96 
97     @NonNull
getNotification( @onNull Context context, boolean isEuDevice, boolean gaUxFeatureEnabled)98     private static Notification getNotification(
99             @NonNull Context context, boolean isEuDevice, boolean gaUxFeatureEnabled) {
100         Notification notification;
101         if (gaUxFeatureEnabled) {
102             if (FlagsFactory.getFlags().getEuNotifFlowChangeEnabled()) {
103                 notification = getGaV2ConsentNotification(context, isEuDevice);
104             } else {
105                 notification = getGaConsentNotification(context, isEuDevice);
106             }
107         } else {
108             notification = getConsentNotification(context, isEuDevice);
109         }
110 
111         // make notification sticky (non-dismissible) for EuDevices when the GA UX feature is on
112         if (gaUxFeatureEnabled && isEuDevice) {
113             notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
114         }
115         return notification;
116     }
117 
118     // setup default consents based on information whether the device is EU or non-EU device and
119     // GA UX feature flag is enabled.
setupConsents( @onNull Context context, boolean isEuDevice, boolean gaUxFeatureEnabled, ConsentManager consentManager)120     private static void setupConsents(
121             @NonNull Context context,
122             boolean isEuDevice,
123             boolean gaUxFeatureEnabled,
124             ConsentManager consentManager) {
125         // Keep the feature flag at the upper level to make it easier to cleanup the code once
126         // the beta functionality is fully deprecated and abandoned.
127         if (gaUxFeatureEnabled) {
128             // EU: all APIs are by default disabled
129             // ROW: all APIs are by default enabled
130             // TODO(b/260266623): change consent state to UNDEFINED
131             if (isEuDevice) {
132                 consentManager.recordTopicsDefaultConsent(false);
133                 consentManager.recordFledgeDefaultConsent(false);
134                 consentManager.recordMeasurementDefaultConsent(false);
135 
136                 consentManager.disable(context, AdServicesApiType.TOPICS);
137                 consentManager.disable(context, AdServicesApiType.FLEDGE);
138                 consentManager.disable(context, AdServicesApiType.MEASUREMENTS);
139             } else {
140                 consentManager.recordTopicsDefaultConsent(true);
141                 consentManager.recordFledgeDefaultConsent(true);
142                 consentManager.recordMeasurementDefaultConsent(true);
143 
144                 consentManager.enable(context, AdServicesApiType.TOPICS);
145                 consentManager.enable(context, AdServicesApiType.FLEDGE);
146                 consentManager.enable(context, AdServicesApiType.MEASUREMENTS);
147             }
148         } else {
149             // For the ROW devices, set the consent to GIVEN (enabled).
150             // For the EU devices, set the consent to REVOKED (disabled)
151             if (!isEuDevice) {
152                 consentManager.enable(context);
153             } else {
154                 consentManager.disable(context);
155             }
156         }
157     }
158 
getGaV2ConsentNotification( @onNull Context context, boolean isEuDevice)159     private static Notification getGaV2ConsentNotification(
160             @NonNull Context context, boolean isEuDevice) {
161         Intent intent = new Intent(context, ConsentNotificationActivity.class);
162         intent.putExtra(IS_EU_DEVICE_ARGUMENT_KEY, isEuDevice);
163         PendingIntent pendingIntent =
164                 PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_IMMUTABLE);
165         NotificationCompat.BigTextStyle textStyle =
166                 new NotificationCompat.BigTextStyle()
167                         .bigText(
168                                 isEuDevice
169                                         ? context.getString(
170                                         R.string.notificationUI_notification_ga_content_eu_v2)
171                                         : context.getString(
172                                         R.string.notificationUI_notification_ga_content_v2));
173         NotificationCompat.Builder notification =
174                 new NotificationCompat.Builder(context, CHANNEL_ID)
175                         .setSmallIcon(R.drawable.ic_info_icon)
176                         .setContentTitle(
177                                 context.getString(
178                                         isEuDevice
179                                                 ? R.string.notificationUI_notification_ga_title_eu_v2
180                                                 : R.string.notificationUI_notification_ga_title_v2))
181                         .setContentText(
182                                 context.getString(
183                                         isEuDevice
184                                                 ? R.string.notificationUI_notification_ga_content_eu_v2
185                                                 : R.string.notificationUI_notification_ga_content_v2))
186                         .setStyle(textStyle)
187                         .setPriority(NOTIFICATION_PRIORITY)
188                         .setAutoCancel(true)
189                         .setContentIntent(pendingIntent);
190         return notification.build();
191     }
192 
193     /**
194      * Returns a {@link NotificationCompat.Builder} which can be used to display consent
195      * notification to the user when GaUxFeature flag is enabled.
196      *
197      * @param context {@link Context} which is used to prepare a {@link NotificationCompat}.
198      */
getGaConsentNotification( @onNull Context context, boolean isEuDevice)199     private static Notification getGaConsentNotification(
200             @NonNull Context context, boolean isEuDevice) {
201         Intent intent = new Intent(context, ConsentNotificationActivity.class);
202         intent.putExtra(IS_EU_DEVICE_ARGUMENT_KEY, isEuDevice);
203         PendingIntent pendingIntent =
204                 PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_IMMUTABLE);
205         NotificationCompat.BigTextStyle textStyle =
206                 new NotificationCompat.BigTextStyle()
207                         .bigText(
208                                 isEuDevice
209                                         ? context.getString(
210                                                 R.string.notificationUI_notification_ga_content_eu)
211                                         : context.getString(
212                                                 R.string.notificationUI_notification_ga_content));
213         NotificationCompat.Builder notification =
214                 new NotificationCompat.Builder(context, CHANNEL_ID)
215                         .setSmallIcon(R.drawable.ic_info_icon)
216                         .setContentTitle(
217                                 context.getString(
218                                         isEuDevice
219                                                 ? R.string.notificationUI_notification_ga_title_eu
220                                                 : R.string.notificationUI_notification_ga_title))
221                         .setContentText(
222                                 context.getString(
223                                         isEuDevice
224                                                 ? R.string.notificationUI_notification_ga_content_eu
225                                                 : R.string.notificationUI_notification_ga_content))
226                         .setStyle(textStyle)
227                         .setPriority(NOTIFICATION_PRIORITY)
228                         .setAutoCancel(true)
229                         .setContentIntent(pendingIntent);
230 
231         if (isEuDevice && !FlagsFactory.getFlags().getNotificationDismissedOnClick()) {
232             notification.setAutoCancel(false);
233         }
234 
235         return notification.build();
236     }
237 
238     /**
239      * Returns a {@link NotificationCompat.Builder} which can be used to display consent
240      * notification to the user.
241      *
242      * @param context {@link Context} which is used to prepare a {@link NotificationCompat}.
243      */
getConsentNotification( @onNull Context context, boolean isEuDevice)244     private static Notification getConsentNotification(
245             @NonNull Context context, boolean isEuDevice) {
246         Intent intent = new Intent(context, ConsentNotificationActivity.class);
247         intent.putExtra(IS_EU_DEVICE_ARGUMENT_KEY, isEuDevice);
248         PendingIntent pendingIntent =
249                 PendingIntent.getActivity(
250                         context, 1, intent, PendingIntent.FLAG_IMMUTABLE);
251         NotificationCompat.BigTextStyle textStyle =
252                 new NotificationCompat.BigTextStyle()
253                         .bigText(
254                                 isEuDevice
255                                         ? context.getString(
256                                                 R.string.notificationUI_notification_content_eu)
257                                         : context.getString(
258                                                 R.string.notificationUI_notification_content));
259         return new NotificationCompat.Builder(context, CHANNEL_ID)
260                 .setSmallIcon(R.drawable.ic_info_icon)
261                 .setContentTitle(
262                         context.getString(
263                                 isEuDevice
264                                         ? R.string.notificationUI_notification_title_eu
265                                         : R.string.notificationUI_notification_title))
266                 .setContentText(
267                         context.getString(
268                                 isEuDevice
269                                         ? R.string.notificationUI_notification_content_eu
270                                         : R.string.notificationUI_notification_content))
271                 .setStyle(textStyle)
272                 .setPriority(NOTIFICATION_PRIORITY)
273                 .setAutoCancel(true)
274                 .setContentIntent(pendingIntent)
275                 .build();
276     }
277 
createNotificationChannel(@onNull Context context)278     private static void createNotificationChannel(@NonNull Context context) {
279         // TODO (b/230372892): styling -> adjust channels to use Android System labels.
280         int importance = NotificationManager.IMPORTANCE_HIGH;
281         NotificationChannel channel =
282                 new NotificationChannel(
283                         CHANNEL_ID,
284                         context.getString(R.string.settingsUI_main_view_title),
285                         importance);
286         channel.setDescription(context.getString(R.string.settingsUI_main_view_title));
287         NotificationManager notificationManager =
288                 context.getSystemService(NotificationManager.class);
289         notificationManager.createNotificationChannel(channel);
290     }
291 }
292