1 /* <lambda>null2 * Copyright (C) 2023 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.picker.preview.ui.view 17 18 import android.app.Flags.liveWallpaperContentHandling 19 import android.app.wallpaper.WallpaperDescription 20 import android.content.Context 21 import android.net.Uri 22 import android.util.AttributeSet 23 import android.view.LayoutInflater 24 import android.view.ViewGroup 25 import android.widget.Button 26 import android.widget.FrameLayout 27 import android.widget.TextView 28 import androidx.core.view.isVisible 29 import androidx.lifecycle.LiveData 30 import androidx.slice.Slice 31 import androidx.slice.widget.SliceLiveData 32 import androidx.slice.widget.SliceView 33 import com.android.wallpaper.R 34 import com.android.wallpaper.config.BaseFlags 35 import com.android.wallpaper.effects.EffectsController.EffectEnumInterface 36 import com.android.wallpaper.model.WallpaperAction 37 import com.android.wallpaper.util.SizeCalculator 38 import com.android.wallpaper.widget.floatingsheetcontent.WallpaperActionSelectionBottomSheet 39 import com.android.wallpaper.widget.floatingsheetcontent.WallpaperActionsToggleAdapter 40 import com.android.wallpaper.widget.floatingsheetcontent.WallpaperActionsToggleAdapter.WallpaperEffectSwitchListener 41 import com.android.wallpaper.widget.floatingsheetcontent.WallpaperEffectsView2 42 import com.google.android.material.bottomsheet.BottomSheetBehavior 43 import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback 44 45 /** 46 * UI that hosts the content of the floating sheet dialog sliding from the bottom when a 47 * correspondent preview action is toggled on. 48 */ 49 class PreviewActionFloatingSheet(context: Context, attrs: AttributeSet?) : 50 FrameLayout(context, attrs) { 51 52 private val floatingSheetView: ViewGroup 53 private val floatingSheetContainer: ViewGroup 54 private val floatingSheetBehavior: BottomSheetBehavior<ViewGroup> 55 56 private var customizeLiveDataAndView: Pair<LiveData<Slice>, SliceView>? = null 57 58 init { 59 val layout = 60 if (BaseFlags.get().isNewPickerUi()) R.layout.floating_sheet3 61 else R.layout.floating_sheet2 62 LayoutInflater.from(context).inflate(layout, this, true) 63 floatingSheetView = requireViewById(R.id.floating_sheet_content) 64 SizeCalculator.adjustBackgroundCornerRadius(floatingSheetView) 65 floatingSheetContainer = requireViewById(R.id.floating_sheet_container) 66 floatingSheetBehavior = BottomSheetBehavior.from(floatingSheetContainer) 67 floatingSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN 68 } 69 70 fun setImageEffectContent( 71 effect: EffectEnumInterface, 72 myPhotosClickListener: OnClickListener, 73 collapseFloatingSheetListener: OnClickListener, 74 effectSwitchListener: WallpaperEffectsView2.EffectSwitchListener, 75 effectDownloadClickListener: WallpaperEffectsView2.EffectDownloadClickListener, 76 status: WallpaperEffectsView2.Status, 77 resultCode: Int?, 78 errorMessage: String?, 79 title: String, 80 effectTextRes: WallpaperEffectsView2.EffectTextRes, 81 ) { 82 val view = 83 LayoutInflater.from(context).inflate(R.layout.wallpaper_effects_view2, this, false) 84 as WallpaperEffectsView2 85 view.setEffectResources(effectTextRes) 86 view.setMyPhotosClickListener(myPhotosClickListener) 87 view.setCollapseFloatingSheetListener(collapseFloatingSheetListener) 88 view.addEffectSwitchListener(effectSwitchListener) 89 view.setEffectDownloadClickListener(effectDownloadClickListener) 90 view.updateEffectStatus(effect, status, resultCode, errorMessage) 91 view.updateEffectTitle(title) 92 floatingSheetView.removeAllViews() 93 floatingSheetView.addView(view) 94 } 95 96 fun setCreativeEffectContent( 97 title: String, 98 subtitle: String, 99 wallpaperActions: List<WallpaperAction>, 100 wallpaperEffectSwitchListener: WallpaperEffectSwitchListener, 101 ) { 102 val view = 103 LayoutInflater.from(context) 104 .inflate(R.layout.wallpaper_action_selection_bottom_sheet, this, false) 105 as WallpaperActionSelectionBottomSheet 106 view.setBottomSheetTitle(title) 107 view.setBottomSheetSubtitle(subtitle) 108 view.setUpActionToggleOptions( 109 WallpaperActionsToggleAdapter( 110 // TODO(b/270729418): enable multiple effect options once final design is 111 // agreed upon. 112 // Forcing only one effect item for now 113 if (wallpaperActions.isNotEmpty()) wallpaperActions.subList(0, 1) else listOf(), 114 wallpaperEffectSwitchListener, 115 ) 116 ) 117 floatingSheetView.removeAllViews() 118 floatingSheetView.addView(view) 119 } 120 121 fun setInformationContent( 122 description: WallpaperDescription?, 123 attributions: List<String>?, 124 onExploreButtonClickListener: OnClickListener?, 125 actionButtonTitle: CharSequence?, 126 ) { 127 val view = LayoutInflater.from(context).inflate(R.layout.wallpaper_info_view2, this, false) 128 val title: TextView = view.requireViewById(R.id.wallpaper_info_title) 129 val subtitle1: TextView = view.requireViewById(R.id.wallpaper_info_subtitle1) 130 val subtitle2: TextView = view.requireViewById(R.id.wallpaper_info_subtitle2) 131 val exploreButton: Button = view.requireViewById(R.id.wallpaper_info_explore_button) 132 133 val combinedAttributions = attributions?.toMutableList() ?: mutableListOf() 134 if (liveWallpaperContentHandling() && description != null) { 135 description.title.let { 136 if (!it.isNullOrEmpty()) { 137 combinedAttributions[0] = it.toString() 138 } 139 } 140 description.description.forEachIndexed { index, char -> 141 if (!char.isNullOrEmpty()) { 142 combinedAttributions[index + 1] = char.toString() 143 } 144 } 145 } 146 147 combinedAttributions.forEachIndexed { index, text -> 148 when (index) { 149 0 -> { 150 if (text.isNotEmpty()) { 151 title.text = text 152 title.isVisible = true 153 } 154 } 155 1 -> { 156 if (text.isNotEmpty()) { 157 subtitle1.text = text 158 subtitle1.isVisible = true 159 } 160 } 161 2 -> { 162 if (text.isNotEmpty()) { 163 subtitle2.text = text 164 subtitle2.isVisible = true 165 } 166 } 167 } 168 } 169 170 exploreButton.isVisible = onExploreButtonClickListener != null 171 actionButtonTitle?.let { exploreButton.text = it } 172 exploreButton.setOnClickListener(onExploreButtonClickListener) 173 174 floatingSheetView.removeAllViews() 175 floatingSheetView.addView(view) 176 } 177 178 fun setCustomizeContent(uri: Uri) { 179 removeCustomizeLiveDataObserver() 180 val view = 181 LayoutInflater.from(context).inflate(R.layout.preview_customize_settings2, this, false) 182 val settingsSliceView: SliceView = 183 view.requireViewById<SliceView>(R.id.settings_slice).apply { 184 mode = SliceView.MODE_LARGE 185 isScrollable = false 186 } 187 customizeLiveDataAndView = SliceLiveData.fromUri(view.context, uri) to settingsSliceView 188 customizeLiveDataAndView?.let { (liveData, observer) -> liveData.observeForever(observer) } 189 floatingSheetView.removeAllViews() 190 floatingSheetView.addView(view) 191 } 192 193 fun expand() { 194 floatingSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED 195 } 196 197 fun collapse() { 198 floatingSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN 199 removeCustomizeLiveDataObserver() 200 } 201 202 /** 203 * Adds Floating Sheet Callback to connected [BottomSheetBehavior]. 204 * 205 * @param callback the callback for floating sheet state changes, has to be in the type of 206 * [BottomSheetBehavior.BottomSheetCallback] since the floating sheet behavior is currently 207 * based on [BottomSheetBehavior] 208 */ 209 fun addFloatingSheetCallback(callback: BottomSheetCallback) { 210 floatingSheetBehavior.addBottomSheetCallback(callback) 211 } 212 213 fun removeFloatingSheetCallback(callback: BottomSheetCallback) { 214 floatingSheetBehavior.removeBottomSheetCallback(callback) 215 } 216 217 private fun removeCustomizeLiveDataObserver() { 218 customizeLiveDataAndView?.let { (liveData, observer) -> liveData.removeObserver(observer) } 219 customizeLiveDataAndView = null 220 } 221 } 222