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 android.view.View.MeasureSpec.EXACTLY;
19 import static android.view.View.MeasureSpec.makeMeasureSpec;
20 
21 import android.app.Activity;
22 import android.app.ProgressDialog;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.graphics.Point;
27 import android.graphics.PorterDuff.Mode;
28 import android.graphics.Rect;
29 import android.graphics.RectF;
30 import android.graphics.drawable.ColorDrawable;
31 import android.graphics.drawable.Drawable;
32 import android.net.Uri;
33 import android.os.Build.VERSION;
34 import android.os.Build.VERSION_CODES;
35 import android.os.Bundle;
36 import android.provider.Settings;
37 import android.service.wallpaper.WallpaperService;
38 import android.util.DisplayMetrics;
39 import android.util.Log;
40 import android.view.Display;
41 import android.view.LayoutInflater;
42 import android.view.SurfaceControlViewHost;
43 import android.view.SurfaceHolder;
44 import android.view.SurfaceView;
45 import android.view.View;
46 import android.view.View.OnClickListener;
47 import android.view.ViewGroup;
48 import android.view.animation.AnimationUtils;
49 import android.widget.Button;
50 import android.widget.FrameLayout;
51 import android.widget.ImageButton;
52 import android.widget.ImageView;
53 import android.widget.TextView;
54 
55 import androidx.annotation.NonNull;
56 import androidx.annotation.Nullable;
57 import androidx.appcompat.app.AlertDialog;
58 import androidx.cardview.widget.CardView;
59 import androidx.core.content.ContextCompat;
60 import androidx.recyclerview.widget.RecyclerView;
61 import androidx.viewpager.widget.PagerAdapter;
62 import androidx.viewpager.widget.ViewPager;
63 
64 import com.android.systemui.shared.system.SurfaceViewRequestUtils;
65 import com.android.wallpaper.R;
66 import com.android.wallpaper.config.Flags;
67 import com.android.wallpaper.model.Category;
68 import com.android.wallpaper.model.LiveWallpaperInfo;
69 import com.android.wallpaper.model.WallpaperInfo;
70 import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
71 import com.android.wallpaper.module.CurrentWallpaperInfoFactory.WallpaperInfoCallback;
72 import com.android.wallpaper.module.ExploreIntentChecker;
73 import com.android.wallpaper.module.InjectorProvider;
74 import com.android.wallpaper.module.LockWallpaperStatusChecker;
75 import com.android.wallpaper.module.UserEventLogger;
76 import com.android.wallpaper.module.WallpaperPreferences;
77 import com.android.wallpaper.module.WallpaperPreferences.PresentationMode;
78 import com.android.wallpaper.module.WallpaperRotationRefresher;
79 import com.android.wallpaper.module.WallpaperRotationRefresher.Listener;
80 import com.android.wallpaper.picker.CategorySelectorFragment.CategorySelectorFragmentHost;
81 import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
82 import com.android.wallpaper.picker.MyPhotosStarter.PermissionChangedListener;
83 import com.android.wallpaper.picker.individual.IndividualPickerFragment.ThumbnailUpdater;
84 import com.android.wallpaper.util.DisplayMetricsRetriever;
85 import com.android.wallpaper.util.PreviewUtils;
86 import com.android.wallpaper.util.ScreenSizeCalculator;
87 import com.android.wallpaper.util.TileSizeCalculator;
88 import com.android.wallpaper.util.WallpaperConnection;
89 import com.android.wallpaper.util.WallpaperConnection.WallpaperConnectionListener;
90 import com.android.wallpaper.widget.LiveTileOverlay;
91 import com.android.wallpaper.widget.PreviewPager;
92 
93 import com.bumptech.glide.Glide;
94 import com.bumptech.glide.MemoryCategory;
95 import com.google.android.material.bottomsheet.BottomSheetBehavior;
96 
97 import java.util.ArrayList;
98 import java.util.Date;
99 import java.util.List;
100 
101 /**
102  * Displays the Main UI for picking a category of wallpapers to choose from.
103  */
104 public class CategoryFragment extends ToolbarFragment
105         implements CategorySelectorFragmentHost, ThumbnailUpdater {
106 
107     /**
108      * Interface to be implemented by an Activity hosting a {@link CategoryFragment}
109      */
110     public interface CategoryFragmentHost extends MyPhotosStarterProvider {
111 
requestExternalStoragePermission(PermissionChangedListener listener)112         void requestExternalStoragePermission(PermissionChangedListener listener);
113 
isReadExternalStoragePermissionGranted()114         boolean isReadExternalStoragePermissionGranted();
115 
showViewOnlyPreview(WallpaperInfo wallpaperInfo)116         void showViewOnlyPreview(WallpaperInfo wallpaperInfo);
117     }
118 
newInstance(CharSequence title)119     public static CategoryFragment newInstance(CharSequence title) {
120         CategoryFragment fragment = new CategoryFragment();
121         fragment.setArguments(ToolbarFragment.createArguments(title));
122         return fragment;
123     }
124 
125     private static final String TAG = "CategoryFragment";
126     private static final int MAX_ALPHA = 255;
127 
128     // The number of ViewHolders that don't pertain to category tiles.
129     // Currently 2: one for the metadata section and one for the "Select wallpaper" header.
130     private static final int NUM_NON_CATEGORY_VIEW_HOLDERS = 0;
131 
132     private static final int SETTINGS_APP_INFO_REQUEST_CODE = 1;
133 
134     private static final String PERMISSION_READ_WALLPAPER_INTERNAL =
135             "android.permission.READ_WALLPAPER_INTERNAL";
136 
137     private ProgressDialog mRefreshWallpaperProgressDialog;
138     private boolean mTestingMode;
139     private ImageView mHomePreview;
140     private SurfaceView mWorkspaceSurface;
141     private SurfaceView mWallpaperSurface;
142     private ImageView mLockscreenPreview;
143     private PreviewPager mPreviewPager;
144     private List<View> mWallPaperPreviews;
145     private WallpaperConnection mWallpaperConnection;
146     private CategorySelectorFragment mCategorySelectorFragment;
147     private boolean mShowSelectedWallpaper;
148     private BottomSheetBehavior mBottomSheetBehavior;
149     private PreviewUtils mPreviewUtils;
150 
151     // Home workspace surface is behind the app window, and so must the home image wallpaper like
152     // the live wallpaper. This view is rendered on mWallpaperSurface for home image wallpaper.
153     private ImageView mHomeImageWallpaper;
154 
CategoryFragment()155     public CategoryFragment() {
156         mCategorySelectorFragment = new CategorySelectorFragment();
157     }
158 
159     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)160     public View onCreateView(LayoutInflater inflater, ViewGroup container,
161                              Bundle savedInstanceState) {
162         View view = inflater.inflate(
163                 ADD_SCALABLE_HEADER
164                 ? R.layout.fragment_category_scalable_picker
165                 : R.layout.fragment_category_picker, container, /* attachToRoot= */ false);
166 
167         mWallPaperPreviews = new ArrayList<>();
168         CardView homePreviewCard = (CardView) inflater.inflate(
169                 R.layout.wallpaper_preview_card, null);
170         mHomePreview = homePreviewCard.findViewById(R.id.wallpaper_preview_image);
171         mWorkspaceSurface = homePreviewCard.findViewById(R.id.workspace_surface);
172         mWallpaperSurface = homePreviewCard.findViewById(R.id.wallpaper_surface);
173         mWallPaperPreviews.add(homePreviewCard);
174 
175         if (LockWallpaperStatusChecker.isLockWallpaperSet(getContext())) {
176             CardView lockscreenPreviewCard = (CardView) inflater.inflate(
177                     R.layout.wallpaper_preview_card, null);
178             mLockscreenPreview = lockscreenPreviewCard.findViewById(R.id.wallpaper_preview_image);
179             lockscreenPreviewCard.findViewById(R.id.workspace_surface).setVisibility(View.GONE);
180             lockscreenPreviewCard.findViewById(R.id.wallpaper_surface).setVisibility(View.GONE);
181             mWallPaperPreviews.add(lockscreenPreviewCard);
182         }
183 
184         mPreviewPager = view.findViewById(R.id.wallpaper_preview_pager);
185         mPreviewPager.setAdapter(new PreviewPagerAdapter(mWallPaperPreviews));
186         mPreviewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
187             int[] mLocation = new int[2];
188             Rect mHomePreviewRect = new Rect();
189             @Override
190             public void onPageScrolled(int position, float positionOffset,
191                     int positionOffsetPixels) {
192                 if (mWallpaperConnection != null) {
193                     mHomePreview.getLocationOnScreen(mLocation);
194                     mHomePreviewRect.set(0, 0, mHomePreview.getMeasuredWidth(),
195                             mHomePreview.getMeasuredHeight());
196                     mHomePreviewRect.offset(mLocation[0], mLocation[1]);
197                     mWallpaperConnection.updatePreviewPosition(mHomePreviewRect);
198                 }
199             }
200 
201             @Override
202             public void onPageSelected(int i) {
203             }
204 
205             @Override
206             public void onPageScrollStateChanged(int i) {
207             }
208         });
209         setupCurrentWallpaperPreview(view);
210 
211         View fragmentContainer = view.findViewById(R.id.category_fragment_container);
212         mBottomSheetBehavior = BottomSheetBehavior.from(fragmentContainer);
213         fragmentContainer.addOnLayoutChangeListener((containerView, left, top, right, bottom,
214                                                      oldLeft, oldTop, oldRight, oldBottom) -> {
215             int minimumHeight = containerView.getHeight() - mPreviewPager.getMeasuredHeight();
216             mBottomSheetBehavior.setPeekHeight(minimumHeight);
217             containerView.setMinimumHeight(minimumHeight);
218             ((CardView) mHomePreview.getParent())
219                     .setRadius(TileSizeCalculator.getPreviewCornerRadius(
220                             getActivity(), homePreviewCard.getMeasuredWidth()));
221         });
222 
223         mPreviewUtils = new PreviewUtils(getContext(),
224                 getString(R.string.grid_control_metadata_name));
225 
226         setUpToolbar(view);
227 
228         getChildFragmentManager()
229                 .beginTransaction()
230                 .replace(R.id.category_fragment_container, mCategorySelectorFragment)
231                 .commitNow();
232         return view;
233     }
234 
235     @Override
onViewCreated(@onNull View view, @Nullable Bundle savedInstanceState)236     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
237         updateWallpaperSurface();
238         updateWorkspaceSurface();
239     }
240 
241     @Override
getDefaultTitle()242     public CharSequence getDefaultTitle() {
243         return getContext().getString(R.string.app_name);
244     }
245 
246     @Override
onResume()247     public void onResume() {
248         super.onResume();
249 
250         WallpaperPreferences preferences = InjectorProvider.getInjector().getPreferences(getActivity());
251         preferences.setLastAppActiveTimestamp(new Date().getTime());
252 
253         // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
254         // PreviewFragment.
255         Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
256 
257         // The wallpaper may have been set while this fragment was paused, so force refresh the current
258         // wallpapers and presentation mode.
259         if (!mShowSelectedWallpaper) {
260             refreshCurrentWallpapers(/* MetadataHolder= */ null, /* forceRefresh= */ true);
261         }
262         if (mWallpaperConnection != null) {
263             mWallpaperConnection.setVisibility(true);
264         }
265     }
266 
267     @Override
onPause()268     public void onPause() {
269         super.onPause();
270         if (mWallpaperConnection != null) {
271             mWallpaperConnection.setVisibility(false);
272         }
273     }
274 
275     @Override
onDestroyView()276     public void onDestroyView() {
277         super.onDestroyView();
278         if (mWallpaperConnection != null) {
279             mWallpaperConnection.disconnect();
280             mWallpaperConnection = null;
281         }
282     }
283 
284     @Override
onDestroy()285     public void onDestroy() {
286         super.onDestroy();
287         if (mWallpaperConnection != null) {
288             mWallpaperConnection.disconnect();
289             mWallpaperConnection = null;
290         }
291         if (mRefreshWallpaperProgressDialog != null) {
292             mRefreshWallpaperProgressDialog.dismiss();
293         }
294     }
295 
296     @Override
onActivityResult(int requestCode, int resultCode, Intent data)297     public void onActivityResult(int requestCode, int resultCode, Intent data) {
298         if (requestCode == SETTINGS_APP_INFO_REQUEST_CODE) {
299             mCategorySelectorFragment.notifyDataSetChanged();
300         }
301     }
302 
303     @Override
requestCustomPhotoPicker(PermissionChangedListener listener)304     public void requestCustomPhotoPicker(PermissionChangedListener listener) {
305         getFragmentHost().getMyPhotosStarter().requestCustomPhotoPicker(listener);
306     }
307 
308     @Override
show(String collectionId)309     public void show(String collectionId) {
310         getChildFragmentManager()
311                 .beginTransaction()
312                 .replace(R.id.category_fragment_container,
313                         InjectorProvider.getInjector().getIndividualPickerFragment(collectionId))
314                 .addToBackStack(null)
315                 .commit();
316         getChildFragmentManager().executePendingTransactions();
317     }
318 
319     @Override
updateThumbnail(WallpaperInfo wallpaperInfo)320     public void updateThumbnail(WallpaperInfo wallpaperInfo) {
321         new android.os.Handler().post(() -> {
322             // A config change may have destroyed the activity since the refresh started, so check
323             // for that.
324             if (getActivity() == null) {
325                 return;
326             }
327 
328             updateThumbnail(wallpaperInfo, mHomePreview, true);
329             updateThumbnail(wallpaperInfo, mLockscreenPreview, false);
330             mShowSelectedWallpaper = true;
331             mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
332         });
333     }
334 
335     @Override
restoreThumbnails()336     public void restoreThumbnails() {
337         refreshCurrentWallpapers(/* MetadataHolder= */ null, /* forceRefresh= */ true);
338         mShowSelectedWallpaper = false;
339     }
340 
341     /**
342      * Pops the child fragment from the stack if {@link CategoryFragment} is visible to the users.
343      *
344      * @return {@code true} if the child fragment is popped, {@code false} otherwise.
345      */
popChildFragment()346     public boolean popChildFragment() {
347         return isVisible() && getChildFragmentManager().popBackStackImmediate();
348     }
349 
350     /**
351      * Inserts the given category into the categories list in priority order.
352      */
addCategory(Category category, boolean loading)353     void addCategory(Category category, boolean loading) {
354         mCategorySelectorFragment.addCategory(category, loading);
355     }
356 
removeCategory(Category category)357     void removeCategory(Category category) {
358         mCategorySelectorFragment.removeCategory(category);
359     }
360 
updateCategory(Category category)361     void updateCategory(Category category) {
362         mCategorySelectorFragment.updateCategory(category);
363     }
364 
clearCategories()365     void clearCategories() {
366         mCategorySelectorFragment.clearCategories();
367     }
368 
369     /**
370      * Notifies the CategoryFragment that no further categories are expected so it may hide
371      * the loading indicator.
372      */
doneFetchingCategories()373     void doneFetchingCategories() {
374         mCategorySelectorFragment.doneFetchingCategories();
375     }
376 
377     /**
378      * Enable a test mode of operation -- in which certain UI features are disabled to allow for
379      * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
380      * constantly keeps the UI thread alive and blocks a test forever.
381      */
setTestingMode(boolean testingMode)382     void setTestingMode(boolean testingMode) {
383         mTestingMode = testingMode;
384     }
385 
canShowCurrentWallpaper()386     private boolean canShowCurrentWallpaper() {
387         Activity activity = getActivity();
388         CategoryFragmentHost host = getFragmentHost();
389         PackageManager packageManager = activity.getPackageManager();
390         String packageName = activity.getPackageName();
391 
392         boolean hasReadWallpaperInternal = packageManager.checkPermission(
393                 PERMISSION_READ_WALLPAPER_INTERNAL, packageName) == PackageManager.PERMISSION_GRANTED;
394         return hasReadWallpaperInternal || host.isReadExternalStoragePermissionGranted();
395     }
396 
showCurrentWallpaper(View rootView, boolean show)397     private void showCurrentWallpaper(View rootView, boolean show) {
398         rootView.findViewById(R.id.wallpaper_preview_pager)
399                 .setVisibility(show ? View.VISIBLE : View.GONE);
400         rootView.findViewById(R.id.permission_needed)
401                 .setVisibility(show ? View.GONE : View.VISIBLE);
402     }
403 
setupCurrentWallpaperPreview(View rootView)404     private void setupCurrentWallpaperPreview(View rootView) {
405         if (canShowCurrentWallpaper()) {
406             showCurrentWallpaper(rootView, true);
407         } else {
408             showCurrentWallpaper(rootView, false);
409 
410             Button mAllowAccessButton = rootView
411                     .findViewById(R.id.permission_needed_allow_access_button);
412             mAllowAccessButton.setOnClickListener(view ->
413                     getFragmentHost().requestExternalStoragePermission(
414                             new PermissionChangedListener() {
415 
416                                 @Override
417                                 public void onPermissionsGranted() {
418                                     showCurrentWallpaper(rootView, true);
419                                     mCategorySelectorFragment.notifyDataSetChanged();
420                                 }
421 
422                                 @Override
423                                 public void onPermissionsDenied(boolean dontAskAgain) {
424                                     if (!dontAskAgain) {
425                                         return;
426                                     }
427                                     showPermissionNeededDialog();
428                                 }
429                             })
430             );
431 
432             // Replace explanation text with text containing the Wallpapers app name which replaces
433             // the placeholder.
434             String appName = getString(R.string.app_name);
435             String explanation = getString(R.string.permission_needed_explanation, appName);
436             TextView explanationView = rootView.findViewById(R.id.permission_needed_explanation);
437             explanationView.setText(explanation);
438         }
439     }
440 
showPermissionNeededDialog()441     private void showPermissionNeededDialog() {
442         String permissionNeededMessage = getString(
443                 R.string.permission_needed_explanation_go_to_settings);
444         AlertDialog dialog = new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme)
445                 .setMessage(permissionNeededMessage)
446                 .setPositiveButton(android.R.string.ok, /* onClickListener= */ null)
447                 .setNegativeButton(
448                         R.string.settings_button_label,
449                         (dialogInterface, i) -> {
450                             Intent appInfoIntent = new Intent();
451                             appInfoIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
452                             Uri uri = Uri.fromParts("package",
453                                     getActivity().getPackageName(), /* fragment= */ null);
454                             appInfoIntent.setData(uri);
455                             startActivityForResult(appInfoIntent, SETTINGS_APP_INFO_REQUEST_CODE);
456                         })
457                 .create();
458         dialog.show();
459     }
460 
getFragmentHost()461     private CategoryFragmentHost getFragmentHost() {
462         return (CategoryFragmentHost) getActivity();
463     }
464 
getWallpaperIntent(android.app.WallpaperInfo info)465     private Intent getWallpaperIntent(android.app.WallpaperInfo info) {
466         return new Intent(WallpaperService.SERVICE_INTERFACE)
467                 .setClassName(info.getPackageName(), info.getServiceName());
468     }
469     /**
470      * Obtains the {@link WallpaperInfo} object(s) representing the wallpaper(s) currently set to the
471      * device from the {@link CurrentWallpaperInfoFactory} and binds them to the provided
472      * {@link MetadataHolder}.
473      */
refreshCurrentWallpapers(@ullable final MetadataHolder holder, boolean forceRefresh)474     private void refreshCurrentWallpapers(@Nullable final MetadataHolder holder,
475                                           boolean forceRefresh) {
476         CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector()
477                 .getCurrentWallpaperFactory(getActivity().getApplicationContext());
478 
479         factory.createCurrentWallpaperInfos(new WallpaperInfoCallback() {
480             @Override
481             public void onWallpaperInfoCreated(
482                     final WallpaperInfo homeWallpaper,
483                     @Nullable final WallpaperInfo lockWallpaper,
484                     @PresentationMode final int presentationMode) {
485 
486                 // Update the metadata displayed on screen. Do this in a Handler so it is scheduled at the
487                 // end of the message queue. This is necessary to ensure we do not remove or add data from
488                 // the adapter while the layout is being computed. RecyclerView documentation therefore
489                 // recommends performing such changes in a Handler.
490                 new android.os.Handler().post(new Runnable() {
491                     @Override
492                     public void run() {
493                         final Activity activity = getActivity();
494                         // A config change may have destroyed the activity since the refresh
495                         // started, so check for that.
496                         if (activity == null) {
497                             return;
498                         }
499 
500                         updateThumbnail(homeWallpaper, mHomePreview, true);
501                         updateThumbnail(lockWallpaper, mLockscreenPreview, false);
502 
503                         // The MetadataHolder may be null if the RecyclerView has not yet created the view
504                         // holder.
505                         if (holder != null) {
506                             holder.bindWallpapers(homeWallpaper, lockWallpaper, presentationMode);
507                         }
508                     }
509                 });
510             }
511         }, forceRefresh);
512     }
513 
setUpLiveWallpaperPreview(WallpaperInfo homeWallpaper, ImageView previewView, Drawable thumbnail)514     private void setUpLiveWallpaperPreview(WallpaperInfo homeWallpaper, ImageView previewView,
515             Drawable thumbnail) {
516         Activity activity = getActivity();
517         if (activity == null) {
518             return;
519         }
520         if (mWallpaperConnection != null) {
521             mWallpaperConnection.disconnect();
522         }
523         if (thumbnail != null) {
524             thumbnail.setBounds(previewView.getLeft(), previewView.getTop(), previewView.getRight(),
525                     previewView.getBottom());
526         }
527 
528         Rect previewLocalRect = new Rect();
529         Rect previewGlobalRect = new Rect();
530         previewView.getLocalVisibleRect(previewLocalRect);
531         previewView.getGlobalVisibleRect(previewGlobalRect);
532         mWallpaperConnection = new WallpaperConnection(
533                 getWallpaperIntent(homeWallpaper.getWallpaperComponent()), activity,
534                 new WallpaperConnectionListener() {
535                     @Override
536                     public void onEngineShown() {
537                         final Drawable placeholder = previewView.getDrawable() == null
538                                 ? new ColorDrawable(getResources().getColor(R.color.secondary_color,
539                                     activity.getTheme()))
540                                 : previewView.getDrawable();
541                         LiveTileOverlay.INSTANCE.setForegroundDrawable(placeholder);
542                         LiveTileOverlay.INSTANCE.attach(previewView.getOverlay());
543                         previewView.animate()
544                                 .setStartDelay(400)
545                                 .setDuration(400)
546                                 .setInterpolator(AnimationUtils.loadInterpolator(getContext(),
547                                         android.R.interpolator.fast_out_linear_in))
548                                 .setUpdateListener(value -> placeholder.setAlpha(
549                                         (int) (MAX_ALPHA * (1 - value.getAnimatedFraction()))))
550                                 .withEndAction(() -> {
551                                     LiveTileOverlay.INSTANCE.setForegroundDrawable(null);
552 
553                                 }).start();
554 
555                     }
556                 }, previewGlobalRect);
557 
558         LiveTileOverlay.INSTANCE.update(new RectF(previewLocalRect),
559                 ((CardView) previewView.getParent()).getRadius());
560 
561         mWallpaperConnection.setVisibility(true);
562         previewView.post(() -> {
563             if (!mWallpaperConnection.connect()) {
564                 mWallpaperConnection = null;
565                 LiveTileOverlay.INSTANCE.detach(previewView.getOverlay());
566             }
567         });
568     }
569 
570     /**
571      * Returns the width to use for the home screen wallpaper in the "single metadata" configuration.
572      */
getSingleWallpaperImageWidth()573     private int getSingleWallpaperImageWidth() {
574         Point screenSize = ScreenSizeCalculator.getInstance()
575                 .getScreenSize(getActivity().getWindowManager().getDefaultDisplay());
576 
577         int height = getResources().getDimensionPixelSize(R.dimen.single_metadata_card_layout_height);
578         return height * screenSize.x / screenSize.y;
579     }
580 
581     /**
582      * Refreshes the current wallpaper in a daily wallpaper rotation.
583      */
refreshDailyWallpaper()584     private void refreshDailyWallpaper() {
585         // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
586         // causes Espresso to hang once the dialog is shown.
587         if (!mTestingMode) {
588             int themeResId;
589             if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
590                 themeResId = R.style.ProgressDialogThemePreL;
591             } else {
592                 themeResId = R.style.LightDialogTheme;
593             }
594             mRefreshWallpaperProgressDialog = new ProgressDialog(getActivity(), themeResId);
595             mRefreshWallpaperProgressDialog.setTitle(null);
596             mRefreshWallpaperProgressDialog.setMessage(
597                     getResources().getString(R.string.refreshing_daily_wallpaper_dialog_message));
598             mRefreshWallpaperProgressDialog.setIndeterminate(true);
599             mRefreshWallpaperProgressDialog.setCancelable(false);
600             mRefreshWallpaperProgressDialog.show();
601         }
602 
603         WallpaperRotationRefresher wallpaperRotationRefresher =
604                 InjectorProvider.getInjector().getWallpaperRotationRefresher();
605         wallpaperRotationRefresher.refreshWallpaper(getContext(), new Listener() {
606             @Override
607             public void onRefreshed() {
608                 // If the fragment is detached from the activity there's nothing to do here and the UI will
609                 // update when the fragment is resumed.
610                 if (getActivity() == null) {
611                     return;
612                 }
613 
614                 if (mRefreshWallpaperProgressDialog != null) {
615                     mRefreshWallpaperProgressDialog.dismiss();
616                 }
617             }
618 
619             @Override
620             public void onError() {
621                 if (getActivity() == null) {
622                     return;
623                 }
624 
625                 if (mRefreshWallpaperProgressDialog != null) {
626                     mRefreshWallpaperProgressDialog.dismiss();
627                 }
628 
629                 AlertDialog errorDialog = new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme)
630                         .setMessage(R.string.refresh_daily_wallpaper_failed_message)
631                         .setPositiveButton(android.R.string.ok, null /* onClickListener */)
632                         .create();
633                 errorDialog.show();
634             }
635         });
636     }
637 
638     /**
639      * Returns the width to use for the home and lock screen wallpapers in the "both metadata"
640      * configuration.
641      */
getBothWallpaperImageWidth()642     private int getBothWallpaperImageWidth() {
643         DisplayMetrics metrics = DisplayMetricsRetriever.getInstance().getDisplayMetrics(getResources(),
644                 getActivity().getWindowManager().getDefaultDisplay());
645 
646         // In the "both metadata" configuration, wallpaper images minus the gutters account for the full
647         // width of the device's screen.
648         return metrics.widthPixels - (3 * getResources().getDimensionPixelSize(R.dimen.grid_padding));
649     }
650 
updateThumbnail(WallpaperInfo wallpaperInfo, ImageView thumbnailView, boolean isHomeWallpaper)651     private void updateThumbnail(WallpaperInfo wallpaperInfo, ImageView thumbnailView,
652                                  boolean isHomeWallpaper) {
653         if (wallpaperInfo == null) {
654             return;
655         }
656 
657         if (thumbnailView == null) {
658             return;
659         }
660 
661         Activity activity = getActivity();
662         if (activity == null) {
663             return;
664         }
665 
666         UserEventLogger eventLogger = InjectorProvider.getInjector().getUserEventLogger(activity);
667 
668         boolean renderInImageWallpaperSurface =
669                 !(wallpaperInfo instanceof LiveWallpaperInfo) && isHomeWallpaper;
670         wallpaperInfo.getThumbAsset(activity.getApplicationContext())
671                 .loadDrawable(activity,
672                         renderInImageWallpaperSurface ? mHomeImageWallpaper : thumbnailView,
673                         getResources().getColor(R.color.secondary_color));
674         if (isHomeWallpaper) {
675             LiveTileOverlay.INSTANCE.detach(thumbnailView.getOverlay());
676             if (wallpaperInfo instanceof LiveWallpaperInfo) {
677                 setUpLiveWallpaperPreview(wallpaperInfo, thumbnailView,
678                         new ColorDrawable(getResources().getColor(
679                                 R.color.secondary_color, activity.getTheme())));
680             } else {
681                 if (mWallpaperConnection != null) {
682                     mWallpaperConnection.disconnect();
683                     mWallpaperConnection = null;
684                 }
685             }
686         }
687         thumbnailView.setOnClickListener(view -> {
688             getFragmentHost().showViewOnlyPreview(wallpaperInfo);
689             eventLogger.logCurrentWallpaperPreviewed();
690         });
691     }
692 
updateWallpaperSurface()693     private void updateWallpaperSurface() {
694         mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback);
695     }
696 
updateWorkspaceSurface()697     private void updateWorkspaceSurface() {
698         mWorkspaceSurface.setZOrderMediaOverlay(true);
699         mWorkspaceSurface.getHolder().addCallback(mWorkspaceSurfaceCallback);
700     }
701 
702     private final SurfaceHolder.Callback mWallpaperSurfaceCallback = new SurfaceHolder.Callback() {
703 
704         @Override
705         public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
706             if (mHomeImageWallpaper == null) {
707                 mHomeImageWallpaper = new ImageView(getContext());
708                 mHomeImageWallpaper.setBackgroundColor(
709                         ContextCompat.getColor(getContext(), R.color.primary_color));
710                 mHomeImageWallpaper.measure(makeMeasureSpec(mHomePreview.getWidth(), EXACTLY),
711                         makeMeasureSpec(mHomePreview.getHeight(), EXACTLY));
712                 mHomeImageWallpaper.layout(0, 0, mHomePreview.getWidth(), mHomePreview.getHeight());
713 
714                 SurfaceControlViewHost host = new SurfaceControlViewHost(getContext(),
715                         getContext().getDisplay(), mWallpaperSurface.getHostToken());
716                 host.setView(mHomeImageWallpaper, mHomeImageWallpaper.getWidth(),
717                         mHomeImageWallpaper.getHeight());
718                 mWallpaperSurface.setChildSurfacePackage(host.getSurfacePackage());
719             }
720         }
721 
722         @Override
723         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
724         }
725 
726         @Override
727         public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
728         }
729     };
730 
731     private final SurfaceHolder.Callback mWorkspaceSurfaceCallback = new SurfaceHolder.Callback() {
732         @Override
733         public void surfaceCreated(SurfaceHolder holder) {
734             Bundle bundle = SurfaceViewRequestUtils.createSurfaceBundle(mWorkspaceSurface);
735             if (mPreviewUtils.supportsPreview()) {
736                 mPreviewUtils.renderPreview(bundle);
737             }
738         }
739 
740         @Override
741         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }
742 
743         @Override
744         public void surfaceDestroyed(SurfaceHolder holder) { }
745     };
746 
747     private interface MetadataHolder {
748         /**
749          * Binds {@link WallpaperInfo} objects representing the currently-set wallpapers to the
750          * ViewHolder layout.
751          */
bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper, @PresentationMode int presentationMode)752         void bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper,
753                             @PresentationMode int presentationMode);
754     }
755 
756     private static class SelectWallpaperHeaderHolder extends RecyclerView.ViewHolder {
SelectWallpaperHeaderHolder(View headerView)757         public SelectWallpaperHeaderHolder(View headerView) {
758             super(headerView);
759         }
760     }
761 
762     /**
763      * ViewHolder subclass for a metadata "card" at the beginning of the RecyclerView.
764      */
765     private class SingleWallpaperMetadataHolder extends RecyclerView.ViewHolder
766             implements MetadataHolder {
767         private WallpaperInfo mWallpaperInfo;
768         private ImageView mWallpaperImage;
769         private TextView mWallpaperPresentationModeSubtitle;
770         private TextView mWallpaperTitle;
771         private TextView mWallpaperSubtitle;
772         private TextView mWallpaperSubtitle2;
773         private ImageButton mWallpaperExploreButtonNoText;
774         private ImageButton mSkipWallpaperButton;
775 
SingleWallpaperMetadataHolder(View metadataView)776         public SingleWallpaperMetadataHolder(View metadataView) {
777             super(metadataView);
778 
779             mWallpaperImage = metadataView.findViewById(R.id.wallpaper_image);
780             mWallpaperImage.getLayoutParams().width = getSingleWallpaperImageWidth();
781 
782             mWallpaperPresentationModeSubtitle =
783                     metadataView.findViewById(R.id.wallpaper_presentation_mode_subtitle);
784             mWallpaperTitle = metadataView.findViewById(R.id.wallpaper_title);
785             mWallpaperSubtitle = metadataView.findViewById(R.id.wallpaper_subtitle);
786             mWallpaperSubtitle2 = metadataView.findViewById(R.id.wallpaper_subtitle2);
787 
788             mWallpaperExploreButtonNoText =
789                     metadataView.findViewById(R.id.wallpaper_explore_button_notext);
790 
791             mSkipWallpaperButton = metadataView.findViewById(R.id.skip_wallpaper_button);
792         }
793 
794         /**
795          * Binds home screen wallpaper to the ViewHolder layout.
796          */
797         @Override
bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper, @PresentationMode int presentationMode)798         public void bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper,
799                 @PresentationMode int presentationMode) {
800             mWallpaperInfo = homeWallpaper;
801 
802             bindWallpaperAsset();
803             bindWallpaperText(presentationMode);
804             bindWallpaperActionButtons(presentationMode);
805         }
806 
bindWallpaperAsset()807         private void bindWallpaperAsset() {
808             final UserEventLogger eventLogger =
809                     InjectorProvider.getInjector().getUserEventLogger(getActivity());
810 
811             mWallpaperInfo.getThumbAsset(getActivity().getApplicationContext()).loadDrawable(
812                     getActivity(), mWallpaperImage, getResources().getColor(R.color.secondary_color));
813 
814             mWallpaperImage.setOnClickListener(new OnClickListener() {
815                 @Override
816                 public void onClick(View v) {
817                     getFragmentHost().showViewOnlyPreview(mWallpaperInfo);
818                     eventLogger.logCurrentWallpaperPreviewed();
819                 }
820             });
821         }
822 
bindWallpaperText(@resentationMode int presentationMode)823         private void bindWallpaperText(@PresentationMode int presentationMode) {
824             Context appContext = getActivity().getApplicationContext();
825 
826             mWallpaperPresentationModeSubtitle.setText(
827                     AttributionFormatter.getHumanReadableWallpaperPresentationMode(
828                             appContext, presentationMode));
829 
830             List<String> attributions = mWallpaperInfo.getAttributions(appContext);
831             if (!attributions.isEmpty()) {
832                 mWallpaperTitle.setText(attributions.get(0));
833             }
834             if (attributions.size() > 1) {
835                 mWallpaperSubtitle.setText(attributions.get(1));
836             } else {
837                 mWallpaperSubtitle.setVisibility(View.INVISIBLE);
838             }
839             if (attributions.size() > 2) {
840                 mWallpaperSubtitle2.setText(attributions.get(2));
841             } else {
842                 mWallpaperSubtitle2.setVisibility(View.INVISIBLE);
843             }
844         }
845 
bindWallpaperActionButtons(@resentationMode int presentationMode)846         private void bindWallpaperActionButtons(@PresentationMode int presentationMode) {
847             final Context appContext = getActivity().getApplicationContext();
848 
849             final String actionUrl = mWallpaperInfo.getActionUrl(appContext);
850             if (actionUrl != null && !actionUrl.isEmpty()) {
851 
852                 Uri exploreUri = Uri.parse(actionUrl);
853 
854                 ExploreIntentChecker intentChecker =
855                         InjectorProvider.getInjector().getExploreIntentChecker(appContext);
856                 intentChecker.fetchValidActionViewIntent(exploreUri, (@Nullable Intent exploreIntent) -> {
857                     if (getActivity() == null) {
858                         return;
859                     }
860 
861                     updateExploreSectionVisibility(presentationMode, exploreIntent);
862                 });
863             } else {
864                 updateExploreSectionVisibility(presentationMode, null /* exploreIntent */);
865             }
866         }
867 
868         /**
869          * Shows or hides appropriate elements in the "Explore section" (containing the Explore button
870          * and the Next Wallpaper button) depending on the current wallpaper.
871          *
872          * @param presentationMode The presentation mode of the current wallpaper.
873          * @param exploreIntent    An optional explore intent for the current wallpaper.
874          */
updateExploreSectionVisibility( @resentationMode int presentationMode, @Nullable Intent exploreIntent)875         private void updateExploreSectionVisibility(
876                 @PresentationMode int presentationMode, @Nullable Intent exploreIntent) {
877 
878             final Context appContext = getActivity().getApplicationContext();
879             final UserEventLogger eventLogger =
880                     InjectorProvider.getInjector().getUserEventLogger(appContext);
881 
882             boolean showSkipWallpaperButton = Flags.skipDailyWallpaperButtonEnabled
883                     && presentationMode == WallpaperPreferences.PRESENTATION_MODE_ROTATING;
884 
885             if (exploreIntent != null) {
886                 mWallpaperExploreButtonNoText.setImageDrawable(getContext().getDrawable(
887                                 mWallpaperInfo.getActionIconRes(appContext)));
888                 mWallpaperExploreButtonNoText.setContentDescription(
889                                 getString(mWallpaperInfo.getActionLabelRes(appContext)));
890                 mWallpaperExploreButtonNoText.setColorFilter(
891                                 getResources().getColor(R.color.currently_set_explore_button_color,
892                                         getContext().getTheme()),
893                                 Mode.SRC_IN);
894                 mWallpaperExploreButtonNoText.setVisibility(View.VISIBLE);
895                 mWallpaperExploreButtonNoText.setOnClickListener((View view) -> {
896                     eventLogger.logActionClicked(mWallpaperInfo.getCollectionId(appContext),
897                            mWallpaperInfo.getActionLabelRes(appContext));
898                     startActivity(exploreIntent);
899                 });
900             }
901 
902             if (showSkipWallpaperButton) {
903                 mSkipWallpaperButton.setVisibility(View.VISIBLE);
904                 mSkipWallpaperButton.setOnClickListener((View view) -> refreshDailyWallpaper());
905             }
906         }
907     }
908 
909     /**
910      * ViewHolder subclass for a metadata "card" at the beginning of the RecyclerView that shows
911      * both home screen and lock screen wallpapers.
912      */
913     private class TwoWallpapersMetadataHolder extends RecyclerView.ViewHolder
914             implements MetadataHolder {
915         private WallpaperInfo mHomeWallpaperInfo;
916         private ImageView mHomeWallpaperImage;
917         private TextView mHomeWallpaperPresentationMode;
918         private TextView mHomeWallpaperTitle;
919         private TextView mHomeWallpaperSubtitle1;
920         private TextView mHomeWallpaperSubtitle2;
921 
922         private ImageButton mHomeWallpaperExploreButton;
923         private ImageButton mSkipWallpaperButton;
924         private ViewGroup mHomeWallpaperPresentationSection;
925 
926         private WallpaperInfo mLockWallpaperInfo;
927         private ImageView mLockWallpaperImage;
928         private TextView mLockWallpaperTitle;
929         private TextView mLockWallpaperSubtitle1;
930         private TextView mLockWallpaperSubtitle2;
931 
932         private ImageButton mLockWallpaperExploreButton;
933 
TwoWallpapersMetadataHolder(View metadataView)934         public TwoWallpapersMetadataHolder(View metadataView) {
935             super(metadataView);
936 
937             // Set the min width of the metadata panel to be the screen width minus space for the
938             // 2 gutters on the sides. This ensures the RecyclerView's GridLayoutManager gives it
939             // a wide-enough initial width to fill up the width of the grid prior to the view being
940             // fully populated.
941             final Display display = getActivity().getWindowManager().getDefaultDisplay();
942             Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(display);
943             metadataView.setMinimumWidth(
944                     screenSize.x - 2 * getResources().getDimensionPixelSize(R.dimen.grid_padding));
945 
946             int bothWallpaperImageWidth = getBothWallpaperImageWidth();
947 
948             FrameLayout homeWallpaperSection = metadataView.findViewById(
949                     R.id.home_wallpaper_section);
950             homeWallpaperSection.setMinimumWidth(bothWallpaperImageWidth);
951             mHomeWallpaperImage = metadataView.findViewById(R.id.home_wallpaper_image);
952 
953             mHomeWallpaperPresentationMode =
954                     metadataView.findViewById(R.id.home_wallpaper_presentation_mode);
955             mHomeWallpaperTitle = metadataView.findViewById(R.id.home_wallpaper_title);
956             mHomeWallpaperSubtitle1 = metadataView.findViewById(R.id.home_wallpaper_subtitle1);
957             mHomeWallpaperSubtitle2 = metadataView.findViewById(R.id.home_wallpaper_subtitle2);
958             mHomeWallpaperPresentationSection = metadataView.findViewById(
959                     R.id.home_wallpaper_presentation_section);
960             mHomeWallpaperExploreButton =
961                     metadataView.findViewById(R.id.home_wallpaper_explore_button);
962             mSkipWallpaperButton = metadataView.findViewById(R.id.skip_home_wallpaper);
963 
964             FrameLayout lockWallpaperSection = metadataView.findViewById(
965                     R.id.lock_wallpaper_section);
966             lockWallpaperSection.setMinimumWidth(bothWallpaperImageWidth);
967             mLockWallpaperImage = metadataView.findViewById(R.id.lock_wallpaper_image);
968 
969             mLockWallpaperTitle = metadataView.findViewById(R.id.lock_wallpaper_title);
970             mLockWallpaperSubtitle1 = metadataView.findViewById(R.id.lock_wallpaper_subtitle1);
971             mLockWallpaperSubtitle2 = metadataView.findViewById(R.id.lock_wallpaper_subtitle2);
972             mLockWallpaperExploreButton =
973                     metadataView.findViewById(R.id.lock_wallpaper_explore_button);
974         }
975 
976         @Override
bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper, @PresentationMode int presentationMode)977         public void bindWallpapers(WallpaperInfo homeWallpaper, WallpaperInfo lockWallpaper,
978                 @PresentationMode int presentationMode) {
979             bindHomeWallpaper(homeWallpaper, presentationMode);
980             bindLockWallpaper(lockWallpaper);
981         }
982 
bindHomeWallpaper(WallpaperInfo homeWallpaper, @PresentationMode int presentationMode)983         private void bindHomeWallpaper(WallpaperInfo homeWallpaper,
984                                        @PresentationMode int presentationMode) {
985             final Context appContext = getActivity().getApplicationContext();
986             final UserEventLogger eventLogger =
987                     InjectorProvider.getInjector().getUserEventLogger(appContext);
988 
989             mHomeWallpaperInfo = homeWallpaper;
990 
991             homeWallpaper.getThumbAsset(appContext).loadDrawable(
992                     getActivity(), mHomeWallpaperImage,
993                     getResources().getColor(R.color.secondary_color, getContext().getTheme()));
994 
995             mHomeWallpaperPresentationMode.setText(
996                     AttributionFormatter.getHumanReadableWallpaperPresentationMode(
997                             appContext, presentationMode));
998 
999             List<String> attributions = homeWallpaper.getAttributions(appContext);
1000             if (!attributions.isEmpty()) {
1001                 mHomeWallpaperTitle.setText(attributions.get(0));
1002             }
1003             if (attributions.size() > 1) {
1004                 mHomeWallpaperSubtitle1.setText(attributions.get(1));
1005             }
1006             if (attributions.size() > 2) {
1007                 mHomeWallpaperSubtitle2.setText(attributions.get(2));
1008             }
1009 
1010             final String homeActionUrl = homeWallpaper.getActionUrl(appContext);
1011 
1012             if (homeActionUrl != null && !homeActionUrl.isEmpty()) {
1013                 Uri homeExploreUri = Uri.parse(homeActionUrl);
1014 
1015                 ExploreIntentChecker intentChecker =
1016                         InjectorProvider.getInjector().getExploreIntentChecker(appContext);
1017 
1018                 intentChecker.fetchValidActionViewIntent(
1019                     homeExploreUri, (@Nullable Intent exploreIntent) -> {
1020                         if (exploreIntent == null || getActivity() == null) {
1021                             return;
1022                         }
1023 
1024                         mHomeWallpaperExploreButton.setVisibility(View.VISIBLE);
1025                         mHomeWallpaperExploreButton.setImageDrawable(getContext().getDrawable(
1026                                 homeWallpaper.getActionIconRes(appContext)));
1027                         mHomeWallpaperExploreButton.setContentDescription(getString(homeWallpaper
1028                                 .getActionLabelRes(appContext)));
1029                         mHomeWallpaperExploreButton.setColorFilter(
1030                                 getResources().getColor(R.color.currently_set_explore_button_color,
1031                                         getContext().getTheme()),
1032                                 Mode.SRC_IN);
1033                         mHomeWallpaperExploreButton.setOnClickListener(v -> {
1034                             eventLogger.logActionClicked(
1035                                     mHomeWallpaperInfo.getCollectionId(appContext),
1036                                     mHomeWallpaperInfo.getActionLabelRes(appContext));
1037                             startActivity(exploreIntent);
1038                         });
1039                     });
1040             } else {
1041                 mHomeWallpaperExploreButton.setVisibility(View.GONE);
1042             }
1043 
1044             if (presentationMode == WallpaperPreferences.PRESENTATION_MODE_ROTATING) {
1045                 mHomeWallpaperPresentationSection.setVisibility(View.VISIBLE);
1046                 if (Flags.skipDailyWallpaperButtonEnabled) {
1047                     mSkipWallpaperButton.setVisibility(View.VISIBLE);
1048                     mSkipWallpaperButton.setColorFilter(
1049                             getResources().getColor(R.color.currently_set_explore_button_color,
1050                                     getContext().getTheme()), Mode.SRC_IN);
1051                     mSkipWallpaperButton.setOnClickListener(view -> refreshDailyWallpaper());
1052                 } else {
1053                     mSkipWallpaperButton.setVisibility(View.GONE);
1054                 }
1055             } else {
1056                 mHomeWallpaperPresentationSection.setVisibility(View.GONE);
1057             }
1058 
1059             mHomeWallpaperImage.setOnClickListener(v -> {
1060                 eventLogger.logCurrentWallpaperPreviewed();
1061                 getFragmentHost().showViewOnlyPreview(mHomeWallpaperInfo);
1062             });
1063         }
1064 
bindLockWallpaper(WallpaperInfo lockWallpaper)1065         private void bindLockWallpaper(WallpaperInfo lockWallpaper) {
1066             if (lockWallpaper == null) {
1067                 Log.e(TAG, "TwoWallpapersMetadataHolder bound without a lock screen wallpaper.");
1068                 return;
1069             }
1070 
1071             final Context appContext = getActivity().getApplicationContext();
1072             final UserEventLogger eventLogger =
1073                     InjectorProvider.getInjector().getUserEventLogger(getActivity());
1074 
1075             mLockWallpaperInfo = lockWallpaper;
1076 
1077             lockWallpaper.getThumbAsset(appContext).loadDrawable(
1078                     getActivity(), mLockWallpaperImage, getResources().getColor(R.color.secondary_color));
1079 
1080             List<String> lockAttributions = lockWallpaper.getAttributions(appContext);
1081             if (!lockAttributions.isEmpty()) {
1082                 mLockWallpaperTitle.setText(lockAttributions.get(0));
1083             }
1084             if (lockAttributions.size() > 1) {
1085                 mLockWallpaperSubtitle1.setText(lockAttributions.get(1));
1086             }
1087             if (lockAttributions.size() > 2) {
1088                 mLockWallpaperSubtitle2.setText(lockAttributions.get(2));
1089             }
1090 
1091             final String lockActionUrl = lockWallpaper.getActionUrl(appContext);
1092 
1093             if (lockActionUrl != null && !lockActionUrl.isEmpty()) {
1094                 Uri lockExploreUri = Uri.parse(lockActionUrl);
1095 
1096                 ExploreIntentChecker intentChecker =
1097                         InjectorProvider.getInjector().getExploreIntentChecker(appContext);
1098                 intentChecker.fetchValidActionViewIntent(
1099                         lockExploreUri, (@Nullable Intent exploreIntent) -> {
1100                             if (exploreIntent == null || getActivity() == null) {
1101                                 return;
1102                             }
1103                             mLockWallpaperExploreButton.setImageDrawable(getContext().getDrawable(
1104                                     lockWallpaper.getActionIconRes(appContext)));
1105                             mLockWallpaperExploreButton.setContentDescription(getString(
1106                                     lockWallpaper.getActionLabelRes(appContext)));
1107                             mLockWallpaperExploreButton.setVisibility(View.VISIBLE);
1108                             mLockWallpaperExploreButton.setColorFilter(
1109                                     getResources().getColor(
1110                                             R.color.currently_set_explore_button_color),
1111                                     Mode.SRC_IN);
1112                             mLockWallpaperExploreButton.setOnClickListener(new OnClickListener() {
1113                                 @Override
1114                                 public void onClick(View v) {
1115                                     eventLogger.logActionClicked(
1116                                             mLockWallpaperInfo.getCollectionId(appContext),
1117                                             mLockWallpaperInfo.getActionLabelRes(appContext));
1118                                     startActivity(exploreIntent);
1119                                 }
1120                             });
1121                         });
1122             } else {
1123                 mLockWallpaperExploreButton.setVisibility(View.GONE);
1124             }
1125 
1126             mLockWallpaperImage.setOnClickListener(new OnClickListener() {
1127                 @Override
1128                 public void onClick(View v) {
1129                     eventLogger.logCurrentWallpaperPreviewed();
1130                     getFragmentHost().showViewOnlyPreview(mLockWallpaperInfo);
1131                 }
1132             });
1133         }
1134     }
1135 
1136     private class PreviewPagerAdapter extends PagerAdapter {
1137 
1138         private List<View> mPages;
1139 
PreviewPagerAdapter(List<View> pages)1140         PreviewPagerAdapter(List<View> pages) {
1141             mPages = pages;
1142         }
1143 
1144         @Override
destroyItem(@onNull ViewGroup container, int position, @NonNull Object object)1145         public void destroyItem(@NonNull ViewGroup container, int position,
1146                                 @NonNull Object object) {
1147             container.removeView((View) object);
1148         }
1149 
1150         @NonNull
1151         @Override
instantiateItem(@onNull ViewGroup container, int position)1152         public Object instantiateItem(@NonNull ViewGroup container, int position) {
1153             View view = mPages.get(position);
1154             container.addView(view);
1155             return view;
1156         }
1157 
1158         @Override
getCount()1159         public int getCount() {
1160             return mPages.size();
1161         }
1162 
1163         @Override
isViewFromObject(@onNull View view, @NonNull Object o)1164         public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
1165             return view == o;
1166         }
1167     }
1168 }
1169