blob: 6dc91f4a8ede5fa7cf46302a2efc09f0c95d7ca9 [file] [log] [blame]
Jon Miranda16ea1b12017-12-12 14:52:48 -08001/*
2 * Copyright (C) 2017 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 */
16package com.android.wallpaper.picker;
17
18import android.app.ProgressDialog;
19import android.content.Context;
20import android.content.DialogInterface;
21import android.content.Intent;
22import android.content.pm.PackageManager;
23import android.graphics.Point;
24import android.graphics.PorterDuff.Mode;
25import android.graphics.drawable.Drawable;
26import android.net.Uri;
27import android.os.Build.VERSION;
28import android.os.Build.VERSION_CODES;
29import android.os.Bundle;
30import android.provider.Settings;
Santiago Etchebehere17b1b252018-05-08 13:34:11 -070031import android.text.TextUtils;
Jon Miranda16ea1b12017-12-12 14:52:48 -080032import android.util.DisplayMetrics;
33import android.util.Log;
34import android.view.Display;
35import android.view.LayoutInflater;
36import android.view.View;
37import android.view.View.OnClickListener;
38import android.view.ViewGroup;
39import android.widget.Button;
40import android.widget.FrameLayout;
41import android.widget.ImageButton;
42import android.widget.ImageView;
43import android.widget.LinearLayout;
44import android.widget.ProgressBar;
45import android.widget.RelativeLayout;
46import android.widget.TextView;
47
48import com.android.wallpaper.R;
49import com.android.wallpaper.asset.Asset;
50import com.android.wallpaper.compat.ButtonDrawableSetterCompat;
51import com.android.wallpaper.config.Flags;
52import com.android.wallpaper.model.Category;
53import com.android.wallpaper.model.WallpaperInfo;
54import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
55import com.android.wallpaper.module.CurrentWallpaperInfoFactory.WallpaperInfoCallback;
56import com.android.wallpaper.module.ExploreIntentChecker;
57import com.android.wallpaper.module.InjectorProvider;
58import com.android.wallpaper.module.LockWallpaperStatusChecker;
59import com.android.wallpaper.module.UserEventLogger;
60import com.android.wallpaper.module.WallpaperPreferences;
61import com.android.wallpaper.module.WallpaperPreferences.PresentationMode;
62import com.android.wallpaper.module.WallpaperRotationRefresher;
63import com.android.wallpaper.module.WallpaperRotationRefresher.Listener;
64import com.android.wallpaper.picker.MyPhotosLauncher.PermissionChangedListener;
65import com.android.wallpaper.util.DisplayMetricsRetriever;
66import com.android.wallpaper.util.ScreenSizeCalculator;
67import com.android.wallpaper.util.TileSizeCalculator;
68import com.android.wallpaper.widget.GridMarginDecoration;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070069
Jon Miranda16ea1b12017-12-12 14:52:48 -080070import com.bumptech.glide.Glide;
71import com.bumptech.glide.MemoryCategory;
72
73import java.util.ArrayList;
74import java.util.Date;
75import java.util.List;
76
Sunny Goyal8600a3f2018-08-15 12:48:01 -070077import androidx.annotation.Nullable;
78import androidx.appcompat.app.AlertDialog;
79import androidx.fragment.app.Fragment;
80import androidx.recyclerview.widget.GridLayoutManager;
81import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup;
82import androidx.recyclerview.widget.RecyclerView;
83import androidx.recyclerview.widget.RecyclerView.ViewHolder;
84
Jon Miranda16ea1b12017-12-12 14:52:48 -080085/**
86 * Displays the Main UI for picking an category of wallpapers to choose from.
87 */
88public class CategoryPickerFragment extends Fragment {
89 private static final String TAG = "CategoryPickerFragment";
90
91 // The number of ViewHolders that don't pertain to category tiles.
92 // Currently 2: one for the metadata section and one for the "Select wallpaper" header.
93 private static final int NUM_NON_CATEGORY_VIEW_HOLDERS = 2;
94
95 /**
96 * The fixed RecyclerView.Adapter position of the ViewHolder for the initial item in the grid --
97 * usually the wallpaper metadata, or a "permission needed" warning UI.
98 */
99 private static final int INITIAL_HOLDER_ADAPTER_POSITION = 0;
100
101 private static final int SETTINGS_APP_INFO_REQUEST_CODE = 1;
102
103 private static final String PERMISSION_READ_WALLPAPER_INTERNAL =
104 "android.permission.READ_WALLPAPER_INTERNAL";
105
106 private RecyclerView mImageGrid;
107 private CategoryAdapter mAdapter;
108 private ArrayList<Category> mCategories;
109 private Point mTileSizePx;
110 private boolean mAwaitingCategories;
111 private ProgressDialog mRefreshWallpaperProgressDialog;
112 private boolean mTestingMode;
113
114 public CategoryPickerFragment() {
115 }
116
117 @Override
118 public void onCreate(Bundle savedInstanceState) {
119 super.onCreate(savedInstanceState);
120 mCategories = new ArrayList<>();
121 mAdapter = new CategoryAdapter(mCategories);
122 }
123
124 @Override
125 public View onCreateView(LayoutInflater inflater, ViewGroup container,
126 Bundle savedInstanceState) {
127 View view = inflater.inflate(
128 R.layout.fragment_category_picker, container, /* attachToRoot */ false);
129
130 mImageGrid = (RecyclerView) view.findViewById(R.id.category_grid);
131 GridMarginDecoration.applyTo(mImageGrid);
132
133 mTileSizePx = TileSizeCalculator.getCategoryTileSize(getActivity());
134
135 if (LockWallpaperStatusChecker.isLockWallpaperSet(getContext())) {
136 mAdapter.setNumMetadataCards(CategoryAdapter.METADATA_VIEW_TWO_CARDS);
137 } else {
138 mAdapter.setNumMetadataCards(CategoryAdapter.METADATA_VIEW_SINGLE_CARD);
139 }
140 mImageGrid.setAdapter(mAdapter);
141
142 GridLayoutManager gridLayoutManager = new GridLayoutManager(getActivity(), getNumColumns());
143 gridLayoutManager.setSpanSizeLookup(new CategorySpanSizeLookup(mAdapter));
144 mImageGrid.setLayoutManager(gridLayoutManager);
145
146 return view;
147 }
148
149 @Override
150 public void onResume() {
151 super.onResume();
152
153 WallpaperPreferences preferences = InjectorProvider.getInjector().getPreferences(getActivity());
154 preferences.setLastAppActiveTimestamp(new Date().getTime());
155
156 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
157 // PreviewFragment.
158 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
159
160 // Refresh metadata since it may have changed since the activity was paused.
161 ViewHolder initialViewHolder =
162 mImageGrid.findViewHolderForAdapterPosition(INITIAL_HOLDER_ADAPTER_POSITION);
163 MetadataHolder metadataHolder = null;
164 if (initialViewHolder instanceof MetadataHolder) {
165 metadataHolder = (MetadataHolder) initialViewHolder;
166 }
167
168 // The wallpaper may have been set while this fragment was paused, so force refresh the current
169 // wallpapers and presentation mode.
170 refreshCurrentWallpapers(metadataHolder, true /* forceRefresh */);
171 }
172
173 @Override
174 public void onDestroy() {
175 super.onDestroy();
176 if (mRefreshWallpaperProgressDialog != null) {
177 mRefreshWallpaperProgressDialog.dismiss();
178 }
179 }
180
181 @Override
182 public void onActivityResult(int requestCode, int resultCode, Intent data) {
183 if (requestCode == SETTINGS_APP_INFO_REQUEST_CODE) {
184 mAdapter.notifyDataSetChanged();
185 }
186 }
187
188 /**
189 * Inserts the given category into the categories list in priority order.
190 */
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700191 public void addCategory(Category category, boolean loading) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800192 // If not previously waiting for categories, enter the waiting state by showing the loading
193 // indicator.
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700194 if (loading && !mAwaitingCategories) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800195 mAdapter.notifyItemChanged(getNumColumns());
196 mAdapter.notifyItemInserted(getNumColumns());
197 mAwaitingCategories = true;
198 }
199
200 int priority = category.getPriority();
201
202 int index = 0;
203 while (index < mCategories.size() && priority >= mCategories.get(index).getPriority()) {
204 index++;
205 }
206
207 mCategories.add(index, category);
208 if (mAdapter != null) {
209 // Offset the index because of the static metadata element at beginning of RecyclerView.
210 mAdapter.notifyItemInserted(index + NUM_NON_CATEGORY_VIEW_HOLDERS);
211 }
212 }
213
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700214 public void removeCategory(Category category) {
215 int index = mCategories.indexOf(category);
216 if (index >= 0) {
217 mCategories.remove(index);
218 mAdapter.notifyItemRemoved(index + NUM_NON_CATEGORY_VIEW_HOLDERS);
219 }
220 }
221
222 public void updateCategory(Category category) {
223 int index = mCategories.indexOf(category);
224 if (index >= 0) {
225 mCategories.remove(index);
226 mCategories.add(index, category);
227 mAdapter.notifyItemChanged(index + NUM_NON_CATEGORY_VIEW_HOLDERS);
228 }
229 }
230
231 public void clearCategories() {
232 mCategories.clear();
233 mAdapter.notifyDataSetChanged();
234 }
235
Jon Miranda16ea1b12017-12-12 14:52:48 -0800236 /**
237 * Notifies the CategoryPickerFragment that no further categories are expected so it may hide
238 * the loading indicator.
239 */
240 public void doneFetchingCategories() {
241 if (mAwaitingCategories) {
242 mAdapter.notifyItemRemoved(mAdapter.getItemCount() - 1);
243 mAwaitingCategories = false;
244 }
245 }
246
247 /**
248 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
249 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
250 * constantly keeps the UI thread alive and blocks a test forever.
251 */
252 void setTestingMode(boolean testingMode) {
253 mTestingMode = testingMode;
254 }
255
256 private boolean canShowCurrentWallpaper() {
257 TopLevelPickerActivity activity = (TopLevelPickerActivity) getActivity();
258 PackageManager packageManager = activity.getPackageManager();
259 String packageName = activity.getPackageName();
260
261 boolean hasReadWallpaperInternal = packageManager.checkPermission(
262 PERMISSION_READ_WALLPAPER_INTERNAL, packageName) == PackageManager.PERMISSION_GRANTED;
263 return hasReadWallpaperInternal || activity.isReadExternalStoragePermissionGranted();
264 }
265
266 /**
267 * Obtains the {@link WallpaperInfo} object(s) representing the wallpaper(s) currently set to the
268 * device from the {@link CurrentWallpaperInfoFactory} and binds them to the provided
269 * {@link MetadataHolder}.
270 */
271 private void refreshCurrentWallpapers(@Nullable final MetadataHolder holder,
272 boolean forceRefresh) {
273 CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector()
274 .getCurrentWallpaperFactory(getActivity().getApplicationContext());
275
276 factory.createCurrentWallpaperInfos(new WallpaperInfoCallback() {
277 @Override
278 public void onWallpaperInfoCreated(
279 final WallpaperInfo homeWallpaper,
280 @Nullable final WallpaperInfo lockWallpaper,
281 @PresentationMode final int presentationMode) {
282
283 // Update the metadata displayed on screen. Do this in a Handler so it is scheduled at the
284 // end of the message queue. This is necessary to ensure we do not remove or add data from
285 // the adapter while the layout is being computed. RecyclerView documentation therefore
286 // recommends performing such changes in a Handler.
287 new android.os.Handler().post(new Runnable() {
288 @Override
289 public void run() {
290 // A config change may have destroyed the activity since the refresh started, so check
291 // for that.
292 if (getActivity() == null) {
293 return;
294 }
295
296 int numMetadataCards = (lockWallpaper == null)
297 ? CategoryAdapter.METADATA_VIEW_SINGLE_CARD
298 : CategoryAdapter.METADATA_VIEW_TWO_CARDS;
299 mAdapter.setNumMetadataCards(numMetadataCards);
300
301 // The MetadataHolder may be null if the RecyclerView has not yet created the view
302 // holder.
303 if (holder != null) {
304 holder.bindWallpapers(homeWallpaper, lockWallpaper, presentationMode);
305 }
306 }
307 });
308 }
309 }, forceRefresh);
310 }
311
312 private int getNumColumns() {
313 return TileSizeCalculator.getNumCategoryColumns(getActivity());
314 }
315
316 /**
317 * Returns the width to use for the home screen wallpaper in the "single metadata" configuration.
318 */
319 private int getSingleWallpaperImageWidth() {
320 Point screenSize = ScreenSizeCalculator.getInstance()
321 .getScreenSize(getActivity().getWindowManager().getDefaultDisplay());
322
323 int height = getResources().getDimensionPixelSize(R.dimen.single_metadata_card_layout_height);
324 return height * screenSize.x / screenSize.y;
325 }
326
327 /**
328 * Refreshes the current wallpaper in a daily wallpaper rotation.
329 */
330 private void refreshDailyWallpaper() {
331 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
332 // causes Espresso to hang once the dialog is shown.
333 if (!mTestingMode) {
334 int themeResId;
335 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
336 themeResId = R.style.ProgressDialogThemePreL;
337 } else {
338 themeResId = R.style.LightDialogTheme;
339 }
340 mRefreshWallpaperProgressDialog = new ProgressDialog(getActivity(), themeResId);
341 mRefreshWallpaperProgressDialog.setTitle(null);
342 mRefreshWallpaperProgressDialog.setMessage(
343 getResources().getString(R.string.refreshing_daily_wallpaper_dialog_message));
344 mRefreshWallpaperProgressDialog.setIndeterminate(true);
Santiago Etchebehereb9198652018-04-16 13:40:53 -0700345 mRefreshWallpaperProgressDialog.setCancelable(false);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800346 mRefreshWallpaperProgressDialog.show();
347 }
348
349 WallpaperRotationRefresher wallpaperRotationRefresher =
350 InjectorProvider.getInjector().getWallpaperRotationRefresher();
351 wallpaperRotationRefresher.refreshWallpaper(getContext(), new Listener() {
352 @Override
353 public void onRefreshed() {
354 // If the fragment is detached from the activity there's nothing to do here and the UI will
355 // update when the fragment is resumed.
356 if (getActivity() == null) {
357 return;
358 }
359
360 if (mRefreshWallpaperProgressDialog != null) {
361 mRefreshWallpaperProgressDialog.dismiss();
362 }
363
364 ViewHolder initialViewHolder =
365 mImageGrid.findViewHolderForAdapterPosition(INITIAL_HOLDER_ADAPTER_POSITION);
366 if (initialViewHolder instanceof MetadataHolder) {
367 MetadataHolder metadataHolder = (MetadataHolder) initialViewHolder;
368 // Update the metadata pane since we know now the UI there is stale.
369 refreshCurrentWallpapers(metadataHolder, true /* forceRefresh */);
370 }
371 }
372
373 @Override
374 public void onError() {
375 if (getActivity() == null) {
376 return;
377 }
378
379 if (mRefreshWallpaperProgressDialog != null) {
380 mRefreshWallpaperProgressDialog.dismiss();
381 }
382
383 AlertDialog errorDialog = new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme)
384 .setMessage(R.string.refresh_daily_wallpaper_failed_message)
385 .setPositiveButton(android.R.string.ok, null /* onClickListener */)
386 .create();
387 errorDialog.show();
388 }
389 });
390 }
391
392 /**
393 * Returns the width to use for the home and lock screen wallpapers in the "both metadata"
394 * configuration.
395 */
396 private int getBothWallpaperImageWidth() {
397 DisplayMetrics metrics = DisplayMetricsRetriever.getInstance().getDisplayMetrics(getResources(),
398 getActivity().getWindowManager().getDefaultDisplay());
399
400 // In the "both metadata" configuration, wallpaper images minus the gutters account for the full
401 // width of the device's screen.
402 return metrics.widthPixels - (3 * getResources().getDimensionPixelSize(R.dimen.grid_padding));
403 }
404
405 private interface MetadataHolder {
406 /**
407 * Binds {@link WallpaperInfo} objects representing the currently-set wallpapers to the
408 * ViewHolder layout.
409 */
410 void bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper,
411 @PresentationMode int presentationMode);
412 }
413
414 private static class SelectWallpaperHeaderHolder extends RecyclerView.ViewHolder {
415 public SelectWallpaperHeaderHolder(View headerView) {
416 super(headerView);
417 }
418 }
419
420 /**
421 * SpanSizeLookup subclass which provides that the item in the first position spans the number of
422 * columns in the RecyclerView and all other items only take up a single span.
423 */
424 private class CategorySpanSizeLookup extends SpanSizeLookup {
425 CategoryAdapter mAdapter;
426
427 public CategorySpanSizeLookup(CategoryAdapter adapter) {
428 mAdapter = adapter;
429 }
430
431 @Override
432 public int getSpanSize(int position) {
433 if (position < NUM_NON_CATEGORY_VIEW_HOLDERS
434 || mAdapter.getItemViewType(position)
435 == CategoryAdapter.ITEM_VIEW_TYPE_LOADING_INDICATOR) {
436 return getNumColumns();
437 }
438
439 return 1;
440 }
441 }
442
443 /**
444 * ViewHolder subclass for a metadata "card" at the beginning of the RecyclerView.
445 */
446 private class SingleWallpaperMetadataHolder extends RecyclerView.ViewHolder
447 implements MetadataHolder {
448 private WallpaperInfo mWallpaperInfo;
449 private ImageView mWallpaperImage;
450 private TextView mWallpaperPresentationMode;
451 private TextView mWallpaperTitle;
452 private TextView mWallpaperSubtitle;
453 private ViewGroup mWallpaperExploreSection;
454 private Button mWallpaperExploreButton;
455 private ImageButton mWallpaperExploreButtonNoText;
456 private ImageButton mSkipWallpaperButton;
457
458 public SingleWallpaperMetadataHolder(View metadataView) {
459 super(metadataView);
460
461 mWallpaperImage = (ImageView) metadataView.findViewById(R.id.wallpaper_image);
462 mWallpaperImage.getLayoutParams().width = getSingleWallpaperImageWidth();
463
464 mWallpaperPresentationMode =
465 (TextView) metadataView.findViewById(R.id.wallpaper_presentation_mode);
466 mWallpaperTitle = (TextView) metadataView.findViewById(R.id.wallpaper_title);
467 mWallpaperSubtitle = (TextView) metadataView.findViewById(R.id.wallpaper_subtitle);
468
469 mWallpaperExploreSection =
470 (ViewGroup) metadataView.findViewById(R.id.wallpaper_explore_section);
471 mWallpaperExploreButton =
472 (Button) metadataView.findViewById(R.id.wallpaper_explore_button);
473 mWallpaperExploreButtonNoText = (ImageButton)
474 metadataView.findViewById(R.id.wallpaper_explore_button_notext);
475
476 mSkipWallpaperButton = (ImageButton) metadataView.findViewById(R.id.skip_wallpaper_button);
477
478 if (Flags.skipDailyWallpaperButtonEnabled) {
Santiago Etchebehered24506c2018-04-05 17:02:42 -0700479 Drawable exploreButtonDrawable = getContext().getDrawable(
480 R.drawable.ic_explore_24px);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800481 // This Drawable's state is shared across the app, so make a copy of it before applying a
482 // color tint as not to affect other clients elsewhere in the app.
483 exploreButtonDrawable = exploreButtonDrawable.getConstantState().newDrawable().mutate();
484 exploreButtonDrawable.setColorFilter(
485 getResources().getColor(R.color.currently_set_explore_button_color), Mode.SRC_IN);
486 ButtonDrawableSetterCompat.setDrawableToButtonStart(
487 mWallpaperExploreButton, exploreButtonDrawable);
488 }
489 }
490
491 /**
492 * Binds home screen wallpaper to the ViewHolder layout.
493 */
494 @Override
495 public void bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper,
496 @PresentationMode int presentationMode) {
497 mWallpaperInfo = homeWallpaper;
498
499 bindWallpaperAsset();
500 bindWallpaperText(presentationMode);
501 bindWallpaperActionButtons(presentationMode);
502 }
503
504 private void bindWallpaperAsset() {
505 final UserEventLogger eventLogger =
506 InjectorProvider.getInjector().getUserEventLogger(getActivity());
507
508 mWallpaperInfo.getThumbAsset(getActivity().getApplicationContext()).loadDrawable(
509 getActivity(), mWallpaperImage, getResources().getColor(R.color.secondary_color));
510
511 mWallpaperImage.setOnClickListener(new OnClickListener() {
512 @Override
513 public void onClick(View v) {
514 ((TopLevelPickerActivity) getActivity()).showViewOnlyPreview(mWallpaperInfo);
515 eventLogger.logCurrentWallpaperPreviewed();
516 }
517 });
518 }
519
520 private void bindWallpaperText(@PresentationMode int presentationMode) {
521 Context appContext = getActivity().getApplicationContext();
522
523 mWallpaperPresentationMode.setText(
524 AttributionFormatter.getHumanReadableWallpaperPresentationMode(
525 appContext, presentationMode));
526
527 List<String> attributions = mWallpaperInfo.getAttributions(appContext);
528 if (!attributions.isEmpty()) {
529 mWallpaperTitle.setText(attributions.get(0));
530 }
Santiago Etchebehere17b1b252018-05-08 13:34:11 -0700531 String subtitleText =
532 AttributionFormatter.formatWallpaperSubtitle(appContext, mWallpaperInfo);
533 if (!TextUtils.isEmpty(subtitleText)) {
534 mWallpaperSubtitle.setText(subtitleText);
535 mWallpaperSubtitle.setVisibility(View.VISIBLE);
536 } else {
537 mWallpaperSubtitle.setVisibility(View.GONE);
538 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800539 }
540
541 private void bindWallpaperActionButtons(@PresentationMode int presentationMode) {
542 final Context appContext = getActivity().getApplicationContext();
543
544 final String actionUrl = mWallpaperInfo.getActionUrl(appContext);
545 if (actionUrl != null && !actionUrl.isEmpty()) {
546
547 Uri exploreUri = Uri.parse(actionUrl);
548
549 ExploreIntentChecker intentChecker =
550 InjectorProvider.getInjector().getExploreIntentChecker(appContext);
551 intentChecker.fetchValidActionViewIntent(exploreUri, (@Nullable Intent exploreIntent) -> {
552 if (getActivity() == null) {
553 return;
554 }
555
556 updateExploreSectionVisibility(presentationMode, exploreIntent);
557 });
558 } else {
559 updateExploreSectionVisibility(presentationMode, null /* exploreIntent */);
560 }
561 }
562
563 /**
564 * Shows or hides appropriate elements in the "Explore section" (containing the Explore button
565 * and the Next Wallpaper button) depending on the current wallpaper.
566 *
567 * @param presentationMode The presentation mode of the current wallpaper.
568 * @param exploreIntent An optional explore intent for the current wallpaper.
569 */
570 private void updateExploreSectionVisibility(
571 @PresentationMode int presentationMode, @Nullable Intent exploreIntent) {
572
573 final Context appContext = getActivity().getApplicationContext();
574 final UserEventLogger eventLogger =
575 InjectorProvider.getInjector().getUserEventLogger(appContext);
576
577 boolean showSkipWallpaperButton = Flags.skipDailyWallpaperButtonEnabled
578 && presentationMode == WallpaperPreferences.PRESENTATION_MODE_ROTATING;
579
580 if (exploreIntent != null || showSkipWallpaperButton) {
581 mWallpaperExploreSection.setVisibility(View.VISIBLE);
582
583 if (exploreIntent != null) {
584 View exploreButton;
585
586 if (showSkipWallpaperButton) {
587 exploreButton = mWallpaperExploreButtonNoText;
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700588 mWallpaperExploreButtonNoText.setImageDrawable(getContext().getDrawable(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700589 mWallpaperInfo.getActionIconRes(appContext)));
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700590 mWallpaperExploreButtonNoText.setContentDescription(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700591 getString(mWallpaperInfo.getActionLabelRes(appContext)));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800592 mWallpaperExploreButtonNoText.setColorFilter(
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700593 getResources().getColor(R.color.currently_set_explore_button_color),
594 Mode.SRC_IN);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800595 mWallpaperExploreButton.setVisibility(View.GONE);
596 } else {
597 exploreButton = mWallpaperExploreButton;
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700598
599 Drawable drawable = getContext().getDrawable(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700600 mWallpaperInfo.getActionIconRes(appContext)).getConstantState()
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700601 .newDrawable().mutate();
602 // Color the "compass" icon with the accent color.
603 drawable.setColorFilter(
604 getResources().getColor(R.color.accent_color), Mode.SRC_IN);
605 ButtonDrawableSetterCompat.setDrawableToButtonStart(
606 mWallpaperExploreButton, drawable);
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700607 mWallpaperExploreButton.setText(
608 mWallpaperInfo.getActionLabelRes(appContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800609 mWallpaperExploreButtonNoText.setVisibility(View.GONE);
610 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800611 exploreButton.setVisibility(View.VISIBLE);
612 exploreButton.setOnClickListener((View view) -> {
Santiago Etchebeherece5613f2018-06-01 13:22:47 -0700613 eventLogger.logActionClicked(mWallpaperInfo.getCollectionId(appContext),
614 mWallpaperInfo.getActionLabelRes(appContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800615 startActivity(exploreIntent);
616 });
617 }
618
619 if (showSkipWallpaperButton) {
620 mSkipWallpaperButton.setVisibility(View.VISIBLE);
621 mSkipWallpaperButton.setColorFilter(
622 getResources().getColor(R.color.currently_set_explore_button_color), Mode.SRC_IN);
623 mSkipWallpaperButton.setOnClickListener((View view) -> refreshDailyWallpaper());
624 }
625 } else {
626 mWallpaperExploreSection.setVisibility(View.GONE);
627 }
628 }
629 }
630
631 /**
632 * ViewHolder subclass for a metadata "card" at the beginning of the RecyclerView that shows
633 * both home screen and lock screen wallpapers.
634 */
635 private class TwoWallpapersMetadataHolder extends RecyclerView.ViewHolder
636 implements MetadataHolder {
637 private WallpaperInfo mHomeWallpaperInfo;
638 private FrameLayout mHomeWallpaperSection;
639 private ImageView mHomeWallpaperImage;
640 private LinearLayout mHomeWallpaperTopSection;
641 private TextView mHomeWallpaperPresentationMode;
642 private TextView mHomeWallpaperTitle;
643 private TextView mHomeWallpaperSubtitle1;
644 private TextView mHomeWallpaperSubtitle2;
645
646 private ImageButton mHomeWallpaperExploreButtonLegacy;
647 private ImageButton mHomeWallpaperExploreButton;
648 private ImageButton mSkipWallpaperButton;
649
650 private FrameLayout mLockWallpaperSection;
651 private WallpaperInfo mLockWallpaperInfo;
652 private ImageView mLockWallpaperImage;
653 private LinearLayout mLockWallpaperTopSection;
654 private TextView mLockWallpaperPresentationMode;
655 private TextView mLockWallpaperTitle;
656 private TextView mLockWallpaperSubtitle1;
657 private TextView mLockWallpaperSubtitle2;
658
659 private ImageButton mLockWallpaperExploreButtonLegacy;
660 private ImageButton mLockWallpaperExploreButton;
661
662 public TwoWallpapersMetadataHolder(View metadataView) {
663 super(metadataView);
664
665 // Set the min width of the metadata panel to be the screen width minus space for the
666 // 2 gutters on the sides. This ensures the RecyclerView's GridLayoutManager gives it
667 // a wide-enough initial width to fill up the width of the grid prior to the view being
668 // fully populated.
669 final Display display = getActivity().getWindowManager().getDefaultDisplay();
670 Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(display);
671 metadataView.setMinimumWidth(
672 screenSize.x - 2 * getResources().getDimensionPixelSize(R.dimen.grid_padding));
673
674 int bothWallpaperImageWidth = getBothWallpaperImageWidth();
675
676 mHomeWallpaperSection = (FrameLayout) metadataView.findViewById(R.id.home_wallpaper_section);
677 mHomeWallpaperSection.setMinimumWidth(bothWallpaperImageWidth);
678 mHomeWallpaperImage = (ImageView) metadataView.findViewById(R.id.home_wallpaper_image);
679 mHomeWallpaperTopSection =
680 (LinearLayout) metadataView.findViewById(R.id.home_wallpaper_top_section);
681 mHomeWallpaperPresentationMode =
682 (TextView) metadataView.findViewById(R.id.home_wallpaper_presentation_mode);
683 mHomeWallpaperTitle = (TextView) metadataView.findViewById(R.id.home_wallpaper_title);
684 mHomeWallpaperSubtitle1 = (TextView) metadataView.findViewById(R.id.home_wallpaper_subtitle1);
685 mHomeWallpaperSubtitle2 = (TextView) metadataView.findViewById(R.id.home_wallpaper_subtitle2);
686 mHomeWallpaperExploreButtonLegacy =
687 (ImageButton) metadataView.findViewById(R.id.home_wallpaper_explore_button_legacy);
688 mHomeWallpaperExploreButton =
689 (ImageButton) metadataView.findViewById(R.id.home_wallpaper_explore_button);
690 mSkipWallpaperButton = (ImageButton) metadataView.findViewById(R.id.skip_home_wallpaper);
691
692 mLockWallpaperSection = (FrameLayout) metadataView.findViewById(R.id.lock_wallpaper_section);
693 mLockWallpaperSection.setMinimumWidth(bothWallpaperImageWidth);
694 mLockWallpaperImage = (ImageView) metadataView.findViewById(R.id.lock_wallpaper_image);
695 mLockWallpaperTopSection =
696 (LinearLayout) metadataView.findViewById(R.id.lock_wallpaper_top_section);
697 mLockWallpaperPresentationMode =
698 (TextView) metadataView.findViewById(R.id.lock_wallpaper_presentation_mode);
699 mLockWallpaperTitle = (TextView) metadataView.findViewById(R.id.lock_wallpaper_title);
700 mLockWallpaperSubtitle1 = (TextView) metadataView.findViewById(R.id.lock_wallpaper_subtitle1);
701 mLockWallpaperSubtitle2 = (TextView) metadataView.findViewById(R.id.lock_wallpaper_subtitle2);
702 mLockWallpaperExploreButtonLegacy =
703 (ImageButton) metadataView.findViewById(R.id.lock_wallpaper_explore_button_legacy);
704 mLockWallpaperExploreButton =
705 (ImageButton) metadataView.findViewById(R.id.lock_wallpaper_explore_button);
706 }
707
708 @Override
709 public void bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper,
710 @PresentationMode int presentationMode) {
711 bindHomeWallpaper(homeWallpaper, presentationMode);
712 bindLockWallpaper(lockWallpaper);
713 }
714
715 private void bindHomeWallpaper(WallpaperInfo homeWallpaper,
716 @PresentationMode int presentationMode) {
717 final Context appContext = getActivity().getApplicationContext();
718 final UserEventLogger eventLogger =
719 InjectorProvider.getInjector().getUserEventLogger(appContext);
720
721 mHomeWallpaperInfo = homeWallpaper;
722
723 homeWallpaper.getThumbAsset(appContext).loadDrawable(
724 getActivity(), mHomeWallpaperImage, getResources().getColor(R.color.secondary_color));
725 mHomeWallpaperTopSection.setVisibility(View.VISIBLE);
726 mHomeWallpaperPresentationMode.setText(
727 AttributionFormatter.getHumanReadableWallpaperPresentationMode(
728 appContext, presentationMode));
729
730 List<String> attributions = homeWallpaper.getAttributions(appContext);
731 if (!attributions.isEmpty()) {
732 mHomeWallpaperTitle.setText(attributions.get(0));
733 }
734 if (attributions.size() > 1) {
735 mHomeWallpaperSubtitle1.setText(attributions.get(1));
736 }
737 if (attributions.size() > 2) {
738 mHomeWallpaperSubtitle2.setText(attributions.get(2));
739 }
740
741 final String homeActionUrl = homeWallpaper.getActionUrl(appContext);
742
743 ImageButton exploreButton = Flags.skipDailyWallpaperButtonEnabled
744 ? mHomeWallpaperExploreButton
745 : mHomeWallpaperExploreButtonLegacy;
746 if (homeActionUrl != null && !homeActionUrl.isEmpty()) {
747 Uri homeExploreUri = Uri.parse(homeActionUrl);
748
749 ExploreIntentChecker intentChecker =
750 InjectorProvider.getInjector().getExploreIntentChecker(appContext);
751
752 intentChecker.fetchValidActionViewIntent(
753 homeExploreUri, (@Nullable Intent exploreIntent) -> {
754 if (exploreIntent == null || getActivity() == null) {
755 return;
756 }
757
758 exploreButton.setVisibility(View.VISIBLE);
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700759 exploreButton.setImageDrawable(getContext().getDrawable(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700760 homeWallpaper.getActionIconRes(appContext)));
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700761 exploreButton.setContentDescription(getString(homeWallpaper
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700762 .getActionLabelRes(appContext)));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800763 exploreButton.setColorFilter(
764 getResources().getColor(R.color.currently_set_explore_button_color), Mode.SRC_IN);
765 exploreButton.setOnClickListener(new OnClickListener() {
766 @Override
767 public void onClick(View v) {
Santiago Etchebeherece5613f2018-06-01 13:22:47 -0700768 eventLogger.logActionClicked(
769 mHomeWallpaperInfo.getCollectionId(appContext),
770 mHomeWallpaperInfo.getActionLabelRes(appContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800771 startActivity(exploreIntent);
772 }
773 });
774 });
775 } else {
776 exploreButton.setVisibility(View.GONE);
777 }
778
779 if (Flags.skipDailyWallpaperButtonEnabled
780 && presentationMode == WallpaperPreferences.PRESENTATION_MODE_ROTATING) {
781 mSkipWallpaperButton.setVisibility(View.VISIBLE);
782 mSkipWallpaperButton.setColorFilter(
783 getResources().getColor(R.color.currently_set_explore_button_color), Mode.SRC_IN);
784 mSkipWallpaperButton.setOnClickListener(new OnClickListener() {
785 @Override
786 public void onClick(View view) {
787 refreshDailyWallpaper();
788 }
789 });
790 } else {
791 mSkipWallpaperButton.setVisibility(View.GONE);
792 }
793
794 mHomeWallpaperImage.setOnClickListener(new OnClickListener() {
795 @Override
796 public void onClick(View v) {
797 eventLogger.logCurrentWallpaperPreviewed();
798 ((TopLevelPickerActivity) getActivity()).showViewOnlyPreview(mHomeWallpaperInfo);
799 }
800 });
801 }
802
803 private void bindLockWallpaper(WallpaperInfo lockWallpaper) {
804 if (lockWallpaper == null) {
805 Log.e(TAG, "TwoWallpapersMetadataHolder bound without a lock screen wallpaper.");
806 return;
807 }
808
809 final Context appContext = getActivity().getApplicationContext();
810 final UserEventLogger eventLogger =
811 InjectorProvider.getInjector().getUserEventLogger(getActivity());
812
813 mLockWallpaperInfo = lockWallpaper;
814
815 lockWallpaper.getThumbAsset(appContext).loadDrawable(
816 getActivity(), mLockWallpaperImage, getResources().getColor(R.color.secondary_color));
817 mLockWallpaperTopSection.setVisibility(View.VISIBLE);
818
819 // Daily wallpaper rotation can't be in effect on only the lock screen, so if there's a
820 // separate lock screen, it must be presented as "Currently set".
821 mLockWallpaperPresentationMode.setText(
822 AttributionFormatter.getHumanReadableWallpaperPresentationMode(
823 appContext, WallpaperPreferences.PRESENTATION_MODE_STATIC));
824
825 List<String> lockAttributions = lockWallpaper.getAttributions(appContext);
826 if (!lockAttributions.isEmpty()) {
827 mLockWallpaperTitle.setText(lockAttributions.get(0));
828 }
829 if (lockAttributions.size() > 1) {
830 mLockWallpaperSubtitle1.setText(lockAttributions.get(1));
831 }
832 if (lockAttributions.size() > 2) {
833 mLockWallpaperSubtitle2.setText(lockAttributions.get(2));
834 }
835
836 final String lockActionUrl = lockWallpaper.getActionUrl(appContext);
837
838 ImageButton exploreButton = Flags.skipDailyWallpaperButtonEnabled
839 ? mLockWallpaperExploreButton
840 : mLockWallpaperExploreButtonLegacy;
841 if (lockActionUrl != null && !lockActionUrl.isEmpty()) {
842 Uri lockExploreUri = Uri.parse(lockActionUrl);
843
844 ExploreIntentChecker intentChecker =
845 InjectorProvider.getInjector().getExploreIntentChecker(appContext);
846 intentChecker.fetchValidActionViewIntent(
847 lockExploreUri, (@Nullable Intent exploreIntent) -> {
848 if (exploreIntent == null || getActivity() == null) {
849 return;
850 }
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700851 exploreButton.setImageDrawable(getContext().getDrawable(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700852 lockWallpaper.getActionIconRes(appContext)));
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700853 exploreButton.setContentDescription(getString(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700854 lockWallpaper.getActionLabelRes(appContext)));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800855 exploreButton.setVisibility(View.VISIBLE);
856 exploreButton.setColorFilter(
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700857 getResources().getColor(
858 R.color.currently_set_explore_button_color),
859 Mode.SRC_IN);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800860 exploreButton.setOnClickListener(new OnClickListener() {
861 @Override
862 public void onClick(View v) {
Santiago Etchebeherece5613f2018-06-01 13:22:47 -0700863 eventLogger.logActionClicked(
864 mLockWallpaperInfo.getCollectionId(appContext),
865 mLockWallpaperInfo.getActionLabelRes(appContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800866 startActivity(exploreIntent);
867 }
868 });
869 });
870 } else {
871 exploreButton.setVisibility(View.GONE);
872 }
873
874 mLockWallpaperImage.setOnClickListener(new OnClickListener() {
875 @Override
876 public void onClick(View v) {
877 eventLogger.logCurrentWallpaperPreviewed();
878 ((TopLevelPickerActivity) getActivity()).showViewOnlyPreview(mLockWallpaperInfo);
879 }
880 });
881 }
882 }
883
884 /**
885 * ViewHolder subclass for a category tile in the RecyclerView.
886 */
887 private class CategoryHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
888 private Category mCategory;
889 private RelativeLayout mTileLayout;
890 private ImageView mImageView;
891 private ImageView mOverlayIconView;
892 private TextView mTitleView;
893
894 public CategoryHolder(View itemView) {
895 super(itemView);
896 itemView.setOnClickListener(this);
897
898 mTileLayout = (RelativeLayout) itemView.findViewById(R.id.tile);
899 mImageView = (ImageView) itemView.findViewById(R.id.image);
900 mOverlayIconView = (ImageView) itemView.findViewById(R.id.overlay_icon);
901 mTitleView = (TextView) itemView.findViewById(R.id.category_title);
902
903 mTileLayout.getLayoutParams().height = mTileSizePx.y;
904 }
905
906 @Override
907 public void onClick(View view) {
908 final UserEventLogger eventLogger =
909 InjectorProvider.getInjector().getUserEventLogger(getActivity());
910 eventLogger.logCategorySelected(mCategory.getCollectionId());
911
912 if (mCategory.supportsCustomPhotos()) {
913 ((MyPhotosLauncher) getActivity()).requestCustomPhotoPicker(
914 new PermissionChangedListener() {
915 @Override
916 public void onPermissionsGranted() {
917 drawThumbnailAndOverlayIcon();
918 }
919
920 @Override
921 public void onPermissionsDenied(boolean dontAskAgain) {
922 // No-op
923 }
924 });
925 return;
926 }
927
928 ((TopLevelPickerActivity) getActivity()).show(mCategory.getCollectionId());
929 }
930
931 /**
932 * Binds the given category to this CategoryHolder.
933 */
934 public void bindCategory(Category category) {
935 mCategory = category;
936 mTitleView.setText(category.getTitle());
937 drawThumbnailAndOverlayIcon();
938 }
939
940 /**
941 * Draws the CategoryHolder's thumbnail and overlay icon.
942 */
943 public void drawThumbnailAndOverlayIcon() {
944 mOverlayIconView.setImageDrawable(mCategory.getOverlayIcon(
945 getActivity().getApplicationContext()));
946
947 // Size the overlay icon according to the category.
948 int overlayIconDimenDp = mCategory.getOverlayIconSizeDp();
949 DisplayMetrics metrics = DisplayMetricsRetriever.getInstance().getDisplayMetrics(
950 getResources(), getActivity().getWindowManager().getDefaultDisplay());
951 int overlayIconDimenPx = (int) (overlayIconDimenDp * metrics.density);
952 mOverlayIconView.getLayoutParams().width = overlayIconDimenPx;
953 mOverlayIconView.getLayoutParams().height = overlayIconDimenPx;
954
955 Asset thumbnail = mCategory.getThumbnail(getActivity().getApplicationContext());
956 if (thumbnail != null) {
957 thumbnail.loadDrawable(getActivity(), mImageView,
958 getResources().getColor(R.color.secondary_color));
959 } else {
960 // TODO(orenb): Replace this workaround for b/62584914 with a proper way of unloading the
961 // ImageView such that no incorrect image is improperly loaded upon rapid scroll.
962 Object nullObj = null;
963 Glide.with(getActivity())
964 .asDrawable()
965 .load(nullObj)
966 .into(mImageView);
967
968 mTitleView.setBackgroundColor(getResources().getColor(
969 R.color.category_title_scrim_color_no_thumbnail));
970 }
971 }
972 }
973
974 /**
975 * ViewHolder subclass for the loading indicator ("spinner") shown when categories are being
976 * fetched.
977 */
978 private class LoadingIndicatorHolder extends RecyclerView.ViewHolder {
979 public LoadingIndicatorHolder(View view) {
980 super(view);
981 ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.loading_indicator);
982 progressBar.getIndeterminateDrawable().setColorFilter(
983 getResources().getColor(R.color.accent_color), Mode.SRC_IN);
984 }
985 }
986
987 /**
988 * ViewHolder subclass for a "card" at the beginning of the RecyclerView showing the app needs the
989 * user to grant the storage permission to show the currently set wallpaper.
990 */
991 private class PermissionNeededHolder extends RecyclerView.ViewHolder {
992 private Button mAllowAccessButton;
993
994 public PermissionNeededHolder(View view) {
995 super(view);
996
997 mAllowAccessButton =
998 (Button) view.findViewById(R.id.permission_needed_allow_access_button);
999 mAllowAccessButton.setOnClickListener((View v) -> {
1000 ((TopLevelPickerActivity) getActivity()).requestExternalStoragePermission(mAdapter);
1001 });
1002
1003 // Replace explanation text with text containing the Wallpapers app name which replaces the
1004 // placeholder.
1005 String appName = getString(R.string.app_name);
1006 String explanation = getString(R.string.permission_needed_explanation, appName);
1007 TextView explanationTextView =
1008 (TextView) view.findViewById(R.id.permission_needed_explanation);
1009 explanationTextView.setText(explanation);
1010 }
1011 }
1012
1013 /**
1014 * RecyclerView Adapter subclass for the category tiles in the RecyclerView.
1015 */
1016 private class CategoryAdapter extends RecyclerView.Adapter<ViewHolder>
1017 implements PermissionChangedListener {
1018 public static final int METADATA_VIEW_SINGLE_CARD = 1;
1019 public static final int METADATA_VIEW_TWO_CARDS = 2;
1020 private static final int ITEM_VIEW_TYPE_METADATA = 1;
1021 private static final int ITEM_VIEW_TYPE_SELECT_WALLPAPER_HEADER = 2;
1022 private static final int ITEM_VIEW_TYPE_CATEGORY = 3;
1023 private static final int ITEM_VIEW_TYPE_LOADING_INDICATOR = 4;
1024 private static final int ITEM_VIEW_TYPE_PERMISSION_NEEDED = 5;
1025 private List<Category> mCategories;
1026 private int mNumMetadataCards;
1027
1028 public CategoryAdapter(List<Category> categories) {
1029 mCategories = categories;
1030 mNumMetadataCards = METADATA_VIEW_SINGLE_CARD;
1031 }
1032
1033 /**
1034 * Sets the number of metadata cards to be shown in the metadata view holder. Updates the UI
1035 * to reflect any changes in that number (e.g., a lock screen wallpaper has been set so we now
1036 * need to show two cards).
1037 */
1038 public void setNumMetadataCards(int numMetadataCards) {
1039 if (numMetadataCards != mNumMetadataCards && getItemCount() > 0) {
1040 notifyItemChanged(0);
1041 }
1042
1043 mNumMetadataCards = numMetadataCards;
1044 }
1045
1046 @Override
1047 public int getItemViewType(int position) {
1048 if (position == 0) {
1049 if (canShowCurrentWallpaper()) {
1050 return ITEM_VIEW_TYPE_METADATA;
1051 } else {
1052 return ITEM_VIEW_TYPE_PERMISSION_NEEDED;
1053 }
1054 } else if (position == 1) {
1055 return ITEM_VIEW_TYPE_SELECT_WALLPAPER_HEADER;
1056 } else if (mAwaitingCategories && position == getItemCount() - 1) {
1057 return ITEM_VIEW_TYPE_LOADING_INDICATOR;
1058 }
1059
1060 return ITEM_VIEW_TYPE_CATEGORY;
1061 }
1062
1063 @Override
1064 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1065 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1066 View view;
1067
1068 switch (viewType) {
1069 case ITEM_VIEW_TYPE_METADATA:
1070 if (mNumMetadataCards == METADATA_VIEW_SINGLE_CARD) {
1071 view = layoutInflater.inflate(
1072 R.layout.grid_item_single_metadata, parent, /* attachToRoot */ false);
1073 return new SingleWallpaperMetadataHolder(view);
1074 } else { // TWO_CARDS
1075 view = layoutInflater.inflate(
1076 R.layout.grid_item_both_metadata, parent, /* attachToRoot */ false);
1077 return new TwoWallpapersMetadataHolder(view);
1078 }
1079 case ITEM_VIEW_TYPE_SELECT_WALLPAPER_HEADER:
1080 view = layoutInflater.inflate(
1081 R.layout.grid_item_select_wallpaper_header, parent, /* attachToRoot */ false);
1082 return new SelectWallpaperHeaderHolder(view);
1083 case ITEM_VIEW_TYPE_LOADING_INDICATOR:
1084 view = layoutInflater.inflate(
1085 R.layout.grid_item_loading_indicator, parent, /* attachToRoot */ false);
1086 return new LoadingIndicatorHolder(view);
1087 case ITEM_VIEW_TYPE_CATEGORY:
1088 view = layoutInflater.inflate(
1089 R.layout.grid_item_category, parent, /* attachToRoot */ false);
1090 return new CategoryHolder(view);
1091 case ITEM_VIEW_TYPE_PERMISSION_NEEDED:
1092 view = layoutInflater.inflate(
1093 R.layout.grid_item_permission_needed, parent, /* attachToRoot */ false);
1094 return new PermissionNeededHolder(view);
1095 default:
1096 Log.e(TAG, "Unsupported viewType " + viewType + " in CategoryAdapter");
1097 return null;
1098 }
1099 }
1100
1101 @Override
1102 public void onBindViewHolder(ViewHolder holder, int position) {
1103 int viewType = getItemViewType(position);
1104
1105 switch (viewType) {
1106 case ITEM_VIEW_TYPE_METADATA:
1107 refreshCurrentWallpapers((MetadataHolder) holder, false /* forceRefresh */);
1108 break;
1109 case ITEM_VIEW_TYPE_SELECT_WALLPAPER_HEADER:
1110 // No op.
1111 break;
1112 case ITEM_VIEW_TYPE_CATEGORY:
1113 // Offset position to get category index to account for the non-category view holders.
1114 Category category = mCategories.get(position - NUM_NON_CATEGORY_VIEW_HOLDERS);
1115 ((CategoryHolder) holder).bindCategory(category);
1116 break;
1117 case ITEM_VIEW_TYPE_LOADING_INDICATOR:
1118 case ITEM_VIEW_TYPE_PERMISSION_NEEDED:
1119 // No op.
1120 break;
1121 default:
1122 Log.e(TAG, "Unsupported viewType " + viewType + " in CategoryAdapter");
1123 }
1124 }
1125
1126 @Override
1127 public int getItemCount() {
1128 // Add to size of categories to account for the metadata related views.
1129 // Add 1 more for the loading indicator if not yet done loading.
1130 int size = mCategories.size() + NUM_NON_CATEGORY_VIEW_HOLDERS;
1131 if (mAwaitingCategories) {
1132 size += 1;
1133 }
1134
1135 return size;
1136 }
1137
1138 @Override
1139 public void onPermissionsGranted() {
1140 notifyDataSetChanged();
1141 }
1142
1143 @Override
1144 public void onPermissionsDenied(boolean dontAskAgain) {
1145 if (!dontAskAgain) {
1146 return;
1147 }
1148
1149 String permissionNeededMessage =
1150 getString(R.string.permission_needed_explanation_go_to_settings);
1151 AlertDialog dialog = new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme)
1152 .setMessage(permissionNeededMessage)
1153 .setPositiveButton(android.R.string.ok, null /* onClickListener */)
1154 .setNegativeButton(
1155 R.string.settings_button_label,
1156 new DialogInterface.OnClickListener() {
1157 @Override
1158 public void onClick(DialogInterface dialogInterface, int i) {
1159 Intent appInfoIntent = new Intent();
1160 appInfoIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
1161 Uri uri = Uri.fromParts(
1162 "package", getActivity().getPackageName(), null /* fragment */);
1163 appInfoIntent.setData(uri);
1164 startActivityForResult(appInfoIntent, SETTINGS_APP_INFO_REQUEST_CODE);
1165 }
1166 })
1167 .create();
1168 dialog.show();
1169 }
1170 }
1171}