1 /*
<lambda>null2  * Copyright (C) 2024 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 package com.android.wallpaper.picker.category.ui.view
18 
19 import android.Manifest
20 import android.app.Activity
21 import android.content.ComponentName
22 import android.content.Intent
23 import android.content.pm.ResolveInfo
24 import android.net.Uri
25 import android.os.Bundle
26 import android.provider.Settings
27 import android.view.LayoutInflater
28 import android.view.View
29 import android.view.ViewGroup
30 import android.widget.TextView
31 import androidx.activity.result.ActivityResultLauncher
32 import androidx.activity.result.contract.ActivityResultContracts
33 import androidx.core.content.ContextCompat
34 import androidx.fragment.app.Fragment
35 import androidx.fragment.app.activityViewModels
36 import androidx.recyclerview.widget.RecyclerView
37 import com.android.wallpaper.R
38 import com.android.wallpaper.model.ImageWallpaperInfo
39 import com.android.wallpaper.module.MultiPanesChecker
40 import com.android.wallpaper.picker.AppbarFragment
41 import com.android.wallpaper.picker.MyPhotosStarter.PermissionChangedListener
42 import com.android.wallpaper.picker.WallpaperPickerDelegate.VIEW_ONLY_PREVIEW_WALLPAPER_REQUEST_CODE
43 import com.android.wallpaper.picker.category.ui.binder.CategoriesBinder
44 import com.android.wallpaper.picker.category.ui.view.providers.IndividualPickerFactory
45 import com.android.wallpaper.picker.category.ui.viewmodel.CategoriesViewModel
46 import com.android.wallpaper.picker.common.preview.data.repository.PersistentWallpaperModelRepository
47 import com.android.wallpaper.picker.data.WallpaperModel
48 import com.android.wallpaper.picker.preview.ui.WallpaperPreviewActivity
49 import com.android.wallpaper.util.ActivityUtils
50 import com.android.wallpaper.util.SizeCalculator
51 import com.android.wallpaper.util.converter.WallpaperModelFactory
52 import com.google.android.material.snackbar.Snackbar
53 import dagger.hilt.android.AndroidEntryPoint
54 import javax.inject.Inject
55 
56 /** This fragment displays the user interface for the categories */
57 @AndroidEntryPoint(AppbarFragment::class)
58 class CategoriesFragment : Hilt_CategoriesFragment() {
59 
60     @Inject lateinit var individualPickerFactory: IndividualPickerFactory
61     @Inject lateinit var persistentWallpaperModelRepository: PersistentWallpaperModelRepository
62     @Inject lateinit var multiPanesChecker: MultiPanesChecker
63     @Inject lateinit var myPhotosStarterImpl: MyPhotosStarterImpl
64     @Inject lateinit var wallpaperModelFactory: WallpaperModelFactory
65 
66     private lateinit var photoPickerLauncher: ActivityResultLauncher<Intent>
67 
68     // TODO: this may need to be scoped to fragment if the architecture changes
69     private val categoriesViewModel by activityViewModels<CategoriesViewModel>()
70 
71     override fun onCreate(savedInstanceState: Bundle?) {
72         super.onCreate(savedInstanceState)
73         photoPickerLauncher =
74             registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
75                 if (result.resultCode != Activity.RESULT_OK) {
76                     return@registerForActivityResult
77                 }
78 
79                 val data: Intent? = result.data
80                 val imageUri: Uri = data?.data ?: return@registerForActivityResult
81                 val imageWallpaperInfo = ImageWallpaperInfo(imageUri)
82                 val context = context ?: return@registerForActivityResult
83                 val wallpaperModel =
84                     wallpaperModelFactory.getWallpaperModel(context, imageWallpaperInfo)
85                 startWallpaperPreviewActivity(wallpaperModel, false)
86             }
87     }
88 
89     override fun onCreateView(
90         inflater: LayoutInflater,
91         container: ViewGroup?,
92         savedInstanceState: Bundle?,
93     ): View {
94         val view =
95             inflater.inflate(R.layout.categories_fragment, container, /* attachToRoot= */ false)
96 
97         setUpToolbar(view)
98         setTitle(getText(R.string.wallpaper_title))
99 
100         CategoriesBinder.bind(
101             categoriesPage = view.requireViewById<RecyclerView>(R.id.content_parent),
102             viewModel = categoriesViewModel,
103             SizeCalculator.getActivityWindowWidthPx(this.activity),
104             lifecycleOwner = viewLifecycleOwner,
105         ) { navigationEvent, callback ->
106             when (navigationEvent) {
107                 is CategoriesViewModel.NavigationEvent.NavigateToWallpaperCollection -> {
108                     switchFragment(
109                         individualPickerFactory.getIndividualPickerInstance(
110                             navigationEvent.categoryId,
111                             navigationEvent.categoryType,
112                         )
113                     )
114                 }
115                 is CategoriesViewModel.NavigationEvent.NavigateToPhotosPicker -> {
116                     // make call to permission handler to grab photos and pass callback
117                     myPhotosStarterImpl.requestCustomPhotoPicker(
118                         object : PermissionChangedListener {
119                             override fun onPermissionsGranted() {
120                                 callback?.invoke()
121                             }
122 
123                             override fun onPermissionsDenied(dontAskAgain: Boolean) {
124                                 if (dontAskAgain) {
125                                     showPermissionSnackbar()
126                                 }
127                             }
128                         },
129                         requireActivity(),
130                         photoPickerLauncher,
131                     )
132                 }
133                 is CategoriesViewModel.NavigationEvent.NavigateToThirdParty -> {
134                     startThirdPartyCategoryActivity(
135                         requireActivity(),
136                         SHOW_CATEGORY_REQUEST_CODE,
137                         navigationEvent.resolveInfo,
138                     )
139                 }
140                 is CategoriesViewModel.NavigationEvent.NavigateToPreviewScreen -> {
141                     startWallpaperPreviewActivity(
142                         navigationEvent.wallpaperModel,
143                         navigationEvent.categoryType ==
144                             CategoriesViewModel.CategoryType.CreativeCategories,
145                     )
146                 }
147             }
148         }
149         return view
150     }
151 
152     private fun startWallpaperPreviewActivity(
153         wallpaperModel: WallpaperModel,
154         isCreativeCategories: Boolean,
155     ) {
156         val appContext = requireContext()
157         val activity = requireActivity()
158         persistentWallpaperModelRepository.setWallpaperModel(wallpaperModel)
159         val isMultiPanel = multiPanesChecker.isMultiPanesEnabled(appContext)
160         val previewIntent =
161             WallpaperPreviewActivity.newIntent(
162                 context = appContext,
163                 isAssetIdPresent = true,
164                 isViewAsHome = true,
165                 isNewTask = isMultiPanel,
166                 shouldCategoryRefresh = isCreativeCategories,
167             )
168         ActivityUtils.startActivityForResultSafely(
169             activity,
170             previewIntent,
171             VIEW_ONLY_PREVIEW_WALLPAPER_REQUEST_CODE, // TODO: provide correct request code
172         )
173     }
174 
175     private fun showPermissionSnackbar() {
176         val snackbar =
177             Snackbar.make(
178                 requireView(),
179                 R.string.settings_snackbar_description,
180                 Snackbar.LENGTH_LONG,
181             )
182         val layout = snackbar.view as Snackbar.SnackbarLayout
183         val textView =
184             layout.findViewById<View>(com.google.android.material.R.id.snackbar_text) as TextView
185         layout.setBackgroundResource(R.drawable.snackbar_background)
186 
187         textView.setTextColor(ContextCompat.getColor(requireContext(), R.color.system_on_primary))
188         snackbar.setActionTextColor(
189             ContextCompat.getColor(requireContext(), R.color.system_surface_container)
190         )
191         snackbar.setAction(requireContext().getString(R.string.settings_snackbar_enable)) {
192             startSettings(SETTINGS_APP_INFO_REQUEST_CODE)
193         }
194         snackbar.show()
195     }
196 
197     private fun startSettings(resultCode: Int) {
198         val activity = activity ?: return
199         val appInfoIntent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
200         val uri = Uri.fromParts("package", activity.packageName, /* fragment= */ null)
201         appInfoIntent.setData(uri)
202         startActivityForResult(appInfoIntent, resultCode)
203     }
204 
205     private fun startThirdPartyCategoryActivity(
206         srcActivity: Activity,
207         requestCode: Int,
208         resolveInfo: ResolveInfo,
209     ) {
210         val itemComponentName =
211             ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)
212         val launchIntent = Intent(Intent.ACTION_SET_WALLPAPER)
213         launchIntent.component = itemComponentName
214         ActivityUtils.startActivityForResultSafely(srcActivity, launchIntent, requestCode)
215     }
216 
217     private fun switchFragment(fragment: Fragment) {
218         parentFragmentManager
219             .beginTransaction()
220             .replace(R.id.fragment_container, fragment)
221             .addToBackStack(null)
222             .commit()
223         parentFragmentManager.executePendingTransactions()
224     }
225 
226     companion object {
227         const val SHOW_CATEGORY_REQUEST_CODE = 0
228         const val SETTINGS_APP_INFO_REQUEST_CODE = 1
229         const val READ_IMAGE_PERMISSION: String = Manifest.permission.READ_MEDIA_IMAGES
230     }
231 }
232