blob: 4a21dbc2020b6672d22f5a027bbdfe00f012ff05 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wallpaper.picker.individual
import android.app.Activity
import android.app.ProgressDialog
import android.app.WallpaperManager
import android.content.DialogInterface
import android.content.res.Configuration
import android.content.res.Resources
import android.content.res.Resources.ID_NULL
import android.graphics.Point
import android.os.Build
import android.os.Build.VERSION_CODES
import android.os.Bundle
import android.service.wallpaper.WallpaperService
import android.text.TextUtils
import android.util.ArraySet
import android.util.Log
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.cardview.widget.CardView
import androidx.core.widget.ContentLoadingProgressBar
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.wallpaper.R
import com.android.wallpaper.model.Category
import com.android.wallpaper.model.CategoryProvider
import com.android.wallpaper.model.CategoryReceiver
import com.android.wallpaper.model.WallpaperCategory
import com.android.wallpaper.model.WallpaperInfo
import com.android.wallpaper.model.WallpaperRotationInitializer
import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference
import com.android.wallpaper.module.InjectorProvider
import com.android.wallpaper.module.PackageStatusNotifier
import com.android.wallpaper.picker.AppbarFragment
import com.android.wallpaper.picker.FragmentTransactionChecker
import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider
import com.android.wallpaper.picker.RotationStarter
import com.android.wallpaper.picker.StartRotationDialogFragment
import com.android.wallpaper.picker.StartRotationErrorDialogFragment
import com.android.wallpaper.util.ActivityUtils
import com.android.wallpaper.util.DiskBasedLogger
import com.android.wallpaper.util.LaunchUtils
import com.android.wallpaper.util.SizeCalculator
import com.android.wallpaper.widget.GridPaddingDecoration
import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate
import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost
import com.bumptech.glide.Glide
import com.bumptech.glide.MemoryCategory
import java.util.Date
/** Displays the Main UI for picking an individual wallpaper image. */
class IndividualPickerFragment2 :
AppbarFragment(),
RotationStarter,
StartRotationErrorDialogFragment.Listener,
StartRotationDialogFragment.Listener {
companion object {
private const val TAG = "IndividualPickerFrag2"
/**
* Position of a special tile that doesn't belong to an individual wallpaper of the
* category, such as "my photos" or "daily rotation".
*/
private const val SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0
private const val ARG_CATEGORY_COLLECTION_ID = "category_collection_id"
private const val UNUSED_REQUEST_CODE = 1
private const val TAG_START_ROTATION_DIALOG = "start_rotation_dialog"
private const val TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog"
private const val PROGRESS_DIALOG_INDETERMINATE = true
private const val KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE"
private const val MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT = 8
private val PROGRESS_DIALOG_NO_TITLE = null
fun newInstance(collectionId: String?): IndividualPickerFragment2 {
val args = Bundle()
args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId)
val fragment = IndividualPickerFragment2()
fragment.arguments = args
return fragment
}
}
private lateinit var imageGrid: RecyclerView
private var adapter: IndividualAdapter? = null
private lateinit var category: WallpaperCategory
private var wallpaperRotationInitializer: WallpaperRotationInitializer? = null
private lateinit var items: MutableList<PickerItem>
private var packageStatusNotifier: PackageStatusNotifier? = null
private var isWallpapersReceived = false
private var appStatusListener: PackageStatusNotifier.Listener? = null
private var progressDialog: ProgressDialog? = null
private var testingMode = false
private var loading: ContentLoadingProgressBar? = null
private lateinit var categoryProvider: CategoryProvider
/**
* Staged error dialog fragments that were unable to be shown when the activity didn't allow
* committing fragment transactions.
*/
private var stagedStartRotationErrorDialogFragment: StartRotationErrorDialogFragment? = null
private var wallpaperManager: WallpaperManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val injector = InjectorProvider.getInjector()
val appContext = requireContext().applicationContext
wallpaperManager = WallpaperManager.getInstance(appContext)
packageStatusNotifier = injector.getPackageStatusNotifier(appContext)
items = ArrayList()
// Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
if (
savedInstanceState != null &&
(savedInstanceState.getInt(KEY_NIGHT_MODE) !=
resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK)
) {
Glide.get(requireContext()).clearMemory()
}
categoryProvider = injector.getCategoryProvider(appContext)
categoryProvider.fetchCategories(
object : CategoryReceiver {
override fun onCategoryReceived(category: Category) {
// Do nothing.
}
override fun doneFetchingCategories() {
val fetchedCategory =
categoryProvider.getCategory(
arguments?.getString(ARG_CATEGORY_COLLECTION_ID)
)
if (fetchedCategory != null && fetchedCategory !is WallpaperCategory) {
return
}
if (fetchedCategory == null) {
DiskBasedLogger.e(TAG, "Failed to find the category.", context)
// The absence of this category in the CategoryProvider indicates a broken
// state, see b/38030129. Hence, finish the activity and return.
getIndividualPickerFragmentHost().moveToPreviousFragment()
Toast.makeText(
context,
R.string.collection_not_exist_msg,
Toast.LENGTH_SHORT
)
.show()
return
}
category = fetchedCategory as WallpaperCategory
onCategoryLoaded()
}
},
false
)
}
fun onCategoryLoaded() {
val fragmentHost = getIndividualPickerFragmentHost()
if (fragmentHost.isHostToolbarShown) {
fragmentHost.setToolbarTitle(category.title)
} else {
setTitle(category.title)
}
wallpaperRotationInitializer = category.wallpaperRotationInitializer
if (mToolbar != null && isRotationEnabled()) {
setUpToolbarMenu(R.menu.individual_picker_menu)
}
fetchWallpapers(false)
if (category.supportsThirdParty()) {
appStatusListener =
PackageStatusNotifier.Listener { pkgName: String?, status: Int ->
if (
status != PackageStatusNotifier.PackageStatus.REMOVED ||
category.containsThirdParty(pkgName)
) {
fetchWallpapers(true)
}
}
packageStatusNotifier?.addListener(
appStatusListener,
WallpaperService.SERVICE_INTERFACE
)
}
}
private fun fetchWallpapers(forceReload: Boolean) {
items.clear()
isWallpapersReceived = false
updateLoading()
val context = requireContext()
category.fetchWallpapers(
context.applicationContext,
{ fetchedWallpapers ->
isWallpapersReceived = true
updateLoading()
val byGroup = fetchedWallpapers.groupBy { it.getGroupName(context) }
val appliedWallpaperIds = getAppliedWallpaperIds()
byGroup.forEach { (groupName, wallpapers) ->
if (!TextUtils.isEmpty(groupName)) {
items.add(
if (items.isEmpty()) {
PickerItem.FirstHeaderItem(groupName)
} else {
PickerItem.HeaderItem(groupName)
}
)
}
items.addAll(
wallpapers.map {
PickerItem.WallpaperItem(
it,
appliedWallpaperIds.contains(it.wallpaperId)
)
}
)
}
maybeSetUpImageGrid()
// Wallpapers may load after the adapter is initialized, in which case we have
// to explicitly notify that the data set has changed.
adapter?.notifyDataSetChanged()
if (fetchedWallpapers.isEmpty()) {
// If there are no more wallpapers and we're on phone, just finish the
// Activity.
val activity: Activity? = activity
activity?.finish()
}
},
forceReload
)
}
private fun updateLoading() {
if (isWallpapersReceived) {
loading?.hide()
} else {
loading?.show()
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(
KEY_NIGHT_MODE,
resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val view: View = inflater.inflate(R.layout.fragment_individual_picker, container, false)
if (getIndividualPickerFragmentHost().isHostToolbarShown) {
view.findViewById<View>(R.id.header_bar).visibility = View.GONE
setUpArrowEnabled(/* upArrow= */ true)
if (isRotationEnabled()) {
getIndividualPickerFragmentHost().setToolbarMenu(R.menu.individual_picker_menu)
}
} else {
setUpToolbar(view)
if (isRotationEnabled()) {
setUpToolbarMenu(R.menu.individual_picker_menu)
}
setTitle(category.title)
}
imageGrid = view.findViewById<View>(R.id.wallpaper_grid) as RecyclerView
loading = view.findViewById(R.id.loading_indicator)
updateLoading()
maybeSetUpImageGrid()
// For nav bar edge-to-edge effect.
imageGrid.setOnApplyWindowInsetsListener { v: View, windowInsets: WindowInsets ->
v.setPadding(
v.paddingLeft,
v.paddingTop,
v.paddingRight,
windowInsets.systemWindowInsetBottom
)
windowInsets.consumeSystemWindowInsets()
}
return view
}
private fun getIndividualPickerFragmentHost():
IndividualPickerFragment.IndividualPickerFragmentHost {
val parentFragment = parentFragment
return if (parentFragment != null) {
parentFragment as IndividualPickerFragment.IndividualPickerFragmentHost
} else {
activity as IndividualPickerFragment.IndividualPickerFragmentHost
}
}
private fun maybeSetUpImageGrid() {
// Skip if mImageGrid been initialized yet
if (!this::imageGrid.isInitialized) {
return
}
// Skip if category hasn't loaded yet
if (!this::category.isInitialized) {
return
}
if (context == null) {
return
}
// Wallpaper count could change, so we may need to change the layout(2 or 3 columns layout)
val gridLayoutManager = imageGrid.layoutManager as GridLayoutManager?
val needUpdateLayout = gridLayoutManager?.spanCount != getNumColumns()
// Skip if the adapter was already created and don't need to change the layout
if (adapter != null && !needUpdateLayout) {
return
}
// Clear the old decoration
val decorationCount = imageGrid.itemDecorationCount
for (i in 0 until decorationCount) {
imageGrid.removeItemDecorationAt(i)
}
imageGrid.addItemDecoration(
GridPaddingDecoration(getGridItemPaddingHorizontal(), getGridItemPaddingBottom())
)
val edgePadding = getEdgePadding()
imageGrid.setPadding(
edgePadding,
imageGrid.paddingTop,
edgePadding,
imageGrid.paddingBottom
)
val tileSizePx =
if (isFewerColumnLayout()) {
SizeCalculator.getFeaturedIndividualTileSize(activity!!)
} else {
SizeCalculator.getIndividualTileSize(activity!!)
}
setUpImageGrid(tileSizePx)
imageGrid.setAccessibilityDelegateCompat(
WallpaperPickerRecyclerViewAccessibilityDelegate(
imageGrid,
parentFragment as BottomSheetHost?,
getNumColumns()
)
)
}
private fun isFewerColumnLayout(): Boolean =
items.count { it is PickerItem.WallpaperItem } <= MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT
private fun getGridItemPaddingHorizontal(): Int {
return if (isFewerColumnLayout()) {
resources.getDimensionPixelSize(
R.dimen.grid_item_featured_individual_padding_horizontal
)
} else {
resources.getDimensionPixelSize(R.dimen.grid_item_individual_padding_horizontal)
}
}
private fun getGridItemPaddingBottom(): Int {
return if (isFewerColumnLayout()) {
resources.getDimensionPixelSize(R.dimen.grid_item_featured_individual_padding_bottom)
} else {
resources.getDimensionPixelSize(R.dimen.grid_item_individual_padding_bottom)
}
}
private fun getEdgePadding(): Int {
return if (isFewerColumnLayout()) {
resources.getDimensionPixelSize(R.dimen.featured_wallpaper_grid_edge_space)
} else {
resources.getDimensionPixelSize(R.dimen.wallpaper_grid_edge_space)
}
}
/**
* Create the adapter and assign it to mImageGrid. Both mImageGrid and mCategory are guaranteed
* to not be null when this method is called.
*/
private fun setUpImageGrid(tileSizePx: Point) {
adapter =
IndividualAdapter(
items,
category,
requireActivity(),
tileSizePx,
isRotationEnabled(),
isFewerColumnLayout()
)
imageGrid.adapter = adapter
val gridLayoutManager = GridLayoutManager(activity, getNumColumns())
gridLayoutManager.spanSizeLookup =
object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (items[position]) {
is PickerItem.FirstHeaderItem,
is PickerItem.HeaderItem -> {
gridLayoutManager.spanCount
}
else -> 1
}
}
}
imageGrid.layoutManager = gridLayoutManager
}
override fun onResume() {
super.onResume()
val preferences = InjectorProvider.getInjector().getPreferences(requireActivity())
preferences.lastAppActiveTimestamp = Date().time
// Reset Glide memory settings to a "normal" level of usage since it may have been lowered
// in PreviewFragment.
Glide.get(requireContext()).setMemoryCategory(MemoryCategory.NORMAL)
// Show the staged 'start rotation' error dialog fragment if there is one that was unable to
// be shown earlier when this fragment's hosting activity didn't allow committing fragment
// transactions.
if (isAdded) {
stagedStartRotationErrorDialogFragment?.show(
parentFragmentManager,
TAG_START_ROTATION_ERROR_DIALOG
)
}
stagedStartRotationErrorDialogFragment = null
}
override fun onDestroyView() {
super.onDestroyView()
getIndividualPickerFragmentHost().removeToolbarMenu()
}
override fun onDestroy() {
super.onDestroy()
progressDialog?.dismiss()
if (appStatusListener != null) {
packageStatusNotifier?.removeListener(appStatusListener)
}
}
override fun onStartRotationDialogDismiss(dialog: DialogInterface) {
// TODO(b/159310028): Refactor fragment layer to make it able to restore from config change.
// This is to handle config change with StartRotationDialog popup, the StartRotationDialog
// still holds a reference to the destroyed Fragment and is calling
// onStartRotationDialogDismissed on that destroyed Fragment.
}
override fun retryStartRotation(@NetworkPreference networkPreference: Int) {
startRotation(networkPreference)
}
/**
* Enable a test mode of operation -- in which certain UI features are disabled to allow for UI
* tests to run correctly. Works around issue in ProgressDialog currently where the dialog
* constantly keeps the UI thread alive and blocks a test forever.
*
* @param testingMode
*/
fun setTestingMode(testingMode: Boolean) {
this.testingMode = testingMode
}
override fun startRotation(@NetworkPreference networkPreference: Int) {
if (!isRotationEnabled()) {
Log.e(TAG, "Rotation is not enabled for this category " + category.title)
return
}
// ProgressDialog endlessly updates the UI thread, keeping it from going idle which
// therefore causes Espresso to hang once the dialog is shown.
if (!testingMode) {
val themeResId =
if (Build.VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
R.style.ProgressDialogThemePreL
} else {
R.style.LightDialogTheme
}
val progressDialog = ProgressDialog(activity, themeResId)
progressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE)
progressDialog.setMessage(resources.getString(R.string.start_rotation_progress_message))
progressDialog.isIndeterminate = PROGRESS_DIALOG_INDETERMINATE
progressDialog.show()
this.progressDialog = progressDialog
}
val appContext = activity!!.applicationContext
wallpaperRotationInitializer?.setFirstWallpaperInRotation(
appContext,
networkPreference,
object : WallpaperRotationInitializer.Listener {
override fun onFirstWallpaperInRotationSet() {
progressDialog?.dismiss()
// The fragment may be detached from its containing activity if the user exits
// the app before the first wallpaper image in rotation finishes downloading.
val activity: Activity? = activity
if (wallpaperRotationInitializer!!.startRotation(appContext)) {
if (activity != null) {
try {
Toast.makeText(
activity,
R.string.wallpaper_set_successfully_message,
Toast.LENGTH_SHORT
)
.show()
} catch (e: Resources.NotFoundException) {
Log.e(TAG, "Could not show toast $e")
}
activity.setResult(Activity.RESULT_OK)
activity.finish()
if (!ActivityUtils.isSUWMode(appContext)) {
// Go back to launcher home.
LaunchUtils.launchHome(appContext)
}
}
} else { // Failed to start rotation.
showStartRotationErrorDialog(networkPreference)
}
}
override fun onError() {
progressDialog?.dismiss()
showStartRotationErrorDialog(networkPreference)
}
}
)
}
private fun showStartRotationErrorDialog(@NetworkPreference networkPreference: Int) {
val activity = activity as FragmentTransactionChecker?
if (activity != null) {
val startRotationErrorDialogFragment =
StartRotationErrorDialogFragment.newInstance(networkPreference)
startRotationErrorDialogFragment.setTargetFragment(
this@IndividualPickerFragment2,
UNUSED_REQUEST_CODE
)
if (activity.isSafeToCommitFragmentTransaction) {
startRotationErrorDialogFragment.show(
parentFragmentManager,
TAG_START_ROTATION_ERROR_DIALOG
)
} else {
stagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment
}
}
}
private fun getNumColumns(): Int {
val activity = this.activity ?: return 1
return if (isFewerColumnLayout()) {
SizeCalculator.getNumFeaturedIndividualColumns(activity)
} else {
SizeCalculator.getNumIndividualColumns(activity)
}
}
/** Returns whether rotation is enabled for this category. */
private fun isRotationEnabled() = wallpaperRotationInitializer != null
override fun onMenuItemClick(item: MenuItem): Boolean {
if (item.itemId == R.id.daily_rotation) {
showRotationDialog()
return true
}
return super.onMenuItemClick(item)
}
/** Popups a daily rotation dialog for the uses to confirm. */
private fun showRotationDialog() {
val startRotationDialogFragment: DialogFragment = StartRotationDialogFragment()
startRotationDialogFragment.setTargetFragment(
this@IndividualPickerFragment2,
UNUSED_REQUEST_CODE
)
startRotationDialogFragment.show(parentFragmentManager, TAG_START_ROTATION_DIALOG)
}
private fun getAppliedWallpaperIds(): Set<String> {
val prefs = InjectorProvider.getInjector().getPreferences(requireContext())
val wallpaperInfo = wallpaperManager?.wallpaperInfo
val appliedWallpaperIds: MutableSet<String> = ArraySet()
val homeWallpaperId =
if (wallpaperInfo != null) {
wallpaperInfo.serviceName
} else {
prefs.homeWallpaperRemoteId
}
if (!TextUtils.isEmpty(homeWallpaperId)) {
appliedWallpaperIds.add(homeWallpaperId)
}
val isLockWallpaperApplied =
wallpaperManager!!.getWallpaperId(WallpaperManager.FLAG_LOCK) >= 0
val lockWallpaperId = prefs.lockWallpaperRemoteId
if (isLockWallpaperApplied && !TextUtils.isEmpty(lockWallpaperId)) {
appliedWallpaperIds.add(lockWallpaperId)
}
return appliedWallpaperIds
}
sealed class PickerItem(val title: CharSequence = "") {
class WallpaperItem(val wallpaperInfo: WallpaperInfo, val isApplied: Boolean) :
PickerItem()
class HeaderItem(title: CharSequence) : PickerItem(title)
class FirstHeaderItem(title: CharSequence) : PickerItem(title)
}
/** RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView. */
class IndividualAdapter(
private val items: List<PickerItem>,
private val category: Category,
private val activity: Activity,
private val tileSizePx: Point,
private val isRotationEnabled: Boolean,
private val isFewerColumnLayout: Boolean
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
const val ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2
const val ITEM_VIEW_TYPE_MY_PHOTOS = 3
const val ITEM_VIEW_TYPE_HEADER = 4
const val ITEM_VIEW_TYPE_HEADER_TOP = 5
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER -> createIndividualHolder(parent)
ITEM_VIEW_TYPE_MY_PHOTOS -> createMyPhotosHolder(parent)
ITEM_VIEW_TYPE_HEADER -> createTitleHolder(parent, /* removePaddingTop= */ false)
ITEM_VIEW_TYPE_HEADER_TOP -> createTitleHolder(parent, /* removePaddingTop= */ true)
else -> {
throw RuntimeException("Unsupported viewType $viewType in IndividualAdapter")
}
}
}
override fun getItemViewType(position: Int): Int {
// A category cannot have both a "start rotation" tile and a "my photos" tile.
return if (
category.supportsCustomPhotos() &&
!isRotationEnabled &&
position == SPECIAL_FIXED_TILE_ADAPTER_POSITION
) {
ITEM_VIEW_TYPE_MY_PHOTOS
} else {
when (items[position]) {
is PickerItem.WallpaperItem -> ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER
is PickerItem.HeaderItem -> ITEM_VIEW_TYPE_HEADER
is PickerItem.FirstHeaderItem -> ITEM_VIEW_TYPE_HEADER_TOP
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (val viewType = getItemViewType(position)) {
ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER -> bindIndividualHolder(holder, position)
ITEM_VIEW_TYPE_MY_PHOTOS -> (holder as MyPhotosViewHolder?)!!.bind()
ITEM_VIEW_TYPE_HEADER,
ITEM_VIEW_TYPE_HEADER_TOP -> {
val textView = holder.itemView as TextView
val item = items[position]
textView.text = item.title
textView.contentDescription = item.title
}
else -> Log.e(TAG, "Unexpected viewType $viewType in IndividualAdapter")
}
}
override fun getItemCount(): Int {
return if (category.supportsCustomPhotos()) {
items.size + 1
} else {
items.size
}
}
private fun createIndividualHolder(parent: ViewGroup): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(activity)
val view: View = layoutInflater.inflate(R.layout.grid_item_image, parent, false)
return PreviewIndividualHolder(activity, tileSizePx.y, view)
}
private fun createMyPhotosHolder(parent: ViewGroup): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(activity)
val view: View = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false)
return MyPhotosViewHolder(
activity,
(activity as MyPhotosStarterProvider).myPhotosStarter,
tileSizePx.y,
view
)
}
private fun createTitleHolder(
parent: ViewGroup,
removePaddingTop: Boolean
): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(activity)
val view =
layoutInflater.inflate(R.layout.grid_item_header, parent, /* attachToRoot= */ false)
if (removePaddingTop) {
view.setPadding(
view.paddingStart,
/* top= */ 0,
view.paddingEnd,
view.paddingBottom
)
}
return object : RecyclerView.ViewHolder(view) {}
}
private fun bindIndividualHolder(holder: RecyclerView.ViewHolder, position: Int) {
val wallpaperIndex = if (category.supportsCustomPhotos()) position - 1 else position
val item = items[wallpaperIndex] as PickerItem.WallpaperItem
val wallpaper = item.wallpaperInfo
wallpaper.computeColorInfo(holder.itemView.context)
(holder as IndividualHolder).bindWallpaper(wallpaper)
val container = holder.itemView.findViewById<CardView>(R.id.wallpaper_container)
val radiusId: Int =
if (isFewerColumnLayout) {
R.dimen.grid_item_all_radius
} else {
R.dimen.grid_item_all_radius_small
}
container.radius = activity.resources.getDimension(radiusId)
showBadge(holder, R.drawable.wallpaper_check_circle_24dp, item.isApplied)
if (!item.isApplied) {
showBadge(holder, wallpaper.badgeDrawableRes, wallpaper.badgeDrawableRes != ID_NULL)
}
}
private fun showBadge(
holder: RecyclerView.ViewHolder,
@DrawableRes icon: Int,
show: Boolean
) {
val badge = holder.itemView.findViewById<ImageView>(R.id.indicator_icon)
if (show) {
val margin =
if (isFewerColumnLayout) {
activity.resources.getDimension(R.dimen.grid_item_badge_margin)
} else {
activity.resources.getDimension(R.dimen.grid_item_badge_margin_small)
}
.toInt()
val layoutParams = badge.layoutParams as RelativeLayout.LayoutParams
layoutParams.setMargins(margin, margin, margin, margin)
badge.layoutParams = layoutParams
badge.setBackgroundResource(icon)
badge.visibility = View.VISIBLE
} else {
badge.visibility = View.GONE
}
}
}
}