/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wallpaper.picker; import static android.app.Activity.RESULT_OK; import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; import static android.stats.style.StyleEnums.SET_WALLPAPER_ENTRY_POINT_WALLPAPER_PREVIEW; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static com.android.wallpaper.module.WallpaperPersister.destinationToFlags; import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.app.WallpaperColors; import android.app.WallpaperInfo; import android.app.WallpaperManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.os.RemoteException; import android.service.wallpaper.IWallpaperConnection; import android.service.wallpaper.WallpaperService; import android.service.wallpaper.WallpaperSettingsActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import androidx.activity.OnBackPressedCallback; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; import com.android.wallpaper.R; import com.android.wallpaper.model.CreativeWallpaperInfo; import com.android.wallpaper.model.SetWallpaperViewModel; import com.android.wallpaper.model.WallpaperAction; import com.android.wallpaper.model.WallpaperInfo.ColorInfo; import com.android.wallpaper.module.WallpaperPersister; import com.android.wallpaper.util.DeletableUtils; import com.android.wallpaper.util.ResourceUtils; import com.android.wallpaper.util.RtlUtils; import com.android.wallpaper.util.WallpaperConnection; import com.android.wallpaper.util.WallpaperSurfaceCallback2; import com.android.wallpaper.widget.FloatingSheet; import com.android.wallpaper.widget.WallpaperColorsLoader; import com.android.wallpaper.widget.WallpaperControlButtonGroup; import com.android.wallpaper.widget.floatingsheetcontent.PreviewCustomizeSettingsContent; import com.android.wallpaper.widget.floatingsheetcontent.WallpaperActionSelectionBottomSheetContent; import com.android.wallpaper.widget.floatingsheetcontent.WallpaperActionsToggleAdapter; import java.util.ArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Fragment which displays the UI for previewing an individual live wallpaper, its attribution * information and settings slices if available. */ public class LivePreviewFragment extends PreviewFragment { public static final String EXTRA_LIVE_WALLPAPER_INFO = "android.live_wallpaper.info"; public static final String KEY_ACTION_DELETE_LIVE_WALLPAPER = "action_delete_live_wallpaper"; private static final String TAG = "LivePreviewFragment"; private static final String KEY_TOOLBAR_GONE = "toolbar_gone"; private static final ExecutorService sExecutorService = Executors.newCachedThreadPool(); private ActivityResultLauncher mSettingsActivityResult; private ActivityResultLauncher mShareActivityResult; private Intent mSettingsActivityIntent; private WallpaperActionSelectionBottomSheetContent mWallpaperActionSelectionBottomSheetContent; private OnBackPressedCallback mSettingsOnBackPressedCallback; private boolean mHasCalledOnSaveInstanceState = false; private boolean mHideOverlaysForShowingSettingsActivity = false; private Future mColorFuture; /** * Instance of {@link WallpaperConnection} used to bind to the live wallpaper service to show * it in this preview fragment. * * @see IWallpaperConnection */ private WallpaperConnection mWallpaperConnection; private WallpaperSurfaceCallback2 mWallpaperSurfaceCallback; private SurfaceView mWallpaperSurface; @Override protected void setWallpaper(int destination) { WallpaperPersister.SetWallpaperCallback callback = SetWallpaperViewModel.getCallback( mViewModelProvider); if (mWallpaperConnection != null) { try { mWallpaperConnection.setWallpaperFlags(destinationToFlags(destination)); } catch (RemoteException e) { callback.onError(e); return; } } mWallpaperSetter.setCurrentWallpaper( getActivity(), mWallpaper, null, SET_WALLPAPER_ENTRY_POINT_WALLPAPER_PREVIEW, destination, 0, null, mWallpaperColors != null ? mWallpaperColors : getColorInfo().getWallpaperColors(), callback); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Context context = requireContext(); mColorFuture = mWallpaper.computeColorInfo(context); mSettingsActivityIntent = getSettingsActivityIntent(getContext(), mWallpaper.getWallpaperComponent()); mSettingsOnBackPressedCallback = new OnBackPressedCallback(true) { @Override public void handleOnBackPressed() { launchSettingsAsOverlay(/* isLaunched= */ false); } }; if (mWallpaper instanceof CreativeWallpaperInfo) { mSettingsActivityResult = registerForActivityResult( CreativeWallpaperInfo.getContract(mSettingsActivityIntent), getCreativeWallpaperPreviewResultCallback()); CreativeWallpaperInfo creativeWallpaper = (CreativeWallpaperInfo) mWallpaper; if (creativeWallpaper.canBeShared()) { mShareActivityResult = registerForActivityResult(CreativeWallpaperInfo.getContract( creativeWallpaper.getShareIntent()), unused -> mWallpaperControlButtonGroup.setChecked( WallpaperControlButtonGroup.SHARE, /* checked= */ false)); } } } private ActivityResultCallback getCreativeWallpaperPreviewResultCallback() { return result -> { CreativeWallpaperInfo creativeWallpaper = (CreativeWallpaperInfo) mWallpaper; WallpaperManager wallpaperManager = WallpaperManager.getInstance(getContext()); boolean isCreativeWallpaperApplied = creativeWallpaper.isApplied( wallpaperManager.getWallpaperInfo(FLAG_SYSTEM), wallpaperManager.getWallpaperInfo(FLAG_LOCK)); if (result == RESULT_OK) { if (creativeWallpaper.canBeDeleted() || isCreativeWallpaperApplied) { // When editing an existing wallpaper and pressing "Done" button causing the // overlays to become visible showOverlays(); overrideOnBackPressed(new OnBackPressedCallback(true) { @Override public void handleOnBackPressed() { getActivity().finish(); } }); } else { // When in the process of creating a new wallpaper and pressing "Done" button. showOverlays(); overrideOnBackPressed(mSettingsOnBackPressedCallback); } } else { // When you initiate the editing process for a wallpaper and then decide to exit // by pressing the back button during editing. if (creativeWallpaper.canBeDeleted() || isCreativeWallpaperApplied) { showOverlays(); } else { // Flow where user opens a template (so the settings activity is launched) // but user just simply presses back (without moving on to the next screen) // TODO: This should ideally be a slide transition, but custom slide transition // does not work properly, so having a fade transition for now finishActivityWithFadeTransition(); } } }; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); if (view == null) { return null; } // Live wallpaper surface Context context = requireContext(); mWallpaperSurface = view.findViewById(R.id.wallpaper_surface); mWallpaperSurfaceCallback = new WallpaperSurfaceCallback2(context, mWallpaperSurface, mColorFuture, this::previewLiveWallpaper); mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback); mWallpaperSurface.setZOrderMediaOverlay(true); setUpLiveWallpaperTouchForwarding(mTouchForwardingLayout); showLiveWallpaperControl(); return view; } private WallpaperActionsToggleAdapter.WallpaperEffectSwitchListener getEffectSwitchListener( Context context, CreativeWallpaperInfo creativeWallpaper) { return (checkedItem) -> { for (WallpaperAction wallpaperActionToggle : creativeWallpaper.getEffectsToggles()) { wallpaperActionToggle.setToggled(false); } if (checkedItem >= 0) { WallpaperAction currentEffect = creativeWallpaper.getEffectsToggles().get( checkedItem); currentEffect.setToggled(true); creativeWallpaper .setCurrentlyAppliedEffectId( currentEffect.getEffectId()); creativeWallpaper.applyEffect(context, currentEffect.getApplyActionUri()); mWallpaperActionSelectionBottomSheetContent.setCurrentlyAppliedEffect( currentEffect.getEffectId()); } else { creativeWallpaper.setCurrentlyAppliedEffectId(null); creativeWallpaper.clearEffects(context); mWallpaperActionSelectionBottomSheetContent.setCurrentlyAppliedEffect(null); } }; } @SuppressLint("ClickableViewAccessibility") private void setUpLiveWallpaperTouchForwarding(TouchForwardingLayout touchForwardingLayout) { touchForwardingLayout.setTargetView(mWallpaperSurface); touchForwardingLayout.setForwardingEnabled(true); touchForwardingLayout.setOnClickListener(v -> { toggleWallpaperPreviewControl(); mTouchForwardingLayout.announceForAccessibility( getString(mPreviewScrim.getVisibility() == View.VISIBLE ? R.string.show_preview_controls_content_description : R.string.hide_preview_controls_content_description) ); }); mWallpaperSurface.setOnTouchListener((v, ev) -> { dispatchTouchEventOnLiveWallpaperSurface(ev); return false; }); } private void dispatchTouchEventOnLiveWallpaperSurface(MotionEvent ev) { if (mWallpaperConnection != null && mWallpaperConnection.getEngine() != null) { int action = ev.getActionMasked(); MotionEvent dup = MotionEvent.obtainNoHistory(ev); dup.setLocation(ev.getX(), ev.getY()); try { mWallpaperConnection.getEngine().dispatchPointer(dup); if (action == MotionEvent.ACTION_UP) { mWallpaperConnection.getEngine().dispatchWallpaperCommand( WallpaperManager.COMMAND_TAP, (int) ev.getX(), (int) ev.getY(), 0, null); } else if (action == MotionEvent.ACTION_POINTER_UP) { int pointerIndex = ev.getActionIndex(); mWallpaperConnection.getEngine().dispatchWallpaperCommand( WallpaperManager.COMMAND_SECONDARY_TAP, (int) ev.getX(pointerIndex), (int) ev.getY(pointerIndex), 0, null); } } catch (RemoteException e) { Log.e(TAG, "Remote exception of wallpaper connection"); } } } protected void showLiveWallpaperControl() { mSetWallpaperButton.setVisibility(VISIBLE); if (mWallpaper instanceof CreativeWallpaperInfo) { CreativeWallpaperInfo creativeWallpaper = (CreativeWallpaperInfo) mWallpaper; mWallpaperControlButtonGroup.showButton( WallpaperControlButtonGroup.EDIT, (buttonView, isChecked) -> { if (isChecked) { launchSettingsAsOverlay(/* isLaunched= */ false); } mWallpaperControlButtonGroup.setChecked( WallpaperControlButtonGroup.EDIT, false); }); WallpaperManager wallpaperManager = WallpaperManager.getInstance(getContext()); boolean isCreativeWallpaperApplied = creativeWallpaper.isApplied( wallpaperManager.getWallpaperInfo(FLAG_SYSTEM), wallpaperManager.getWallpaperInfo(FLAG_LOCK)); if (!isCreativeWallpaperApplied && creativeWallpaper.canBeDeleted()) { mWallpaperControlButtonGroup.showButton( WallpaperControlButtonGroup.DELETE, (buttonView, isChecked) -> { if (isChecked) { showDeleteConfirmDialog( () -> creativeWallpaper.requestDelete(requireContext())); } }); } else { mWallpaperControlButtonGroup.hideButton(WallpaperControlButtonGroup.DELETE); } if (creativeWallpaper.canBeShared() && mShareActivityResult != null) { mWallpaperControlButtonGroup.showButton( WallpaperControlButtonGroup.SHARE, (buttonView, isChecked) -> { if (isChecked) { mShareActivityResult.launch(null); } }); } if (creativeWallpaper.doesSupportWallpaperEffects()) { showWallpaperEffectsButton(); } } else if (DeletableUtils.canBeDeleted(requireContext(), mWallpaper.getWallpaperComponent())) { mWallpaperControlButtonGroup.showButton(WallpaperControlButtonGroup.DELETE, (buttonView, isChecked) -> { if (isChecked) { showDeleteConfirmDialog(() -> { Context context = getContext(); if (context != null) { DeletableUtils.deleteLiveWallpaper(context, mWallpaper); } } ); } }); } WallpaperInfo info = mWallpaper.getWallpaperComponent(); Uri uriSettingsSlice = getSettingsSliceUri(info); if (uriSettingsSlice != null) { mFloatingSheet.putFloatingSheetContent(FloatingSheet.CUSTOMIZE, new PreviewCustomizeSettingsContent(requireContext(), uriSettingsSlice)); mWallpaperControlButtonGroup.showButton(WallpaperControlButtonGroup.CUSTOMIZE, getFloatingSheetControlButtonChangeListener( WallpaperControlButtonGroup.CUSTOMIZE, FloatingSheet.CUSTOMIZE)); } else if (mSettingsActivityIntent != null && !(mWallpaper instanceof CreativeWallpaperInfo)) { mWallpaperControlButtonGroup.showButton( WallpaperControlButtonGroup.CUSTOMIZE, (buttonView, isChecked) -> startActivity(mSettingsActivityIntent)); } // update button group top margin ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mWallpaperControlButtonGroup.getLayoutParams(); params.topMargin = getResources().getDimensionPixelSize( R.dimen.wallpaper_control_button_group_margin_top); mWallpaperControlButtonGroup.requestLayout(); } private void showWallpaperEffectsButton() { mWallpaperControlButtonGroup.showButton(WallpaperControlButtonGroup.EFFECTS, getFloatingSheetControlButtonChangeListener(WallpaperControlButtonGroup.EFFECTS, FloatingSheet.EFFECTS)); } private void launchSettingsAsOverlay(boolean isLaunched) { hideOverlays(); mSettingsOnBackPressedCallback.remove(); if (!isLaunched) { mSettingsActivityResult.launch(null); } } private void hideOverlays() { mToolbar.setVisibility(GONE); mSetWallpaperButton.setVisibility(GONE); mWallpaperControlButtonGroup.setVisibility(GONE); // remove callback to prevent overlay from showing again after floating sheet collapses mFloatingSheet.removeFloatingSheetCallback(mShowOverlayOnHideFloatingSheetCallback); mFloatingSheet.setVisibility(GONE); // deselects all control buttons and sets floating sheet to collapse mWallpaperControlButtonGroup.deselectAllFloatingSheetControlButtons(); hideScreenPreviewOverlay(true); mHideOverlaysForShowingSettingsActivity = true; } private void showOverlays() { mToolbar.setVisibility(VISIBLE); mSetWallpaperButton.setVisibility(VISIBLE); mWallpaperControlButtonGroup.setVisibility(VISIBLE); mFloatingSheet.addFloatingSheetCallback(mShowOverlayOnHideFloatingSheetCallback); mFloatingSheet.setVisibility(VISIBLE); hideScreenPreviewOverlay(false); mHideOverlaysForShowingSettingsActivity = false; } protected void overrideOnBackPressed(OnBackPressedCallback onBackPressedCallback) { FragmentActivity activity = getActivity(); if (activity == null) { return; } activity.getOnBackPressedDispatcher().addCallback( getViewLifecycleOwner(), onBackPressedCallback ); } @Override public void onSaveInstanceState(@NonNull Bundle outState) { mHasCalledOnSaveInstanceState = true; outState.putBoolean(KEY_TOOLBAR_GONE, mToolbar.getVisibility() == GONE); super.onSaveInstanceState(outState); } private void initializeEffectButton(CreativeWallpaperInfo creativeWallpaper, Context context) { if (!creativeWallpaper.doesSupportWallpaperEffects()) { return; } sExecutorService.execute(() -> { ArrayList effects = creativeWallpaper.getWallpaperEffects(context); if (effects == null) { return; } creativeWallpaper.setEffectsToggles(effects); mWallpaperActionSelectionBottomSheetContent = createWallpaperActionSelectionBottomSheetContent(context, creativeWallpaper); mFloatingSheet.putFloatingSheetContent(FloatingSheet.EFFECTS, mWallpaperActionSelectionBottomSheetContent); }); } private WallpaperActionSelectionBottomSheetContent createWallpaperActionSelectionBottomSheetContent(Context context, CreativeWallpaperInfo creativeWallpaper) { return new WallpaperActionSelectionBottomSheetContent(context, creativeWallpaper.getEffectsBottomSheetTitle(), creativeWallpaper.getEffectsBottomSheetSubtitle(), creativeWallpaper.getClearActionsUri(), creativeWallpaper.getEffectsToggles(), creativeWallpaper.getCurrentlyAppliedEffectId(), getEffectSwitchListener(context, creativeWallpaper)); } @Override public void onViewStateRestored(@Nullable Bundle savedInstanceState) { mHasCalledOnSaveInstanceState = false; if (mWallpaper instanceof CreativeWallpaperInfo) { Context context = requireContext(); CreativeWallpaperInfo creativeWallpaper = (CreativeWallpaperInfo) mWallpaper; boolean isSettingsActivityPresent = savedInstanceState != null && savedInstanceState.getBoolean(KEY_TOOLBAR_GONE, false); initializeEffectButton(creativeWallpaper, context); if (savedInstanceState == null) { // First time at Fragment should initialize wallpaper preview. creativeWallpaper.initializeWallpaperPreview(context); } if (savedInstanceState == null || isSettingsActivityPresent) { WallpaperManager wallpaperManager = WallpaperManager.getInstance(context); boolean isCreativeWallpaperApplied = creativeWallpaper.isApplied( wallpaperManager.getWallpaperInfo(FLAG_SYSTEM), wallpaperManager.getWallpaperInfo(FLAG_LOCK)); // First time at Fragment or settings activity is at present. if (!isCreativeWallpaperApplied && !creativeWallpaper.canBeDeleted()) { // If it cannot be deleted, we must be creating a new one, launch settings. // savedInstanceState != null means is rotate state and previous fragment // already launch settings. launchSettingsAsOverlay(/* isLaunched= */ savedInstanceState != null); } else if (isSettingsActivityPresent) { hideOverlays(); mSettingsOnBackPressedCallback.remove(); } } else { showOverlays(); overrideOnBackPressed(mSettingsOnBackPressedCallback); } } super.onViewStateRestored(savedInstanceState); } @Override public void onDestroy() { // Clean up the surface view if (mWallpaperConnection != null) { mWallpaperConnection.destroy(); mWallpaperConnection = null; } mWallpaperSurfaceCallback.cleanUp(); mWallpaperSurface.getHolder().removeCallback(mWallpaperSurfaceCallback); if (mWallpaper instanceof CreativeWallpaperInfo) { if (!mHasCalledOnSaveInstanceState) { // onDestroy without rotation should clean up wallpaper preview. ((CreativeWallpaperInfo) mWallpaper).cleanUpWallpaperPreview(getContext()); } } super.onDestroy(); } private void previewLiveWallpaper() { mWallpaperSurface.post(() -> { ImageView homeImageWallpaper = mWallpaperSurfaceCallback.getHomeImageWallpaper(); if (homeImageWallpaper != null) { loadPreviewImage(homeImageWallpaper); } setUpLiveWallpaperPreview(mWallpaper); }); } protected void loadPreviewImage(ImageView homeImageWallpaper) { Context context = getContext(); Context appContext = context != null ? context.getApplicationContext() : null; Activity activity = getActivity(); if (activity == null || appContext == null) { return; } mWallpaperSurfaceCallback.setHomeImageWallpaperBlur(true); ColorInfo colorInfo = getColorInfo(); Integer placeholderColor = colorInfo.getPlaceholderColor(); // This is for showing a lower resolution image before the live wallpaper shows WallpaperPreviewBitmapTransformation transformation = new WallpaperPreviewBitmapTransformation(appContext, RtlUtils.isRtl(context)); mWallpaper.getThumbAsset(activity.getApplicationContext()) .loadLowResDrawable(activity, homeImageWallpaper, placeholderColor != null ? placeholderColor : ResourceUtils.getColorAttr(activity, android.R.attr.colorBackground), transformation); } private ColorInfo getColorInfo() { try { return mColorFuture.get(50, TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { Log.i(TAG, "Couldn't obtain placeholder color", e); } return new ColorInfo(new WallpaperColors(Color.valueOf(Color.TRANSPARENT), /* secondaryColor= */ null, /* tertiaryColor= */ null), /* placeholderColor= */ null); } protected void setUpLiveWallpaperPreview( com.android.wallpaper.model.WallpaperInfo homeWallpaper) { Activity activity = getActivity(); if (activity == null || activity.isFinishing()) { return; } if (mWallpaperConnection != null) { mWallpaperConnection.disconnect(); } if (WallpaperConnection.isPreviewAvailable()) { android.app.WallpaperInfo info = homeWallpaper.getWallpaperComponent(); Intent wallpaperIntent = new Intent(WallpaperService.SERVICE_INTERFACE) .setClassName(info.getPackageName(), info.getServiceName()); mWallpaperConnection = new WallpaperConnection( wallpaperIntent, activity, new WallpaperConnection.WallpaperConnectionListener() { @Override public void onEngineShown() { Activity activity = getActivity(); if (activity == null) { return; } ImageView homeImageWallpaper = mWallpaperSurfaceCallback.getHomeImageWallpaper(); if (homeImageWallpaper != null) { homeImageWallpaper.animate() .setStartDelay(250) .setDuration(250) .alpha(0f) .setInterpolator(PreviewFragment.ALPHA_OUT) .start(); } } @Override public void onWallpaperColorsChanged(WallpaperColors colors, int displayId) { LivePreviewFragment.super.onWallpaperColorsChanged(colors); } }, mWallpaperSurface, null, mIsViewAsHome ? FLAG_SYSTEM : FLAG_LOCK, mIsAssetIdPresent ? WallpaperConnection.WhichPreview.EDIT_NON_CURRENT : WallpaperConnection.WhichPreview.EDIT_CURRENT); mWallpaperConnection.setVisibility(true); } else { WallpaperColorsLoader.getWallpaperColors( activity, homeWallpaper.getThumbAsset(activity), this::onWallpaperColorsChanged); } if (mWallpaperConnection != null && !mWallpaperConnection.connect()) { mWallpaperConnection = null; } } @Override public void onResume() { super.onResume(); if (mWallpaperConnection != null) { mWallpaperConnection.setVisibility(true); } } @Override public void onPause() { super.onPause(); if (mWallpaperConnection != null) { mWallpaperConnection.setVisibility(mHideOverlaysForShowingSettingsActivity); } } protected void showDeleteConfirmDialog(Runnable deleteRunnable) { final AlertDialog alertDialog = new AlertDialog.Builder(getContext()) .setMessage(R.string.delete_wallpaper_confirmation) .setOnDismissListener(dialog -> mWallpaperControlButtonGroup.setChecked( WallpaperControlButtonGroup.DELETE, false)) .setPositiveButton(R.string.delete_live_wallpaper, (dialog, which) -> { deleteRunnable.run(); finishActivityWithFadeTransition(); }) .setNegativeButton(android.R.string.cancel, null /* listener */) .create(); alertDialog.show(); } @Nullable private Intent getSettingsActivityIntent(Context context, WallpaperInfo info) { String settingsActivity = info.getSettingsActivity(); if (context == null || settingsActivity == null) { return null; } Intent intent = new Intent(); intent.setComponent( new ComponentName(info.getPackageName(), settingsActivity)); intent.putExtra(WallpaperSettingsActivity.EXTRA_PREVIEW_MODE, true); PackageManager pm = context.getPackageManager(); ActivityInfo activityInfo = intent.resolveActivityInfo(pm, 0); if (activityInfo == null) { Log.i(TAG, "Couldn't find wallpaper settings activity: " + settingsActivity); return null; } return intent; } private Uri getSettingsSliceUri(android.app.WallpaperInfo info) { return info.getSettingsSliceUri(); } }