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 package com.android.systemui.screenshot 17 18 import android.app.Notification 19 import android.app.NotificationManager 20 import android.app.PendingIntent 21 import android.app.admin.DevicePolicyManager 22 import android.content.Context 23 import android.os.UserHandle 24 import android.view.Display 25 import com.android.internal.R 26 import com.android.internal.messages.nano.SystemMessageProto 27 import com.android.systemui.SystemUIApplication 28 import com.android.systemui.util.NotificationChannels 29 import dagger.assisted.Assisted 30 import dagger.assisted.AssistedFactory 31 import dagger.assisted.AssistedInject 32 33 /** Convenience class to handle showing and hiding notifications while taking a screenshot. */ 34 class ScreenshotNotificationsController 35 @AssistedInject 36 internal constructor( 37 @Assisted private val displayId: Int, 38 private val context: Context, 39 private val notificationManager: NotificationManager, 40 private val devicePolicyManager: DevicePolicyManager, 41 ) { 42 private val res = context.resources 43 44 /** 45 * Sends a notification that the screenshot capture has failed. 46 * 47 * Errors for the non-default display are shown in a unique separate notification. 48 */ notifyScreenshotErrornull49 fun notifyScreenshotError(msgResId: Int) { 50 val displayErrorString = 51 if (displayId != Display.DEFAULT_DISPLAY) { 52 " ($externalDisplayString)" 53 } else { 54 "" 55 } 56 val errorMsg = res.getString(msgResId) + displayErrorString 57 58 // Repurpose the existing notification or create a new one 59 val builder = 60 Notification.Builder(context, NotificationChannels.ALERTS) 61 .setTicker(res.getString(com.android.systemui.res.R.string.screenshot_failed_title)) 62 .setContentTitle( 63 res.getString(com.android.systemui.res.R.string.screenshot_failed_title) 64 ) 65 .setContentText(errorMsg) 66 .setSmallIcon(com.android.systemui.res.R.drawable.stat_notify_image_error) 67 .setWhen(System.currentTimeMillis()) 68 .setVisibility(Notification.VISIBILITY_PUBLIC) // ok to show outside lockscreen 69 .setCategory(Notification.CATEGORY_ERROR) 70 .setAutoCancel(true) 71 .setColor(context.getColor(R.color.system_notification_accent_color)) 72 val intent = 73 devicePolicyManager.createAdminSupportIntent( 74 DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE 75 ) 76 if (intent != null) { 77 val pendingIntent = 78 PendingIntent.getActivityAsUser( 79 context, 80 0, 81 intent, 82 PendingIntent.FLAG_IMMUTABLE, 83 null, 84 UserHandle.CURRENT 85 ) 86 builder.setContentIntent(pendingIntent) 87 } 88 SystemUIApplication.overrideNotificationAppName(context, builder, true) 89 val notification = Notification.BigTextStyle(builder).bigText(errorMsg).build() 90 // A different id for external displays to keep the 2 error notifications separated. 91 val id = 92 if (displayId == Display.DEFAULT_DISPLAY) { 93 SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT 94 } else { 95 SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT_EXTERNAL_DISPLAY 96 } 97 notificationManager.notify(id, notification) 98 } 99 100 private val externalDisplayString: String 101 get() = 102 res.getString( 103 com.android.systemui.res.R.string.screenshot_failed_external_display_indication 104 ) 105 106 /** Factory for [ScreenshotNotificationsController]. */ 107 @AssistedFactory interfacenull108 fun interface Factory { 109 fun create(displayId: Int): ScreenshotNotificationsController 110 } 111 } 112