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