1 /*
<lambda>null2  * Copyright (C) 2020 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 
17 package com.android.systemui.statusbar.notification.row.wrapper
18 
19 import android.content.Context
20 import android.graphics.drawable.AnimatedImageDrawable
21 import android.view.View
22 import android.view.ViewGroup
23 import com.android.internal.widget.CachingIconView
24 import com.android.internal.widget.ConversationLayout
25 import com.android.internal.widget.MessagingGroup
26 import com.android.internal.widget.MessagingImageMessage
27 import com.android.internal.widget.MessagingLinearLayout
28 import com.android.systemui.res.R
29 import com.android.systemui.statusbar.notification.NotificationFadeAware
30 import com.android.systemui.statusbar.notification.NotificationUtils
31 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
32 import com.android.systemui.statusbar.notification.row.wrapper.NotificationMessagingTemplateViewWrapper.setCustomImageMessageTransform
33 import com.android.systemui.util.children
34 
35 /**
36  * Wraps a notification containing a conversation template
37  */
38 class NotificationConversationTemplateViewWrapper constructor(
39     ctx: Context,
40     view: View,
41     row: ExpandableNotificationRow
42 ) : NotificationTemplateViewWrapper(ctx, view, row) {
43 
44     private val minHeightWithActions: Int = NotificationUtils.getFontScaledHeight(
45             ctx,
46             R.dimen.notification_messaging_actions_min_height
47     )
48     private val conversationLayout: ConversationLayout = view as ConversationLayout
49 
50     private lateinit var conversationIconContainer: View
51     private lateinit var conversationIconView: CachingIconView
52     private lateinit var conversationBadgeBg: View
53     private lateinit var expandBtn: View
54     private lateinit var expandBtnContainer: View
55     private lateinit var imageMessageContainer: ViewGroup
56     private lateinit var messageContainers: ArrayList<MessagingGroup>
57     private lateinit var messagingLinearLayout: MessagingLinearLayout
58     private lateinit var conversationTitleView: View
59     private lateinit var importanceRing: View
60     private lateinit var appName: View
61     private var facePileBottomBg: View? = null
62     private var facePileBottom: View? = null
63     private var facePileTop: View? = null
64 
65     private fun resolveViews() {
66         messagingLinearLayout = conversationLayout.messagingLinearLayout
67         imageMessageContainer = conversationLayout.imageMessageContainer
68         messageContainers = conversationLayout.messagingGroups
69         with(conversationLayout) {
70             conversationIconContainer =
71                     requireViewById(com.android.internal.R.id.conversation_icon_container)
72             conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
73             conversationBadgeBg =
74                     requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
75             expandBtn = requireViewById(com.android.internal.R.id.expand_button)
76             expandBtnContainer = requireViewById(com.android.internal.R.id.expand_button_container)
77             importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring)
78             appName = requireViewById(com.android.internal.R.id.app_name_text)
79             conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
80             facePileTop = findViewById(com.android.internal.R.id.conversation_face_pile_top)
81             facePileBottom = findViewById(com.android.internal.R.id.conversation_face_pile_bottom)
82             facePileBottomBg =
83                     findViewById(com.android.internal.R.id.conversation_face_pile_bottom_background)
84         }
85     }
86 
87     override fun onContentUpdated(row: ExpandableNotificationRow) {
88         // Reinspect the notification. Before the super call, because the super call also updates
89         // the transformation types and we need to have our values set by then.
90         resolveViews()
91         super.onContentUpdated(row)
92     }
93 
94     override fun updateTransformedTypes() {
95         // This also clears the existing types
96         super.updateTransformedTypes()
97 
98         mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TITLE, conversationTitleView)
99         addTransformedViews(
100                 messagingLinearLayout,
101                 appName
102         )
103 
104         setCustomImageMessageTransform(mTransformationHelper, imageMessageContainer)
105 
106         addViewsTransformingToSimilar(
107                 conversationIconView,
108                 conversationBadgeBg,
109                 expandBtn,
110                 importanceRing,
111                 facePileTop,
112                 facePileBottom,
113                 facePileBottomBg
114         )
115     }
116 
117     override fun getShelfTransformationTarget(): View? =
118             if (conversationLayout.isImportantConversation)
119                 if (conversationIconView.visibility != View.GONE)
120                     conversationIconView
121                 else
122                     // A notification with a fallback icon was set to important. Currently
123                     // the transformation doesn't work for these and needs to be fixed.
124                     // In the meantime those are using the icon.
125                     super.getShelfTransformationTarget()
126             else
127                 super.getShelfTransformationTarget()
128 
129     override fun setRemoteInputVisible(visible: Boolean) =
130             conversationLayout.showHistoricMessages(visible)
131 
132     override fun updateExpandability(
133         expandable: Boolean,
134         onClickListener: View.OnClickListener,
135         requestLayout: Boolean
136     ) = conversationLayout.updateExpandability(expandable, onClickListener)
137 
138     override fun disallowSingleClick(x: Float, y: Float): Boolean {
139         val isOnExpandButton = expandBtnContainer.visibility == View.VISIBLE &&
140                 isOnView(expandBtnContainer, x, y)
141         return isOnExpandButton || super.disallowSingleClick(x, y)
142     }
143 
144     override fun getMinLayoutHeight(): Int =
145             if (mActionsContainer != null && mActionsContainer.visibility != View.GONE)
146                 minHeightWithActions
147             else
148                 super.getMinLayoutHeight()
149 
150     override fun setNotificationFaded(faded: Boolean) {
151         // Do not call super
152         NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
153         NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
154     }
155 
156     // Starts or stops the animations in any drawables contained in this Conversation Notification.
157     override fun setAnimationsRunning(running: Boolean) {
158         // We apply to both the child message containers in a conversation group,
159         // and the top level image message container.
160         val containers = messageContainers.asSequence().map { it.messageContainer } +
161                 sequenceOf(imageMessageContainer)
162         val drawables =
163                 containers
164                         .flatMap { it.children }
165                         .mapNotNull { child ->
166                             (child as? MessagingImageMessage)?.let { imageMessage ->
167                                 imageMessage.drawable as? AnimatedImageDrawable
168                             }
169                         }
170         drawables.toSet().forEach {
171             when {
172                 running -> it.start()
173                 !running -> it.stop()
174             }
175         }
176     }
177 }
178