1 /*
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  */
16 package com.android.wallpaper.picker;
17 
18 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
19 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.EDIT;
20 
21 import android.app.Activity;
22 import android.app.ActivityOptions;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.res.Resources.NotFoundException;
26 import android.graphics.drawable.Drawable;
27 import android.graphics.drawable.LayerDrawable;
28 import android.graphics.drawable.RippleDrawable;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.util.Log;
32 import android.view.LayoutInflater;
33 import android.view.SurfaceView;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.view.animation.Interpolator;
37 import android.view.animation.PathInterpolator;
38 import android.widget.Button;
39 import android.widget.Toast;
40 
41 import androidx.activity.OnBackPressedCallback;
42 import androidx.annotation.CallSuper;
43 import androidx.annotation.IntDef;
44 import androidx.annotation.LayoutRes;
45 import androidx.annotation.Nullable;
46 import androidx.fragment.app.FragmentActivity;
47 import androidx.lifecycle.ViewModelProvider;
48 
49 import com.android.wallpaper.R;
50 import com.android.wallpaper.model.LiveWallpaperInfo;
51 import com.android.wallpaper.model.SetWallpaperViewModel;
52 import com.android.wallpaper.model.WallpaperInfo;
53 import com.android.wallpaper.module.Injector;
54 import com.android.wallpaper.module.InjectorProvider;
55 import com.android.wallpaper.module.LargeScreenMultiPanesChecker;
56 import com.android.wallpaper.module.UserEventLogger;
57 import com.android.wallpaper.module.WallpaperPersister.Destination;
58 import com.android.wallpaper.module.WallpaperPreferences;
59 import com.android.wallpaper.module.WallpaperSetter;
60 import com.android.wallpaper.util.FullScreenAnimation;
61 import com.android.wallpaper.util.ResourceUtils;
62 import com.android.wallpaper.widget.BottomActionBar;
63 import com.android.wallpaper.widget.BottomActionBar.BottomSheetContent;
64 import com.android.wallpaper.widget.WallpaperInfoView;
65 
66 import com.google.android.material.tabs.TabLayout;
67 
68 import java.util.Date;
69 import java.util.List;
70 import java.util.Optional;
71 
72 /**
73  * Base Fragment to display the UI for previewing an individual wallpaper
74  */
75 public abstract class PreviewFragment extends AppbarFragment implements
76         SetWallpaperDialogFragment.Listener, SetWallpaperErrorDialogFragment.Listener,
77         LoadWallpaperErrorDialogFragment.Listener {
78 
79     public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
80 
81     /**
82      * User can view wallpaper and attributions in full screen, but "Set wallpaper" button is
83      * hidden.
84      */
85     static final int MODE_VIEW_ONLY = 0;
86 
87     /**
88      * User can view wallpaper and attributions in full screen and click "Set wallpaper" to set the
89      * wallpaper with pan and crop position to the device.
90      */
91     static final int MODE_CROP_AND_SET_WALLPAPER = 1;
92 
93     /**
94      * Possible preview modes for the fragment.
95      */
96     @IntDef({
97             MODE_VIEW_ONLY,
98             MODE_CROP_AND_SET_WALLPAPER})
99     public @interface PreviewMode {
100     }
101 
102     public static final String ARG_WALLPAPER = "wallpaper";
103     public static final String ARG_PREVIEW_MODE = "preview_mode";
104     public static final String ARG_VIEW_AS_HOME = "view_as_home";
105     public static final String ARG_FULL_SCREEN = "view_full_screen";
106     public static final String ARG_TESTING_MODE_ENABLED = "testing_mode_enabled";
107 
108     /**
109      * Creates and returns new instance of {@link ImagePreviewFragment} with the provided wallpaper
110      * set as an argument.
111      */
newInstance(WallpaperInfo wallpaperInfo, @PreviewMode int mode, boolean viewAsHome, boolean viewFullScreen, boolean testingModeEnabled)112     public static PreviewFragment newInstance(WallpaperInfo wallpaperInfo, @PreviewMode int mode,
113             boolean viewAsHome, boolean viewFullScreen, boolean testingModeEnabled) {
114         Bundle args = new Bundle();
115         args.putParcelable(ARG_WALLPAPER, wallpaperInfo);
116         args.putInt(ARG_PREVIEW_MODE, mode);
117         args.putBoolean(ARG_VIEW_AS_HOME, viewAsHome);
118         args.putBoolean(ARG_FULL_SCREEN, viewFullScreen);
119         args.putBoolean(ARG_TESTING_MODE_ENABLED, testingModeEnabled);
120 
121         PreviewFragment fragment = wallpaperInfo instanceof LiveWallpaperInfo
122                 ? new LivePreviewFragment() : new ImagePreviewFragment();
123         fragment.setArguments(args);
124         return fragment;
125     }
126 
127     private static final String TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT =
128             "load_wallpaper_error_dialog";
129     private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
130             "set_wallpaper_error_dialog";
131     private static final int UNUSED_REQUEST_CODE = 1;
132     private static final String TAG = "PreviewFragment";
133 
134     /**
135      * When true, enables a test mode of operation -- in which certain UI features are disabled to
136      * allow for UI tests to run correctly. Works around issue in ProgressDialog currently where the
137      * dialog constantly keeps the UI thread alive and blocks a test forever.
138      */
139     protected boolean mTestingModeEnabled;
140 
141     protected WallpaperInfo mWallpaper;
142     protected WallpaperPreviewBitmapTransformation mPreviewBitmapTransformation;
143     protected WallpaperSetter mWallpaperSetter;
144     protected UserEventLogger mUserEventLogger;
145     protected BottomActionBar mBottomActionBar;
146     // For full screen animations.
147     protected View mRootView;
148     protected FullScreenAnimation mFullScreenAnimation;
149     @PreviewMode protected int mPreviewMode;
150     protected boolean mViewAsHome;
151     // For full screen preview in a separate Activity.
152     protected boolean mShowInFullScreen;
153 
154     protected SetWallpaperViewModel mSetWallpaperViewModel;
155     protected ViewModelProvider mViewModelProvider;
156     protected Optional<Integer> mLastSelectedTabPositionOptional = Optional.empty();
157     private OnBackPressedCallback mOnBackPressedCallback;
158 
159     /**
160      * Staged error dialog fragments that were unable to be shown when the hosting activity didn't
161      * allow committing fragment transactions.
162      */
163     private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
164     private LoadWallpaperErrorDialogFragment mStagedLoadWallpaperErrorDialogFragment;
165 
166     @Override
onCreate(Bundle savedInstanceState)167     public void onCreate(Bundle savedInstanceState) {
168         super.onCreate(savedInstanceState);
169         Context appContext = getContext().getApplicationContext();
170         Injector injector = InjectorProvider.getInjector();
171 
172         mUserEventLogger = injector.getUserEventLogger(appContext);
173         mWallpaper = getArguments().getParcelable(ARG_WALLPAPER);
174         mPreviewBitmapTransformation = new WallpaperPreviewBitmapTransformation(
175                 appContext, isRtl());
176 
177         //noinspection ResourceType
178         mPreviewMode = getArguments().getInt(ARG_PREVIEW_MODE);
179         mViewAsHome = getArguments().getBoolean(ARG_VIEW_AS_HOME);
180         mShowInFullScreen = getArguments().getBoolean(ARG_FULL_SCREEN);
181 
182         mTestingModeEnabled = getArguments().getBoolean(ARG_TESTING_MODE_ENABLED);
183         mWallpaperSetter = new WallpaperSetter(injector.getWallpaperPersister(appContext),
184                 injector.getPreferences(appContext), mUserEventLogger, mTestingModeEnabled);
185 
186         mViewModelProvider = new ViewModelProvider(requireActivity());
187         mSetWallpaperViewModel = mViewModelProvider.get(SetWallpaperViewModel.class);
188 
189         Activity activity = getActivity();
190         List<String> attributions = getAttributions(activity);
191         if (attributions.size() > 0 && attributions.get(0) != null) {
192             activity.setTitle(attributions.get(0));
193         }
194     }
195 
196     @Override
getToolbarColorId()197     protected int getToolbarColorId() {
198         return android.R.color.transparent;
199     }
200 
201     @Override
202     @CallSuper
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)203     public View onCreateView(LayoutInflater inflater, ViewGroup container,
204                              Bundle savedInstanceState) {
205         View view = inflater.inflate(getLayoutResId(), container, false);
206         setUpToolbar(view);
207 
208         mRootView = view;
209         mFullScreenAnimation = new FullScreenAnimation(view);
210 
211         getActivity().getWindow().getDecorView().setOnApplyWindowInsetsListener(
212                 (v, windowInsets) -> {
213                     v.setPadding(
214                             v.getPaddingLeft(),
215                             0,
216                             v.getPaddingRight(),
217                             0);
218 
219                     mFullScreenAnimation.setWindowInsets(windowInsets);
220                     mFullScreenAnimation.placeViews();
221 
222                     // Update preview header's padding top to align status bar height.
223                     View previewHeader = v.findViewById(R.id.preview_header);
224                     previewHeader.setPadding(previewHeader.getPaddingLeft(),
225                             mFullScreenAnimation.getStatusBarHeight(),
226                             previewHeader.getPaddingRight(), previewHeader.getPaddingBottom());
227 
228                     return windowInsets.consumeSystemWindowInsets();
229                 }
230         );
231 
232         return view;
233     }
234 
235     @Override
onBottomActionBarReady(BottomActionBar bottomActionBar)236     protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
237         super.onBottomActionBarReady(bottomActionBar);
238         mBottomActionBar = bottomActionBar;
239         if (!mShowInFullScreen) {
240             mBottomActionBar.setActionClickListener(EDIT, (view) -> {
241                 // Starts a full preview Activity when in multi-pane resolution
242                 LargeScreenMultiPanesChecker multiPanesChecker = new LargeScreenMultiPanesChecker();
243                 if (multiPanesChecker.isMultiPanesEnabled(getContext())) {
244                     showInFullScreenActivity(mWallpaper);
245                 } else {
246                     mFullScreenAnimation.startAnimation(/* toFullScreen= */ true);
247                 }
248                 mBottomActionBar.deselectAction(EDIT);
249             });
250         } else {
251             bottomActionBar.post(
252                     () -> mFullScreenAnimation.startAnimation(/* toFullScreen= */ true));
253         }
254         setFullScreenActions(mRootView.findViewById(R.id.fullscreen_buttons_container));
255 
256         if (mOnBackPressedCallback == null) {
257             mOnBackPressedCallback = new OnBackPressedCallback(true) {
258                 @Override
259                 public void handleOnBackPressed() {
260                     if (mFullScreenAnimation.isFullScreen() && !mShowInFullScreen) {
261                         mFullScreenAnimation.startAnimation(/* toFullScreen= */ false);
262                         return;
263                     }
264                     if (mBottomActionBar != null && !mBottomActionBar.isBottomSheetCollapsed()) {
265                         mBottomActionBar.collapseBottomSheetIfExpanded();
266                         return;
267                     }
268                     getActivity().finish();
269                 }
270             };
271             getActivity().getOnBackPressedDispatcher().addCallback(this, mOnBackPressedCallback);
272         }
273     }
274 
showInFullScreenActivity(WallpaperInfo wallpaperInfo)275     private void showInFullScreenActivity(WallpaperInfo wallpaperInfo) {
276         if (wallpaperInfo == null) {
277             return;
278         }
279         startActivity(FullPreviewActivity.newIntent(getActivity(), wallpaperInfo,
280                 /* viewAsHome= */ mLastSelectedTabPositionOptional.orElse(0) == 0),
281                 ActivityOptions.makeSceneTransitionAnimation(getActivity()).toBundle());
282     }
283 
setFullScreenActions(View container)284     protected void setFullScreenActions(View container) {
285         if (!mShowInFullScreen) {
286             // Update the button text for the current workspace visibility.
287             Button hideUiPreviewButton = container.findViewById(R.id.hide_ui_preview_button);
288             hideUiPreviewButton.setText(mFullScreenAnimation.getWorkspaceVisibility()
289                     ? R.string.hide_ui_preview_text
290                     : R.string.show_ui_preview_text);
291             hideUiPreviewButton.setOnClickListener(
292                     (button) -> {
293                         boolean visible = mFullScreenAnimation.getWorkspaceVisibility();
294                         // Update the button text for the next workspace visibility.
295                         ((Button) button).setText(visible
296                                 ? R.string.show_ui_preview_text
297                                 : R.string.hide_ui_preview_text);
298                         mFullScreenAnimation.setWorkspaceVisibility(!visible);
299                         button.announceForAccessibility(
300                                 visible ? getString(R.string.hint_hide_ui_preview)
301                                         : getString(R.string.hint_show_ui_preview));
302                     }
303             );
304             container.findViewById(R.id.set_as_wallpaper_button).setOnClickListener(
305                     this::onSetWallpaperClicked);
306         } else {
307             container.findViewById(R.id.hide_ui_preview_button).setVisibility(View.GONE);
308             container.findViewById(R.id.set_as_wallpaper_button).setVisibility(View.GONE);
309             setUpToolbarMenu(R.menu.fullpreview_menu);
310             setUpToolbarMenuClickListener(R.id.action_hide_ui, view -> {
311                 boolean visible = mFullScreenAnimation.getWorkspaceVisibility();
312                 mFullScreenAnimation.setWorkspaceVisibility(!visible);
313                 View hideUiView = view.findViewById(R.id.hide_ui_view);
314                 RippleDrawable ripple = (RippleDrawable) hideUiView.getBackground();
315                 LayerDrawable layerDrawable = (LayerDrawable) ripple.getDrawable(/* index= */ 0);
316                 Drawable backgroundDrawable = layerDrawable.getDrawable(/* index= */ 0);
317                 backgroundDrawable.setTint(!visible ? ResourceUtils.getColorAttr(getActivity(),
318                         com.android.internal.R.attr.colorAccentSecondary)
319                         : ResourceUtils.getColorAttr(getActivity(),
320                                 com.android.internal.R.attr.colorAccentPrimary));
321             });
322             setUpToolbarMenuClickListener(R.id.action_set_wallpaper,
323                     view -> mWallpaperSetter.requestDestination(getActivity(), getFragmentManager(),
324                             this, mWallpaper instanceof LiveWallpaperInfo));
325         }
326 
327         mFullScreenAnimation.ensureBottomActionBarIsCorrectlyLocated();
328     }
329 
getAttributions(Context context)330     protected List<String> getAttributions(Context context) {
331         return mWallpaper.getAttributions(context);
332     }
333 
334     @LayoutRes
getLayoutResId()335     protected abstract int getLayoutResId();
336 
createWorkspaceSurfaceCallback( SurfaceView workspaceSurface)337     protected WorkspaceSurfaceHolderCallback createWorkspaceSurfaceCallback(
338             SurfaceView workspaceSurface) {
339         return new WorkspaceSurfaceHolderCallback(workspaceSurface, getContext());
340     }
341 
342     @Override
onResume()343     public void onResume() {
344         super.onResume();
345 
346         WallpaperPreferences preferences =
347                 InjectorProvider.getInjector().getPreferences(getActivity());
348         preferences.setLastAppActiveTimestamp(new Date().getTime());
349 
350         // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is
351         // one that was unable to be shown earlier when this fragment's hosting activity didn't
352         // allow committing fragment transactions.
353         if (mStagedLoadWallpaperErrorDialogFragment != null) {
354             mStagedLoadWallpaperErrorDialogFragment.show(
355                     requireFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT);
356             mStagedLoadWallpaperErrorDialogFragment = null;
357         }
358         if (mStagedSetWallpaperErrorDialogFragment != null) {
359             mStagedSetWallpaperErrorDialogFragment.show(
360                     requireFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
361             mStagedSetWallpaperErrorDialogFragment = null;
362         }
363 
364         mSetWallpaperViewModel.getStatus().observe(requireActivity(), setWallpaperStatus -> {
365             switch (setWallpaperStatus) {
366                 case SUCCESS:
367                     // Give a few millis before finishing to allow for the dialog dismiss
368                     // and animations to finish
369                     Handler.getMain().postDelayed(() -> finishActivity(true), 300);
370                     break;
371                 case ERROR:
372                     showSetWallpaperErrorDialog(mSetWallpaperViewModel.getDestination());
373                     break;
374                 default:
375                     // Do nothing in this case, either status is pending, or unknown
376             }
377         });
378     }
379 
isLoaded()380     protected abstract boolean isLoaded();
381 
382     @Override
onSet(int destination)383     public void onSet(int destination) {
384         mSetWallpaperViewModel.setDestination(destination);
385         setCurrentWallpaper(destination);
386     }
387 
388     @Override
onDialogDismissed(boolean withItemSelected)389     public void onDialogDismissed(boolean withItemSelected) {
390         mBottomActionBar.deselectAction(APPLY);
391     }
392 
393     @Override
onClickTryAgain(@estination int wallpaperDestination)394     public void onClickTryAgain(@Destination int wallpaperDestination) {
395         mSetWallpaperViewModel.setDestination(wallpaperDestination);
396         setCurrentWallpaper(wallpaperDestination);
397     }
398 
399     @Override
onClickOk()400     public void onClickOk() {
401         FragmentActivity activity = getActivity();
402         if (activity != null) {
403             activity.finish();
404         }
405     }
406 
407     @Override
onDestroy()408     public void onDestroy() {
409         super.onDestroy();
410         mWallpaperSetter.cleanUp();
411     }
412 
413     @Override
getDefaultTitle()414     public CharSequence getDefaultTitle() {
415         return getContext().getString(R.string.preview);
416     }
417 
onSetWallpaperClicked(View button)418     protected void onSetWallpaperClicked(View button) {
419         mWallpaperSetter.requestDestination(getActivity(), getFragmentManager(), this,
420                 mWallpaper instanceof LiveWallpaperInfo);
421     }
422 
setUpTabs(TabLayout tabs)423     protected void setUpTabs(TabLayout tabs) {
424         tabs.addTab(tabs.newTab().setText(R.string.home_screen_message));
425         tabs.addTab(tabs.newTab().setText(R.string.lock_screen_message));
426         tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
427             @Override
428             public void onTabSelected(TabLayout.Tab tab) {
429                 mLastSelectedTabPositionOptional = Optional.of(tab.getPosition());
430                 updateScreenPreview(/* isHomeSelected= */ tab.getPosition() == 0);
431             }
432 
433             @Override
434             public void onTabUnselected(TabLayout.Tab tab) {}
435 
436             @Override
437             public void onTabReselected(TabLayout.Tab tab) {}
438         });
439 
440         // The TabLayout only contains below tabs
441         // 0. Home tab
442         // 1. Lock tab
443         int tabPosition = mLastSelectedTabPositionOptional.orElseGet(() -> mViewAsHome ? 0 : 1);
444         tabs.getTabAt(tabPosition).select();
445         updateScreenPreview(/* isHomeSelected= */ tabPosition == 0);
446     }
447 
updateScreenPreview(boolean isHomeSelected)448     protected abstract void updateScreenPreview(boolean isHomeSelected);
449 
450     /**
451      * Sets current wallpaper to the device based on current zoom and scroll state.
452      *
453      * @param destination The wallpaper destination i.e. home vs. lockscreen vs. both.
454      */
setCurrentWallpaper(@estination int destination)455     protected abstract void setCurrentWallpaper(@Destination int destination);
456 
finishActivity(boolean success)457     protected void finishActivity(boolean success) {
458         Activity activity = getActivity();
459         if (activity == null) {
460             return;
461         }
462         if (success) {
463             try {
464                 Toast.makeText(activity,
465                         R.string.wallpaper_set_successfully_message, Toast.LENGTH_SHORT).show();
466             } catch (NotFoundException e) {
467                 Log.e(TAG, "Could not show toast " + e);
468             }
469             activity.setResult(Activity.RESULT_OK);
470         }
471         activity.finish();
472         activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
473     }
474 
showSetWallpaperErrorDialog(@estination int wallpaperDestination)475     protected void showSetWallpaperErrorDialog(@Destination int wallpaperDestination) {
476         SetWallpaperErrorDialogFragment newFragment = SetWallpaperErrorDialogFragment.newInstance(
477                 R.string.set_wallpaper_error_message, wallpaperDestination);
478         newFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
479 
480         // Show 'set wallpaper' error dialog now if it's safe to commit fragment transactions,
481         // otherwise stage it for later when the hosting activity is in a state to commit fragment
482         // transactions.
483         BasePreviewActivity activity = (BasePreviewActivity) requireActivity();
484         if (activity.isSafeToCommitFragmentTransaction()) {
485             newFragment.show(requireFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
486         } else {
487             mStagedSetWallpaperErrorDialogFragment = newFragment;
488         }
489     }
490 
491     /**
492      * Shows 'load wallpaper' error dialog now or stage it to be shown when the hosting activity is
493      * in a state that allows committing fragment transactions.
494      */
showLoadWallpaperErrorDialog()495     protected void showLoadWallpaperErrorDialog() {
496         LoadWallpaperErrorDialogFragment dialogFragment =
497                 LoadWallpaperErrorDialogFragment.newInstance();
498         dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
499 
500         // Show 'load wallpaper' error dialog now or stage it to be shown when the hosting
501         // activity is in a state that allows committing fragment transactions.
502         BasePreviewActivity activity = (BasePreviewActivity) getActivity();
503         if (activity != null && activity.isSafeToCommitFragmentTransaction()) {
504             dialogFragment.show(requireFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT);
505         } else {
506             mStagedLoadWallpaperErrorDialogFragment = dialogFragment;
507         }
508     }
509 
510     /**
511      * Returns whether layout direction is RTL (or false for LTR). Since native RTL layout support
512      * was added in API 17, returns false for versions lower than 17.
513      */
isRtl()514     protected boolean isRtl() {
515         return getResources().getConfiguration().getLayoutDirection()
516                     == View.LAYOUT_DIRECTION_RTL;
517     }
518 
519     protected final class WallpaperInfoContent extends BottomSheetContent<WallpaperInfoView> {
520 
521         @Nullable private Intent mExploreIntent;
522         private CharSequence mActionLabel;
523 
WallpaperInfoContent(Context context)524         protected WallpaperInfoContent(Context context) {
525             super(context);
526         }
527 
528         @Override
getViewId()529         public int getViewId() {
530             return R.layout.wallpaper_info_view;
531         }
532 
533         @Override
onViewCreated(WallpaperInfoView view)534         public void onViewCreated(WallpaperInfoView view) {
535             if (mWallpaper == null) {
536                 return;
537             }
538 
539             if (mActionLabel == null) {
540                 setUpExploreIntentAndLabel(() -> populateWallpaperInfo(view));
541             } else {
542                 populateWallpaperInfo(view);
543             }
544         }
545 
setUpExploreIntentAndLabel(@ullable Runnable callback)546         private void setUpExploreIntentAndLabel(@Nullable Runnable callback) {
547             Context context = getContext();
548             if (context == null) {
549                 return;
550             }
551 
552             WallpaperInfoHelper.loadExploreIntent(context, mWallpaper,
553                     (actionLabel, exploreIntent) -> {
554                         mActionLabel = actionLabel;
555                         mExploreIntent = exploreIntent;
556                         if (callback != null) {
557                             callback.run();
558                         }
559                     }
560             );
561         }
562 
onExploreClicked(View button)563         private void onExploreClicked(View button) {
564             Context context = getContext();
565             if (context == null) {
566                 return;
567             }
568 
569             mUserEventLogger.logActionClicked(mWallpaper.getCollectionId(context),
570                     mWallpaper.getActionLabelRes(context));
571 
572             startActivity(mExploreIntent);
573         }
574 
populateWallpaperInfo(WallpaperInfoView view)575         private void populateWallpaperInfo(WallpaperInfoView view) {
576             view.populateWallpaperInfo(
577                     mWallpaper,
578                     mActionLabel,
579                     WallpaperInfoHelper.shouldShowExploreButton(
580                             getContext(), mExploreIntent),
581                     this::onExploreClicked);
582         }
583     }
584 }
585