| /* |
| * Copyright (C) 2017 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; |
| |
| import android.app.Activity; |
| import android.app.ProgressDialog; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.graphics.Point; |
| import android.graphics.PorterDuff.Mode; |
| import android.graphics.Rect; |
| import android.net.Uri; |
| import android.os.Build.VERSION; |
| import android.os.Build.VERSION_CODES; |
| import android.os.Bundle; |
| import android.provider.Settings; |
| import android.util.DisplayMetrics; |
| import android.util.Log; |
| import android.view.Display; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| import android.view.ViewGroup; |
| import android.widget.Button; |
| import android.widget.FrameLayout; |
| import android.widget.ImageButton; |
| import android.widget.ImageView; |
| import android.widget.ProgressBar; |
| import android.widget.TextView; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.appcompat.app.AlertDialog; |
| import androidx.cardview.widget.CardView; |
| import androidx.recyclerview.widget.GridLayoutManager; |
| import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup; |
| import androidx.recyclerview.widget.RecyclerView; |
| import androidx.recyclerview.widget.RecyclerView.ViewHolder; |
| import androidx.viewpager.widget.PagerAdapter; |
| |
| import com.android.wallpaper.R; |
| import com.android.wallpaper.asset.Asset; |
| import com.android.wallpaper.config.Flags; |
| import com.android.wallpaper.model.Category; |
| import com.android.wallpaper.model.WallpaperInfo; |
| import com.android.wallpaper.module.CurrentWallpaperInfoFactory; |
| import com.android.wallpaper.module.CurrentWallpaperInfoFactory.WallpaperInfoCallback; |
| import com.android.wallpaper.module.ExploreIntentChecker; |
| import com.android.wallpaper.module.InjectorProvider; |
| import com.android.wallpaper.module.LockWallpaperStatusChecker; |
| import com.android.wallpaper.module.UserEventLogger; |
| import com.android.wallpaper.module.WallpaperPreferences; |
| import com.android.wallpaper.module.WallpaperPreferences.PresentationMode; |
| import com.android.wallpaper.module.WallpaperRotationRefresher; |
| import com.android.wallpaper.module.WallpaperRotationRefresher.Listener; |
| import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider; |
| import com.android.wallpaper.picker.MyPhotosStarter.PermissionChangedListener; |
| import com.android.wallpaper.util.DisplayMetricsRetriever; |
| import com.android.wallpaper.util.ScreenSizeCalculator; |
| import com.android.wallpaper.util.TileSizeCalculator; |
| import com.android.wallpaper.widget.PreviewPager; |
| |
| import com.bumptech.glide.Glide; |
| import com.bumptech.glide.MemoryCategory; |
| import com.google.android.material.bottomsheet.BottomSheetBehavior; |
| |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.List; |
| |
| /** |
| * Displays the Main UI for picking a category of wallpapers to choose from. |
| */ |
| public class CategoryFragment extends ToolbarFragment { |
| |
| /** |
| * Interface to be implemented by an Activity hosting a {@link CategoryFragment} |
| */ |
| public interface CategoryFragmentHost extends MyPhotosStarterProvider { |
| |
| void requestExternalStoragePermission(PermissionChangedListener listener); |
| |
| boolean isReadExternalStoragePermissionGranted(); |
| |
| void showViewOnlyPreview(WallpaperInfo wallpaperInfo); |
| |
| void show(String collectionId); |
| } |
| |
| public static CategoryFragment newInstance(CharSequence title) { |
| CategoryFragment fragment = new CategoryFragment(); |
| fragment.setArguments(ToolbarFragment.createArguments(title)); |
| return fragment; |
| } |
| |
| private static final String TAG = "CategoryFragment"; |
| |
| // The number of ViewHolders that don't pertain to category tiles. |
| // Currently 2: one for the metadata section and one for the "Select wallpaper" header. |
| private static final int NUM_NON_CATEGORY_VIEW_HOLDERS = 0; |
| |
| /** |
| * The fixed RecyclerView.Adapter position of the ViewHolder for the initial item in the grid -- |
| * usually the wallpaper metadata, or a "permission needed" warning UI. |
| */ |
| private static final int INITIAL_HOLDER_ADAPTER_POSITION = 0; |
| |
| private static final int SETTINGS_APP_INFO_REQUEST_CODE = 1; |
| |
| private static final String PERMISSION_READ_WALLPAPER_INTERNAL = |
| "android.permission.READ_WALLPAPER_INTERNAL"; |
| |
| private RecyclerView mImageGrid; |
| private CategoryAdapter mAdapter; |
| private ArrayList<Category> mCategories = new ArrayList<>(); |
| private Point mTileSizePx; |
| private boolean mAwaitingCategories; |
| private ProgressDialog mRefreshWallpaperProgressDialog; |
| private boolean mTestingMode; |
| private ImageView mHomePreview; |
| private ImageView mLockscreenPreview; |
| private PreviewPager mPreviewPager; |
| private List<View> mWallPaperPreviews; |
| |
| public CategoryFragment() { |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| mAdapter = new CategoryAdapter(mCategories); |
| } |
| |
| @Override |
| public View onCreateView(LayoutInflater inflater, ViewGroup container, |
| Bundle savedInstanceState) { |
| View view = inflater.inflate( |
| ADD_SCALABLE_HEADER |
| ? R.layout.fragment_category_scalable_picker |
| : R.layout.fragment_category_picker, container, /* attachToRoot= */ false); |
| |
| mWallPaperPreviews = new ArrayList<>(); |
| CardView homePreviewCard = (CardView) inflater.inflate( |
| R.layout.wallpaper_preview_card, null); |
| mHomePreview = homePreviewCard.findViewById(R.id.wallpaper_preview_image); |
| mWallPaperPreviews.add(homePreviewCard); |
| |
| if (LockWallpaperStatusChecker.isLockWallpaperSet(getContext())) { |
| CardView lockscreenPreviewCard = (CardView) inflater.inflate( |
| R.layout.wallpaper_preview_card, null); |
| mLockscreenPreview = lockscreenPreviewCard.findViewById(R.id.wallpaper_preview_image); |
| mWallPaperPreviews.add(lockscreenPreviewCard); |
| } |
| |
| mPreviewPager = view.findViewById(R.id.wallpaper_preview_pager); |
| mPreviewPager.setAdapter(new PreviewPagerAdapter(mWallPaperPreviews)); |
| setupCurrentWallpaperPreview(view); |
| |
| mImageGrid = view.findViewById(R.id.category_grid); |
| mImageGrid.addItemDecoration(new GridPaddingDecoration( |
| getResources().getDimensionPixelSize(R.dimen.grid_padding))); |
| view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { |
| @Override |
| public void onLayoutChange(View view, int left, int top, int right, int bottom, |
| int oldLeft, int oldTop, int oldRight, int oldBottom) { |
| View toolBar = view.findViewById(R.id.toolbar); |
| int gridCollapsedHeight = view.getHeight() |
| - toolBar.getHeight() |
| - getResources().getDimensionPixelOffset(R.dimen.preview_pager_height); |
| BottomSheetBehavior.from(mImageGrid).setPeekHeight(gridCollapsedHeight); |
| mImageGrid.setMinimumHeight(gridCollapsedHeight); |
| mImageGrid.getLayoutParams().height = view.getHeight() - toolBar.getHeight(); |
| view.removeOnLayoutChangeListener(this); |
| } |
| }); |
| |
| mTileSizePx = TileSizeCalculator.getCategoryTileSize(getActivity()); |
| |
| mImageGrid.setAdapter(mAdapter); |
| |
| GridLayoutManager gridLayoutManager = new GridLayoutManager(getActivity(), getNumColumns()); |
| mImageGrid.setLayoutManager(gridLayoutManager); |
| setUpToolbar(view); |
| return view; |
| } |
| |
| @Override |
| public CharSequence getDefaultTitle() { |
| return getContext().getString(R.string.app_name); |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| |
| WallpaperPreferences preferences = InjectorProvider.getInjector().getPreferences(getActivity()); |
| preferences.setLastAppActiveTimestamp(new Date().getTime()); |
| |
| // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in |
| // PreviewFragment. |
| Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL); |
| |
| // Refresh metadata since it may have changed since the activity was paused. |
| ViewHolder initialViewHolder = |
| mImageGrid.findViewHolderForAdapterPosition(INITIAL_HOLDER_ADAPTER_POSITION); |
| MetadataHolder metadataHolder = null; |
| if (initialViewHolder instanceof MetadataHolder) { |
| metadataHolder = (MetadataHolder) initialViewHolder; |
| } |
| |
| // The wallpaper may have been set while this fragment was paused, so force refresh the current |
| // wallpapers and presentation mode. |
| refreshCurrentWallpapers(metadataHolder, true /* forceRefresh */); |
| } |
| |
| @Override |
| public void onDestroy() { |
| super.onDestroy(); |
| if (mRefreshWallpaperProgressDialog != null) { |
| mRefreshWallpaperProgressDialog.dismiss(); |
| } |
| } |
| |
| @Override |
| public void onActivityResult(int requestCode, int resultCode, Intent data) { |
| if (requestCode == SETTINGS_APP_INFO_REQUEST_CODE) { |
| mAdapter.notifyDataSetChanged(); |
| } |
| } |
| |
| /** |
| * Inserts the given category into the categories list in priority order. |
| */ |
| public void addCategory(Category category, boolean loading) { |
| // If not previously waiting for categories, enter the waiting state by showing the loading |
| // indicator. |
| if (loading && !mAwaitingCategories) { |
| mAdapter.notifyItemChanged(getNumColumns()); |
| mAdapter.notifyItemInserted(getNumColumns()); |
| mAwaitingCategories = true; |
| } |
| // Not add existing category to category list |
| if (mCategories.indexOf(category) >= 0) { |
| updateCategory(category); |
| return; |
| } |
| |
| int priority = category.getPriority(); |
| |
| int index = 0; |
| while (index < mCategories.size() && priority >= mCategories.get(index).getPriority()) { |
| index++; |
| } |
| |
| mCategories.add(index, category); |
| if (mAdapter != null) { |
| // Offset the index because of the static metadata element at beginning of RecyclerView. |
| mAdapter.notifyItemInserted(index + NUM_NON_CATEGORY_VIEW_HOLDERS); |
| } |
| } |
| |
| public void removeCategory(Category category) { |
| int index = mCategories.indexOf(category); |
| if (index >= 0) { |
| mCategories.remove(index); |
| mAdapter.notifyItemRemoved(index + NUM_NON_CATEGORY_VIEW_HOLDERS); |
| } |
| } |
| |
| public void updateCategory(Category category) { |
| int index = mCategories.indexOf(category); |
| if (index >= 0) { |
| mCategories.remove(index); |
| mCategories.add(index, category); |
| mAdapter.notifyItemChanged(index + NUM_NON_CATEGORY_VIEW_HOLDERS); |
| } |
| } |
| |
| public void clearCategories() { |
| mCategories.clear(); |
| mAdapter.notifyDataSetChanged(); |
| } |
| |
| /** |
| * Notifies the CategoryFragment that no further categories are expected so it may hide |
| * the loading indicator. |
| */ |
| public void doneFetchingCategories() { |
| if (mAwaitingCategories) { |
| mAdapter.notifyItemRemoved(mAdapter.getItemCount() - 1); |
| mAwaitingCategories = false; |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| void setTestingMode(boolean testingMode) { |
| mTestingMode = testingMode; |
| } |
| |
| private boolean canShowCurrentWallpaper() { |
| Activity activity = getActivity(); |
| CategoryFragmentHost host = getFragmentHost(); |
| PackageManager packageManager = activity.getPackageManager(); |
| String packageName = activity.getPackageName(); |
| |
| boolean hasReadWallpaperInternal = packageManager.checkPermission( |
| PERMISSION_READ_WALLPAPER_INTERNAL, packageName) == PackageManager.PERMISSION_GRANTED; |
| return hasReadWallpaperInternal || host.isReadExternalStoragePermissionGranted(); |
| } |
| |
| private void showCurrentWallpaper(View rootView, boolean show) { |
| rootView.findViewById(R.id.wallpaper_preview_pager) |
| .setVisibility(show ? View.VISIBLE : View.GONE); |
| rootView.findViewById(R.id.permission_needed) |
| .setVisibility(show ? View.GONE : View.VISIBLE); |
| } |
| |
| private void setupCurrentWallpaperPreview(View rootView) { |
| if (canShowCurrentWallpaper()) { |
| showCurrentWallpaper(rootView, true); |
| } else { |
| showCurrentWallpaper(rootView, false); |
| |
| Button mAllowAccessButton = rootView |
| .findViewById(R.id.permission_needed_allow_access_button); |
| mAllowAccessButton.setOnClickListener(view -> |
| getFragmentHost().requestExternalStoragePermission( |
| new PermissionChangedListener() { |
| |
| @Override |
| public void onPermissionsGranted() { |
| showCurrentWallpaper(rootView, true); |
| mAdapter.notifyDataSetChanged(); |
| } |
| |
| @Override |
| public void onPermissionsDenied(boolean dontAskAgain) { |
| if (!dontAskAgain) { |
| return; |
| } |
| showPermissionNeededDialog(); |
| } |
| }) |
| ); |
| |
| // Replace explanation text with text containing the Wallpapers app name which replaces |
| // the placeholder. |
| String appName = getString(R.string.app_name); |
| String explanation = getString(R.string.permission_needed_explanation, appName); |
| TextView explanationView = rootView.findViewById(R.id.permission_needed_explanation); |
| explanationView.setText(explanation); |
| } |
| } |
| |
| private void showPermissionNeededDialog() { |
| String permissionNeededMessage = getString( |
| R.string.permission_needed_explanation_go_to_settings); |
| AlertDialog dialog = new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme) |
| .setMessage(permissionNeededMessage) |
| .setPositiveButton(android.R.string.ok, /* onClickListener= */ null) |
| .setNegativeButton( |
| R.string.settings_button_label, |
| (dialogInterface, i) -> { |
| Intent appInfoIntent = new Intent(); |
| appInfoIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); |
| Uri uri = Uri.fromParts("package", |
| getActivity().getPackageName(), /* fragment= */ null); |
| appInfoIntent.setData(uri); |
| startActivityForResult(appInfoIntent, SETTINGS_APP_INFO_REQUEST_CODE); |
| }) |
| .create(); |
| dialog.show(); |
| } |
| |
| private CategoryFragmentHost getFragmentHost() { |
| return (CategoryFragmentHost) getActivity(); |
| } |
| |
| /** |
| * Obtains the {@link WallpaperInfo} object(s) representing the wallpaper(s) currently set to the |
| * device from the {@link CurrentWallpaperInfoFactory} and binds them to the provided |
| * {@link MetadataHolder}. |
| */ |
| private void refreshCurrentWallpapers(@Nullable final MetadataHolder holder, |
| boolean forceRefresh) { |
| CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector() |
| .getCurrentWallpaperFactory(getActivity().getApplicationContext()); |
| |
| factory.createCurrentWallpaperInfos(new WallpaperInfoCallback() { |
| @Override |
| public void onWallpaperInfoCreated( |
| final WallpaperInfo homeWallpaper, |
| @Nullable final WallpaperInfo lockWallpaper, |
| @PresentationMode final int presentationMode) { |
| |
| // Update the metadata displayed on screen. Do this in a Handler so it is scheduled at the |
| // end of the message queue. This is necessary to ensure we do not remove or add data from |
| // the adapter while the layout is being computed. RecyclerView documentation therefore |
| // recommends performing such changes in a Handler. |
| new android.os.Handler().post(new Runnable() { |
| @Override |
| public void run() { |
| // A config change may have destroyed the activity since the refresh started, so check |
| // for that. |
| if (getActivity() == null) { |
| return; |
| } |
| |
| UserEventLogger eventLogger = |
| InjectorProvider.getInjector().getUserEventLogger(getActivity()); |
| homeWallpaper.getThumbAsset(getActivity().getApplicationContext()) |
| .loadDrawable(getActivity(), |
| mHomePreview, |
| getResources().getColor(R.color.secondary_color)); |
| mHomePreview.setOnClickListener(view -> { |
| getFragmentHost().showViewOnlyPreview(homeWallpaper); |
| eventLogger.logCurrentWallpaperPreviewed(); |
| }); |
| if (lockWallpaper != null) { |
| lockWallpaper.getThumbAsset(getActivity().getApplicationContext()) |
| .loadDrawable(getActivity(), |
| mLockscreenPreview, |
| getResources().getColor(R.color.secondary_color)); |
| mLockscreenPreview.setOnClickListener(view -> { |
| getFragmentHost().showViewOnlyPreview(lockWallpaper); |
| eventLogger.logCurrentWallpaperPreviewed(); |
| }); |
| } |
| |
| // The MetadataHolder may be null if the RecyclerView has not yet created the view |
| // holder. |
| if (holder != null) { |
| holder.bindWallpapers(homeWallpaper, lockWallpaper, presentationMode); |
| } |
| } |
| }); |
| } |
| }, forceRefresh); |
| } |
| |
| private int getNumColumns() { |
| Activity activity = getActivity(); |
| return activity == null ? 0 : TileSizeCalculator.getNumCategoryColumns(activity); |
| } |
| |
| /** |
| * Returns the width to use for the home screen wallpaper in the "single metadata" configuration. |
| */ |
| private int getSingleWallpaperImageWidth() { |
| Point screenSize = ScreenSizeCalculator.getInstance() |
| .getScreenSize(getActivity().getWindowManager().getDefaultDisplay()); |
| |
| int height = getResources().getDimensionPixelSize(R.dimen.single_metadata_card_layout_height); |
| return height * screenSize.x / screenSize.y; |
| } |
| |
| /** |
| * Refreshes the current wallpaper in a daily wallpaper rotation. |
| */ |
| private void refreshDailyWallpaper() { |
| // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore |
| // causes Espresso to hang once the dialog is shown. |
| if (!mTestingMode) { |
| int themeResId; |
| if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) { |
| themeResId = R.style.ProgressDialogThemePreL; |
| } else { |
| themeResId = R.style.LightDialogTheme; |
| } |
| mRefreshWallpaperProgressDialog = new ProgressDialog(getActivity(), themeResId); |
| mRefreshWallpaperProgressDialog.setTitle(null); |
| mRefreshWallpaperProgressDialog.setMessage( |
| getResources().getString(R.string.refreshing_daily_wallpaper_dialog_message)); |
| mRefreshWallpaperProgressDialog.setIndeterminate(true); |
| mRefreshWallpaperProgressDialog.setCancelable(false); |
| mRefreshWallpaperProgressDialog.show(); |
| } |
| |
| WallpaperRotationRefresher wallpaperRotationRefresher = |
| InjectorProvider.getInjector().getWallpaperRotationRefresher(); |
| wallpaperRotationRefresher.refreshWallpaper(getContext(), new Listener() { |
| @Override |
| public void onRefreshed() { |
| // If the fragment is detached from the activity there's nothing to do here and the UI will |
| // update when the fragment is resumed. |
| if (getActivity() == null) { |
| return; |
| } |
| |
| if (mRefreshWallpaperProgressDialog != null) { |
| mRefreshWallpaperProgressDialog.dismiss(); |
| } |
| |
| ViewHolder initialViewHolder = |
| mImageGrid.findViewHolderForAdapterPosition(INITIAL_HOLDER_ADAPTER_POSITION); |
| if (initialViewHolder instanceof MetadataHolder) { |
| MetadataHolder metadataHolder = (MetadataHolder) initialViewHolder; |
| // Update the metadata pane since we know now the UI there is stale. |
| refreshCurrentWallpapers(metadataHolder, true /* forceRefresh */); |
| } |
| } |
| |
| @Override |
| public void onError() { |
| if (getActivity() == null) { |
| return; |
| } |
| |
| if (mRefreshWallpaperProgressDialog != null) { |
| mRefreshWallpaperProgressDialog.dismiss(); |
| } |
| |
| AlertDialog errorDialog = new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme) |
| .setMessage(R.string.refresh_daily_wallpaper_failed_message) |
| .setPositiveButton(android.R.string.ok, null /* onClickListener */) |
| .create(); |
| errorDialog.show(); |
| } |
| }); |
| } |
| |
| /** |
| * Returns the width to use for the home and lock screen wallpapers in the "both metadata" |
| * configuration. |
| */ |
| private int getBothWallpaperImageWidth() { |
| DisplayMetrics metrics = DisplayMetricsRetriever.getInstance().getDisplayMetrics(getResources(), |
| getActivity().getWindowManager().getDefaultDisplay()); |
| |
| // In the "both metadata" configuration, wallpaper images minus the gutters account for the full |
| // width of the device's screen. |
| return metrics.widthPixels - (3 * getResources().getDimensionPixelSize(R.dimen.grid_padding)); |
| } |
| |
| private interface MetadataHolder { |
| /** |
| * Binds {@link WallpaperInfo} objects representing the currently-set wallpapers to the |
| * ViewHolder layout. |
| */ |
| void bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper, |
| @PresentationMode int presentationMode); |
| } |
| |
| private static class SelectWallpaperHeaderHolder extends RecyclerView.ViewHolder { |
| public SelectWallpaperHeaderHolder(View headerView) { |
| super(headerView); |
| } |
| } |
| |
| /** |
| * SpanSizeLookup subclass which provides that the item in the first position spans the number of |
| * columns in the RecyclerView and all other items only take up a single span. |
| */ |
| private class CategorySpanSizeLookup extends SpanSizeLookup { |
| CategoryAdapter mAdapter; |
| |
| public CategorySpanSizeLookup(CategoryAdapter adapter) { |
| mAdapter = adapter; |
| } |
| |
| @Override |
| public int getSpanSize(int position) { |
| if (position < NUM_NON_CATEGORY_VIEW_HOLDERS |
| || mAdapter.getItemViewType(position) |
| == CategoryAdapter.ITEM_VIEW_TYPE_LOADING_INDICATOR) { |
| return getNumColumns(); |
| } |
| |
| return 1; |
| } |
| } |
| |
| /** |
| * ViewHolder subclass for a metadata "card" at the beginning of the RecyclerView. |
| */ |
| private class SingleWallpaperMetadataHolder extends RecyclerView.ViewHolder |
| implements MetadataHolder { |
| private WallpaperInfo mWallpaperInfo; |
| private ImageView mWallpaperImage; |
| private TextView mWallpaperPresentationModeSubtitle; |
| private TextView mWallpaperTitle; |
| private TextView mWallpaperSubtitle; |
| private TextView mWallpaperSubtitle2; |
| private ImageButton mWallpaperExploreButtonNoText; |
| private ImageButton mSkipWallpaperButton; |
| |
| public SingleWallpaperMetadataHolder(View metadataView) { |
| super(metadataView); |
| |
| mWallpaperImage = metadataView.findViewById(R.id.wallpaper_image); |
| mWallpaperImage.getLayoutParams().width = getSingleWallpaperImageWidth(); |
| |
| mWallpaperPresentationModeSubtitle = |
| metadataView.findViewById(R.id.wallpaper_presentation_mode_subtitle); |
| mWallpaperTitle = metadataView.findViewById(R.id.wallpaper_title); |
| mWallpaperSubtitle = metadataView.findViewById(R.id.wallpaper_subtitle); |
| mWallpaperSubtitle2 = metadataView.findViewById(R.id.wallpaper_subtitle2); |
| |
| mWallpaperExploreButtonNoText = |
| metadataView.findViewById(R.id.wallpaper_explore_button_notext); |
| |
| mSkipWallpaperButton = metadataView.findViewById(R.id.skip_wallpaper_button); |
| } |
| |
| /** |
| * Binds home screen wallpaper to the ViewHolder layout. |
| */ |
| @Override |
| public void bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper, |
| @PresentationMode int presentationMode) { |
| mWallpaperInfo = homeWallpaper; |
| |
| bindWallpaperAsset(); |
| bindWallpaperText(presentationMode); |
| bindWallpaperActionButtons(presentationMode); |
| } |
| |
| private void bindWallpaperAsset() { |
| final UserEventLogger eventLogger = |
| InjectorProvider.getInjector().getUserEventLogger(getActivity()); |
| |
| mWallpaperInfo.getThumbAsset(getActivity().getApplicationContext()).loadDrawable( |
| getActivity(), mWallpaperImage, getResources().getColor(R.color.secondary_color)); |
| |
| mWallpaperImage.setOnClickListener(new OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| getFragmentHost().showViewOnlyPreview(mWallpaperInfo); |
| eventLogger.logCurrentWallpaperPreviewed(); |
| } |
| }); |
| } |
| |
| private void bindWallpaperText(@PresentationMode int presentationMode) { |
| Context appContext = getActivity().getApplicationContext(); |
| |
| mWallpaperPresentationModeSubtitle.setText( |
| AttributionFormatter.getHumanReadableWallpaperPresentationMode( |
| appContext, presentationMode)); |
| |
| List<String> attributions = mWallpaperInfo.getAttributions(appContext); |
| if (!attributions.isEmpty()) { |
| mWallpaperTitle.setText(attributions.get(0)); |
| } |
| if (attributions.size() > 1) { |
| mWallpaperSubtitle.setText(attributions.get(1)); |
| } else { |
| mWallpaperSubtitle.setVisibility(View.INVISIBLE); |
| } |
| if (attributions.size() > 2) { |
| mWallpaperSubtitle2.setText(attributions.get(2)); |
| } else { |
| mWallpaperSubtitle2.setVisibility(View.INVISIBLE); |
| } |
| } |
| |
| private void bindWallpaperActionButtons(@PresentationMode int presentationMode) { |
| final Context appContext = getActivity().getApplicationContext(); |
| |
| final String actionUrl = mWallpaperInfo.getActionUrl(appContext); |
| if (actionUrl != null && !actionUrl.isEmpty()) { |
| |
| Uri exploreUri = Uri.parse(actionUrl); |
| |
| ExploreIntentChecker intentChecker = |
| InjectorProvider.getInjector().getExploreIntentChecker(appContext); |
| intentChecker.fetchValidActionViewIntent(exploreUri, (@Nullable Intent exploreIntent) -> { |
| if (getActivity() == null) { |
| return; |
| } |
| |
| updateExploreSectionVisibility(presentationMode, exploreIntent); |
| }); |
| } else { |
| updateExploreSectionVisibility(presentationMode, null /* exploreIntent */); |
| } |
| } |
| |
| /** |
| * Shows or hides appropriate elements in the "Explore section" (containing the Explore button |
| * and the Next Wallpaper button) depending on the current wallpaper. |
| * |
| * @param presentationMode The presentation mode of the current wallpaper. |
| * @param exploreIntent An optional explore intent for the current wallpaper. |
| */ |
| private void updateExploreSectionVisibility( |
| @PresentationMode int presentationMode, @Nullable Intent exploreIntent) { |
| |
| final Context appContext = getActivity().getApplicationContext(); |
| final UserEventLogger eventLogger = |
| InjectorProvider.getInjector().getUserEventLogger(appContext); |
| |
| boolean showSkipWallpaperButton = Flags.skipDailyWallpaperButtonEnabled |
| && presentationMode == WallpaperPreferences.PRESENTATION_MODE_ROTATING; |
| |
| if (exploreIntent != null) { |
| mWallpaperExploreButtonNoText.setImageDrawable(getContext().getDrawable( |
| mWallpaperInfo.getActionIconRes(appContext))); |
| mWallpaperExploreButtonNoText.setContentDescription( |
| getString(mWallpaperInfo.getActionLabelRes(appContext))); |
| mWallpaperExploreButtonNoText.setColorFilter( |
| getResources().getColor(R.color.currently_set_explore_button_color, |
| getContext().getTheme()), |
| Mode.SRC_IN); |
| mWallpaperExploreButtonNoText.setVisibility(View.VISIBLE); |
| mWallpaperExploreButtonNoText.setOnClickListener((View view) -> { |
| eventLogger.logActionClicked(mWallpaperInfo.getCollectionId(appContext), |
| mWallpaperInfo.getActionLabelRes(appContext)); |
| startActivity(exploreIntent); |
| }); |
| } |
| |
| if (showSkipWallpaperButton) { |
| mSkipWallpaperButton.setVisibility(View.VISIBLE); |
| mSkipWallpaperButton.setOnClickListener((View view) -> refreshDailyWallpaper()); |
| } |
| } |
| } |
| |
| /** |
| * ViewHolder subclass for a metadata "card" at the beginning of the RecyclerView that shows |
| * both home screen and lock screen wallpapers. |
| */ |
| private class TwoWallpapersMetadataHolder extends RecyclerView.ViewHolder |
| implements MetadataHolder { |
| private WallpaperInfo mHomeWallpaperInfo; |
| private ImageView mHomeWallpaperImage; |
| private TextView mHomeWallpaperPresentationMode; |
| private TextView mHomeWallpaperTitle; |
| private TextView mHomeWallpaperSubtitle1; |
| private TextView mHomeWallpaperSubtitle2; |
| |
| private ImageButton mHomeWallpaperExploreButton; |
| private ImageButton mSkipWallpaperButton; |
| private ViewGroup mHomeWallpaperPresentationSection; |
| |
| private WallpaperInfo mLockWallpaperInfo; |
| private ImageView mLockWallpaperImage; |
| private TextView mLockWallpaperTitle; |
| private TextView mLockWallpaperSubtitle1; |
| private TextView mLockWallpaperSubtitle2; |
| |
| private ImageButton mLockWallpaperExploreButton; |
| |
| public TwoWallpapersMetadataHolder(View metadataView) { |
| super(metadataView); |
| |
| // Set the min width of the metadata panel to be the screen width minus space for the |
| // 2 gutters on the sides. This ensures the RecyclerView's GridLayoutManager gives it |
| // a wide-enough initial width to fill up the width of the grid prior to the view being |
| // fully populated. |
| final Display display = getActivity().getWindowManager().getDefaultDisplay(); |
| Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(display); |
| metadataView.setMinimumWidth( |
| screenSize.x - 2 * getResources().getDimensionPixelSize(R.dimen.grid_padding)); |
| |
| int bothWallpaperImageWidth = getBothWallpaperImageWidth(); |
| |
| FrameLayout homeWallpaperSection = metadataView.findViewById( |
| R.id.home_wallpaper_section); |
| homeWallpaperSection.setMinimumWidth(bothWallpaperImageWidth); |
| mHomeWallpaperImage = metadataView.findViewById(R.id.home_wallpaper_image); |
| |
| mHomeWallpaperPresentationMode = |
| metadataView.findViewById(R.id.home_wallpaper_presentation_mode); |
| mHomeWallpaperTitle = metadataView.findViewById(R.id.home_wallpaper_title); |
| mHomeWallpaperSubtitle1 = metadataView.findViewById(R.id.home_wallpaper_subtitle1); |
| mHomeWallpaperSubtitle2 = metadataView.findViewById(R.id.home_wallpaper_subtitle2); |
| mHomeWallpaperPresentationSection = metadataView.findViewById( |
| R.id.home_wallpaper_presentation_section); |
| mHomeWallpaperExploreButton = |
| metadataView.findViewById(R.id.home_wallpaper_explore_button); |
| mSkipWallpaperButton = metadataView.findViewById(R.id.skip_home_wallpaper); |
| |
| FrameLayout lockWallpaperSection = metadataView.findViewById( |
| R.id.lock_wallpaper_section); |
| lockWallpaperSection.setMinimumWidth(bothWallpaperImageWidth); |
| mLockWallpaperImage = metadataView.findViewById(R.id.lock_wallpaper_image); |
| |
| mLockWallpaperTitle = metadataView.findViewById(R.id.lock_wallpaper_title); |
| mLockWallpaperSubtitle1 = metadataView.findViewById(R.id.lock_wallpaper_subtitle1); |
| mLockWallpaperSubtitle2 = metadataView.findViewById(R.id.lock_wallpaper_subtitle2); |
| mLockWallpaperExploreButton = |
| metadataView.findViewById(R.id.lock_wallpaper_explore_button); |
| } |
| |
| @Override |
| public void bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper, |
| @PresentationMode int presentationMode) { |
| bindHomeWallpaper(homeWallpaper, presentationMode); |
| bindLockWallpaper(lockWallpaper); |
| } |
| |
| private void bindHomeWallpaper(WallpaperInfo homeWallpaper, |
| @PresentationMode int presentationMode) { |
| final Context appContext = getActivity().getApplicationContext(); |
| final UserEventLogger eventLogger = |
| InjectorProvider.getInjector().getUserEventLogger(appContext); |
| |
| mHomeWallpaperInfo = homeWallpaper; |
| |
| homeWallpaper.getThumbAsset(appContext).loadDrawable( |
| getActivity(), mHomeWallpaperImage, |
| getResources().getColor(R.color.secondary_color, getContext().getTheme())); |
| |
| mHomeWallpaperPresentationMode.setText( |
| AttributionFormatter.getHumanReadableWallpaperPresentationMode( |
| appContext, presentationMode)); |
| |
| List<String> attributions = homeWallpaper.getAttributions(appContext); |
| if (!attributions.isEmpty()) { |
| mHomeWallpaperTitle.setText(attributions.get(0)); |
| } |
| if (attributions.size() > 1) { |
| mHomeWallpaperSubtitle1.setText(attributions.get(1)); |
| } |
| if (attributions.size() > 2) { |
| mHomeWallpaperSubtitle2.setText(attributions.get(2)); |
| } |
| |
| final String homeActionUrl = homeWallpaper.getActionUrl(appContext); |
| |
| if (homeActionUrl != null && !homeActionUrl.isEmpty()) { |
| Uri homeExploreUri = Uri.parse(homeActionUrl); |
| |
| ExploreIntentChecker intentChecker = |
| InjectorProvider.getInjector().getExploreIntentChecker(appContext); |
| |
| intentChecker.fetchValidActionViewIntent( |
| homeExploreUri, (@Nullable Intent exploreIntent) -> { |
| if (exploreIntent == null || getActivity() == null) { |
| return; |
| } |
| |
| mHomeWallpaperExploreButton.setVisibility(View.VISIBLE); |
| mHomeWallpaperExploreButton.setImageDrawable(getContext().getDrawable( |
| homeWallpaper.getActionIconRes(appContext))); |
| mHomeWallpaperExploreButton.setContentDescription(getString(homeWallpaper |
| .getActionLabelRes(appContext))); |
| mHomeWallpaperExploreButton.setColorFilter( |
| getResources().getColor(R.color.currently_set_explore_button_color, |
| getContext().getTheme()), |
| Mode.SRC_IN); |
| mHomeWallpaperExploreButton.setOnClickListener(v -> { |
| eventLogger.logActionClicked( |
| mHomeWallpaperInfo.getCollectionId(appContext), |
| mHomeWallpaperInfo.getActionLabelRes(appContext)); |
| startActivity(exploreIntent); |
| }); |
| }); |
| } else { |
| mHomeWallpaperExploreButton.setVisibility(View.GONE); |
| } |
| |
| if (presentationMode == WallpaperPreferences.PRESENTATION_MODE_ROTATING) { |
| mHomeWallpaperPresentationSection.setVisibility(View.VISIBLE); |
| if (Flags.skipDailyWallpaperButtonEnabled) { |
| mSkipWallpaperButton.setVisibility(View.VISIBLE); |
| mSkipWallpaperButton.setColorFilter( |
| getResources().getColor(R.color.currently_set_explore_button_color, |
| getContext().getTheme()), Mode.SRC_IN); |
| mSkipWallpaperButton.setOnClickListener(view -> refreshDailyWallpaper()); |
| } else { |
| mSkipWallpaperButton.setVisibility(View.GONE); |
| } |
| } else { |
| mHomeWallpaperPresentationSection.setVisibility(View.GONE); |
| } |
| |
| mHomeWallpaperImage.setOnClickListener(v -> { |
| eventLogger.logCurrentWallpaperPreviewed(); |
| getFragmentHost().showViewOnlyPreview(mHomeWallpaperInfo); |
| }); |
| } |
| |
| private void bindLockWallpaper(WallpaperInfo lockWallpaper) { |
| if (lockWallpaper == null) { |
| Log.e(TAG, "TwoWallpapersMetadataHolder bound without a lock screen wallpaper."); |
| return; |
| } |
| |
| final Context appContext = getActivity().getApplicationContext(); |
| final UserEventLogger eventLogger = |
| InjectorProvider.getInjector().getUserEventLogger(getActivity()); |
| |
| mLockWallpaperInfo = lockWallpaper; |
| |
| lockWallpaper.getThumbAsset(appContext).loadDrawable( |
| getActivity(), mLockWallpaperImage, getResources().getColor(R.color.secondary_color)); |
| |
| List<String> lockAttributions = lockWallpaper.getAttributions(appContext); |
| if (!lockAttributions.isEmpty()) { |
| mLockWallpaperTitle.setText(lockAttributions.get(0)); |
| } |
| if (lockAttributions.size() > 1) { |
| mLockWallpaperSubtitle1.setText(lockAttributions.get(1)); |
| } |
| if (lockAttributions.size() > 2) { |
| mLockWallpaperSubtitle2.setText(lockAttributions.get(2)); |
| } |
| |
| final String lockActionUrl = lockWallpaper.getActionUrl(appContext); |
| |
| if (lockActionUrl != null && !lockActionUrl.isEmpty()) { |
| Uri lockExploreUri = Uri.parse(lockActionUrl); |
| |
| ExploreIntentChecker intentChecker = |
| InjectorProvider.getInjector().getExploreIntentChecker(appContext); |
| intentChecker.fetchValidActionViewIntent( |
| lockExploreUri, (@Nullable Intent exploreIntent) -> { |
| if (exploreIntent == null || getActivity() == null) { |
| return; |
| } |
| mLockWallpaperExploreButton.setImageDrawable(getContext().getDrawable( |
| lockWallpaper.getActionIconRes(appContext))); |
| mLockWallpaperExploreButton.setContentDescription(getString( |
| lockWallpaper.getActionLabelRes(appContext))); |
| mLockWallpaperExploreButton.setVisibility(View.VISIBLE); |
| mLockWallpaperExploreButton.setColorFilter( |
| getResources().getColor( |
| R.color.currently_set_explore_button_color), |
| Mode.SRC_IN); |
| mLockWallpaperExploreButton.setOnClickListener(new OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| eventLogger.logActionClicked( |
| mLockWallpaperInfo.getCollectionId(appContext), |
| mLockWallpaperInfo.getActionLabelRes(appContext)); |
| startActivity(exploreIntent); |
| } |
| }); |
| }); |
| } else { |
| mLockWallpaperExploreButton.setVisibility(View.GONE); |
| } |
| |
| mLockWallpaperImage.setOnClickListener(new OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| eventLogger.logCurrentWallpaperPreviewed(); |
| getFragmentHost().showViewOnlyPreview(mLockWallpaperInfo); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * ViewHolder subclass for a category tile in the RecyclerView. |
| */ |
| private class CategoryHolder extends RecyclerView.ViewHolder implements View.OnClickListener { |
| private Category mCategory; |
| private CardView mCategoryView; |
| private ImageView mImageView; |
| private ImageView mOverlayIconView; |
| private TextView mTitleView; |
| |
| public CategoryHolder(View itemView) { |
| super(itemView); |
| itemView.setOnClickListener(this); |
| |
| mCategoryView = itemView.findViewById(R.id.category); |
| mImageView = itemView.findViewById(R.id.image); |
| mOverlayIconView = itemView.findViewById(R.id.overlay_icon); |
| mTitleView = itemView.findViewById(R.id.category_title); |
| |
| mCategoryView.getLayoutParams().height = mTileSizePx.y; |
| } |
| |
| @Override |
| public void onClick(View view) { |
| final UserEventLogger eventLogger = |
| InjectorProvider.getInjector().getUserEventLogger(getActivity()); |
| eventLogger.logCategorySelected(mCategory.getCollectionId()); |
| |
| if (mCategory.supportsCustomPhotos()) { |
| getFragmentHost().getMyPhotosStarter().requestCustomPhotoPicker( |
| new PermissionChangedListener() { |
| @Override |
| public void onPermissionsGranted() { |
| drawThumbnailAndOverlayIcon(); |
| } |
| |
| @Override |
| public void onPermissionsDenied(boolean dontAskAgain) { |
| // No-op |
| } |
| }); |
| return; |
| } |
| |
| getFragmentHost().show(mCategory.getCollectionId()); |
| } |
| |
| /** |
| * Binds the given category to this CategoryHolder. |
| */ |
| public void bindCategory(Category category) { |
| mCategory = category; |
| mTitleView.setText(category.getTitle()); |
| drawThumbnailAndOverlayIcon(); |
| } |
| |
| /** |
| * Draws the CategoryHolder's thumbnail and overlay icon. |
| */ |
| public void drawThumbnailAndOverlayIcon() { |
| mOverlayIconView.setImageDrawable(mCategory.getOverlayIcon( |
| getActivity().getApplicationContext())); |
| |
| // Size the overlay icon according to the category. |
| int overlayIconDimenDp = mCategory.getOverlayIconSizeDp(); |
| DisplayMetrics metrics = DisplayMetricsRetriever.getInstance().getDisplayMetrics( |
| getResources(), getActivity().getWindowManager().getDefaultDisplay()); |
| int overlayIconDimenPx = (int) (overlayIconDimenDp * metrics.density); |
| mOverlayIconView.getLayoutParams().width = overlayIconDimenPx; |
| mOverlayIconView.getLayoutParams().height = overlayIconDimenPx; |
| |
| Asset thumbnail = mCategory.getThumbnail(getActivity().getApplicationContext()); |
| if (thumbnail != null) { |
| thumbnail.loadDrawable(getActivity(), mImageView, |
| getResources().getColor(R.color.secondary_color)); |
| } else { |
| // TODO(orenb): Replace this workaround for b/62584914 with a proper way of unloading the |
| // ImageView such that no incorrect image is improperly loaded upon rapid scroll. |
| Object nullObj = null; |
| Glide.with(getActivity()) |
| .asDrawable() |
| .load(nullObj) |
| .into(mImageView); |
| |
| } |
| } |
| } |
| |
| /** |
| * ViewHolder subclass for the loading indicator ("spinner") shown when categories are being |
| * fetched. |
| */ |
| private class LoadingIndicatorHolder extends RecyclerView.ViewHolder { |
| public LoadingIndicatorHolder(View view) { |
| super(view); |
| ProgressBar progressBar = view.findViewById(R.id.loading_indicator); |
| progressBar.getIndeterminateDrawable().setColorFilter( |
| getResources().getColor(R.color.accent_color), Mode.SRC_IN); |
| } |
| } |
| |
| /** |
| * ViewHolder subclass for a "card" at the beginning of the RecyclerView showing the app needs the |
| * user to grant the storage permission to show the currently set wallpaper. |
| */ |
| private class PermissionNeededHolder extends RecyclerView.ViewHolder { |
| private Button mAllowAccessButton; |
| |
| public PermissionNeededHolder(View view) { |
| super(view); |
| |
| mAllowAccessButton = view.findViewById(R.id.permission_needed_allow_access_button); |
| mAllowAccessButton.setOnClickListener((View v) -> { |
| getFragmentHost().requestExternalStoragePermission(mAdapter); |
| }); |
| |
| // Replace explanation text with text containing the Wallpapers app name which replaces the |
| // placeholder. |
| String appName = getString(R.string.app_name); |
| String explanation = getString(R.string.permission_needed_explanation, appName); |
| TextView explanationTextView = view.findViewById(R.id.permission_needed_explanation); |
| explanationTextView.setText(explanation); |
| } |
| } |
| |
| /** |
| * RecyclerView Adapter subclass for the category tiles in the RecyclerView. |
| */ |
| private class CategoryAdapter extends RecyclerView.Adapter<ViewHolder> |
| implements PermissionChangedListener { |
| private static final int ITEM_VIEW_TYPE_CATEGORY = 3; |
| private static final int ITEM_VIEW_TYPE_LOADING_INDICATOR = 4; |
| private List<Category> mCategories; |
| |
| public CategoryAdapter(List<Category> categories) { |
| mCategories = categories; |
| } |
| |
| @Override |
| public int getItemViewType(int position) { |
| if (mAwaitingCategories && position == getItemCount() - 1) { |
| return ITEM_VIEW_TYPE_LOADING_INDICATOR; |
| } |
| |
| return ITEM_VIEW_TYPE_CATEGORY; |
| } |
| |
| @Override |
| public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { |
| LayoutInflater layoutInflater = LayoutInflater.from(getActivity()); |
| View view; |
| |
| switch (viewType) { |
| case ITEM_VIEW_TYPE_LOADING_INDICATOR: |
| view = layoutInflater.inflate( |
| R.layout.grid_item_loading_indicator, parent, /* attachToRoot */ false); |
| return new LoadingIndicatorHolder(view); |
| case ITEM_VIEW_TYPE_CATEGORY: |
| view = layoutInflater.inflate( |
| R.layout.grid_item_category, parent, /* attachToRoot */ false); |
| return new CategoryHolder(view); |
| default: |
| Log.e(TAG, "Unsupported viewType " + viewType + " in CategoryAdapter"); |
| return null; |
| } |
| } |
| |
| @Override |
| public void onBindViewHolder(ViewHolder holder, int position) { |
| int viewType = getItemViewType(position); |
| |
| switch (viewType) { |
| case ITEM_VIEW_TYPE_CATEGORY: |
| // Offset position to get category index to account for the non-category view holders. |
| Category category = mCategories.get(position - NUM_NON_CATEGORY_VIEW_HOLDERS); |
| ((CategoryHolder) holder).bindCategory(category); |
| break; |
| case ITEM_VIEW_TYPE_LOADING_INDICATOR: |
| // No op. |
| break; |
| default: |
| Log.e(TAG, "Unsupported viewType " + viewType + " in CategoryAdapter"); |
| } |
| } |
| |
| @Override |
| public int getItemCount() { |
| // Add to size of categories to account for the metadata related views. |
| // Add 1 more for the loading indicator if not yet done loading. |
| int size = mCategories.size() + NUM_NON_CATEGORY_VIEW_HOLDERS; |
| if (mAwaitingCategories) { |
| size += 1; |
| } |
| |
| return size; |
| } |
| |
| @Override |
| public void onPermissionsGranted() { |
| notifyDataSetChanged(); |
| } |
| |
| @Override |
| public void onPermissionsDenied(boolean dontAskAgain) { |
| if (!dontAskAgain) { |
| return; |
| } |
| |
| String permissionNeededMessage = |
| getString(R.string.permission_needed_explanation_go_to_settings); |
| AlertDialog dialog = new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme) |
| .setMessage(permissionNeededMessage) |
| .setPositiveButton(android.R.string.ok, null /* onClickListener */) |
| .setNegativeButton( |
| R.string.settings_button_label, |
| new DialogInterface.OnClickListener() { |
| @Override |
| public void onClick(DialogInterface dialogInterface, int i) { |
| Intent appInfoIntent = new Intent(); |
| appInfoIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); |
| Uri uri = Uri.fromParts( |
| "package", getActivity().getPackageName(), null /* fragment */); |
| appInfoIntent.setData(uri); |
| startActivityForResult(appInfoIntent, SETTINGS_APP_INFO_REQUEST_CODE); |
| } |
| }) |
| .create(); |
| dialog.show(); |
| } |
| } |
| |
| private class GridPaddingDecoration extends RecyclerView.ItemDecoration { |
| |
| private int mPadding; |
| |
| GridPaddingDecoration(int padding) { |
| mPadding = padding; |
| } |
| |
| @Override |
| public void getItemOffsets(Rect outRect, View view, RecyclerView parent, |
| RecyclerView.State state) { |
| int position = parent.getChildAdapterPosition(view) - NUM_NON_CATEGORY_VIEW_HOLDERS; |
| if (position >= 0) { |
| outRect.left = mPadding; |
| outRect.right = mPadding; |
| } |
| } |
| } |
| |
| private class PreviewPagerAdapter extends PagerAdapter { |
| |
| private List<View> mPages; |
| |
| PreviewPagerAdapter(List<View> pages) { |
| mPages = pages; |
| } |
| |
| @Override |
| public void destroyItem(@NonNull ViewGroup container, int position, |
| @NonNull Object object) { |
| container.removeView((View) object); |
| } |
| |
| @NonNull |
| @Override |
| public Object instantiateItem(@NonNull ViewGroup container, int position) { |
| View view = mPages.get(position); |
| container.addView(view); |
| return view; |
| } |
| |
| @Override |
| public int getCount() { |
| return mPages.size(); |
| } |
| |
| @Override |
| public boolean isViewFromObject(@NonNull View view, @NonNull Object o) { |
| return view == o; |
| } |
| } |
| } |