1 /* 2 * Copyright (C) 2015 Square, Inc. 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 leakcanary.internal 17 18 import android.Manifest.permission.POST_NOTIFICATIONS 19 import android.app.Notification 20 import android.app.NotificationChannel 21 import android.app.NotificationManager 22 import android.app.PendingIntent 23 import android.content.Context 24 import android.os.Build.VERSION.SDK_INT 25 import android.os.Build.VERSION_CODES.JELLY_BEAN 26 import android.os.Build.VERSION_CODES.O 27 import com.squareup.leakcanary.core.R 28 import leakcanary.LeakCanary 29 import leakcanary.internal.InternalLeakCanary.FormFactor.MOBILE 30 import shark.SharkLog 31 32 internal object Notifications { 33 34 private var notificationPermissionRequested = false 35 36 // Instant apps cannot show background notifications 37 // See https://github.com/square/leakcanary/issues/1197 38 // TV devices can't show notifications. 39 // Watch devices: not sure, but probably not a good idea anyway? 40 val canShowNotification: Boolean 41 get() { 42 if (InternalLeakCanary.formFactor != MOBILE) { 43 return false 44 } 45 if (InternalLeakCanary.isInstantApp || !InternalLeakCanary.applicationVisible) { 46 return false 47 } 48 if (!LeakCanary.config.showNotifications) { 49 return false 50 } 51 if (SDK_INT >= 33) { 52 val application = InternalLeakCanary.application 53 if (application.applicationInfo.targetSdkVersion >= 33) { 54 val notificationManager = 55 application.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 56 if (!notificationManager.areNotificationsEnabled()) { 57 if (notificationPermissionRequested) { <lambda>null58 SharkLog.d { "Not showing notification: already requested missing POST_NOTIFICATIONS permission." } 59 } else { <lambda>null60 SharkLog.d { "Not showing notification: requesting missing POST_NOTIFICATIONS permission." } 61 application.startActivity( 62 RequestPermissionActivity.createIntent( 63 application, 64 POST_NOTIFICATIONS 65 ) 66 ) 67 notificationPermissionRequested = true 68 } 69 return false 70 } 71 if (notificationManager.areNotificationsPaused()) { <lambda>null72 SharkLog.d { "Not showing notification, notifications are paused." } 73 return false 74 } 75 } 76 } 77 return true 78 } 79 80 @Suppress("LongParameterList") showNotificationnull81 fun showNotification( 82 context: Context, 83 contentTitle: CharSequence, 84 contentText: CharSequence, 85 pendingIntent: PendingIntent?, 86 notificationId: Int, 87 type: NotificationType 88 ) { 89 if (!canShowNotification) { 90 return 91 } 92 93 val builder = if (SDK_INT >= O) { 94 Notification.Builder(context, type.name) 95 } else Notification.Builder(context) 96 97 builder 98 .setContentText(contentText) 99 .setContentTitle(contentTitle) 100 .setAutoCancel(true) 101 .setContentIntent(pendingIntent) 102 103 val notification = 104 buildNotification(context, builder, type) 105 val notificationManager = 106 context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 107 notificationManager.notify(notificationId, notification) 108 } 109 buildNotificationnull110 fun buildNotification( 111 context: Context, 112 builder: Notification.Builder, 113 type: NotificationType 114 ): Notification { 115 builder.setSmallIcon(R.drawable.leak_canary_leak) 116 .setWhen(System.currentTimeMillis()) 117 118 if (SDK_INT >= O) { 119 val notificationManager = 120 context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 121 var notificationChannel: NotificationChannel? = 122 notificationManager.getNotificationChannel(type.name) 123 if (notificationChannel == null) { 124 val channelName = context.getString(type.nameResId) 125 notificationChannel = 126 NotificationChannel(type.name, channelName, type.importance) 127 notificationManager.createNotificationChannel(notificationChannel) 128 } 129 builder.setChannelId(type.name) 130 builder.setGroup(type.name) 131 } 132 133 return if (SDK_INT < JELLY_BEAN) { 134 @Suppress("DEPRECATION") 135 builder.notification 136 } else { 137 builder.build() 138 } 139 } 140 } 141