1 /* <lambda>null2 * Copyright (C) 2024 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 package com.android.wallpaper.widget.floatingsheetcontent 17 18 import android.content.Context 19 import android.os.Handler 20 import android.util.AttributeSet 21 import android.view.View 22 import android.widget.Button 23 import android.widget.CompoundButton 24 import android.widget.LinearLayout 25 import android.widget.ProgressBar 26 import android.widget.Switch 27 import android.widget.TextView 28 import androidx.annotation.VisibleForTesting 29 import androidx.annotation.VisibleForTesting.Companion.PRIVATE 30 import com.android.wallpaper.R 31 import com.android.wallpaper.effects.EffectsController 32 import com.android.wallpaper.effects.EffectsController.EffectEnumInterface 33 34 /** A view for displaying wallpaper info. */ 35 class WallpaperEffectsView2(context: Context?, attrs: AttributeSet?) : 36 LinearLayout(context, attrs) { 37 private var effectSwitchListener: EffectSwitchListener? = null 38 private var downloadClickListener: EffectDownloadClickListener? = null 39 private var effectSwitch: Switch? = null 40 41 private var effectTextRes: EffectTextRes = 42 EffectTextRes( 43 effectTitle = "", 44 effectFailedTitle = "", 45 effectSubTitle = "", 46 retryInstruction = "", 47 noEffectInstruction = "", 48 ) 49 50 @VisibleForTesting(otherwise = PRIVATE) var effectTitle: TextView? = null 51 @VisibleForTesting(otherwise = PRIVATE) var description: TextView? = null 52 @VisibleForTesting(otherwise = PRIVATE) var title: TextView? = null 53 @VisibleForTesting(otherwise = PRIVATE) var myPhotosButton: Button? = null 54 @VisibleForTesting(otherwise = PRIVATE) var continueButton: Button? = null 55 @VisibleForTesting(otherwise = PRIVATE) var tryAgainLaterButton: Button? = null 56 @VisibleForTesting(otherwise = PRIVATE) var switchLayout: View? = null 57 @VisibleForTesting(otherwise = PRIVATE) var container: View? = null 58 @VisibleForTesting(otherwise = PRIVATE) var downloadProgression: ProgressBar? = null 59 @VisibleForTesting(otherwise = PRIVATE) var downloadButton: Button? = null 60 @VisibleForTesting(otherwise = PRIVATE) var downloadButtonLayout: View? = null 61 62 data class EffectTextRes( 63 val effectTitle: String, 64 val effectFailedTitle: String, 65 val effectSubTitle: String, 66 val retryInstruction: String, 67 val noEffectInstruction: String, 68 ) 69 70 enum class Status { 71 IDLE, 72 FAILED, 73 SUCCESS, 74 PROCESSING, 75 SHOW_DOWNLOAD_BUTTON, 76 DOWNLOADING, // downloading of ml models in progress 77 } 78 79 /** 80 * Adds an Effects switch listener. 81 * 82 * @param listener The effects switch listener. 83 */ 84 fun addEffectSwitchListener(listener: EffectSwitchListener?) { 85 effectSwitchListener = listener 86 } 87 88 /** 89 * Sets an Effects download button listener. 90 * 91 * @param listener The effects download button listener. 92 */ 93 fun setEffectDownloadClickListener(listener: EffectDownloadClickListener?) { 94 downloadClickListener = listener 95 } 96 97 /** 98 * Updates the effect switch status. 99 * 100 * @param checked The status of the switch. 101 */ 102 private fun updateEffectSwitchStatus(effect: EffectEnumInterface, checked: Boolean) { 103 effectSwitch?.isChecked = checked 104 effectSwitch?.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> 105 switchChanged(effect, isChecked) 106 } 107 } 108 109 /** 110 * Sets click listener to myPhotos button. 111 * 112 * @param clickListener listener for myPhotos. 113 */ 114 fun setMyPhotosClickListener(clickListener: OnClickListener?) { 115 myPhotosButton?.setOnClickListener(clickListener) 116 } 117 118 /** 119 * Sets click listener to 'try again' and 'continue' button. 120 * 121 * @param clickListener listener for myPhotos. 122 */ 123 fun setCollapseFloatingSheetListener(clickListener: OnClickListener?) { 124 tryAgainLaterButton?.setOnClickListener(clickListener) 125 continueButton?.setOnClickListener(clickListener) 126 } 127 128 fun setEffectResources(effectTextRes: EffectTextRes) { 129 this.effectTextRes = effectTextRes 130 updateDefaultTextIfEmpty(effectTextRes) 131 } 132 133 private fun updateDefaultTextIfEmpty(effectTextRes: EffectTextRes) { 134 tryAgainLaterButton?.updateTextIfEmpty(effectTextRes.noEffectInstruction) 135 title?.updateTextIfEmpty(effectTextRes.effectTitle) 136 description?.updateTextIfEmpty(effectTextRes.effectSubTitle) 137 } 138 139 private fun TextView.updateTextIfEmpty(newText: String) { 140 this.text = this.text.ifEmpty { newText } 141 } 142 143 /** 144 * Updates the wallpaper effect view by status. 145 * 146 * @param status Last status code of wallpaper effect. 147 * @param resultCode the result code to handle different layouts. 148 * @param errorMessage the description of the sheet. 149 */ 150 fun updateEffectStatus( 151 effect: EffectEnumInterface, 152 status: Status, 153 resultCode: Int?, 154 errorMessage: String? 155 ) { 156 when (status) { 157 Status.IDLE -> { 158 showBasicLayout() 159 updateEffectSwitchStatus(effect, /* checked = */ false) 160 effectSwitch?.isEnabled = true 161 switchLayout?.visibility = VISIBLE 162 container?.visibility = VISIBLE 163 } 164 Status.SUCCESS -> { 165 showBasicLayout() 166 updateEffectSwitchStatus(effect, /* checked = */ true) 167 Handler().postDelayed({ effectSwitch?.isEnabled = true }, 500) 168 switchLayout?.visibility = VISIBLE 169 container?.visibility = VISIBLE 170 } 171 Status.PROCESSING -> { 172 showBasicLayout() 173 updateEffectSwitchStatus(effect, /* checked = */ true) 174 effectSwitch?.isEnabled = false 175 switchLayout?.visibility = VISIBLE 176 container?.visibility = VISIBLE 177 } 178 Status.FAILED -> showFailedLayout(errorMessage) 179 Status.SHOW_DOWNLOAD_BUTTON -> { 180 description?.text = effectTextRes.effectSubTitle 181 switchLayout?.visibility = INVISIBLE 182 container?.visibility = INVISIBLE 183 downloadButtonLayout?.visibility = VISIBLE 184 downloadProgression?.visibility = INVISIBLE 185 } 186 Status.DOWNLOADING -> { 187 switchLayout?.visibility = INVISIBLE 188 container?.visibility = INVISIBLE 189 downloadButtonLayout?.visibility = INVISIBLE 190 downloadProgression?.visibility = VISIBLE 191 } 192 } 193 controlButtonByCode( 194 myPhotosButton, 195 resultCode, 196 EffectsController.RESULT_ERROR_TRY_ANOTHER_PHOTO 197 ) 198 controlButtonByCode( 199 tryAgainLaterButton, 200 resultCode, 201 EffectsController.RESULT_ERROR_TRY_AGAIN_LATER 202 ) 203 controlButtonByCode(continueButton, resultCode, EffectsController.RESULT_ERROR_CONTINUE) 204 } 205 206 private fun showBasicLayout() { 207 title?.text = effectTextRes.effectTitle 208 description?.text = effectTextRes.effectSubTitle 209 switchLayout?.visibility = VISIBLE 210 container?.visibility = INVISIBLE 211 downloadProgression?.visibility = INVISIBLE 212 downloadButtonLayout?.visibility = INVISIBLE 213 } 214 215 private fun showFailedLayout(errorMessage: String?) { 216 switchLayout?.visibility = INVISIBLE 217 title?.text = effectTextRes.effectFailedTitle 218 description?.text = errorMessage 219 container?.visibility = VISIBLE 220 downloadProgression?.visibility = INVISIBLE 221 downloadButtonLayout?.visibility = INVISIBLE 222 } 223 224 private fun controlButtonByCode(view: View?, resultCode: Int?, mask: Int) { 225 view?.visibility = if (resultCode == null || resultCode and mask == 0) GONE else VISIBLE 226 } 227 228 /** 229 * Updates the wallpaper effect switch title. 230 * 231 * @param title The title of the switch. 232 */ 233 fun updateEffectTitle(title: String?) { 234 effectTitle!!.text = title 235 } 236 237 private fun switchChanged(effect: EffectEnumInterface, isChecked: Boolean) { 238 effectSwitchListener?.onEffectSwitchChanged(effect, isChecked) 239 } 240 241 override fun onFinishInflate() { 242 super.onFinishInflate() 243 effectSwitch = findViewById(R.id.wallpaper_effect_switch) 244 effectTitle = findViewById(R.id.wallpaper_effect_toggle_title) 245 title = findViewById(R.id.wallpaper_effects_title) 246 description = findViewById(R.id.wallpaper_effects_subtitle) 247 myPhotosButton = findViewById(R.id.open_my_photo_button) 248 tryAgainLaterButton = findViewById(R.id.try_again_button) 249 continueButton = findViewById(R.id.continue_button) 250 container = findViewById(R.id.buttons_container) 251 switchLayout = findViewById(R.id.wallpaper_effect_linear_layout) 252 switchLayout?.setOnClickListener { 253 if (effectSwitch?.isEnabled == true) effectSwitch?.toggle() 254 } 255 downloadButton = findViewById(R.id.download_model_button) 256 downloadButton?.setOnClickListener { downloadClickListener?.onEffectDownloadClick() } 257 258 downloadButtonLayout = findViewById(R.id.button_layout) 259 downloadProgression = findViewById(R.id.action_progress) 260 } 261 262 /** Implements a listener to know when an effect switch has been selected/unselected. */ 263 interface EffectSwitchListener { 264 /** 265 * Called when one of the effect switches has change the checked value. 266 * 267 * @param effect The effect connected to the switch. 268 * @param isChecked if the switch is checked. 269 */ 270 fun onEffectSwitchChanged(effect: EffectEnumInterface, isChecked: Boolean) 271 } 272 273 /** Implements a listener to know when an effect download button has been click. */ 274 interface EffectDownloadClickListener { 275 fun onEffectDownloadClick() 276 } 277 } 278