1 /* 2 * Copyright 2021 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.collection.inflation 18 19 import android.app.Notification 20 import android.app.RemoteInput 21 import android.graphics.drawable.Icon 22 import android.text.TextUtils 23 24 /** 25 * An immutable object which contains minimal state extracted from an entry that represents state 26 * which can change without a direct app update (e.g. with a ranking update). 27 * Diffing two entries determines if view re-inflation is needed. 28 */ 29 class NotifUiAdjustment internal constructor( 30 val key: String, 31 val smartActions: List<Notification.Action>, 32 val smartReplies: List<CharSequence>, 33 val isConversation: Boolean, 34 val isSnoozeEnabled: Boolean, 35 val isMinimized: Boolean, 36 val needsRedaction: Boolean, 37 ) { 38 companion object { 39 @JvmStatic 40 fun needReinflate( 41 oldAdjustment: NotifUiAdjustment, 42 newAdjustment: NotifUiAdjustment 43 ): Boolean = when { 44 oldAdjustment === newAdjustment -> false 45 oldAdjustment.isConversation != newAdjustment.isConversation -> true 46 oldAdjustment.isSnoozeEnabled != newAdjustment.isSnoozeEnabled -> true 47 oldAdjustment.isMinimized != newAdjustment.isMinimized -> true 48 oldAdjustment.needsRedaction != newAdjustment.needsRedaction -> true 49 areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions) -> true 50 newAdjustment.smartReplies != oldAdjustment.smartReplies -> true 51 else -> false 52 } 53 54 private fun areDifferent( 55 first: List<Notification.Action>, 56 second: List<Notification.Action> 57 ): Boolean = when { 58 first === second -> false 59 first.size != second.size -> true 60 else -> first.asSequence().zip(second.asSequence()).any { 61 (!TextUtils.equals(it.first.title, it.second.title)) || 62 (areDifferent(it.first.getIcon(), it.second.getIcon())) || 63 (it.first.actionIntent != it.second.actionIntent) || 64 (areDifferent(it.first.remoteInputs, it.second.remoteInputs)) 65 } 66 } 67 68 private fun areDifferent(first: Icon?, second: Icon?): Boolean = when { 69 first === second -> false 70 first == null || second == null -> true 71 else -> !first.sameAs(second) 72 } 73 74 private fun areDifferent( 75 first: Array<RemoteInput>?, 76 second: Array<RemoteInput>? 77 ): Boolean = when { 78 first === second -> false 79 first == null || second == null -> true 80 first.size != second.size -> true 81 else -> first.asSequence().zip(second.asSequence()).any { 82 (!TextUtils.equals(it.first.label, it.second.label)) || 83 (areDifferent(it.first.choices, it.second.choices)) 84 } 85 } 86 87 private fun areDifferent( 88 first: Array<CharSequence>?, 89 second: Array<CharSequence>? 90 ): Boolean = when { 91 first === second -> false 92 first == null || second == null -> true 93 first.size != second.size -> true 94 else -> first.asSequence().zip(second.asSequence()).any { 95 !TextUtils.equals(it.first, it.second) 96 } 97 } 98 } 99 } 100