• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.VISIBLE;
19 
20 import static com.android.wallpaper.util.LaunchSourceUtils.LAUNCH_SOURCE_LAUNCHER;
21 import static com.android.wallpaper.util.LaunchSourceUtils.LAUNCH_SOURCE_SETTINGS_HOMEPAGE;
22 import static com.android.wallpaper.util.LaunchSourceUtils.WALLPAPER_LAUNCH_SOURCE;
23 
24 import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED;
25 import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN;
26 
27 import android.animation.Animator;
28 import android.animation.AnimatorListenerAdapter;
29 import android.annotation.SuppressLint;
30 import android.app.Activity;
31 import android.app.AlertDialog;
32 import android.app.WallpaperColors;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.content.res.Resources.NotFoundException;
36 import android.graphics.drawable.Drawable;
37 import android.os.Bundle;
38 import android.util.Log;
39 import android.view.LayoutInflater;
40 import android.view.SurfaceView;
41 import android.view.View;
42 import android.view.ViewGroup;
43 import android.view.animation.Interpolator;
44 import android.view.animation.PathInterpolator;
45 import android.widget.CompoundButton;
46 import android.widget.FrameLayout;
47 import android.widget.ProgressBar;
48 import android.widget.Toast;
49 import android.widget.Toolbar;
50 
51 import androidx.annotation.CallSuper;
52 import androidx.annotation.IntDef;
53 import androidx.annotation.Nullable;
54 import androidx.appcompat.content.res.AppCompatResources;
55 import androidx.core.content.res.ResourcesCompat;
56 import androidx.core.view.AccessibilityDelegateCompat;
57 import androidx.core.view.ViewCompat;
58 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
59 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
60 import androidx.fragment.app.Fragment;
61 import androidx.fragment.app.FragmentActivity;
62 import androidx.lifecycle.ViewModelProvider;
63 
64 import com.android.customization.model.color.WallpaperColorResources;
65 import com.android.wallpaper.R;
66 import com.android.wallpaper.model.LiveWallpaperInfo;
67 import com.android.wallpaper.model.SetWallpaperViewModel;
68 import com.android.wallpaper.model.WallpaperInfo;
69 import com.android.wallpaper.module.Injector;
70 import com.android.wallpaper.module.InjectorProvider;
71 import com.android.wallpaper.module.UserEventLogger;
72 import com.android.wallpaper.module.WallpaperPersister.Destination;
73 import com.android.wallpaper.module.WallpaperSetter;
74 import com.android.wallpaper.util.PreviewUtils;
75 import com.android.wallpaper.util.ResourceUtils;
76 import com.android.wallpaper.widget.DuoTabs;
77 import com.android.wallpaper.widget.FloatingSheet;
78 import com.android.wallpaper.widget.WallpaperControlButtonGroup;
79 import com.android.wallpaper.widget.floatingsheetcontent.WallpaperInfoContent;
80 
81 import com.google.android.material.bottomsheet.BottomSheetBehavior;
82 import com.google.android.material.transition.MaterialSharedAxis;
83 
84 /**
85  * Base Fragment to display the UI for previewing an individual wallpaper.
86  */
87 public abstract class PreviewFragment extends Fragment implements WallpaperColorThemePreview {
88 
89     public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
90 
91     /**
92      * User can view wallpaper and attributions in full screen, but "Set wallpaper" button is
93      * hidden.
94      */
95     public static final int MODE_VIEW_ONLY = 0;
96 
97     /**
98      * User can view wallpaper and attributions in full screen and click "Set wallpaper" to set the
99      * wallpaper with pan and crop position to the device.
100      */
101     public static final int MODE_CROP_AND_SET_WALLPAPER = 1;
102 
103     /**
104      * Possible preview modes for the fragment.
105      */
106     @IntDef({
107             MODE_VIEW_ONLY,
108             MODE_CROP_AND_SET_WALLPAPER})
109     public @interface PreviewMode {
110     }
111 
112     public static final String ARG_IS_NEW_TASK = "is_new_task";
113     public static final String ARG_IS_ASSET_ID_PRESENT = "is_asset_id_present";
114     public static final String ARG_WALLPAPER = "wallpaper";
115     public static final String ARG_VIEW_AS_HOME = "view_as_home";
116 
117     private static final String TAG = "PreviewFragment";
118 
119     protected WallpaperInfo mWallpaper;
120     protected WallpaperSetter mWallpaperSetter;
121     protected ViewModelProvider mViewModelProvider;
122     protected WallpaperColors mWallpaperColors;
123     protected UserEventLogger mUserEventLogger;
124     private SetWallpaperViewModel mSetWallpaperViewModel;
125 
126     // UI
127     private SurfaceView mWorkspaceSurface;
128     private WorkspaceSurfaceHolderCallback mWorkspaceSurfaceCallback;
129     private SurfaceView mLockSurface;
130     private WorkspaceSurfaceHolderCallback mLockSurfaceCallback;
131     private View mHideFloatingSheetTouchLayout;
132     private DuoTabs mOverlayTabs;
133     private @DuoTabs.Tab int mInitSelectedTab;
134     private View mExitFullPreviewButton;
135     protected View mSetWallpaperButton;
136     protected FrameLayout mSetWallpaperButtonContainer;
137     protected View mPreviewScrim;
138     protected Toolbar mToolbar;
139     protected WallpaperControlButtonGroup mWallpaperControlButtonGroup;
140     protected FloatingSheet mFloatingSheet;
141     protected TouchForwardingLayout mTouchForwardingLayout;
142 
143     protected ProgressBar mProgressBar;
144 
145     protected boolean mIsViewAsHome;
146 
147     /**
148      * We create an instance of WallpaperInfo from CurrentWallpaperInfo when a user taps on
149      * the preview of a wallpapers in the wallpaper picker main screen. However, there are
150      * other instances as well in which an instance of the specific WallpaperInfo is created. This
151      * variable is used in order to identify whether the instance created has an assetId or not.
152      * This is needed for restricting the destination where a wallpaper can be set after editing
153      * it.
154      */
155     protected boolean mIsAssetIdPresent;
156 
157     /**
158      * True if the activity of this fragment is launched with {@link Intent#FLAG_ACTIVITY_NEW_TASK}.
159      */
160     private boolean mIsNewTask;
161 
162     // The system "short" animation time duration, in milliseconds. This
163     // duration is ideal for subtle animations or animations that occur
164     // very frequently.
165     private int mShortAnimTimeMillis;
166 
167     private final BottomSheetBehavior.BottomSheetCallback mStandardFloatingSheetCallback =
168             new BottomSheetBehavior.BottomSheetCallback() {
169                 @Override
170                 public void onStateChanged(@androidx.annotation.NonNull View bottomSheet,
171                         int newState) {
172                     if (newState == STATE_EXPANDED) {
173                         mHideFloatingSheetTouchLayout.setVisibility(View.VISIBLE);
174                         mTouchForwardingLayout.setVisibility(View.GONE);
175                     }
176                     if (newState == STATE_HIDDEN) {
177                         mWallpaperControlButtonGroup.deselectAllFloatingSheetControlButtons();
178                         mHideFloatingSheetTouchLayout.setVisibility(View.GONE);
179                         mTouchForwardingLayout.setVisibility(VISIBLE);
180                         mTouchForwardingLayout.requestFocus();
181                     }
182                 }
183 
184                 @Override
185                 public void onSlide(@androidx.annotation.NonNull View bottomSheet,
186                         float slideOffset) {
187                 }
188             };
189 
190     protected final BottomSheetBehavior.BottomSheetCallback
191             mShowOverlayOnHideFloatingSheetCallback =
192             new BottomSheetBehavior.BottomSheetCallback() {
193                 @Override
194                 public void onStateChanged(@androidx.annotation.NonNull View bottomSheet,
195                         int newState) {
196                     if (newState == STATE_HIDDEN) {
197                         hideScreenPreviewOverlay(/* hide= */false);
198                     }
199                 }
200 
201                 @Override
202                 public void onSlide(@androidx.annotation.NonNull View bottomSheet,
203                         float slideOffset) {
204                 }
205             };
206 
207     /**
208      * Sets current wallpaper to the device based on current zoom and scroll state.
209      *
210      * @param destination The wallpaper destination i.e. home vs. lockscreen vs. both.
211      */
setWallpaper(@estination int destination)212     protected abstract void setWallpaper(@Destination int destination);
213 
214     @Override
onCreate(Bundle savedInstanceState)215     public void onCreate(Bundle savedInstanceState) {
216         super.onCreate(savedInstanceState);
217         Bundle args = requireArguments();
218         mWallpaper = args.getParcelable(ARG_WALLPAPER);
219         mIsViewAsHome = args.getBoolean(ARG_VIEW_AS_HOME);
220         mIsAssetIdPresent = args.getBoolean(ARG_IS_ASSET_ID_PRESENT);
221         mIsNewTask = args.getBoolean(ARG_IS_NEW_TASK);
222         mInitSelectedTab = mIsViewAsHome ? DuoTabs.TAB_SECONDARY : DuoTabs.TAB_PRIMARY;
223         Context appContext = requireContext().getApplicationContext();
224         Injector injector = InjectorProvider.getInjector();
225 
226         mUserEventLogger = injector.getUserEventLogger(appContext);
227         mWallpaperSetter = new WallpaperSetter(injector.getWallpaperPersister(appContext),
228                 injector.getPreferences(appContext), mUserEventLogger,
229                 injector.getCurrentWallpaperInfoFactory(appContext), false);
230         mViewModelProvider = new ViewModelProvider(requireActivity());
231         mSetWallpaperViewModel = mViewModelProvider.get(SetWallpaperViewModel.class);
232         mSetWallpaperViewModel.getStatus().observe(requireActivity(), setWallpaperStatus -> {
233             switch (setWallpaperStatus) {
234                 case SUCCESS:
235                     onSetWallpaperSuccess();
236                     break;
237                 case ERROR:
238                     showSetWallpaperErrorDialog();
239                     break;
240                 default:
241                     // Do nothing when UNKNOWN or PENDING
242             }
243         });
244 
245         mShortAnimTimeMillis = getResources().getInteger(android.R.integer.config_shortAnimTime);
246         setEnterTransition(new MaterialSharedAxis(MaterialSharedAxis.X, /* forward */ true));
247         setReturnTransition(new MaterialSharedAxis(MaterialSharedAxis.X, /* forward */ false));
248         setExitTransition(new MaterialSharedAxis(MaterialSharedAxis.X, /* forward */ true));
249         setReenterTransition(new MaterialSharedAxis(MaterialSharedAxis.X, /* forward */ false));
250 
251     }
252 
253     @Override
254     @CallSuper
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)255     public View onCreateView(LayoutInflater inflater, ViewGroup container,
256             Bundle savedInstanceState) {
257         View view = inflater.inflate(R.layout.fragment_wallpaper_preview, container, false);
258         // Progress indicator
259         mProgressBar = view.findViewById(R.id.action_progress);
260         // Toolbar
261         mToolbar = view.findViewById(R.id.toolbar);
262         setUpToolbar();
263         // TouchForwardingLayout
264         mTouchForwardingLayout = view.findViewById(R.id.touch_forwarding_layout);
265         mTouchForwardingLayout.setOnClickAccessibilityDescription(
266                 R.string.hide_preview_controls_action);
267         // Preview overlay
268         mWorkspaceSurface = view.findViewById(R.id.workspace_surface);
269         mWorkspaceSurfaceCallback = new WorkspaceSurfaceHolderCallback(
270                 mWorkspaceSurface,
271                 new PreviewUtils(
272                         requireContext(),
273                         getString(R.string.grid_control_metadata_name)),
274                 shouldApplyWallpaperColors());
275         // Hide the work space's bottom row initially to avoid overlapping with the overlay tabs.
276         mWorkspaceSurfaceCallback.setHideBottomRow(true);
277         mLockSurface = view.findViewById(R.id.lock_screen_overlay_surface);
278         mLockSurfaceCallback = new WorkspaceSurfaceHolderCallback(
279                 mLockSurface,
280                 new PreviewUtils(
281                         requireContext().getApplicationContext(),
282                         null,
283                         getString(R.string.lock_screen_preview_provider_authority)),
284                 shouldApplyWallpaperColors());
285         setUpScreenPreviewOverlay();
286         // Set wallpaper button
287         mSetWallpaperButtonContainer = view.findViewById(R.id.button_set_wallpaper_container);
288         mSetWallpaperButton = view.findViewById(R.id.button_set_wallpaper);
289         mSetWallpaperButtonContainer.setOnClickListener(
290                 v -> showDestinationSelectionDialogForWallpaper(mWallpaper));
291         // Overlay tabs
292         mOverlayTabs = view.findViewById(R.id.overlay_tabs);
293         mOverlayTabs.setTabText(getString(R.string.lock_screen_message),
294                 getString(R.string.home_screen_message));
295         mOverlayTabs.setOnTabSelectedListener(this::updateScreenPreviewOverlay);
296         mOverlayTabs.selectTab(mInitSelectedTab);
297         // Floating sheet and button control group
298         mFloatingSheet = view.findViewById(R.id.floating_sheet);
299         mHideFloatingSheetTouchLayout = view.findViewById(R.id.hide_floating_sheet_touch_layout);
300         mWallpaperControlButtonGroup = view.findViewById(R.id.wallpaper_control_button_group);
301         setUpFloatingSheet(requireContext());
302         mWallpaperControlButtonGroup.showButton(WallpaperControlButtonGroup.INFORMATION,
303                 getFloatingSheetControlButtonChangeListener(WallpaperControlButtonGroup.INFORMATION,
304                         FloatingSheet.INFORMATION));
305         mPreviewScrim = view.findViewById(R.id.preview_scrim);
306         mExitFullPreviewButton = view.findViewById(R.id.exit_full_preview_button);
307         mExitFullPreviewButton.setOnClickListener(v -> toggleWallpaperPreviewControl());
308         return view;
309     }
310 
setUpToolbar()311     private void setUpToolbar() {
312         Activity activity = getActivity();
313         if (activity == null) {
314             return;
315         }
316         mToolbar.setTitle(R.string.preview);
317         mToolbar.setTitleTextColor(getResources().getColor(R.color.preview_toolbar_text_light));
318         mToolbar.setBackgroundResource(android.R.color.transparent);
319         activity.getWindow().setStatusBarColor(
320                 getResources().getColor(android.R.color.transparent));
321         activity.getWindow().setNavigationBarColor(
322                 getResources().getColor(android.R.color.transparent));
323 
324         // The hosting activity needs to implement AppbarFragment.AppbarFragmentHost
325         AppbarFragment.AppbarFragmentHost host = (AppbarFragment.AppbarFragmentHost) activity;
326         if (host.isUpArrowSupported()) {
327             mToolbar.setNavigationIcon(getToolbarBackIcon());
328             mToolbar.setNavigationContentDescription(R.string.bottom_action_bar_back);
329             mToolbar.setNavigationOnClickListener(view -> {
330                 host.onUpArrowPressed();
331             });
332         }
333     }
334 
335     @Nullable
getToolbarBackIcon()336     private Drawable getToolbarBackIcon() {
337         Drawable backIcon = ResourcesCompat.getDrawable(getResources(),
338                 R.drawable.material_ic_arrow_back_black_24,
339                 null);
340         if (backIcon == null) {
341             return null;
342         }
343         backIcon.setAutoMirrored(true);
344         backIcon.setTint(getResources().getColor(R.color.preview_toolbar_text_light));
345         return backIcon;
346     }
347 
setUpScreenPreviewOverlay()348     private void setUpScreenPreviewOverlay() {
349         int placeHolderColor = ResourceUtils.getColorAttr(requireContext(),
350                 android.R.attr.colorBackground);
351         mWorkspaceSurface.setResizeBackgroundColor(placeHolderColor);
352         mWorkspaceSurface.setZOrderMediaOverlay(true);
353         mWorkspaceSurface.getHolder().addCallback(mWorkspaceSurfaceCallback);
354         mLockSurface.setResizeBackgroundColor(placeHolderColor);
355         mLockSurface.setZOrderMediaOverlay(true);
356         mLockSurface.getHolder().addCallback(mLockSurfaceCallback);
357     }
358 
359     @SuppressLint("ClickableViewAccessibility")
setUpFloatingSheet(Context context)360     private void setUpFloatingSheet(Context context) {
361         setHideFloatingSheetLayoutAccessibilityAction();
362         mHideFloatingSheetTouchLayout.setContentDescription(
363                 getString(R.string.preview_screen_description));
364         mHideFloatingSheetTouchLayout.setOnClickListener(v -> mFloatingSheet.collapse());
365         mHideFloatingSheetTouchLayout.setVisibility(View.GONE);
366         mFloatingSheet.addFloatingSheetCallback(mStandardFloatingSheetCallback);
367         mFloatingSheet.addFloatingSheetCallback(mShowOverlayOnHideFloatingSheetCallback);
368         mFloatingSheet.putFloatingSheetContent(FloatingSheet.INFORMATION,
369                 new WallpaperInfoContent(context, mWallpaper));
370     }
371 
getFloatingSheetControlButtonChangeListener( @allpaperControlButtonGroup.WallpaperControlType int wallpaperType, @FloatingSheet.Companion.FloatingSheetContentType int floatingSheetType)372     protected CompoundButton.OnCheckedChangeListener getFloatingSheetControlButtonChangeListener(
373             @WallpaperControlButtonGroup.WallpaperControlType int wallpaperType,
374             @FloatingSheet.Companion.FloatingSheetContentType int floatingSheetType) {
375         return (buttonView, isChecked) -> {
376             if (isChecked) {
377                 mWallpaperControlButtonGroup.deselectOtherFloatingSheetControlButtons(
378                         wallpaperType);
379                 if (mFloatingSheet.isFloatingSheetCollapsed()) {
380                     hideScreenPreviewOverlay(/* hide= */true);
381                     mFloatingSheet.updateContentView(floatingSheetType);
382                     mFloatingSheet.expand();
383                 } else {
384                     mFloatingSheet.updateContentViewWithAnimation(floatingSheetType);
385                 }
386             } else {
387                 if (!mWallpaperControlButtonGroup.isFloatingSheetControlButtonSelected()) {
388                     mFloatingSheet.collapse();
389                 }
390             }
391         };
392     }
393 
394     private void setHideFloatingSheetLayoutAccessibilityAction() {
395         ViewCompat.setAccessibilityDelegate(mHideFloatingSheetTouchLayout,
396                 new AccessibilityDelegateCompat() {
397                 @Override
398                 public void onInitializeAccessibilityNodeInfo(View host,
399                         AccessibilityNodeInfoCompat info) {
400                     super.onInitializeAccessibilityNodeInfo(host, info);
401                     CharSequence description = host.getResources().getString(
402                             R.string.hide_wallpaper_info_action);
403                     AccessibilityActionCompat clickAction = new AccessibilityActionCompat(
404                             AccessibilityNodeInfoCompat.ACTION_CLICK, description);
405                     info.addAction(clickAction);
406                 }
407             });
408     }
409 
410     @Override
411     public void onDestroy() {
412         super.onDestroy();
413         if (mWallpaperSetter != null) {
414             mWallpaperSetter.cleanUp();
415         }
416         if (mWorkspaceSurfaceCallback != null) {
417             mWorkspaceSurfaceCallback.cleanUp();
418         }
419         if (mLockSurfaceCallback != null) {
420             mLockSurfaceCallback.cleanUp();
421         }
422     }
423 
424     protected void onWallpaperColorsChanged(@Nullable WallpaperColors colors) {
425         //  Early return to not block the instrumentation test.
426         if (InjectorProvider.getInjector().isInstrumentationTest()) {
427             return;
428         }
429         if (!shouldApplyWallpaperColors()) {
430             return;
431         }
432         mWallpaperColors = colors;
433         Context context = getContext();
434         if (context == null || colors == null) {
435             return;
436         }
437         // Apply the wallpaper color resources to the fragment context. So the views created by
438         // the context will apply the given wallpaper color.
439         new WallpaperColorResources(colors).apply(context);
440         mSetWallpaperButton.setBackground(null);
441         mSetWallpaperButton.setBackgroundResource(R.drawable.set_wallpaper_button_background);
442         mExitFullPreviewButton.setForeground(
443                 AppCompatResources.getDrawable(context, R.drawable.exit_full_preview_cross));
444         mWallpaperControlButtonGroup.updateBackgroundColor();
445         mOverlayTabs.updateBackgroundColor();
446         // Update the color theme for the home screen overlay
447         updateWorkspacePreview(mWorkspaceSurface, mWorkspaceSurfaceCallback, colors,
448                 /* hideBottomRow= */ mOverlayTabs.getVisibility() == VISIBLE);
449         // Update the color theme for the lock screen overlay
450         updateWorkspacePreview(mLockSurface, mLockSurfaceCallback, colors,
451                 /* hideBottomRow= */ mOverlayTabs.getVisibility() == VISIBLE);
452         mFloatingSheet.setColor(context);
453     }
454 
455     private void updateScreenPreviewOverlay(@DuoTabs.Tab int tab) {
456         if (mWorkspaceSurface != null) {
457             mWorkspaceSurface.setVisibility(
458                     tab == DuoTabs.TAB_SECONDARY ? View.VISIBLE : View.INVISIBLE);
459             mWorkspaceSurface.setZOrderMediaOverlay(tab == DuoTabs.TAB_SECONDARY);
460         }
461         if (mLockSurface != null) {
462             mLockSurface.setVisibility(
463                     tab == DuoTabs.TAB_PRIMARY ? View.VISIBLE : View.INVISIBLE);
464             mLockSurface.setZOrderMediaOverlay(tab == DuoTabs.TAB_PRIMARY);
465         }
466     }
467 
468     protected void toggleWallpaperPreviewControl() {
469         boolean wasVisible = mPreviewScrim.getVisibility() == VISIBLE;
470         mTouchForwardingLayout.setOnClickAccessibilityDescription(
471                 wasVisible ? R.string.show_preview_controls_action
472                         : R.string.hide_preview_controls_action);
473         animateWallpaperPreviewControl(wasVisible);
474     }
475 
476     private void animateWallpaperPreviewControl(boolean hide) {
477         // When hiding the preview control, we should show the workspace bottom row components
478         hideBottomRow(!hide);
479         mPreviewScrim.animate()
480                 .alpha(hide ? 0f : 1f)
481                 .setDuration(mShortAnimTimeMillis)
482                 .setListener(new ViewAnimatorListener(mPreviewScrim, hide));
483         mWallpaperControlButtonGroup.animate().alpha(hide ? 0f : 1f)
484                 .setDuration(mShortAnimTimeMillis)
485                 .setListener(new ViewAnimatorListener(mWallpaperControlButtonGroup, hide));
486         mOverlayTabs.animate().alpha(hide ? 0f : 1f)
487                 .setDuration(mShortAnimTimeMillis)
488                 .setListener(new ViewAnimatorListener(mOverlayTabs, hide));
489         mSetWallpaperButtonContainer.animate().alpha(hide ? 0f : 1f)
490                 .setDuration(mShortAnimTimeMillis)
491                 .setListener(new ViewAnimatorListener(mSetWallpaperButtonContainer, hide));
492         mToolbar.animate().alpha(hide ? 0f : 1f)
493                 .setDuration(mShortAnimTimeMillis)
494                 .setListener(new ViewAnimatorListener(mToolbar, hide));
495         // The show and hide of the button is the opposite of the wallpaper preview control
496         mExitFullPreviewButton.animate().alpha(!hide ? 0f : 1f)
497                 .setDuration(mShortAnimTimeMillis)
498                 .setListener(new ViewAnimatorListener(mExitFullPreviewButton, !hide));
499     }
500 
501     private void hideBottomRow(boolean hide) {
502         if (mWorkspaceSurfaceCallback != null) {
503             Bundle data = new Bundle();
504             data.putBoolean(WorkspaceSurfaceHolderCallback.KEY_HIDE_BOTTOM_ROW, hide);
505             mWorkspaceSurfaceCallback.send(WorkspaceSurfaceHolderCallback.MESSAGE_ID_UPDATE_PREVIEW,
506                     data);
507         }
508     }
509 
510     protected void hideScreenPreviewOverlay(boolean hide) {
511         mPreviewScrim.setVisibility(hide ? View.INVISIBLE : View.VISIBLE);
512         mOverlayTabs.setVisibility(hide ? View.INVISIBLE : View.VISIBLE);
513         boolean isLockSelected = mOverlayTabs.getSelectedTab() == DuoTabs.TAB_PRIMARY;
514         if (isLockSelected) {
515             mLockSurface.setVisibility(hide ? View.INVISIBLE : View.VISIBLE);
516             mLockSurface.setZOrderMediaOverlay(!hide);
517         } else {
518             mWorkspaceSurface.setVisibility(hide ? View.INVISIBLE : View.VISIBLE);
519             mWorkspaceSurface.setZOrderMediaOverlay(!hide);
520         }
521     }
522 
523     protected void onSetWallpaperSuccess() {
524         Activity activity = getActivity();
525         if (activity == null) {
526             return;
527         }
528         try {
529             Toast.makeText(activity, R.string.wallpaper_set_successfully_message,
530                     Toast.LENGTH_SHORT).show();
531         } catch (NotFoundException e) {
532             Log.e(TAG, "Could not show toast " + e);
533         }
534         activity.setResult(Activity.RESULT_OK);
535         finishActivityWithFadeTransition();
536 
537         // Start activity to go back to main screen.
538         if (mIsNewTask) {
539             Intent intent = new Intent(requireActivity(), TrampolinePickerActivity.class);
540             intent.putExtra(WALLPAPER_LAUNCH_SOURCE,
541                     mIsViewAsHome ? LAUNCH_SOURCE_LAUNCHER : LAUNCH_SOURCE_SETTINGS_HOMEPAGE);
542             intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
543             startActivity(intent);
544         }
545     }
546 
547     protected void finishActivityWithFadeTransition() {
548         Activity activity = getActivity();
549         if (activity == null) {
550             return;
551         }
552         activity.finish();
553     }
554 
555     private void showDestinationSelectionDialogForWallpaper(WallpaperInfo wallpaperInfo) {
556 
557         // This logic is implemented for the editing of live wallpapers. The purpose is to
558         // restrict users to set the edited creative wallpaper only to the destination from
559         // where they originally started the editing process. For instance, if they began editing
560         // by clicking on the homescreen preview, they would be allowed to set the wallpaper on the
561         // homescreen and both the homescreen and lockscreen. On the other hand, if they initiated
562         // editing by clicking on the lockscreen preview, they would only be allowed to set the
563         // wallpaper on the lockscreen and both the homescreen and lockscreen. It's essential to
564         // note that this restriction only applies when the editing process is started by tapping
565         // on the preview available on the wallpaper picker home page.
566         boolean isLockOption = true;
567         boolean isHomeOption = true;
568         if (wallpaperInfo instanceof LiveWallpaperInfo) {
569             if (!mIsAssetIdPresent) {
570                 isHomeOption = mIsViewAsHome;
571                 isLockOption = !mIsViewAsHome;
572             }
573         }
574 
575         mWallpaperSetter.requestDestination(getActivity(), getParentFragmentManager(),
576                 destination -> {
577                     mSetWallpaperViewModel.setDestination(destination);
578                     setWallpaper(destination);
579                 },
580                 wallpaperInfo instanceof LiveWallpaperInfo, isHomeOption, isLockOption);
581     }
582 
583     protected void showSetWallpaperErrorDialog() {
584         new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme)
585                 .setMessage(R.string.set_wallpaper_error_message)
586                 .setPositiveButton(R.string.try_again, (dialogInterface, i) ->
587                         setWallpaper(mSetWallpaperViewModel.getDestination())
588                 )
589                 .setNegativeButton(android.R.string.cancel, null)
590                 .create()
591                 .show();
592     }
593 
594     protected void showLoadWallpaperErrorDialog() {
595         new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme)
596                 .setMessage(R.string.load_wallpaper_error_message)
597                 .setPositiveButton(android.R.string.ok,
598                         (dialogInterface, i) -> finishFragmentActivity())
599                 .setOnDismissListener(dialog -> finishFragmentActivity())
600                 .create()
601                 .show();
602     }
603 
604     private void finishFragmentActivity() {
605         FragmentActivity activity = getActivity();
606         if (activity != null) {
607             activity.finish();
608         }
609     }
610 
611     private static class ViewAnimatorListener extends AnimatorListenerAdapter {
612         final View mView;
613         final boolean mHide;
614 
615         private ViewAnimatorListener(View view, boolean hide) {
616             mView = view;
617             mHide = hide;
618         }
619 
620         @Override
621         public void onAnimationStart(Animator animation) {
622             mView.setVisibility(VISIBLE);
623         }
624 
625         @Override
626         public void onAnimationEnd(Animator animation) {
627             mView.setVisibility(mHide ? View.INVISIBLE : VISIBLE);
628         }
629     }
630 }
631