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.app.Activity.RESULT_OK;
19 import static android.app.WallpaperManager.FLAG_LOCK;
20 import static android.app.WallpaperManager.FLAG_SYSTEM;
21 import static android.stats.style.StyleEnums.SET_WALLPAPER_ENTRY_POINT_WALLPAPER_PREVIEW;
22 import static android.view.View.GONE;
23 import static android.view.View.VISIBLE;
24 
25 import static com.android.wallpaper.module.WallpaperPersister.destinationToFlags;
26 
27 import android.annotation.SuppressLint;
28 import android.app.Activity;
29 import android.app.AlertDialog;
30 import android.app.WallpaperColors;
31 import android.app.WallpaperInfo;
32 import android.app.WallpaperManager;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.pm.ActivityInfo;
37 import android.content.pm.PackageManager;
38 import android.graphics.Color;
39 import android.net.Uri;
40 import android.os.Bundle;
41 import android.os.RemoteException;
42 import android.service.wallpaper.IWallpaperConnection;
43 import android.service.wallpaper.WallpaperService;
44 import android.service.wallpaper.WallpaperSettingsActivity;
45 import android.util.Log;
46 import android.view.LayoutInflater;
47 import android.view.MotionEvent;
48 import android.view.SurfaceView;
49 import android.view.View;
50 import android.view.ViewGroup;
51 import android.widget.ImageView;
52 
53 import androidx.activity.OnBackPressedCallback;
54 import androidx.activity.result.ActivityResultCallback;
55 import androidx.activity.result.ActivityResultLauncher;
56 import androidx.annotation.NonNull;
57 import androidx.annotation.Nullable;
58 import androidx.fragment.app.FragmentActivity;
59 
60 import com.android.wallpaper.R;
61 import com.android.wallpaper.model.CreativeWallpaperInfo;
62 import com.android.wallpaper.model.SetWallpaperViewModel;
63 import com.android.wallpaper.model.WallpaperAction;
64 import com.android.wallpaper.model.WallpaperInfo.ColorInfo;
65 import com.android.wallpaper.module.WallpaperPersister;
66 import com.android.wallpaper.util.DeletableUtils;
67 import com.android.wallpaper.util.ResourceUtils;
68 import com.android.wallpaper.util.RtlUtils;
69 import com.android.wallpaper.util.WallpaperConnection;
70 import com.android.wallpaper.util.WallpaperSurfaceCallback2;
71 import com.android.wallpaper.widget.FloatingSheet;
72 import com.android.wallpaper.widget.WallpaperColorsLoader;
73 import com.android.wallpaper.widget.WallpaperControlButtonGroup;
74 import com.android.wallpaper.widget.floatingsheetcontent.PreviewCustomizeSettingsContent;
75 import com.android.wallpaper.widget.floatingsheetcontent.WallpaperActionSelectionBottomSheetContent;
76 import com.android.wallpaper.widget.floatingsheetcontent.WallpaperActionsToggleAdapter;
77 
78 import java.util.ArrayList;
79 import java.util.concurrent.ExecutionException;
80 import java.util.concurrent.ExecutorService;
81 import java.util.concurrent.Executors;
82 import java.util.concurrent.Future;
83 import java.util.concurrent.TimeUnit;
84 import java.util.concurrent.TimeoutException;
85 
86 /**
87  * Fragment which displays the UI for previewing an individual live wallpaper, its attribution
88  * information and settings slices if available.
89  */
90 public class LivePreviewFragment extends PreviewFragment {
91 
92     public static final String EXTRA_LIVE_WALLPAPER_INFO = "android.live_wallpaper.info";
93     public static final String KEY_ACTION_DELETE_LIVE_WALLPAPER = "action_delete_live_wallpaper";
94 
95     private static final String TAG = "LivePreviewFragment";
96     private static final String KEY_TOOLBAR_GONE = "toolbar_gone";
97     private static final ExecutorService sExecutorService = Executors.newCachedThreadPool();
98     private ActivityResultLauncher<Void> mSettingsActivityResult;
99     private ActivityResultLauncher<Void> mShareActivityResult;
100     private Intent mSettingsActivityIntent;
101     private WallpaperActionSelectionBottomSheetContent mWallpaperActionSelectionBottomSheetContent;
102     private OnBackPressedCallback mSettingsOnBackPressedCallback;
103     private boolean mHasCalledOnSaveInstanceState = false;
104     private boolean mHideOverlaysForShowingSettingsActivity = false;
105     private Future<ColorInfo> mColorFuture;
106     /**
107      * Instance of {@link WallpaperConnection} used to bind to the live wallpaper service to show
108      * it in this preview fragment.
109      *
110      * @see IWallpaperConnection
111      */
112     private WallpaperConnection mWallpaperConnection;
113     private WallpaperSurfaceCallback2 mWallpaperSurfaceCallback;
114     private SurfaceView mWallpaperSurface;
115 
116     @Override
setWallpaper(int destination)117     protected void setWallpaper(int destination) {
118         WallpaperPersister.SetWallpaperCallback callback = SetWallpaperViewModel.getCallback(
119                 mViewModelProvider);
120         if (mWallpaperConnection != null) {
121             try {
122                 mWallpaperConnection.setWallpaperFlags(destinationToFlags(destination));
123             } catch (RemoteException e) {
124                 callback.onError(e);
125                 return;
126             }
127         }
128         mWallpaperSetter.setCurrentWallpaper(
129                 getActivity(),
130                 mWallpaper,
131                 null,
132                 SET_WALLPAPER_ENTRY_POINT_WALLPAPER_PREVIEW,
133                 destination,
134                 0,
135                 null,
136                 mWallpaperColors != null ? mWallpaperColors : getColorInfo().getWallpaperColors(),
137                 callback);
138     }
139 
140 
141     @Override
onCreate(Bundle savedInstanceState)142     public void onCreate(Bundle savedInstanceState) {
143         super.onCreate(savedInstanceState);
144         Context context = requireContext();
145         mColorFuture = mWallpaper.computeColorInfo(context);
146         mSettingsActivityIntent = getSettingsActivityIntent(getContext(),
147                 mWallpaper.getWallpaperComponent());
148         mSettingsOnBackPressedCallback = new OnBackPressedCallback(true) {
149             @Override
150             public void handleOnBackPressed() {
151                 launchSettingsAsOverlay(/* isLaunched= */ false);
152             }
153         };
154         if (mWallpaper instanceof CreativeWallpaperInfo) {
155             mSettingsActivityResult = registerForActivityResult(
156                     CreativeWallpaperInfo.getContract(mSettingsActivityIntent),
157                     getCreativeWallpaperPreviewResultCallback());
158             CreativeWallpaperInfo creativeWallpaper = (CreativeWallpaperInfo) mWallpaper;
159             if (creativeWallpaper.canBeShared()) {
160                 mShareActivityResult = registerForActivityResult(CreativeWallpaperInfo.getContract(
161                                 creativeWallpaper.getShareIntent()),
162                         unused -> mWallpaperControlButtonGroup.setChecked(
163                                 WallpaperControlButtonGroup.SHARE, /* checked= */ false));
164             }
165         }
166     }
167 
getCreativeWallpaperPreviewResultCallback()168     private ActivityResultCallback<Integer> getCreativeWallpaperPreviewResultCallback() {
169         return result -> {
170             CreativeWallpaperInfo creativeWallpaper = (CreativeWallpaperInfo) mWallpaper;
171             WallpaperManager wallpaperManager = WallpaperManager.getInstance(getContext());
172             boolean isCreativeWallpaperApplied = creativeWallpaper.isApplied(
173                     wallpaperManager.getWallpaperInfo(FLAG_SYSTEM),
174                     wallpaperManager.getWallpaperInfo(FLAG_LOCK));
175             if (result == RESULT_OK) {
176                 if (creativeWallpaper.canBeDeleted() || isCreativeWallpaperApplied) {
177                     // When editing an existing wallpaper and pressing "Done" button causing the
178                     // overlays to become visible
179                     showOverlays();
180                     overrideOnBackPressed(new OnBackPressedCallback(true) {
181                         @Override
182                         public void handleOnBackPressed() {
183                             getActivity().finish();
184                         }
185                     });
186                 } else {
187                     // When in the process of creating a new wallpaper and pressing "Done" button.
188                     showOverlays();
189                     overrideOnBackPressed(mSettingsOnBackPressedCallback);
190                 }
191             } else {
192                 // When you initiate the editing process for a wallpaper and then decide to exit
193                 // by pressing the back button during editing.
194                 if (creativeWallpaper.canBeDeleted() || isCreativeWallpaperApplied) {
195                     showOverlays();
196                 } else {
197                     // Flow where user opens a template (so the settings activity is launched)
198                     // but user just simply presses back (without moving on to the next screen)
199                     // TODO: This should ideally be a slide transition, but custom slide transition
200                     // does not work properly, so having a fade transition for now
201                     finishActivityWithFadeTransition();
202                 }
203             }
204         };
205     }
206 
207     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)208     public View onCreateView(LayoutInflater inflater, ViewGroup container,
209             Bundle savedInstanceState) {
210         View view = super.onCreateView(inflater, container, savedInstanceState);
211         if (view == null) {
212             return null;
213         }
214         // Live wallpaper surface
215         Context context = requireContext();
216         mWallpaperSurface = view.findViewById(R.id.wallpaper_surface);
217         mWallpaperSurfaceCallback = new WallpaperSurfaceCallback2(context, mWallpaperSurface,
218                 mColorFuture, this::previewLiveWallpaper);
219         mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback);
220         mWallpaperSurface.setZOrderMediaOverlay(true);
221         setUpLiveWallpaperTouchForwarding(mTouchForwardingLayout);
222         showLiveWallpaperControl();
223         return view;
224     }
225 
getEffectSwitchListener( Context context, CreativeWallpaperInfo creativeWallpaper)226     private WallpaperActionsToggleAdapter.WallpaperEffectSwitchListener getEffectSwitchListener(
227             Context context, CreativeWallpaperInfo creativeWallpaper) {
228         return (checkedItem) -> {
229             for (WallpaperAction wallpaperActionToggle : creativeWallpaper.getEffectsToggles()) {
230                 wallpaperActionToggle.setToggled(false);
231             }
232 
233             if (checkedItem >= 0) {
234                 WallpaperAction currentEffect =
235                         creativeWallpaper.getEffectsToggles().get(
236                                 checkedItem);
237                 currentEffect.setToggled(true);
238                 creativeWallpaper
239                         .setCurrentlyAppliedEffectId(
240                                 currentEffect.getEffectId());
241                 creativeWallpaper.applyEffect(context,
242                         currentEffect.getApplyActionUri());
243                 mWallpaperActionSelectionBottomSheetContent.setCurrentlyAppliedEffect(
244                         currentEffect.getEffectId());
245             } else {
246                 creativeWallpaper.setCurrentlyAppliedEffectId(null);
247                 creativeWallpaper.clearEffects(context);
248                 mWallpaperActionSelectionBottomSheetContent.setCurrentlyAppliedEffect(null);
249             }
250         };
251     }
252 
253     @SuppressLint("ClickableViewAccessibility")
254     private void setUpLiveWallpaperTouchForwarding(TouchForwardingLayout touchForwardingLayout) {
255         touchForwardingLayout.setTargetView(mWallpaperSurface);
256         touchForwardingLayout.setForwardingEnabled(true);
257         touchForwardingLayout.setOnClickListener(v -> {
258             toggleWallpaperPreviewControl();
259             mTouchForwardingLayout.announceForAccessibility(
260                     getString(mPreviewScrim.getVisibility() == View.VISIBLE
261                             ? R.string.show_preview_controls_content_description
262                             : R.string.hide_preview_controls_content_description)
263             );
264         });
265         mWallpaperSurface.setOnTouchListener((v, ev) -> {
266             dispatchTouchEventOnLiveWallpaperSurface(ev);
267             return false;
268         });
269     }
270 
271     private void dispatchTouchEventOnLiveWallpaperSurface(MotionEvent ev) {
272         if (mWallpaperConnection != null && mWallpaperConnection.getEngine() != null) {
273             int action = ev.getActionMasked();
274             MotionEvent dup = MotionEvent.obtainNoHistory(ev);
275             dup.setLocation(ev.getX(), ev.getY());
276             try {
277                 mWallpaperConnection.getEngine().dispatchPointer(dup);
278                 if (action == MotionEvent.ACTION_UP) {
279                     mWallpaperConnection.getEngine().dispatchWallpaperCommand(
280                             WallpaperManager.COMMAND_TAP,
281                             (int) ev.getX(), (int) ev.getY(), 0, null);
282                 } else if (action == MotionEvent.ACTION_POINTER_UP) {
283                     int pointerIndex = ev.getActionIndex();
284                     mWallpaperConnection.getEngine().dispatchWallpaperCommand(
285                             WallpaperManager.COMMAND_SECONDARY_TAP,
286                             (int) ev.getX(pointerIndex), (int) ev.getY(pointerIndex), 0,
287                             null);
288                 }
289             } catch (RemoteException e) {
290                 Log.e(TAG, "Remote exception of wallpaper connection");
291             }
292         }
293     }
294 
295     protected void showLiveWallpaperControl() {
296         mSetWallpaperButton.setVisibility(VISIBLE);
297         if (mWallpaper instanceof CreativeWallpaperInfo) {
298             CreativeWallpaperInfo creativeWallpaper = (CreativeWallpaperInfo) mWallpaper;
299             mWallpaperControlButtonGroup.showButton(
300                     WallpaperControlButtonGroup.EDIT,
301                     (buttonView, isChecked) -> {
302                         if (isChecked) {
303                             launchSettingsAsOverlay(/* isLaunched= */ false);
304                         }
305                         mWallpaperControlButtonGroup.setChecked(
306                                 WallpaperControlButtonGroup.EDIT, false);
307                     });
308             WallpaperManager wallpaperManager = WallpaperManager.getInstance(getContext());
309             boolean isCreativeWallpaperApplied = creativeWallpaper.isApplied(
310                     wallpaperManager.getWallpaperInfo(FLAG_SYSTEM),
311                     wallpaperManager.getWallpaperInfo(FLAG_LOCK));
312             if (!isCreativeWallpaperApplied && creativeWallpaper.canBeDeleted()) {
313                 mWallpaperControlButtonGroup.showButton(
314                         WallpaperControlButtonGroup.DELETE,
315                         (buttonView, isChecked) -> {
316                             if (isChecked) {
317                                 showDeleteConfirmDialog(
318                                         () -> creativeWallpaper.requestDelete(requireContext()));
319                             }
320                         });
321             } else {
322                 mWallpaperControlButtonGroup.hideButton(WallpaperControlButtonGroup.DELETE);
323             }
324             if (creativeWallpaper.canBeShared() && mShareActivityResult != null) {
325                 mWallpaperControlButtonGroup.showButton(
326                         WallpaperControlButtonGroup.SHARE,
327                         (buttonView, isChecked) -> {
328                             if (isChecked) {
329                                 mShareActivityResult.launch(null);
330                             }
331                         });
332             }
333 
334             if (creativeWallpaper.doesSupportWallpaperEffects()) {
335                 showWallpaperEffectsButton();
336             }
337         } else if (DeletableUtils.canBeDeleted(requireContext(),
338                 mWallpaper.getWallpaperComponent())) {
339             mWallpaperControlButtonGroup.showButton(WallpaperControlButtonGroup.DELETE,
340                     (buttonView, isChecked) -> {
341                         if (isChecked) {
342                             showDeleteConfirmDialog(() -> {
343                                         Context context = getContext();
344                                         if (context != null) {
345                                             DeletableUtils.deleteLiveWallpaper(context, mWallpaper);
346                                         }
347                                     }
348                             );
349                         }
350                     });
351         }
352         WallpaperInfo info = mWallpaper.getWallpaperComponent();
353         Uri uriSettingsSlice = getSettingsSliceUri(info);
354         if (uriSettingsSlice != null) {
355             mFloatingSheet.putFloatingSheetContent(FloatingSheet.CUSTOMIZE,
356                     new PreviewCustomizeSettingsContent(requireContext(), uriSettingsSlice));
357             mWallpaperControlButtonGroup.showButton(WallpaperControlButtonGroup.CUSTOMIZE,
358                     getFloatingSheetControlButtonChangeListener(
359                             WallpaperControlButtonGroup.CUSTOMIZE, FloatingSheet.CUSTOMIZE));
360         } else if (mSettingsActivityIntent != null
361                 && !(mWallpaper instanceof CreativeWallpaperInfo)) {
362             mWallpaperControlButtonGroup.showButton(
363                     WallpaperControlButtonGroup.CUSTOMIZE,
364                     (buttonView, isChecked) -> startActivity(mSettingsActivityIntent));
365         }
366         // update button group top margin
367         ViewGroup.MarginLayoutParams params =
368                 (ViewGroup.MarginLayoutParams) mWallpaperControlButtonGroup.getLayoutParams();
369         params.topMargin = getResources().getDimensionPixelSize(
370                 R.dimen.wallpaper_control_button_group_margin_top);
371         mWallpaperControlButtonGroup.requestLayout();
372     }
373 
374     private void showWallpaperEffectsButton() {
375         mWallpaperControlButtonGroup.showButton(WallpaperControlButtonGroup.EFFECTS,
376                 getFloatingSheetControlButtonChangeListener(WallpaperControlButtonGroup.EFFECTS,
377                         FloatingSheet.EFFECTS));
378     }
379 
380     private void launchSettingsAsOverlay(boolean isLaunched) {
381         hideOverlays();
382         mSettingsOnBackPressedCallback.remove();
383         if (!isLaunched) {
384             mSettingsActivityResult.launch(null);
385         }
386     }
387 
388     private void hideOverlays() {
389         mToolbar.setVisibility(GONE);
390         mSetWallpaperButton.setVisibility(GONE);
391         mWallpaperControlButtonGroup.setVisibility(GONE);
392         // remove callback to prevent overlay from showing again after floating sheet collapses
393         mFloatingSheet.removeFloatingSheetCallback(mShowOverlayOnHideFloatingSheetCallback);
394         mFloatingSheet.setVisibility(GONE);
395         // deselects all control buttons and sets floating sheet to collapse
396         mWallpaperControlButtonGroup.deselectAllFloatingSheetControlButtons();
397         hideScreenPreviewOverlay(true);
398         mHideOverlaysForShowingSettingsActivity = true;
399     }
400 
401     private void showOverlays() {
402         mToolbar.setVisibility(VISIBLE);
403         mSetWallpaperButton.setVisibility(VISIBLE);
404         mWallpaperControlButtonGroup.setVisibility(VISIBLE);
405         mFloatingSheet.addFloatingSheetCallback(mShowOverlayOnHideFloatingSheetCallback);
406         mFloatingSheet.setVisibility(VISIBLE);
407         hideScreenPreviewOverlay(false);
408         mHideOverlaysForShowingSettingsActivity = false;
409     }
410 
411     protected void overrideOnBackPressed(OnBackPressedCallback onBackPressedCallback) {
412         FragmentActivity activity = getActivity();
413         if (activity == null) {
414             return;
415         }
416         activity.getOnBackPressedDispatcher().addCallback(
417                 getViewLifecycleOwner(),
418                 onBackPressedCallback
419         );
420     }
421 
422     @Override
423     public void onSaveInstanceState(@NonNull Bundle outState) {
424         mHasCalledOnSaveInstanceState = true;
425         outState.putBoolean(KEY_TOOLBAR_GONE, mToolbar.getVisibility() == GONE);
426         super.onSaveInstanceState(outState);
427     }
428 
429     private void initializeEffectButton(CreativeWallpaperInfo creativeWallpaper, Context context) {
430         if (!creativeWallpaper.doesSupportWallpaperEffects()) {
431             return;
432         }
433 
434         sExecutorService.execute(() -> {
435             ArrayList<WallpaperAction> effects = creativeWallpaper.getWallpaperEffects(context);
436             if (effects == null) {
437                 return;
438             }
439 
440             creativeWallpaper.setEffectsToggles(effects);
441             mWallpaperActionSelectionBottomSheetContent =
442                     createWallpaperActionSelectionBottomSheetContent(context, creativeWallpaper);
443             mFloatingSheet.putFloatingSheetContent(FloatingSheet.EFFECTS,
444                     mWallpaperActionSelectionBottomSheetContent);
445         });
446     }
447 
448     private WallpaperActionSelectionBottomSheetContent
449             createWallpaperActionSelectionBottomSheetContent(Context context,
450             CreativeWallpaperInfo creativeWallpaper) {
451         return new WallpaperActionSelectionBottomSheetContent(context,
452                 creativeWallpaper.getEffectsBottomSheetTitle(),
453                 creativeWallpaper.getEffectsBottomSheetSubtitle(),
454                 creativeWallpaper.getClearActionsUri(),
455                 creativeWallpaper.getEffectsToggles(),
456                 creativeWallpaper.getCurrentlyAppliedEffectId(),
457                 getEffectSwitchListener(context, creativeWallpaper));
458     }
459 
460     @Override
461     public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
462         mHasCalledOnSaveInstanceState = false;
463         if (mWallpaper instanceof CreativeWallpaperInfo) {
464             Context context = requireContext();
465             CreativeWallpaperInfo creativeWallpaper = (CreativeWallpaperInfo) mWallpaper;
466             boolean isSettingsActivityPresent = savedInstanceState != null
467                     && savedInstanceState.getBoolean(KEY_TOOLBAR_GONE, false);
468             initializeEffectButton(creativeWallpaper, context);
469             if (savedInstanceState == null) {
470                 // First time at Fragment should initialize wallpaper preview.
471                 creativeWallpaper.initializeWallpaperPreview(context);
472             }
473 
474             if (savedInstanceState == null || isSettingsActivityPresent) {
475                 WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
476                 boolean isCreativeWallpaperApplied = creativeWallpaper.isApplied(
477                         wallpaperManager.getWallpaperInfo(FLAG_SYSTEM),
478                         wallpaperManager.getWallpaperInfo(FLAG_LOCK));
479                 // First time at Fragment or settings activity is at present.
480                 if (!isCreativeWallpaperApplied && !creativeWallpaper.canBeDeleted()) {
481                     // If it cannot be deleted, we must be creating a new one, launch settings.
482                     // savedInstanceState != null means is rotate state and previous fragment
483                     // already launch settings.
484                     launchSettingsAsOverlay(/* isLaunched= */ savedInstanceState != null);
485                 } else if (isSettingsActivityPresent) {
486                     hideOverlays();
487                     mSettingsOnBackPressedCallback.remove();
488                 }
489 
490             } else {
491                 showOverlays();
492                 overrideOnBackPressed(mSettingsOnBackPressedCallback);
493             }
494         }
495         super.onViewStateRestored(savedInstanceState);
496     }
497 
498     @Override
499     public void onDestroy() {
500         // Clean up the surface view
501         if (mWallpaperConnection != null) {
502             mWallpaperConnection.destroy();
503             mWallpaperConnection = null;
504         }
505         mWallpaperSurfaceCallback.cleanUp();
506         mWallpaperSurface.getHolder().removeCallback(mWallpaperSurfaceCallback);
507 
508         if (mWallpaper instanceof CreativeWallpaperInfo) {
509             if (!mHasCalledOnSaveInstanceState) {
510                 // onDestroy without rotation should clean up wallpaper preview.
511                 ((CreativeWallpaperInfo) mWallpaper).cleanUpWallpaperPreview(getContext());
512             }
513         }
514         super.onDestroy();
515     }
516 
517     private void previewLiveWallpaper() {
518         mWallpaperSurface.post(() -> {
519             ImageView homeImageWallpaper = mWallpaperSurfaceCallback.getHomeImageWallpaper();
520             if (homeImageWallpaper != null) {
521                 loadPreviewImage(homeImageWallpaper);
522             }
523             setUpLiveWallpaperPreview(mWallpaper);
524         });
525     }
526 
527     protected void loadPreviewImage(ImageView homeImageWallpaper) {
528         Context context = getContext();
529         Context appContext = context != null ? context.getApplicationContext() : null;
530         Activity activity = getActivity();
531         if (activity == null || appContext == null) {
532             return;
533         }
534         mWallpaperSurfaceCallback.setHomeImageWallpaperBlur(true);
535         ColorInfo colorInfo = getColorInfo();
536         Integer placeholderColor = colorInfo.getPlaceholderColor();
537         // This is for showing a lower resolution image before the live wallpaper shows
538         WallpaperPreviewBitmapTransformation transformation =
539                 new WallpaperPreviewBitmapTransformation(appContext, RtlUtils.isRtl(context));
540         mWallpaper.getThumbAsset(activity.getApplicationContext())
541                 .loadLowResDrawable(activity,
542                         homeImageWallpaper,
543                         placeholderColor != null
544                                 ? placeholderColor
545                                 : ResourceUtils.getColorAttr(activity,
546                                         android.R.attr.colorBackground),
547                         transformation);
548     }
549 
550     private ColorInfo getColorInfo() {
551         try {
552             return mColorFuture.get(50, TimeUnit.MILLISECONDS);
553         } catch (InterruptedException | ExecutionException | TimeoutException e) {
554             Log.i(TAG, "Couldn't obtain placeholder color", e);
555         }
556         return new ColorInfo(new WallpaperColors(Color.valueOf(Color.TRANSPARENT),
557                 /* secondaryColor= */ null, /* tertiaryColor= */ null),
558                 /* placeholderColor= */ null);
559     }
560 
561     protected void setUpLiveWallpaperPreview(
562             com.android.wallpaper.model.WallpaperInfo homeWallpaper) {
563         Activity activity = getActivity();
564         if (activity == null || activity.isFinishing()) {
565             return;
566         }
567 
568         if (mWallpaperConnection != null) {
569             mWallpaperConnection.disconnect();
570         }
571 
572         if (WallpaperConnection.isPreviewAvailable()) {
573             android.app.WallpaperInfo info = homeWallpaper.getWallpaperComponent();
574             Intent wallpaperIntent = new Intent(WallpaperService.SERVICE_INTERFACE)
575                     .setClassName(info.getPackageName(), info.getServiceName());
576             mWallpaperConnection = new WallpaperConnection(
577                     wallpaperIntent,
578                     activity,
579                     new WallpaperConnection.WallpaperConnectionListener() {
580                         @Override
581                         public void onEngineShown() {
582                             Activity activity = getActivity();
583                             if (activity == null) {
584                                 return;
585                             }
586                             ImageView homeImageWallpaper =
587                                     mWallpaperSurfaceCallback.getHomeImageWallpaper();
588                             if (homeImageWallpaper != null) {
589                                 homeImageWallpaper.animate()
590                                         .setStartDelay(250)
591                                         .setDuration(250)
592                                         .alpha(0f)
593                                         .setInterpolator(PreviewFragment.ALPHA_OUT)
594                                         .start();
595                             }
596                         }
597 
598                         @Override
599                         public void onWallpaperColorsChanged(WallpaperColors colors,
600                                 int displayId) {
601                             LivePreviewFragment.super.onWallpaperColorsChanged(colors);
602                         }
603                     },
604                     mWallpaperSurface,
605                     null,
606                     mIsViewAsHome ? FLAG_SYSTEM : FLAG_LOCK,
607                     mIsAssetIdPresent ? WallpaperConnection.WhichPreview.EDIT_NON_CURRENT
608                             : WallpaperConnection.WhichPreview.EDIT_CURRENT);
609             mWallpaperConnection.setVisibility(true);
610         } else {
611             WallpaperColorsLoader.getWallpaperColors(
612                     activity,
613                     homeWallpaper.getThumbAsset(activity),
614                     this::onWallpaperColorsChanged);
615         }
616 
617         if (mWallpaperConnection != null && !mWallpaperConnection.connect()) {
618             mWallpaperConnection = null;
619         }
620     }
621 
622     @Override
623     public void onResume() {
624         super.onResume();
625         if (mWallpaperConnection != null) {
626             mWallpaperConnection.setVisibility(true);
627         }
628     }
629 
630     @Override
631     public void onPause() {
632         super.onPause();
633         if (mWallpaperConnection != null) {
634             mWallpaperConnection.setVisibility(mHideOverlaysForShowingSettingsActivity);
635         }
636     }
637 
638     protected void showDeleteConfirmDialog(Runnable deleteRunnable) {
639         final AlertDialog alertDialog = new AlertDialog.Builder(getContext())
640                 .setMessage(R.string.delete_wallpaper_confirmation)
641                 .setOnDismissListener(dialog -> mWallpaperControlButtonGroup.setChecked(
642                         WallpaperControlButtonGroup.DELETE, false))
643                 .setPositiveButton(R.string.delete_live_wallpaper,
644                         (dialog, which) -> {
645                             deleteRunnable.run();
646                             finishActivityWithFadeTransition();
647                         })
648                 .setNegativeButton(android.R.string.cancel, null /* listener */)
649                 .create();
650         alertDialog.show();
651     }
652 
653     @Nullable
654     private Intent getSettingsActivityIntent(Context context, WallpaperInfo info) {
655         String settingsActivity = info.getSettingsActivity();
656         if (context == null || settingsActivity == null) {
657             return null;
658         }
659         Intent intent = new Intent();
660         intent.setComponent(
661                 new ComponentName(info.getPackageName(), settingsActivity));
662         intent.putExtra(WallpaperSettingsActivity.EXTRA_PREVIEW_MODE, true);
663         PackageManager pm = context.getPackageManager();
664         ActivityInfo activityInfo = intent.resolveActivityInfo(pm, 0);
665         if (activityInfo == null) {
666             Log.i(TAG, "Couldn't find wallpaper settings activity: " + settingsActivity);
667             return null;
668         }
669         return intent;
670     }
671 
672     private Uri getSettingsSliceUri(android.app.WallpaperInfo info) {
673         return info.getSettingsSliceUri();
674     }
675 }
676