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 android.adservices.common.AdServicesCommonManager.ACTION_ADSERVICES_NOTIFICATION_DISPLAYED; 20 import static android.adservices.common.AdServicesCommonManager.ACTION_VIEW_ADSERVICES_CONSENT_PAGE; 21 import static android.adservices.common.AdServicesPermissions.MODIFY_ADSERVICES_STATE; 22 import static android.adservices.common.AdServicesPermissions.MODIFY_ADSERVICES_STATE_COMPAT; 23 24 import static com.android.adservices.service.FlagsConstants.KEY_GA_UX_FEATURE_ENABLED; 25 import static com.android.adservices.service.FlagsConstants.KEY_PAS_UX_ENABLED; 26 import static com.android.adservices.service.FlagsConstants.KEY_RECORD_MANUAL_INTERACTION_ENABLED; 27 import static com.android.adservices.service.FlagsConstants.KEY_UI_OTA_RESOURCES_FEATURE_ENABLED; 28 import static com.android.adservices.service.FlagsConstants.KEY_UI_OTA_STRINGS_FEATURE_ENABLED; 29 import static com.android.adservices.service.consent.ConsentManager.MANUAL_INTERACTIONS_RECORDED; 30 import static com.android.adservices.service.consent.ConsentManager.NO_MANUAL_INTERACTIONS_RECORDED; 31 import static com.android.adservices.ui.UxUtil.isUxStatesReady; 32 import static com.android.adservices.ui.ganotifications.ConsentNotificationPasFragment.IS_RENOTIFY_KEY; 33 import static com.android.adservices.ui.ganotifications.ConsentNotificationPasFragment.IS_STRICT_CONSENT_BEHAVIOR; 34 35 import android.app.Notification; 36 import android.app.NotificationChannel; 37 import android.app.NotificationManager; 38 import android.app.PendingIntent; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.pm.PackageManager; 42 import android.content.pm.ResolveInfo; 43 import android.os.Build; 44 45 import androidx.annotation.NonNull; 46 import androidx.annotation.RequiresApi; 47 import androidx.core.app.NotificationCompat; 48 import androidx.core.app.NotificationManagerCompat; 49 50 import com.android.adservices.LogUtil; 51 import com.android.adservices.api.R; 52 import com.android.adservices.service.FlagsFactory; 53 import com.android.adservices.service.common.PermissionHelper; 54 import com.android.adservices.service.consent.AdServicesApiType; 55 import com.android.adservices.service.consent.ConsentManager; 56 import com.android.adservices.service.stats.UiStatsLogger; 57 import com.android.adservices.service.ui.data.UxStatesManager; 58 import com.android.adservices.ui.OTAResourcesManager; 59 import com.android.adservices.ui.UxUtil; 60 61 import java.util.List; 62 63 /** Provides methods which can be used to display Privacy Sandbox consent notification. */ 64 @RequiresApi(Build.VERSION_CODES.S) 65 public class ConsentNotificationTrigger { 66 67 /* Random integer for NotificationCompat purposes. */ 68 public static final int NOTIFICATION_ID = 67920; 69 private static final String CHANNEL_ID = "PRIVACY_SANDBOX_CHANNEL"; 70 private static final int NOTIFICATION_PRIORITY = NotificationCompat.PRIORITY_MAX; 71 // Request codes should be different for notifications that are not mutually exclusive per user. 72 // When in doubt, always use a different request code. 73 private static final int BETA_REQUEST_CODE = 1; 74 private static final int GA_REQUEST_CODE = 2; 75 private static final int U18_REQUEST_CODE = 3; 76 private static final int GA_WITH_PAS_REQUEST_CODE = 4; 77 private static final int NON_PERSONALIZED_REQUEST_CODE = 5; 78 private static final int PERSONALIZED_FIRST_TIME_REQUEST_CODE = 6; 79 private static final int PERSONALIZED_RENOTIFY_REQUEST_CODE = 7; 80 81 /** 82 * Shows consent notification as the highest priority notification to the user. Differs from V1 83 * as no business logic is called, and an outside app must handle the corresponding intents for 84 * notification page content and consent changes. 85 * 86 * @param context Context which is used to display {@link NotificationCompat}. 87 * @param isRenotify is the notification re-notifying the user. 88 * @param isNewAdPersonalizationModuleEnabled is a personalization API being enabled. 89 * @param isOngoingNotification is the notification supposed to be persistent/ongoing. 90 */ 91 @SuppressWarnings("AvoidStaticContext") showConsentNotificationV2( @onNull Context context, boolean isRenotify, boolean isNewAdPersonalizationModuleEnabled, boolean isOngoingNotification)92 public static void showConsentNotificationV2( 93 @NonNull Context context, 94 boolean isRenotify, 95 boolean isNewAdPersonalizationModuleEnabled, 96 boolean isOngoingNotification) { 97 LogUtil.d("Started requesting notification."); 98 UiStatsLogger.logRequestedNotification(); 99 100 NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); 101 ConsentManager consentManager = ConsentManager.getInstance(); 102 if (!notificationManager.areNotificationsEnabled()) { 103 LogUtil.d("Notification is disabled."); 104 recordNotificationDisplayed(context, true, consentManager); 105 UiStatsLogger.logNotificationDisabled(); 106 return; 107 } 108 109 // Set OTA resources if it exists. 110 if (UxStatesManager.getInstance().getFlag(KEY_UI_OTA_STRINGS_FEATURE_ENABLED) 111 || UxStatesManager.getInstance().getFlag(KEY_UI_OTA_RESOURCES_FEATURE_ENABLED)) { 112 OTAResourcesManager.applyOTAResources(context.getApplicationContext(), true); 113 } 114 115 createNotificationChannel(context); 116 Notification notification = 117 getConsentNotificationV2( 118 context, 119 isRenotify, 120 isNewAdPersonalizationModuleEnabled, 121 isOngoingNotification); 122 123 notificationManager.notify(NOTIFICATION_ID, notification); 124 LogUtil.d("Sending broadcast about notification being displayed."); 125 sendNotificationBroadcastIntent(context); 126 // TODO(b/378683120):Notification still recorded as displayed to show entry point. 127 // Change to a unified displayed bit or show entry point bit. 128 recordNotificationDisplayed(context, true, consentManager); 129 consentManager.alignUserChoicesIfNeeded(); 130 UiStatsLogger.logNotificationDisplayed(); 131 LogUtil.d("Notification was displayed."); 132 } 133 getConsentNotificationV2( @onNull Context context, boolean isRenotify, boolean isNewAdPersonalizationModuleEnabled, boolean isOngoingNotification)134 private static Notification getConsentNotificationV2( 135 @NonNull Context context, 136 boolean isRenotify, 137 boolean isNewAdPersonalizationModuleEnabled, 138 boolean isOngoingNotification) { 139 140 Intent intent = getNotificationV2Intent(context); 141 int requestCode; 142 String contentTitle; 143 String contentText; 144 // TODO(b/380065660): Rename resources to exclude business logic 145 if (isNewAdPersonalizationModuleEnabled) { 146 if (isRenotify) { 147 requestCode = PERSONALIZED_RENOTIFY_REQUEST_CODE; 148 contentTitle = context.getString(R.string.notificationUI_pas_re_notification_title); 149 contentText = 150 context.getString(R.string.notificationUI_pas_re_notification_content); 151 } else { 152 requestCode = PERSONALIZED_FIRST_TIME_REQUEST_CODE; 153 contentTitle = context.getString(R.string.notificationUI_pas_notification_title); 154 contentText = context.getString(R.string.notificationUI_pas_notification_content); 155 } 156 } else { 157 requestCode = NON_PERSONALIZED_REQUEST_CODE; 158 contentTitle = context.getString(R.string.notificationUI_u18_notification_title); 159 contentText = context.getString(R.string.notificationUI_u18_notification_content); 160 } 161 162 intent.putExtra(IS_RENOTIFY_KEY, isRenotify); 163 164 PendingIntent pendingIntent = 165 PendingIntent.getActivity( 166 context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE); 167 168 NotificationCompat.BigTextStyle textStyle = 169 new NotificationCompat.BigTextStyle().bigText(contentText); 170 171 NotificationCompat.Builder notificationBuilder = 172 new NotificationCompat.Builder(context, CHANNEL_ID) 173 .setSmallIcon(R.drawable.ic_info_icon) 174 .setContentTitle(contentTitle) 175 .setContentText(contentText) 176 .setStyle(textStyle) 177 .setPriority(NOTIFICATION_PRIORITY) 178 .setAutoCancel(true) 179 .setContentIntent(pendingIntent); 180 Notification notification = notificationBuilder.build(); 181 if (isOngoingNotification) { 182 notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR; 183 } 184 return notification; 185 } 186 getNotificationV2Intent(Context context)187 private static Intent getNotificationV2Intent(Context context) { 188 Intent intent = new Intent(ACTION_VIEW_ADSERVICES_CONSENT_PAGE); 189 PackageManager pm = context.getPackageManager(); 190 List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0); 191 for (ResolveInfo resolveInfo : activities) { 192 String packageName = resolveInfo.activityInfo.packageName; 193 if (PermissionHelper.hasPermission(context, packageName, MODIFY_ADSERVICES_STATE) 194 || PermissionHelper.hasPermission( 195 context, packageName, MODIFY_ADSERVICES_STATE_COMPAT)) { 196 LogUtil.d( 197 "Matching Activity found for %s action in : %s", 198 ACTION_VIEW_ADSERVICES_CONSENT_PAGE, packageName); 199 return intent.setPackage(packageName); 200 } 201 LogUtil.w( 202 "Matching Activity found for %s action in : %s, but it has neither %s nor" 203 + " %s permission", 204 ACTION_VIEW_ADSERVICES_CONSENT_PAGE, 205 packageName, 206 MODIFY_ADSERVICES_STATE, 207 MODIFY_ADSERVICES_STATE_COMPAT); 208 } 209 LogUtil.w( 210 "No activity available in system image to handle " 211 + "%s action. Falling back to " 212 + "ConsentNotificationActivity", 213 ACTION_VIEW_ADSERVICES_CONSENT_PAGE); 214 return new Intent(context, ConsentNotificationActivity.class); 215 } 216 sendNotificationBroadcastIntent(Context context)217 private static void sendNotificationBroadcastIntent(Context context) { 218 Intent intent = new Intent(ACTION_ADSERVICES_NOTIFICATION_DISPLAYED); 219 PackageManager pm = context.getPackageManager(); 220 List<ResolveInfo> activities = pm.queryBroadcastReceivers(intent, 0); 221 for (ResolveInfo resolveInfo : activities) { 222 String packageName = resolveInfo.activityInfo.packageName; 223 if (PermissionHelper.hasPermission(context, packageName, MODIFY_ADSERVICES_STATE) 224 || PermissionHelper.hasPermission( 225 context, packageName, MODIFY_ADSERVICES_STATE_COMPAT)) { 226 LogUtil.d( 227 "Matching Broadcast receiver found for %s action in : %s", 228 ACTION_ADSERVICES_NOTIFICATION_DISPLAYED, packageName); 229 context.sendBroadcast(intent.setPackage(packageName)); 230 return; 231 } 232 LogUtil.w( 233 "Matching Broadcast receiver found for %s action in : %s, but it has neither " 234 + "%s nor %s permission", 235 ACTION_ADSERVICES_NOTIFICATION_DISPLAYED, 236 packageName, 237 MODIFY_ADSERVICES_STATE, 238 MODIFY_ADSERVICES_STATE_COMPAT); 239 } 240 LogUtil.w( 241 "No explicit broadcast receiver available in system image to handle " 242 + "%s action. Sending implicit broadcast instead.", 243 ACTION_ADSERVICES_NOTIFICATION_DISPLAYED); 244 context.sendBroadcast(intent); 245 } 246 247 /** 248 * Shows consent notification as the highest priority notification to the user. 249 * 250 * @param context Context which is used to display {@link NotificationCompat} 251 */ 252 @SuppressWarnings("AvoidStaticContext") // UX class showConsentNotification(@onNull Context context, boolean isEuDevice)253 public static void showConsentNotification(@NonNull Context context, boolean isEuDevice) { 254 LogUtil.d("Started requesting notification."); 255 UiStatsLogger.logRequestedNotification(); 256 257 boolean gaUxFeatureEnabled = 258 UxStatesManager.getInstance().getFlag(KEY_GA_UX_FEATURE_ENABLED); 259 260 NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); 261 ConsentManager consentManager = ConsentManager.getInstance(); 262 if (!notificationManager.areNotificationsEnabled()) { 263 LogUtil.d("Notification is disabled."); 264 recordNotificationDisplayed(context, gaUxFeatureEnabled, consentManager); 265 UiStatsLogger.logNotificationDisabled(); 266 return; 267 } 268 269 // Set OTA resources if it exists. 270 if (UxStatesManager.getInstance().getFlag(KEY_UI_OTA_STRINGS_FEATURE_ENABLED) 271 || UxStatesManager.getInstance().getFlag(KEY_UI_OTA_RESOURCES_FEATURE_ENABLED)) { 272 OTAResourcesManager.applyOTAResources(context.getApplicationContext(), true); 273 } 274 275 createNotificationChannel(context); 276 Notification notification = 277 getNotification(context, isEuDevice, gaUxFeatureEnabled, consentManager); 278 279 notificationManager.notify(NOTIFICATION_ID, notification); 280 recordNotificationDisplayed(context, gaUxFeatureEnabled, consentManager); 281 282 // must setup consents after recording notification displayed data to ensure accurate UX in 283 // logs 284 setupConsents(context, isEuDevice, gaUxFeatureEnabled, consentManager); 285 UiStatsLogger.logNotificationDisplayed(); 286 LogUtil.d("Notification was displayed."); 287 } 288 289 @SuppressWarnings("AvoidStaticContext") // UX class recordNotificationDisplayed( @onNull Context context, boolean gaUxFeatureEnabled, ConsentManager consentManager)290 private static void recordNotificationDisplayed( 291 @NonNull Context context, boolean gaUxFeatureEnabled, ConsentManager consentManager) { 292 if (UxStatesManager.getInstance().getFlag(KEY_RECORD_MANUAL_INTERACTION_ENABLED) 293 && consentManager.getUserManualInteractionWithConsent() 294 != MANUAL_INTERACTIONS_RECORDED) { 295 consentManager.recordUserManualInteractionWithConsent(NO_MANUAL_INTERACTIONS_RECORDED); 296 } 297 298 if (isUxStatesReady()) { 299 switch (UxUtil.getUx()) { 300 case GA_UX: 301 if (UxStatesManager.getInstance().getFlag(KEY_PAS_UX_ENABLED)) { 302 consentManager.recordPasNotificationDisplayed(true); 303 break; 304 } 305 consentManager.recordGaUxNotificationDisplayed(true); 306 break; 307 case U18_UX: 308 consentManager.setU18NotificationDisplayed(true); 309 break; 310 case BETA_UX: 311 consentManager.recordNotificationDisplayed(true); 312 break; 313 default: 314 break; 315 } 316 } else { 317 if (gaUxFeatureEnabled) { 318 consentManager.recordGaUxNotificationDisplayed(true); 319 } 320 consentManager.recordNotificationDisplayed(true); 321 } 322 } 323 324 @NonNull 325 @SuppressWarnings("AvoidStaticContext") // UX class getNotification( @onNull Context context, boolean isEuDevice, boolean gaUxFeatureEnabled, ConsentManager consentManager)326 private static Notification getNotification( 327 @NonNull Context context, 328 boolean isEuDevice, 329 boolean gaUxFeatureEnabled, 330 ConsentManager consentManager) { 331 Notification notification; 332 if (isUxStatesReady()) { 333 switch (UxUtil.getUx()) { 334 case GA_UX: 335 if (UxStatesManager.getInstance().getFlag(KEY_PAS_UX_ENABLED)) { 336 notification = 337 getPasConsentNotification(context, consentManager, isEuDevice); 338 break; 339 } 340 notification = getGaV2ConsentNotification(context, isEuDevice); 341 break; 342 case U18_UX: 343 notification = getU18ConsentNotification(context); 344 break; 345 case BETA_UX: 346 notification = getConsentNotification(context, isEuDevice); 347 break; 348 default: 349 notification = getGaV2ConsentNotification(context, isEuDevice); 350 } 351 } else { 352 if (gaUxFeatureEnabled) { 353 notification = getGaV2ConsentNotification(context, isEuDevice); 354 } else { 355 notification = getConsentNotification(context, isEuDevice); 356 } 357 } 358 359 // make notification sticky (non-dismissible) for EuDevices when the GA UX feature is on 360 if (gaUxFeatureEnabled && isEuDevice) { 361 notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR; 362 } 363 return notification; 364 } 365 366 // setup default consents based on information whether the device is EU or non-EU device and 367 // GA UX feature flag is enabled. 368 @SuppressWarnings("AvoidStaticContext") // UX class setupConsents( @onNull Context context, boolean isEuDevice, boolean gaUxFeatureEnabled, ConsentManager consentManager)369 private static void setupConsents( 370 @NonNull Context context, 371 boolean isEuDevice, 372 boolean gaUxFeatureEnabled, 373 ConsentManager consentManager) { 374 if (isUxStatesReady()) { 375 switch (UxUtil.getUx()) { 376 case GA_UX: 377 if (isPasRenotifyUser(consentManager)) { 378 // Is PAS renotify user, respect previous consents. 379 break; 380 } 381 setUpGaConsent(context, isEuDevice, consentManager); 382 break; 383 case U18_UX: 384 consentManager.recordMeasurementDefaultConsent(true); 385 consentManager.enable(context, AdServicesApiType.MEASUREMENTS); 386 break; 387 case BETA_UX: 388 if (!isEuDevice) { 389 consentManager.enable(context); 390 } else { 391 consentManager.disable(context); 392 } 393 break; 394 default: 395 // Default behavior is GA UX. 396 setUpGaConsent(context, isEuDevice, consentManager); 397 break; 398 } 399 } else { 400 // Keep the feature flag at the upper level to make it easier to cleanup the code once 401 // the beta functionality is fully deprecated and abandoned. 402 if (gaUxFeatureEnabled) { 403 setUpGaConsent(context, isEuDevice, consentManager); 404 } else { 405 // For the ROW devices, set the consent to GIVEN (enabled). 406 // For the EU devices, set the consent to REVOKED (disabled) 407 if (!isEuDevice) { 408 consentManager.enable(context); 409 } else { 410 consentManager.disable(context); 411 } 412 } 413 } 414 } 415 isPasRenotifyUser(ConsentManager consentManager)416 private static boolean isPasRenotifyUser(ConsentManager consentManager) { 417 return UxStatesManager.getInstance().getFlag(KEY_PAS_UX_ENABLED) 418 && (isFledgeOrMsmtEnabled(consentManager) 419 || consentManager.getUserManualInteractionWithConsent() 420 == MANUAL_INTERACTIONS_RECORDED); 421 } 422 423 @SuppressWarnings("AvoidStaticContext") // UX class getGaV2ConsentNotification( @onNull Context context, boolean isEuDevice)424 private static Notification getGaV2ConsentNotification( 425 @NonNull Context context, boolean isEuDevice) { 426 Intent intent = new Intent(context, ConsentNotificationActivity.class); 427 428 PendingIntent pendingIntent = 429 PendingIntent.getActivity( 430 context, GA_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE); 431 432 String bigText = 433 isEuDevice 434 ? context.getString(R.string.notificationUI_notification_ga_content_eu_v2) 435 : context.getString(R.string.notificationUI_notification_ga_content_v2); 436 437 NotificationCompat.BigTextStyle textStyle = 438 new NotificationCompat.BigTextStyle().bigText(bigText); 439 440 String contentTitle = 441 context.getString( 442 isEuDevice 443 ? R.string.notificationUI_notification_ga_title_eu_v2 444 : R.string.notificationUI_notification_ga_title_v2); 445 String contentText = 446 context.getString( 447 isEuDevice 448 ? R.string.notificationUI_notification_ga_content_eu_v2 449 : R.string.notificationUI_notification_ga_content_v2); 450 451 NotificationCompat.Builder notification = 452 new NotificationCompat.Builder(context, CHANNEL_ID) 453 .setSmallIcon(R.drawable.ic_info_icon) 454 .setContentTitle(contentTitle) 455 .setContentText(contentText) 456 .setStyle(textStyle) 457 .setPriority(NOTIFICATION_PRIORITY) 458 .setAutoCancel(true) 459 .setContentIntent(pendingIntent); 460 return notification.build(); 461 } 462 463 /** 464 * Returns a {@link NotificationCompat.Builder} which can be used to display consent 465 * notification to the user. 466 * 467 * @param context {@link Context} which is used to prepare a {@link NotificationCompat}. 468 */ 469 @SuppressWarnings("AvoidStaticContext") // UX class getConsentNotification( @onNull Context context, boolean isEuDevice)470 private static Notification getConsentNotification( 471 @NonNull Context context, boolean isEuDevice) { 472 Intent intent = new Intent(context, ConsentNotificationActivity.class); 473 474 PendingIntent pendingIntent = 475 PendingIntent.getActivity( 476 context, BETA_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE); 477 NotificationCompat.BigTextStyle textStyle = 478 new NotificationCompat.BigTextStyle() 479 .bigText( 480 isEuDevice 481 ? context.getString( 482 R.string.notificationUI_notification_content_eu) 483 : context.getString( 484 R.string.notificationUI_notification_content)); 485 return new NotificationCompat.Builder(context, CHANNEL_ID) 486 .setSmallIcon(R.drawable.ic_info_icon) 487 .setContentTitle( 488 context.getString( 489 isEuDevice 490 ? R.string.notificationUI_notification_title_eu 491 : R.string.notificationUI_notification_title)) 492 .setContentText( 493 context.getString( 494 isEuDevice 495 ? R.string.notificationUI_notification_content_eu 496 : R.string.notificationUI_notification_content)) 497 .setStyle(textStyle) 498 .setPriority(NOTIFICATION_PRIORITY) 499 .setAutoCancel(true) 500 .setContentIntent(pendingIntent) 501 .build(); 502 } 503 504 /** 505 * Returns a {@link NotificationCompat.Builder} which can be used to display consent 506 * notification to U18 users. 507 * 508 * @param context {@link Context} which is used to prepare a {@link NotificationCompat}. 509 */ 510 @SuppressWarnings("AvoidStaticContext") // UX class getU18ConsentNotification(@onNull Context context)511 private static Notification getU18ConsentNotification(@NonNull Context context) { 512 Intent intent = new Intent(context, ConsentNotificationActivity.class); 513 514 PendingIntent pendingIntent = 515 PendingIntent.getActivity( 516 context, U18_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE); 517 NotificationCompat.BigTextStyle textStyle = 518 new NotificationCompat.BigTextStyle() 519 .bigText( 520 context.getString( 521 R.string.notificationUI_u18_notification_content)); 522 return new NotificationCompat.Builder(context, CHANNEL_ID) 523 .setSmallIcon(R.drawable.ic_info_icon) 524 .setContentTitle(context.getString(R.string.notificationUI_u18_notification_title)) 525 .setContentText(context.getString(R.string.notificationUI_u18_notification_content)) 526 .setStyle(textStyle) 527 .setPriority(NOTIFICATION_PRIORITY) 528 .setAutoCancel(true) 529 .setContentIntent(pendingIntent) 530 .build(); 531 } 532 533 @SuppressWarnings("AvoidStaticContext") // UX class getPasConsentNotification( @onNull Context context, ConsentManager consentManager, boolean isEuDevice)534 private static Notification getPasConsentNotification( 535 @NonNull Context context, ConsentManager consentManager, boolean isEuDevice) { 536 boolean isRenotify = isFledgeOrMsmtEnabled(consentManager); 537 Intent intent = new Intent(context, ConsentNotificationActivity.class); 538 intent.putExtra(IS_RENOTIFY_KEY, isRenotify); 539 if (!FlagsFactory.getFlags().getAdServicesConsentBusinessLogicMigrationEnabled()) { 540 // isEuDevice argument here includes AdId disabled ROW users, which cannot be obtained 541 // within the notification activity from DeviceRegionProvider. 542 intent.putExtra(IS_STRICT_CONSENT_BEHAVIOR, isEuDevice); 543 } 544 545 PendingIntent pendingIntent = 546 PendingIntent.getActivity( 547 context, GA_WITH_PAS_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE); 548 549 String bigText = 550 isRenotify 551 ? context.getString(R.string.notificationUI_pas_re_notification_content) 552 : context.getString(R.string.notificationUI_pas_notification_content); 553 554 NotificationCompat.BigTextStyle textStyle = 555 new NotificationCompat.BigTextStyle().bigText(bigText); 556 557 String contentTitle = 558 context.getString( 559 isRenotify 560 ? R.string.notificationUI_pas_re_notification_title 561 : R.string.notificationUI_pas_notification_title); 562 String contentText = 563 context.getString( 564 isRenotify 565 ? R.string.notificationUI_pas_re_notification_content 566 : R.string.notificationUI_pas_notification_content); 567 568 NotificationCompat.Builder notification = 569 new NotificationCompat.Builder(context, CHANNEL_ID) 570 .setSmallIcon(R.drawable.ic_info_icon) 571 .setContentTitle(contentTitle) 572 .setContentText(contentText) 573 .setStyle(textStyle) 574 .setPriority(NOTIFICATION_PRIORITY) 575 .setAutoCancel(true) 576 .setContentIntent(pendingIntent); 577 return notification.build(); 578 } 579 isFledgeOrMsmtEnabled(ConsentManager consentManager)580 private static boolean isFledgeOrMsmtEnabled(ConsentManager consentManager) { 581 return consentManager.getConsent(AdServicesApiType.FLEDGE).isGiven() 582 || consentManager.getConsent(AdServicesApiType.MEASUREMENTS).isGiven(); 583 } 584 585 @SuppressWarnings("AvoidStaticContext") // UX class createNotificationChannel(@onNull Context context)586 private static void createNotificationChannel(@NonNull Context context) { 587 // TODO (b/230372892): styling -> adjust channels to use Android System labels. 588 int importance = NotificationManager.IMPORTANCE_HIGH; 589 NotificationChannel channel = 590 new NotificationChannel( 591 CHANNEL_ID, 592 context.getString(R.string.settingsUI_main_view_title), 593 importance); 594 channel.setDescription(context.getString(R.string.settingsUI_main_view_title)); 595 NotificationManager notificationManager = 596 context.getSystemService(NotificationManager.class); 597 notificationManager.createNotificationChannel(channel); 598 } 599 600 @SuppressWarnings("AvoidStaticContext") // UX class setUpGaConsent( @onNull Context context, boolean isEuDevice, ConsentManager consentManager)601 private static void setUpGaConsent( 602 @NonNull Context context, boolean isEuDevice, ConsentManager consentManager) { 603 if (isEuDevice) { 604 consentManager.recordTopicsDefaultConsent(false); 605 consentManager.recordFledgeDefaultConsent(false); 606 consentManager.recordMeasurementDefaultConsent(false); 607 608 consentManager.disable(context, AdServicesApiType.TOPICS); 609 consentManager.disable(context, AdServicesApiType.FLEDGE); 610 consentManager.disable(context, AdServicesApiType.MEASUREMENTS); 611 } else { 612 consentManager.recordTopicsDefaultConsent(true); 613 consentManager.recordFledgeDefaultConsent(true); 614 consentManager.recordMeasurementDefaultConsent(true); 615 616 consentManager.enable(context, AdServicesApiType.TOPICS); 617 consentManager.enable(context, AdServicesApiType.FLEDGE); 618 consentManager.enable(context, AdServicesApiType.MEASUREMENTS); 619 } 620 } 621 } 622