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