blob: c57e57e49e00382f56bed699d9189d279d3a8a7e [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.adservices.ui.notifications;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__ACTION__REQUESTED_NOTIFICATION;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__EU;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__ROW;
import static com.android.adservices.ui.notifications.ConsentNotificationFragment.IS_EU_DEVICE_ARGUMENT_KEY;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import com.android.adservices.api.R;
import com.android.adservices.service.FlagsFactory;
import com.android.adservices.service.consent.AdServicesApiType;
import com.android.adservices.service.consent.ConsentManager;
import com.android.adservices.service.stats.AdServicesLoggerImpl;
import com.android.adservices.service.stats.UIStats;
/** Provides methods which can be used to display Privacy Sandbox consent notification. */
public class ConsentNotificationTrigger {
// Random integer for NotificationCompat purposes
private static final int NOTIFICATION_ID = 67920;
private static final String CHANNEL_ID = "PRIVACY_SANDBOX_CHANNEL";
private static final int NOTIFICATION_PRIORITY = NotificationCompat.PRIORITY_MAX;
/**
* Shows consent notification as the highest priority notification to the user.
*
* @param context Context which is used to display {@link NotificationCompat}
*/
public static void showConsentNotification(@NonNull Context context, boolean isEuDevice) {
boolean gaUxFeatureEnabled = FlagsFactory.getFlags().getGaUxFeatureEnabled();
UIStats uiStats =
new UIStats.Builder()
.setCode(AD_SERVICES_SETTINGS_USAGE_REPORTED)
.setRegion(
isEuDevice
? AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__EU
: AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__ROW)
.setAction(
AD_SERVICES_SETTINGS_USAGE_REPORTED__ACTION__REQUESTED_NOTIFICATION)
.build();
AdServicesLoggerImpl.getInstance().logUIStats(uiStats);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
ConsentManager consentManager = ConsentManager.getInstance(context);
if (!notificationManager.areNotificationsEnabled()) {
recordNotificationDisplayed(gaUxFeatureEnabled, consentManager);
// TODO(b/242001860): add logging
return;
}
setupConsents(context, isEuDevice, gaUxFeatureEnabled, consentManager);
createNotificationChannel(context);
Notification notification = getNotification(context, isEuDevice, gaUxFeatureEnabled);
notificationManager.notify(NOTIFICATION_ID, notification);
recordNotificationDisplayed(gaUxFeatureEnabled, consentManager);
}
private static void recordNotificationDisplayed(
boolean gaUxFeatureEnabled, ConsentManager consentManager) {
if (gaUxFeatureEnabled) {
consentManager.recordGaUxNotificationDisplayed();
} else {
consentManager.recordNotificationDisplayed();
}
}
@NonNull
private static Notification getNotification(
@NonNull Context context, boolean isEuDevice, boolean gaUxFeatureEnabled) {
Notification notification =
gaUxFeatureEnabled
? getGaConsentNotification(context, isEuDevice)
: getConsentNotification(context, isEuDevice);
// make notification sticky (non-dismissible) for EuDevices when the GA UX feature is on
if (gaUxFeatureEnabled && isEuDevice) {
notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
}
return notification;
}
// setup default consents based on information whether the device is EU or non-EU device and
// GA UX feature flag is enabled.
private static void setupConsents(
@NonNull Context context,
boolean isEuDevice,
boolean gaUxFeatureEnabled,
ConsentManager consentManager) {
// Keep the feature flag at the upper level to make it easier to cleanup the code once
// the beta functionality is fully deprecated and abandoned.
if (gaUxFeatureEnabled) {
// EU: all APIs are by default disabled
// ROW: all APIs are by default enabled
// TODO(b/260266623): change consent state to UNDEFINED
if (isEuDevice) {
consentManager.disable(context, AdServicesApiType.TOPICS);
consentManager.disable(context, AdServicesApiType.FLEDGE);
consentManager.disable(context, AdServicesApiType.MEASUREMENTS);
} else {
consentManager.enable(context, AdServicesApiType.TOPICS);
consentManager.enable(context, AdServicesApiType.FLEDGE);
consentManager.enable(context, AdServicesApiType.MEASUREMENTS);
}
} else {
// For the ROW devices, set the consent to GIVEN (enabled).
// For the EU devices, set the consent to REVOKED (disabled)
if (!isEuDevice) {
consentManager.enable(context);
} else {
consentManager.disable(context);
}
}
}
/**
* Returns a {@link NotificationCompat.Builder} which can be used to display consent
* notification to the user when GaUxFeature flag is enabled.
*
* @param context {@link Context} which is used to prepare a {@link NotificationCompat}.
*/
private static Notification getGaConsentNotification(
@NonNull Context context, boolean isEuDevice) {
Intent intent = new Intent(context, ConsentNotificationActivity.class);
intent.putExtra(IS_EU_DEVICE_ARGUMENT_KEY, isEuDevice);
PendingIntent pendingIntent =
PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_IMMUTABLE);
NotificationCompat.BigTextStyle textStyle =
new NotificationCompat.BigTextStyle()
.bigText(
isEuDevice
? context.getString(
R.string.notificationUI_notification_ga_content_eu)
: context.getString(
R.string.notificationUI_notification_ga_content));
NotificationCompat.Builder notification =
new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_info_icon)
.setContentTitle(
context.getString(
isEuDevice
? R.string.notificationUI_notification_ga_title_eu
: R.string.notificationUI_notification_ga_title))
.setContentText(
context.getString(
isEuDevice
? R.string.notificationUI_notification_ga_content_eu
: R.string.notificationUI_notification_ga_content))
.setStyle(textStyle)
.setPriority(NOTIFICATION_PRIORITY)
.setAutoCancel(true)
.setContentIntent(pendingIntent);
// EU needs a "View Details" CTA
return isEuDevice
? notification
.addAction(
R.string.notificationUI_notification_ga_cta_eu,
context.getString(R.string.notificationUI_notification_ga_cta_eu),
pendingIntent)
.build()
: notification.build();
}
/**
* Returns a {@link NotificationCompat.Builder} which can be used to display consent
* notification to the user.
*
* @param context {@link Context} which is used to prepare a {@link NotificationCompat}.
*/
private static Notification getConsentNotification(
@NonNull Context context, boolean isEuDevice) {
Intent intent = new Intent(context, ConsentNotificationActivity.class);
intent.putExtra(IS_EU_DEVICE_ARGUMENT_KEY, isEuDevice);
PendingIntent pendingIntent =
PendingIntent.getActivity(
context, 1, intent, PendingIntent.FLAG_IMMUTABLE);
NotificationCompat.BigTextStyle textStyle =
new NotificationCompat.BigTextStyle()
.bigText(
isEuDevice
? context.getString(
R.string.notificationUI_notification_content_eu)
: context.getString(
R.string.notificationUI_notification_content));
return new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_info_icon)
.setContentTitle(
context.getString(
isEuDevice
? R.string.notificationUI_notification_title_eu
: R.string.notificationUI_notification_title))
.setContentText(
context.getString(
isEuDevice
? R.string.notificationUI_notification_content_eu
: R.string.notificationUI_notification_content))
.setStyle(textStyle)
.setPriority(NOTIFICATION_PRIORITY)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.addAction(
isEuDevice
? R.string.notificationUI_notification_cta_eu
: R.string.notificationUI_notification_cta,
context.getString(
isEuDevice
? R.string.notificationUI_notification_cta_eu
: R.string.notificationUI_notification_cta),
pendingIntent)
.build();
}
private static void createNotificationChannel(@NonNull Context context) {
// TODO (b/230372892): styling -> adjust channels to use Android System labels.
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel =
new NotificationChannel(
CHANNEL_ID,
context.getString(R.string.settingsUI_main_view_title),
importance);
channel.setDescription(context.getString(R.string.settingsUI_main_view_title));
NotificationManager notificationManager =
context.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}