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.fragment
17 
18 import android.app.Activity.RESULT_OK
19 import android.app.Flags.liveWallpaperContentHandling
20 import android.app.wallpaper.WallpaperDescription
21 import android.content.Context
22 import android.content.Intent
23 import android.os.Bundle
24 import android.view.LayoutInflater
25 import android.view.SurfaceView
26 import android.view.View
27 import android.view.ViewGroup
28 import android.widget.Button
29 import android.widget.Toolbar
30 import androidx.activity.result.contract.ActivityResultContract
31 import androidx.activity.result.contract.ActivityResultContracts
32 import androidx.core.view.isVisible
33 import androidx.fragment.app.activityViewModels
34 import androidx.navigation.fragment.findNavController
35 import com.android.wallpaper.R
36 import com.android.wallpaper.model.WallpaperInfoContract
37 import com.android.wallpaper.picker.AppbarFragment
38 import com.android.wallpaper.picker.data.LiveWallpaperData
39 import com.android.wallpaper.picker.data.WallpaperModel.LiveWallpaperModel
40 import com.android.wallpaper.picker.di.modules.MainDispatcher
41 import com.android.wallpaper.picker.preview.data.repository.WallpaperPreviewRepository
42 import com.android.wallpaper.picker.preview.ui.binder.FullWallpaperPreviewBinder
43 import com.android.wallpaper.picker.preview.ui.fragment.SmallPreviewFragment.Companion.ARG_EDIT_INTENT
44 import com.android.wallpaper.picker.preview.ui.viewmodel.PreviewActionsViewModel
45 import com.android.wallpaper.picker.preview.ui.viewmodel.WallpaperPreviewViewModel
46 import com.android.wallpaper.util.DisplayUtils
47 import com.android.wallpaper.util.wallpaperconnection.WallpaperConnectionUtils
48 import dagger.hilt.android.AndroidEntryPoint
49 import dagger.hilt.android.qualifiers.ApplicationContext
50 import javax.inject.Inject
51 import kotlinx.coroutines.CompletableDeferred
52 import kotlinx.coroutines.CoroutineScope
53 import kotlinx.coroutines.runBlocking
54 
55 /** Shows full preview with an edit activity overlay. */
56 @AndroidEntryPoint(AppbarFragment::class)
57 class CreativeEditPreviewFragment : Hilt_CreativeEditPreviewFragment() {
58 
59     @Inject @ApplicationContext lateinit var appContext: Context
60     @Inject @MainDispatcher lateinit var mainScope: CoroutineScope
61     @Inject lateinit var displayUtils: DisplayUtils
62     @Inject lateinit var previewRepository: WallpaperPreviewRepository
63     @Inject lateinit var wallpaperConnectionUtils: WallpaperConnectionUtils
64 
65     private lateinit var currentView: View
66 
67     private val wallpaperPreviewViewModel by activityViewModels<WallpaperPreviewViewModel>()
68 
69     override fun onCreateView(
70         inflater: LayoutInflater,
71         container: ViewGroup?,
72         savedInstanceState: Bundle?,
73     ): View? {
74         currentView = inflater.inflate(R.layout.fragment_full_preview, container, false)
75         setUpToolbar(currentView, true, true)
76 
77         wallpaperPreviewViewModel.setDefaultFullPreviewConfigViewModel(
78             deviceDisplayType = displayUtils.getCurrentDisplayType(requireActivity())
79         )
80 
81         currentView.requireViewById<Toolbar>(R.id.toolbar).isVisible = false
82         currentView.requireViewById<SurfaceView>(R.id.workspace_surface).isVisible = false
83         currentView.requireViewById<Button>(R.id.crop_wallpaper_button).isVisible = false
84 
85         val intent =
86             arguments?.getParcelable(ARG_EDIT_INTENT, Intent::class.java)
87                 ?: throw IllegalArgumentException(
88                     "To render the first screen in the create new creative wallpaper flow, the intent for rendering the edit activity overlay can not be null."
89                 )
90         val isCreateNew =
91             intent.getBooleanExtra(PreviewActionsViewModel.EXTRA_KEY_IS_CREATE_NEW, false)
92         val creativeWallpaperEditActivityResult =
93             if (isCreateNew) {
94                 requireActivity().activityResultRegistry.register(
95                     CREATIVE_RESULT_REGISTRY,
96                     ActivityResultContracts.StartActivityForResult(),
97                 ) {
98                     // Reset full preview view model to disable full to small preview transition
99                     wallpaperPreviewViewModel.resetFullPreviewConfigViewModel()
100                     wallpaperPreviewViewModel.isCurrentlyEditingCreativeWallpaper = false
101                     // Callback when the overlaying edit activity is finished. Result code of
102                     // RESULT_OK means the user clicked on the check button; RESULT_CANCELED
103                     // otherwise.
104                     if (it.resultCode == RESULT_OK) {
105                         if (liveWallpaperContentHandling()) {
106                             updatePreview(it.resultCode, it.data)
107                         }
108                         // When clicking on the check button, navigate to the small preview
109                         // fragment.
110                         findNavController()
111                             .navigate(
112                                 R.id.action_creativeEditPreviewFragment_to_smallPreviewFragment
113                             )
114                     } else {
115                         activity?.finish()
116                     }
117                 }
118             } else {
119                 requireActivity().activityResultRegistry.register(
120                     CREATIVE_RESULT_REGISTRY,
121                     object : ActivityResultContract<Intent, Int>() {
122                         override fun createIntent(context: Context, input: Intent): Intent {
123                             return input
124                         }
125 
126                         override fun parseResult(resultCode: Int, intent: Intent?): Int {
127                             wallpaperPreviewViewModel.isCurrentlyEditingCreativeWallpaper = false
128                             if (liveWallpaperContentHandling()) {
129                                 updatePreview(resultCode, intent)
130                             }
131                             return resultCode
132                         }
133                     },
134                 ) {
135                     // Reset full preview view model to disable full to small preview transition
136                     wallpaperPreviewViewModel.resetFullPreviewConfigViewModel()
137                     findNavController().popBackStack()
138                 }
139             }
140 
141         if (!wallpaperPreviewViewModel.isCurrentlyEditingCreativeWallpaper) {
142             wallpaperPreviewViewModel.isCurrentlyEditingCreativeWallpaper = true
143             creativeWallpaperEditActivityResult.launch(intent)
144         }
145 
146         return currentView
147     }
148 
149     // Updates the current preview using the WallpaperDescription returned with the Intent if any
150     private fun updatePreview(resultCode: Int, intent: Intent?) {
151         if (!liveWallpaperContentHandling()) return
152         if (resultCode == RESULT_OK) {
153             val component =
154                 (previewRepository.wallpaperModel.value as LiveWallpaperModel)
155                     .liveWallpaperData
156                     .systemWallpaperInfo
157                     .component
158             intent
159                 ?.extras
160                 ?.getParcelable(
161                     WallpaperInfoContract.WALLPAPER_DESCRIPTION_CONTENT_HANDLING,
162                     WallpaperDescription::class.java,
163                 )
164                 ?.let {
165                     if (it.component != null) {
166                         it
167                     } else {
168                         // Live wallpaper services can't provide their component name, so
169                         // set it here
170                         it.toBuilder().setComponent(component).build()
171                     }
172                 }
173                 ?.let { description ->
174                     (previewRepository.wallpaperModel.value as LiveWallpaperModel).let {
175                         val sourceLiveData = it.liveWallpaperData
176                         val updatedLiveData =
177                             LiveWallpaperData(
178                                 sourceLiveData.groupName,
179                                 sourceLiveData.systemWallpaperInfo,
180                                 sourceLiveData.isTitleVisible,
181                                 sourceLiveData.isApplied,
182                                 sourceLiveData.isEffectWallpaper,
183                                 sourceLiveData.effectNames,
184                                 sourceLiveData.contextDescription,
185                                 description,
186                             )
187                         val updatedWallpaper =
188                             LiveWallpaperModel(
189                                 it.commonWallpaperData,
190                                 updatedLiveData,
191                                 it.creativeWallpaperData,
192                                 it.internalLiveWallpaperData,
193                             )
194                         runBlocking { previewRepository.setWallpaperModel(updatedWallpaper) }
195                     }
196                 }
197         }
198     }
199 
200     override fun onViewStateRestored(savedInstanceState: Bundle?) {
201         super.onViewStateRestored(savedInstanceState)
202 
203         FullWallpaperPreviewBinder.bind(
204             applicationContext = appContext,
205             view = currentView,
206             viewModel = wallpaperPreviewViewModel,
207             transition = null,
208             displayUtils = displayUtils,
209             mainScope = mainScope,
210             lifecycleOwner = viewLifecycleOwner,
211             savedInstanceState = savedInstanceState,
212             wallpaperConnectionUtils = wallpaperConnectionUtils,
213             isFirstBindingDeferred = CompletableDeferred(savedInstanceState == null),
214         )
215     }
216 
217     companion object {
218         private const val CREATIVE_RESULT_REGISTRY = "creative_result_registry"
219     }
220 }
221