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