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