diff options
Diffstat (limited to 'src/com/android/wallpaper/model/WallpaperSectionController.java')
-rw-r--r-- | src/com/android/wallpaper/model/WallpaperSectionController.java | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/src/com/android/wallpaper/model/WallpaperSectionController.java b/src/com/android/wallpaper/model/WallpaperSectionController.java new file mode 100644 index 0000000..f8bd2f3 --- /dev/null +++ b/src/com/android/wallpaper/model/WallpaperSectionController.java @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2021 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.model; + +import static android.Manifest.permission.READ_EXTERNAL_STORAGE; + +import android.animation.Animator; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.WallpaperColors; +import android.app.WallpaperManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.net.Uri; +import android.os.Bundle; +import android.provider.Settings; +import android.service.wallpaper.WallpaperService; +import android.view.LayoutInflater; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.MainThread; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.cardview.widget.CardView; +import androidx.core.widget.ContentLoadingProgressBar; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.OnLifecycleEvent; + +import com.android.wallpaper.R; +import com.android.wallpaper.asset.Asset; +import com.android.wallpaper.asset.BitmapCachingAsset; +import com.android.wallpaper.module.CurrentWallpaperInfoFactory; +import com.android.wallpaper.module.InjectorProvider; +import com.android.wallpaper.module.UserEventLogger; +import com.android.wallpaper.picker.CategorySelectorFragment; +import com.android.wallpaper.picker.MyPhotosStarter; +import com.android.wallpaper.picker.WallpaperSectionView; +import com.android.wallpaper.picker.WorkspaceSurfaceHolderCallback; +import com.android.wallpaper.util.ResourceUtils; +import com.android.wallpaper.util.WallpaperConnection; +import com.android.wallpaper.util.WallpaperSurfaceCallback; +import com.android.wallpaper.widget.LockScreenPreviewer; + +/** The class to control the wallpaper section view. */ +public class WallpaperSectionController implements + CustomizationSectionController<WallpaperSectionView>, + LifecycleObserver { + + private static final String PERMISSION_READ_WALLPAPER_INTERNAL = + "android.permission.READ_WALLPAPER_INTERNAL"; + private static final int SETTINGS_APP_INFO_REQUEST_CODE = 1; + + private CardView mHomePreviewCard; + private ContentLoadingProgressBar mHomePreviewProgress; + private SurfaceView mWorkspaceSurface; + private WorkspaceSurfaceHolderCallback mWorkspaceSurfaceCallback; + private SurfaceView mHomeWallpaperSurface; + private WallpaperSurfaceCallback mHomeWallpaperSurfaceCallback; + private SurfaceView mLockWallpaperSurface; + private WallpaperSurfaceCallback mLockWallpaperSurfaceCallback; + private CardView mLockscreenPreviewCard; + private ViewGroup mLockPreviewContainer; + private ContentLoadingProgressBar mLockscreenPreviewProgress; + private WallpaperConnection mWallpaperConnection; + + // The wallpaper information which is currently shown on the home preview. + private WallpaperInfo mHomePreviewWallpaperInfo; + // The wallpaper information which is currently shown on the lock preview. + private WallpaperInfo mLockPreviewWallpaperInfo; + + private LockScreenPreviewer mLockScreenPreviewer; + + private final Activity mActivity; + private final Context mAppContext; + private final LifecycleOwner mLifecycleOwner; + private final PermissionRequester mPermissionRequester; + private final WallpaperColorsViewModel mWallpaperColorsViewModel; + private final WorkspaceViewModel mWorkspaceViewModel; + private final CustomizationSectionNavigationController mSectionNavigationController; + private final WallpaperPreviewNavigator mWallpaperPreviewNavigator; + private final Bundle mSavedInstanceState; + + public WallpaperSectionController(Activity activity, LifecycleOwner lifecycleOwner, + PermissionRequester permissionRequester, WallpaperColorsViewModel colorsViewModel, + WorkspaceViewModel workspaceViewModel, + CustomizationSectionNavigationController sectionNavigationController, + WallpaperPreviewNavigator wallpaperPreviewNavigator, + Bundle savedInstanceState) { + mActivity = activity; + mLifecycleOwner = lifecycleOwner; + mPermissionRequester = permissionRequester; + mAppContext = mActivity.getApplicationContext(); + mWallpaperColorsViewModel = colorsViewModel; + mWorkspaceViewModel = workspaceViewModel; + mSectionNavigationController = sectionNavigationController; + mWallpaperPreviewNavigator = wallpaperPreviewNavigator; + mSavedInstanceState = savedInstanceState; + } + + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + @MainThread + public void onResume() { + refreshCurrentWallpapers(/* forceRefresh= */ mSavedInstanceState == null); + if (mWallpaperConnection != null) { + mWallpaperConnection.setVisibility(true); + } + } + + @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) + @MainThread + public void onPause() { + if (mWallpaperConnection != null) { + mWallpaperConnection.setVisibility(false); + } + } + + @OnLifecycleEvent(Lifecycle.Event.ON_STOP) + @MainThread + public void onStop() { + if (mWallpaperConnection != null) { + mWallpaperConnection.disconnect(); + mWallpaperConnection = null; + } + } + + @Override + public boolean isAvailable(Context context) { + return true; + } + + @Override + public WallpaperSectionView createView(Context context) { + WallpaperSectionView wallpaperSectionView = (WallpaperSectionView) LayoutInflater.from( + context).inflate(R.layout.wallpaper_section_view, /* root= */ null); + mHomePreviewCard = wallpaperSectionView.findViewById(R.id.home_preview); + mHomePreviewCard.setContentDescription(mAppContext.getString( + R.string.wallpaper_preview_card_content_description)); + mWorkspaceSurface = mHomePreviewCard.findViewById(R.id.workspace_surface); + mHomePreviewProgress = mHomePreviewCard.findViewById(R.id.wallpaper_preview_spinner); + mWorkspaceSurfaceCallback = new WorkspaceSurfaceHolderCallback( + mWorkspaceSurface, mAppContext); + mHomeWallpaperSurface = mHomePreviewCard.findViewById(R.id.wallpaper_surface); + mHomeWallpaperSurfaceCallback = new WallpaperSurfaceCallback(mActivity, mHomePreviewCard, + mHomeWallpaperSurface, () -> { + if (mHomePreviewWallpaperInfo != null) { + maybeLoadThumbnail(mHomePreviewWallpaperInfo, mHomeWallpaperSurfaceCallback); + } + }); + + mLockscreenPreviewCard = wallpaperSectionView.findViewById(R.id.lock_preview); + mLockscreenPreviewCard.setContentDescription(mAppContext.getString( + R.string.lockscreen_wallpaper_preview_card_content_description)); + mLockscreenPreviewProgress = mLockscreenPreviewCard.findViewById( + R.id.wallpaper_preview_spinner); + mLockscreenPreviewCard.findViewById(R.id.workspace_surface).setVisibility(View.GONE); + mLockWallpaperSurface = mLockscreenPreviewCard.findViewById(R.id.wallpaper_surface); + mLockWallpaperSurfaceCallback = new WallpaperSurfaceCallback(mActivity, + mLockscreenPreviewCard, mLockWallpaperSurface, () -> { + if (mLockPreviewWallpaperInfo != null) { + maybeLoadThumbnail(mLockPreviewWallpaperInfo, mLockWallpaperSurfaceCallback); + } + }); + mLockPreviewContainer = mLockscreenPreviewCard.findViewById( + R.id.lock_screen_preview_container); + mLockPreviewContainer.setVisibility(View.INVISIBLE); + mLockScreenPreviewer = new LockScreenPreviewer(mLifecycleOwner.getLifecycle(), context, + mLockPreviewContainer); + + setupCurrentWallpaperPreview(wallpaperSectionView); + final int shortDuration = mAppContext.getResources().getInteger( + android.R.integer.config_shortAnimTime); + fadeWallpaperPreview(true, shortDuration); + mLifecycleOwner.getLifecycle().addObserver(this); + updateWallpaperSurface(); + updateWorkspaceSurface(); + + wallpaperSectionView.findViewById(R.id.wallpaper_picker_entry).setOnClickListener( + v -> mSectionNavigationController.navigateTo(new CategorySelectorFragment())); + + mWorkspaceViewModel.getUpdateWorkspace().observe(mLifecycleOwner, update -> + updateWorkspacePreview(mWorkspaceSurface, mWorkspaceSurfaceCallback, + mWallpaperColorsViewModel.getHomeWallpaperColors().getValue()) + ); + + return wallpaperSectionView; + } + + private void updateWorkspacePreview(SurfaceView workspaceSurface, + WorkspaceSurfaceHolderCallback callback, @Nullable WallpaperColors colors) { + // Reattach SurfaceView to trigger #surfaceCreated to update preview for different option. + ViewGroup parent = (ViewGroup) workspaceSurface.getParent(); + int viewIndex = parent.indexOfChild(workspaceSurface); + parent.removeView(workspaceSurface); + if (callback != null) { + callback.resetLastSurface(); + callback.setWallpaperColors(colors); + } + parent.addView(workspaceSurface, viewIndex); + } + + @Override + public void release() { + if (mLockScreenPreviewer != null) { + mLockScreenPreviewer.release(); + mLockScreenPreviewer = null; + } + if (mHomeWallpaperSurfaceCallback != null) { + mHomeWallpaperSurfaceCallback.cleanUp(); + } + if (mLockWallpaperSurfaceCallback != null) { + mLockWallpaperSurfaceCallback.cleanUp(); + } + if (mWorkspaceSurfaceCallback != null) { + mWorkspaceSurfaceCallback.cleanUp(); + } + mLifecycleOwner.getLifecycle().removeObserver(this); + } + + private void setupCurrentWallpaperPreview(View rootView) { + if (canShowCurrentWallpaper()) { + showCurrentWallpaper(rootView, true); + } else { + showCurrentWallpaper(rootView, false); + + Button mAllowAccessButton = rootView + .findViewById(R.id.permission_needed_allow_access_button); + mAllowAccessButton.setOnClickListener(view -> + mPermissionRequester.requestExternalStoragePermission( + new MyPhotosStarter.PermissionChangedListener() { + + @Override + public void onPermissionsGranted() { + showCurrentWallpaper(rootView, true); + } + + @Override + public void onPermissionsDenied(boolean dontAskAgain) { + if (!dontAskAgain) { + return; + } + showPermissionNeededDialog(); + } + }) + ); + + // Replace explanation text with text containing the Wallpapers app name which replaces + // the placeholder. + Resources resources = mAppContext.getResources(); + String appName = resources.getString(R.string.app_name); + String explanation = resources.getString(R.string.permission_needed_explanation, + appName); + TextView explanationView = rootView.findViewById(R.id.permission_needed_explanation); + explanationView.setText(explanation); + } + } + + private boolean canShowCurrentWallpaper() { + return isPermissionGranted(mAppContext, PERMISSION_READ_WALLPAPER_INTERNAL) + || isPermissionGranted(mAppContext, READ_EXTERNAL_STORAGE); + } + + private boolean isPermissionGranted(Context context, String permission) { + return context.getPackageManager().checkPermission(permission, + context.getPackageName()) == PackageManager.PERMISSION_GRANTED; + } + + private void showCurrentWallpaper(View rootView, boolean show) { + rootView.findViewById(R.id.home_preview) + .setVisibility(show ? View.VISIBLE : View.GONE); + rootView.findViewById(R.id.lock_preview) + .setVisibility(show ? View.VISIBLE : View.GONE); + rootView.findViewById(R.id.permission_needed) + .setVisibility(show ? View.GONE : View.VISIBLE); + } + + private void showPermissionNeededDialog() { + String permissionNeededMessage = mAppContext.getResources().getString( + R.string.permission_needed_explanation_go_to_settings); + AlertDialog dialog = new AlertDialog.Builder(mAppContext, R.style.LightDialogTheme) + .setMessage(permissionNeededMessage) + .setPositiveButton(android.R.string.ok, /* onClickListener= */ null) + .setNegativeButton( + R.string.settings_button_label, + (dialogInterface, i) -> { + Intent appInfoIntent = new Intent(); + appInfoIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", + mAppContext.getPackageName(), /* fragment= */ null); + appInfoIntent.setData(uri); + mActivity.startActivityForResult(appInfoIntent, + SETTINGS_APP_INFO_REQUEST_CODE); + }) + .create(); + dialog.show(); + } + + /** + * Obtains the {@link WallpaperInfo} object(s) representing the wallpaper(s) currently set to + * the device from the {@link CurrentWallpaperInfoFactory}. + */ + private void refreshCurrentWallpapers(boolean forceRefresh) { + CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector() + .getCurrentWallpaperFactory(mAppContext); + + factory.createCurrentWallpaperInfos( + (homeWallpaper, lockWallpaper, presentationMode) -> { + // A config change may have destroyed the activity since the refresh + // started, so check for that. + if (!isActivityAlive()) { + return; + } + + mHomePreviewWallpaperInfo = homeWallpaper; + mLockPreviewWallpaperInfo = + lockWallpaper == null ? homeWallpaper : lockWallpaper; + + mHomePreviewWallpaperInfo.computePlaceholderColor(mAppContext); + if (lockWallpaper != null) { + lockWallpaper.computePlaceholderColor(mAppContext); + } + updatePreview(mHomePreviewWallpaperInfo, true); + updatePreview(mLockPreviewWallpaperInfo, false); + + WallpaperManager manager = WallpaperManager.getInstance(mAppContext); + + WallpaperColors homeColors = + manager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM); + onHomeWallpaperColorsChanged(homeColors); + WallpaperColors lockColors = homeColors; + + if (lockWallpaper != null) { + lockColors = manager.getWallpaperColors(WallpaperManager.FLAG_LOCK); + + } + onLockWallpaperColorsChanged(lockColors); + }, forceRefresh); + } + + private void updatePreview(WallpaperInfo wallpaperInfo, boolean isHomeWallpaper) { + if (wallpaperInfo == null) { + return; + } + + if (!isActivityAlive()) { + return; + } + + UserEventLogger eventLogger = InjectorProvider.getInjector().getUserEventLogger( + mAppContext); + + WallpaperSurfaceCallback surfaceCallback = isHomeWallpaper + ? mHomeWallpaperSurfaceCallback : mLockWallpaperSurfaceCallback; + // Load thumb regardless of live wallpaper to make sure we have a placeholder while + // the live wallpaper initializes in that case. + maybeLoadThumbnail(wallpaperInfo, surfaceCallback); + + if (isHomeWallpaper) { + if (mWallpaperConnection != null) { + mWallpaperConnection.disconnect(); + mWallpaperConnection = null; + } + if (wallpaperInfo instanceof LiveWallpaperInfo) { + setUpLiveWallpaperPreview(wallpaperInfo); + } + } + + View preview = isHomeWallpaper ? mHomePreviewCard : mLockscreenPreviewCard; + preview.setOnClickListener(view -> { + mWallpaperPreviewNavigator.showViewOnlyPreview(wallpaperInfo, isHomeWallpaper); + eventLogger.logCurrentWallpaperPreviewed(); + }); + } + + @NonNull + private Asset maybeLoadThumbnail(WallpaperInfo wallpaperInfo, + WallpaperSurfaceCallback surfaceCallback) { + ImageView imageView = surfaceCallback.getHomeImageWallpaper(); + Asset thumbAsset = new BitmapCachingAsset(mAppContext, + wallpaperInfo.getThumbAsset(mAppContext)); + if (imageView != null && imageView.getDrawable() == null) { + thumbAsset.loadPreviewImage(mActivity, imageView, + ResourceUtils.getColorAttr(mActivity, android.R.attr.colorSecondary)); + } + return thumbAsset; + } + + private void onHomeWallpaperColorsChanged(WallpaperColors wallpaperColors) { + if (wallpaperColors != null && wallpaperColors.equals( + mWallpaperColorsViewModel.getHomeWallpaperColors().getValue())) { + return; + } + mWallpaperColorsViewModel.getHomeWallpaperColors().setValue(wallpaperColors); + } + + private void onLockWallpaperColorsChanged(WallpaperColors wallpaperColors) { + if (wallpaperColors != null && wallpaperColors.equals( + mWallpaperColorsViewModel.getLockWallpaperColors().getValue())) { + return; + } + mWallpaperColorsViewModel.getLockWallpaperColors().setValue(wallpaperColors); + if (mLockScreenPreviewer != null) { + mLockScreenPreviewer.setColor(wallpaperColors); + } + } + + private void setUpLiveWallpaperPreview(WallpaperInfo homeWallpaper) { + if (!isActivityAlive()) { + return; + } + + if (WallpaperConnection.isPreviewAvailable()) { + final boolean isLockLive = mLockPreviewWallpaperInfo instanceof LiveWallpaperInfo; + mWallpaperConnection = new WallpaperConnection( + getWallpaperIntent(homeWallpaper.getWallpaperComponent()), mActivity, + new WallpaperConnection.WallpaperConnectionListener() { + @Override + public void onWallpaperColorsChanged(WallpaperColors colors, + int displayId) { + if (isLockLive && mLockScreenPreviewer != null) { + mLockScreenPreviewer.setColor(colors); + onLockWallpaperColorsChanged(colors); + } + onHomeWallpaperColorsChanged(colors); + } + }, + mHomeWallpaperSurface, isLockLive ? mLockWallpaperSurface : null); + + mWallpaperConnection.setVisibility(true); + mHomeWallpaperSurface.post(() -> { + if (mWallpaperConnection != null && !mWallpaperConnection.connect()) { + mWallpaperConnection = null; + } + }); + } + } + + private Intent getWallpaperIntent(android.app.WallpaperInfo info) { + return new Intent(WallpaperService.SERVICE_INTERFACE) + .setClassName(info.getPackageName(), info.getServiceName()); + } + + private void updateWallpaperSurface() { + mHomeWallpaperSurface.getHolder().addCallback(mHomeWallpaperSurfaceCallback); + mHomeWallpaperSurface.setZOrderMediaOverlay(true); + mLockWallpaperSurface.getHolder().addCallback(mLockWallpaperSurfaceCallback); + mLockWallpaperSurface.setZOrderMediaOverlay(true); + } + + private void updateWorkspaceSurface() { + mWorkspaceSurface.setZOrderMediaOverlay(true); + mWorkspaceSurface.getHolder().addCallback(mWorkspaceSurfaceCallback); + } + + private boolean isActivityAlive() { + return !mActivity.isDestroyed() && !mActivity.isFinishing(); + } + + private void fadeWallpaperPreview(boolean isFadeIn, int duration) { + setupFade(mHomePreviewCard, mHomePreviewProgress, duration, isFadeIn); + setupFade(mLockscreenPreviewCard, mLockscreenPreviewProgress, duration, isFadeIn); + } + + private void setupFade(CardView cardView, ContentLoadingProgressBar progressBar, int duration, + boolean fadeIn) { + cardView.setAlpha(fadeIn ? 0.0f : 1.0f); + cardView.animate() + .alpha(fadeIn ? 1.0f : 0.0f) + .setDuration(duration) + .setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationCancel(Animator animator) { + progressBar.hide(); + setWallpaperPreviewsVisibility(View.VISIBLE); + } + + @Override + public void onAnimationEnd(Animator animator) { + progressBar.hide(); + setWallpaperPreviewsVisibility(View.VISIBLE); + } + + @Override + public void onAnimationRepeat(Animator animator) { + } + + @Override + public void onAnimationStart(Animator animator) { + setWallpaperPreviewsVisibility(View.INVISIBLE); + } + }); + progressBar.animate() + .alpha(fadeIn ? 1.0f : 0.0f) + .setDuration(duration * 2) + .setStartDelay(duration) + .withStartAction(progressBar::show) + .withEndAction(progressBar::hide); + } + + private void setWallpaperPreviewsVisibility(int visibility) { + if (mHomeWallpaperSurface != null) { + mHomeWallpaperSurface.setVisibility(visibility); + } + if (mLockWallpaperSurface != null) { + mLockWallpaperSurface.setVisibility(visibility); + } + if (mWorkspaceSurface != null) { + mWorkspaceSurface.setVisibility(visibility); + } + if (mLockPreviewContainer != null) { + mLockPreviewContainer.setVisibility(visibility); + } + } + + @Override + public void onTransitionOut() { + if (mHomeWallpaperSurface != null) { + mHomeWallpaperSurface.setUseAlpha(); + mHomeWallpaperSurface.setAlpha(0f); + } + if (mLockWallpaperSurface != null) { + mLockWallpaperSurface.setUseAlpha(); + mLockWallpaperSurface.setAlpha(0f); + } + if (mWorkspaceSurface != null) { + mWorkspaceSurface.setUseAlpha(); + mWorkspaceSurface.setAlpha(0f); + } + if (mLockPreviewContainer != null) { + mLockPreviewContainer.setAlpha(0f); + } + } +} |