• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.fragment.app.commit
37 import androidx.fragment.app.replace
38 import androidx.recyclerview.widget.RecyclerView
39 import com.android.wallpaper.R
40 import com.android.wallpaper.config.BaseFlags
41 import com.android.wallpaper.model.ImageWallpaperInfo
42 import com.android.wallpaper.module.MultiPanesChecker
43 import com.android.wallpaper.picker.AppbarFragment
44 import com.android.wallpaper.picker.MyPhotosStarter
45 import com.android.wallpaper.picker.WallpaperPickerDelegate.VIEW_ONLY_PREVIEW_WALLPAPER_REQUEST_CODE
46 import com.android.wallpaper.picker.category.ui.binder.BannerProvider
47 import com.android.wallpaper.picker.category.ui.binder.CategoriesBinder
48 import com.android.wallpaper.picker.category.ui.view.providers.IndividualPickerFactory
49 import com.android.wallpaper.picker.category.ui.viewmodel.CategoriesViewModel
50 import com.android.wallpaper.picker.common.preview.data.repository.PersistentWallpaperModelRepository
51 import com.android.wallpaper.picker.customization.ui.binder.ColorUpdateBinder
52 import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
53 import com.android.wallpaper.picker.data.WallpaperModel
54 import com.android.wallpaper.picker.preview.ui.WallpaperPreviewActivity
55 import com.android.wallpaper.util.ActivityUtils
56 import com.android.wallpaper.util.SizeCalculator
57 import com.android.wallpaper.util.converter.WallpaperModelFactory
58 import com.google.android.material.appbar.AppBarLayout
59 import com.google.android.material.snackbar.Snackbar
60 import dagger.hilt.android.AndroidEntryPoint
61 import javax.inject.Inject
62 
63 /** This fragment displays the user interface for the categories */
64 @AndroidEntryPoint(AppbarFragment::class)
65 class CategoriesFragment : Hilt_CategoriesFragment() {
66 
67     @Inject lateinit var individualPickerFactory: IndividualPickerFactory
68     @Inject lateinit var persistentWallpaperModelRepository: PersistentWallpaperModelRepository
69     @Inject lateinit var multiPanesChecker: MultiPanesChecker
70     @Inject lateinit var myPhotosStarterImpl: MyPhotosStarterImpl
71     @Inject lateinit var wallpaperModelFactory: WallpaperModelFactory
72     @Inject lateinit var colorUpdateViewModel: ColorUpdateViewModel
73     @Inject lateinit var bannerProvider: BannerProvider
74     private lateinit var photoPickerLauncher: ActivityResultLauncher<Intent>
75 
76     // TODO: this may need to be scoped to fragment if the architecture changes
77     private val categoriesViewModel by activityViewModels<CategoriesViewModel>()
78 
79     override fun onCreate(savedInstanceState: Bundle?) {
80         super.onCreate(savedInstanceState)
81         photoPickerLauncher =
82             registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
83                 if (result.resultCode != Activity.RESULT_OK) {
84                     return@registerForActivityResult
85                 }
86 
87                 val data: Intent? = result.data
88                 val imageUri: Uri = data?.data ?: return@registerForActivityResult
89                 val imageWallpaperInfo = ImageWallpaperInfo(imageUri)
90                 val context = context ?: return@registerForActivityResult
91                 val wallpaperModel =
92                     wallpaperModelFactory.getWallpaperModel(context, imageWallpaperInfo)
93                 startWallpaperPreviewActivity(wallpaperModel, false)
94             }
95     }
96 
97     override fun onCreateView(
98         inflater: LayoutInflater,
99         container: ViewGroup?,
100         savedInstanceState: Bundle?,
101     ): View {
102         val view =
103             inflater.inflate(R.layout.categories_fragment, container, /* attachToRoot= */ false)
104 
105         setUpToolbar(view)
106         setTitle(getText(R.string.wallpaper_title))
107 
108         val isNewPickerUi = BaseFlags.get().isNewPickerUi()
109         if (isNewPickerUi) {
110             ColorUpdateBinder.bind(
111                 setColor = { _ ->
112                     // There is no way to programmatically set app:liftOnScrollColor in
113                     // AppBarLayout, therefore remove and re-add view to update colors based on new
114                     // context
115                     val contentParent = view.requireViewById<ViewGroup>(R.id.content_parent)
116                     val appBarLayout = view.requireViewById<AppBarLayout>(R.id.app_bar)
117                     contentParent.removeView(appBarLayout)
118                     layoutInflater.inflate(R.layout.section_header_content, contentParent, true)
119                     setUpToolbar(view)
120                     setTitle(getText(R.string.wallpaper_title))
121                     contentParent.requestApplyInsets()
122                 },
123                 color = colorUpdateViewModel.colorSurfaceContainer,
124                 shouldAnimate = { false },
125                 lifecycleOwner = viewLifecycleOwner,
126             )
127         }
128 
129         CategoriesBinder.bind(
130             categoriesPage = view.requireViewById<RecyclerView>(R.id.content_parent),
131             viewModel = categoriesViewModel,
132             windowWidth = SizeCalculator.getActivityWindowWidthPx(this.activity),
133             colorUpdateViewModel = colorUpdateViewModel,
134             shouldAnimateColor = { false },
135             bannerProvider = bannerProvider,
136             lifecycleOwner = viewLifecycleOwner,
137         ) { navigationEvent, callback ->
138             when (navigationEvent) {
139                 is CategoriesViewModel.NavigationEvent.NavigateToWallpaperCollection -> {
140                     switchFragment(
141                         individualPickerFactory.getIndividualPickerInstance(
142                             navigationEvent.categoryId,
143                             navigationEvent.categoryType,
144                         )
145                     )
146                 }
147                 is CategoriesViewModel.NavigationEvent.NavigateToPhotosPicker -> {
148                     if (BaseFlags.get().isPhotoPickerEnabled()) {
149                         parentFragmentManager.commit {
150                             replace<PhotoPickerFragment>(R.id.fragment_container)
151                             addToBackStack(null)
152                         }
153                     } else {
154                         // make call to permission handler to grab photos and pass callback
155                         myPhotosStarterImpl.requestCustomPhotoPicker(
156                             object : MyPhotosStarter.PermissionChangedListener {
157                                 override fun onPermissionsGranted() {
158                                     callback?.invoke()
159                                 }
160 
161                                 override fun onPermissionsDenied(dontAskAgain: Boolean) {
162                                     if (dontAskAgain) {
163                                         showPermissionSnackbar()
164                                     }
165                                 }
166                             },
167                             requireActivity(),
168                             photoPickerLauncher,
169                         )
170                     }
171                 }
172                 is CategoriesViewModel.NavigationEvent.NavigateToThirdParty -> {
173                     startThirdPartyCategoryActivity(
174                         requireActivity(),
175                         SHOW_CATEGORY_REQUEST_CODE,
176                         navigationEvent.resolveInfo,
177                     )
178                 }
179                 is CategoriesViewModel.NavigationEvent.NavigateToPreviewScreen -> {
180                     startWallpaperPreviewActivity(
181                         navigationEvent.wallpaperModel,
182                         navigationEvent.categoryType ==
183                             CategoriesViewModel.CategoryType.CreativeCategories,
184                     )
185                 }
186             }
187         }
188         return view
189     }
190 
191     private fun startWallpaperPreviewActivity(
192         wallpaperModel: WallpaperModel,
193         isCreativeCategories: Boolean,
194     ) {
195         val appContext = requireContext()
196         val activity = requireActivity()
197         persistentWallpaperModelRepository.setWallpaperModel(wallpaperModel)
198         val isMultiPanel = multiPanesChecker.isMultiPanesEnabled(appContext)
199         val previewIntent =
200             WallpaperPreviewActivity.newIntent(
201                 context = appContext,
202                 isAssetIdPresent = true,
203                 isViewAsHome = true,
204                 isNewTask = isMultiPanel,
205                 shouldCategoryRefresh = isCreativeCategories,
206             )
207         ActivityUtils.startActivityForResultSafely(
208             activity,
209             previewIntent,
210             VIEW_ONLY_PREVIEW_WALLPAPER_REQUEST_CODE, // TODO: provide correct request code
211         )
212     }
213 
214     private fun showPermissionSnackbar() {
215         val snackbar =
216             Snackbar.make(
217                 requireView(),
218                 R.string.settings_snackbar_description,
219                 Snackbar.LENGTH_LONG,
220             )
221         val layout = snackbar.view as Snackbar.SnackbarLayout
222         val textView =
223             layout.findViewById<View>(com.google.android.material.R.id.snackbar_text) as TextView
224         layout.setBackgroundResource(R.drawable.snackbar_background)
225 
226         textView.setTextColor(ContextCompat.getColor(requireContext(), R.color.system_on_primary))
227         snackbar.setActionTextColor(
228             ContextCompat.getColor(requireContext(), R.color.system_surface_container)
229         )
230         snackbar.setAction(requireContext().getString(R.string.settings_snackbar_enable)) {
231             startSettings(SETTINGS_APP_INFO_REQUEST_CODE)
232         }
233         snackbar.show()
234     }
235 
236     private fun startSettings(resultCode: Int) {
237         val activity = activity ?: return
238         val appInfoIntent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
239         val uri = Uri.fromParts("package", activity.packageName, /* fragment= */ null)
240         appInfoIntent.setData(uri)
241         startActivityForResult(appInfoIntent, resultCode)
242     }
243 
244     private fun startThirdPartyCategoryActivity(
245         srcActivity: Activity,
246         requestCode: Int,
247         resolveInfo: ResolveInfo,
248     ) {
249         val itemComponentName =
250             ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)
251         val launchIntent = Intent(Intent.ACTION_SET_WALLPAPER)
252         launchIntent.component = itemComponentName
253         ActivityUtils.startActivityForResultSafely(srcActivity, launchIntent, requestCode)
254     }
255 
256     private fun switchFragment(fragment: Fragment) {
257         parentFragmentManager
258             .beginTransaction()
259             .replace(R.id.fragment_container, fragment)
260             .addToBackStack(null)
261             .commit()
262         parentFragmentManager.executePendingTransactions()
263     }
264 
265     companion object {
266         const val SHOW_CATEGORY_REQUEST_CODE = 0
267         const val SETTINGS_APP_INFO_REQUEST_CODE = 1
268         const val READ_IMAGE_PERMISSION: String = Manifest.permission.READ_MEDIA_IMAGES
269     }
270 }
271