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  */
17 
18 package com.android.customization.picker.grid.ui.fragment
19 
20 import android.os.Bundle
21 import android.util.Log
22 import android.view.LayoutInflater
23 import android.view.View
24 import android.view.ViewGroup
25 import android.view.ViewGroup.MarginLayoutParams
26 import android.widget.Button
27 import android.widget.Toast
28 import androidx.core.content.ContextCompat
29 import androidx.core.view.ViewCompat
30 import androidx.core.view.WindowInsetsCompat
31 import androidx.core.view.isVisible
32 import androidx.core.view.updateLayoutParams
33 import androidx.lifecycle.ViewModelProvider
34 import androidx.transition.Transition
35 import androidx.transition.doOnStart
36 import com.android.customization.model.CustomizationManager.Callback
37 import com.android.customization.module.ThemePickerInjector
38 import com.android.customization.picker.grid.domain.interactor.GridInteractor
39 import com.android.customization.picker.grid.ui.binder.GridScreenBinder
40 import com.android.customization.picker.grid.ui.viewmodel.GridScreenViewModel
41 import com.android.themepicker.R
42 import com.android.wallpaper.config.BaseFlags
43 import com.android.wallpaper.model.Screen
44 import com.android.wallpaper.module.CurrentWallpaperInfoFactory
45 import com.android.wallpaper.module.InjectorProvider
46 import com.android.wallpaper.picker.AppbarFragment
47 import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
48 import com.android.wallpaper.picker.customization.ui.binder.ScreenPreviewBinder
49 import com.android.wallpaper.picker.customization.ui.viewmodel.ScreenPreviewViewModel
50 import com.android.wallpaper.util.PreviewUtils
51 import kotlinx.coroutines.Dispatchers
52 import kotlinx.coroutines.ExperimentalCoroutinesApi
53 import kotlinx.coroutines.suspendCancellableCoroutine
54 
55 private val TAG = GridFragment::class.java.simpleName
56 
57 @OptIn(ExperimentalCoroutinesApi::class)
58 class GridFragment : AppbarFragment() {
59 
60     private lateinit var gridInteractor: GridInteractor
61 
62     override fun onCreateView(
63         inflater: LayoutInflater,
64         container: ViewGroup?,
65         savedInstanceState: Bundle?
66     ): View {
67         val view =
68             inflater.inflate(
69                 R.layout.fragment_grid,
70                 container,
71                 false,
72             )
73         ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets ->
74             val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
75             v.updateLayoutParams<MarginLayoutParams> {
76                 topMargin = insets.top
77                 bottomMargin = insets.bottom
78             }
79             WindowInsetsCompat.CONSUMED
80         }
81         setUpToolbar(view)
82 
83         val isGridApplyButtonEnabled = BaseFlags.get().isGridApplyButtonEnabled(requireContext())
84 
85         val injector = InjectorProvider.getInjector() as ThemePickerInjector
86 
87         val wallpaperInfoFactory = injector.getCurrentWallpaperInfoFactory(requireContext())
88         var screenPreviewBinding =
89             bindScreenPreview(
90                 view,
91                 wallpaperInfoFactory,
92                 injector.getWallpaperInteractor(requireContext()),
93                 injector.getGridInteractor(requireContext())
94             )
95 
96         val viewModelFactory = injector.getGridScreenViewModelFactory(requireContext())
97         gridInteractor = injector.getGridInteractor(requireContext())
98         GridScreenBinder.bind(
99             view = view,
100             viewModel =
101                 ViewModelProvider(
102                     this,
103                     viewModelFactory,
104                 )[GridScreenViewModel::class.java],
105             lifecycleOwner = this,
106             backgroundDispatcher = Dispatchers.IO,
107             onOptionsChanged = {
108                 screenPreviewBinding.destroy()
109                 screenPreviewBinding =
110                     bindScreenPreview(
111                         view,
112                         wallpaperInfoFactory,
113                         injector.getWallpaperInteractor(requireContext()),
114                         gridInteractor,
115                     )
116                 if (isGridApplyButtonEnabled) {
117                     val applyButton: Button = view.requireViewById(R.id.apply_button)
118                     applyButton.isEnabled = !gridInteractor.isSelectedOptionApplied()
119                 }
120             },
121             isGridApplyButtonEnabled = isGridApplyButtonEnabled,
122             onOptionApplied = {
123                 gridInteractor.applySelectedOption(
124                     object : Callback {
125                         override fun onSuccess() {
126                             Toast.makeText(
127                                     context,
128                                     getString(
129                                         R.string.toast_of_changing_grid,
130                                         gridInteractor.getSelectOptionStateFlow().value?.title
131                                     ),
132                                     Toast.LENGTH_SHORT
133                                 )
134                                 .show()
135                             val applyButton: Button = view.requireViewById(R.id.apply_button)
136                             applyButton.isEnabled = false
137                         }
138 
139                         override fun onError(throwable: Throwable?) {
140                             val errorMsg =
141                                 getString(
142                                     R.string.toast_of_failure_to_change_grid,
143                                     gridInteractor.getSelectOptionStateFlow().value?.title
144                                 )
145                             Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show()
146                             Log.e(TAG, errorMsg, throwable)
147                         }
148                     }
149                 )
150             }
151         )
152 
153         (returnTransition as? Transition)?.doOnStart {
154             view.requireViewById<View>(R.id.preview).isVisible = false
155         }
156 
157         return view
158     }
159 
160     override fun getDefaultTitle(): CharSequence {
161         return getString(R.string.grid_title)
162     }
163 
164     override fun getToolbarTextColor(): Int {
165         return ContextCompat.getColor(
166             requireContext(),
167             com.android.wallpaper.R.color.system_on_surface
168         )
169     }
170 
171     private fun bindScreenPreview(
172         view: View,
173         wallpaperInfoFactory: CurrentWallpaperInfoFactory,
174         wallpaperInteractor: WallpaperInteractor,
175         gridInteractor: GridInteractor
176     ): ScreenPreviewBinder.Binding {
177         return ScreenPreviewBinder.bind(
178             activity = requireActivity(),
179             previewView = view.requireViewById(R.id.preview),
180             viewModel =
181                 ScreenPreviewViewModel(
182                     previewUtils =
183                         PreviewUtils(
184                             context = requireContext(),
185                             authorityMetadataKey =
186                                 requireContext()
187                                     .getString(
188                                         com.android.wallpaper.R.string.grid_control_metadata_name,
189                                     ),
190                         ),
191                     initialExtrasProvider = {
192                         val bundle = Bundle()
193                         bundle.putString(
194                             "name",
195                             gridInteractor.getSelectOptionStateFlow().value?.name
196                         )
197                         bundle
198                     },
199                     wallpaperInfoProvider = {
200                         suspendCancellableCoroutine { continuation ->
201                             wallpaperInfoFactory.createCurrentWallpaperInfos(
202                                 context,
203                                 /* forceRefresh= */ true,
204                             ) { homeWallpaper, lockWallpaper, _ ->
205                                 continuation.resume(homeWallpaper ?: lockWallpaper, null)
206                             }
207                         }
208                     },
209                     wallpaperInteractor = wallpaperInteractor,
210                     screen = Screen.HOME_SCREEN,
211                 ),
212             lifecycleOwner = viewLifecycleOwner,
213             offsetToStart = false,
214             onWallpaperPreviewDirty = { activity?.recreate() },
215         )
216     }
217 
218     override fun onBackPressed(): Boolean {
219         if (BaseFlags.get().isGridApplyButtonEnabled(requireContext())) {
220             gridInteractor.clearSelectedOption()
221         }
222         return super.onBackPressed()
223     }
224 }
225