• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2019 The Android Open Source Project
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *       http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package com.example.android.bubbles.data
17 
18 import android.app.Notification
19 import android.app.NotificationChannel
20 import android.app.NotificationManager
21 import android.app.PendingIntent
22 import android.app.Person
23 import android.content.Context
24 import android.content.Intent
25 import android.graphics.BitmapFactory
26 import android.graphics.drawable.Icon
27 import android.net.Uri
28 import androidx.annotation.WorkerThread
29 import com.example.android.bubbles.BubbleActivity
30 import com.example.android.bubbles.MainActivity
31 import com.example.android.bubbles.R
32 
33 /**
34  * Handles all operations related to [Notification].
35  */
36 class NotificationHelper(private val context: Context) {
37 
38     companion object {
39         /**
40          * The notification channel for messages. This is used for showing Bubbles.
41          */
42         private const val CHANNEL_NEW_MESSAGES = "new_messages"
43 
44         private const val REQUEST_CONTENT = 1
45         private const val REQUEST_BUBBLE = 2
46     }
47 
48     private val notificationManager = context.getSystemService(NotificationManager::class.java)
49 
50     fun setUpNotificationChannels() {
51         if (notificationManager.getNotificationChannel(CHANNEL_NEW_MESSAGES) == null) {
52             notificationManager.createNotificationChannel(
53                 NotificationChannel(
54                     CHANNEL_NEW_MESSAGES,
55                     context.getString(R.string.channel_new_messages),
56                     // The importance must be IMPORTANCE_HIGH to show Bubbles.
57                     NotificationManager.IMPORTANCE_HIGH
58                 ).apply {
59                     description = context.getString(R.string.channel_new_messages_description)
60                 }
61             )
62         }
63     }
64 
65     @WorkerThread
66     fun showNotification(chat: Chat, fromUser: Boolean) {
67         val icon = Icon.createWithAdaptiveBitmap(
68             BitmapFactory.decodeResource(
69                 context.resources,
70                 chat.contact.icon
71             )
72         )
73         val person = Person.Builder()
74             .setName(chat.contact.name)
75             .setIcon(icon)
76             .build()
77         val contentUri = Uri.parse("https://android.example.com/chat/${chat.contact.id}")
78         val builder = Notification.Builder(context, CHANNEL_NEW_MESSAGES)
79             // A notification can be shown as a bubble by calling setBubbleMetadata()
80             .setBubbleMetadata(
81                 Notification.BubbleMetadata.Builder()
82                     // The height of the expanded bubble.
83                     .setDesiredHeight(context.resources.getDimensionPixelSize(R.dimen.bubble_height))
84                     // The icon of the bubble.
85                     // TODO: The icon is not displayed in Android Q Beta 2.
86                     .setIcon(icon)
87                     .apply {
88                         // When the bubble is explicitly opened by the user, we can show the bubble automatically
89                         // in the expanded state. This works only when the app is in the foreground.
90                         // TODO: This does not yet work in Android Q Beta 2.
91                         if (fromUser) {
92                             setAutoExpandBubble(true)
93                             setSuppressInitialNotification(true)
94                         }
95                     }
96                     // The Intent to be used for the expanded bubble.
97                     .setIntent(
98                         PendingIntent.getActivity(
99                             context,
100                             REQUEST_BUBBLE,
101                             // Launch BubbleActivity as the expanded bubble.
102                             Intent(context, BubbleActivity::class.java)
103                                 .setAction(Intent.ACTION_VIEW)
104                                 .setData(Uri.parse("https://android.example.com/chat/${chat.contact.id}")),
105                             PendingIntent.FLAG_UPDATE_CURRENT
106                         )
107                     )
108                     .build()
109             )
110             // The user can turn off the bubble in system settings. In that case, this notification is shown as a
111             // normal notification instead of a bubble. Make sure that this notification works as a normal notification
112             // as well.
113             .setContentTitle(chat.contact.name)
114             .setSmallIcon(R.drawable.ic_message)
115             .setCategory(Notification.CATEGORY_MESSAGE)
116             .addPerson(person)
117             .setShowWhen(true)
118             // The content Intent is used when the user clicks on the "Open Content" icon button on the expanded bubble,
119             // as well as when the fall-back notification is clicked.
120             .setContentIntent(
121                 PendingIntent.getActivity(
122                     context,
123                     REQUEST_CONTENT,
124                     Intent(context, MainActivity::class.java)
125                         .setAction(Intent.ACTION_VIEW)
126                         .setData(contentUri),
127                     PendingIntent.FLAG_UPDATE_CURRENT
128                 )
129             )
130 
131         if (fromUser) {
132             // This is a Bubble explicitly opened by the user.
133             builder.setContentText(context.getString(R.string.chat_with_contact, chat.contact.name))
134         } else {
135             // Let's add some more content to the notification in case it falls back to a normal notification.
136             val lastOutgoingId = chat.messages.last { !it.isIncoming }.id
137             val newMessages = chat.messages.filter { message ->
138                 message.id > lastOutgoingId
139             }
140             val lastMessage = newMessages.last()
141             builder
142                 .setStyle(
143                     if (lastMessage.photo != null) {
144                         Notification.BigPictureStyle()
145                             .bigPicture(BitmapFactory.decodeResource(context.resources, lastMessage.photo))
146                             .bigLargeIcon(icon)
147                             .setSummaryText(lastMessage.text)
148                     } else {
149                         Notification.MessagingStyle(person)
150                             .apply {
151                                 for (message in newMessages) {
152                                     addMessage(message.text, message.timestamp, person)
153                                 }
154                             }
155                             .setGroupConversation(false)
156                     }
157                 )
158                 .setContentText(newMessages.joinToString("\n") { it.text })
159                 .setWhen(newMessages.last().timestamp)
160         }
161 
162         notificationManager.notify(chat.contact.id.toInt(), builder.build())
163     }
164 
165     fun dismissNotification(id: Long) {
166         notificationManager.cancel(id.toInt())
167     }
168 
169     fun canBubble(): Boolean {
170         val channel = notificationManager.getNotificationChannel(CHANNEL_NEW_MESSAGES)
171         return notificationManager.areBubblesAllowed() && channel.canBubble()
172     }
173 }
174