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