[LivePicker 1/n] Extract common PreviewFragment
Before incorporating LivePicker preview in WPP, create a new
ImagePreviewFragment and keep PreviewFragment as a base
class with the common code.
Also clean up a bit the existing logic.
Bug: 141391722
Change-Id: Ifbe0ec5f982e56a4b30ec67a851094eddba35bde
diff --git a/src/com/android/wallpaper/module/WallpaperSetter.java b/src/com/android/wallpaper/module/WallpaperSetter.java
index 830d914..a33a5f1 100644
--- a/src/com/android/wallpaper/module/WallpaperSetter.java
+++ b/src/com/android/wallpaper/module/WallpaperSetter.java
@@ -72,8 +72,9 @@
* @param callback optional callback to be notified when the wallpaper is set.
*/
public void setCurrentWallpaper(Activity containerActivity, WallpaperInfo wallpaper,
- Asset wallpaperAsset, @Destination final int destination, float wallpaperScale,
- @Nullable Rect cropRect, @Nullable SetWallpaperCallback callback) {
+ @Nullable Asset wallpaperAsset, @Destination final int destination,
+ float wallpaperScale, @Nullable Rect cropRect,
+ @Nullable SetWallpaperCallback callback) {
if (wallpaper instanceof LiveWallpaperInfo) {
setCurrentLiveWallpaper(containerActivity, (LiveWallpaperInfo) wallpaper, destination,
callback);
diff --git a/src/com/android/wallpaper/picker/ImagePreviewFragment.java b/src/com/android/wallpaper/picker/ImagePreviewFragment.java
new file mode 100755
index 0000000..d0b0fa5
--- /dev/null
+++ b/src/com/android/wallpaper/picker/ImagePreviewFragment.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2019 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 com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.ContextThemeWrapper;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.wallpaper.R;
+import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.module.WallpaperPersister.Destination;
+import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
+import com.android.wallpaper.util.ScreenSizeCalculator;
+import com.android.wallpaper.util.WallpaperCropUtils;
+import com.android.wallpaper.widget.MaterialProgressDrawable;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.MemoryCategory;
+import com.davemorrissey.labs.subscaleview.ImageSource;
+import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+
+import java.util.List;
+
+/**
+ * Fragment which displays the UI for previewing an individual wallpaper and its attribution
+ * information.
+ */
+public class ImagePreviewFragment extends PreviewFragment {
+
+ private static final float DEFAULT_WALLPAPER_MAX_ZOOM = 8f;
+
+ private SubsamplingScaleImageView mFullResImageView;
+ private Asset mWallpaperAsset;
+ private TextView mAttributionTitle;
+ private TextView mAttributionSubtitle1;
+ private TextView mAttributionSubtitle2;
+ private Point mDefaultCropSurfaceSize;
+ private Point mScreenSize;
+ private Point mRawWallpaperSize; // Native size of wallpaper image.
+ private ImageView mLoadingIndicator;
+ private MaterialProgressDrawable mProgressDrawable;
+ private ImageView mLowResImageView;
+ private View mSpacer;
+
+ /**
+ * Creates and returns new instance of {@link ImagePreviewFragment} with the provided wallpaper
+ * set as an argument.
+ */
+ public static ImagePreviewFragment newInstance(
+ WallpaperInfo wallpaperInfo, @PreviewMode int mode, boolean testingModeEnabled) {
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_WALLPAPER, wallpaperInfo);
+ args.putInt(ARG_PREVIEW_MODE, mode);
+ args.putBoolean(ARG_TESTING_MODE_ENABLED, testingModeEnabled);
+
+ ImagePreviewFragment fragment = new ImagePreviewFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ private static int getAttrColor(Context context, int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+ int colorAccent = ta.getColor(0, 0);
+ ta.recycle();
+ return colorAccent;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mWallpaperAsset = mWallpaper.getAsset(requireContext().getApplicationContext());
+ }
+
+ @Override
+ protected int getLayoutResId() {
+ return R.layout.fragment_preview;
+ }
+
+ @Override
+ protected int getBottomSheetResId() {
+ return R.id.bottom_sheet;
+ }
+
+ @Override
+ protected int getSetWallpaperButtonResId() {
+ return R.id.preview_attribution_pane_set_wallpaper_button;
+ }
+
+ @Override
+ protected int getExploreButtonResId() {
+ return R.id.preview_attribution_pane_explore_button;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+
+ Activity activity = requireActivity();
+ // Set toolbar as the action bar.
+
+ mFullResImageView = view.findViewById(R.id.full_res_image);
+ mLoadingIndicator = view.findViewById(R.id.loading_indicator);
+
+ mAttributionTitle = view.findViewById(R.id.preview_attribution_pane_title);
+ mAttributionSubtitle1 = view.findViewById(R.id.preview_attribution_pane_subtitle1);
+ mAttributionSubtitle2 = view.findViewById(R.id.preview_attribution_pane_subtitle2);
+
+ mLowResImageView = view.findViewById(R.id.low_res_image);
+ mSpacer = view.findViewById(R.id.spacer);
+
+ // Trim some memory from Glide to make room for the full-size image in this fragment.
+ Glide.get(activity).setMemoryCategory(MemoryCategory.LOW);
+
+ mDefaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
+ getResources(), activity.getWindowManager().getDefaultDisplay());
+ mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize(
+ activity.getWindowManager().getDefaultDisplay());
+
+ // Load a low-res placeholder image if there's a thumbnail available from the asset that can
+ // be shown to the user more quickly than the full-sized image.
+ if (mWallpaperAsset.hasLowResDataSource()) {
+ mWallpaperAsset.loadLowResDrawable(activity, mLowResImageView, Color.BLACK,
+ new WallpaperPreviewBitmapTransformation(activity.getApplicationContext(),
+ isRtl()));
+ }
+
+ mWallpaperAsset.decodeRawDimensions(getActivity(), dimensions -> {
+ // Don't continue loading the wallpaper if the Fragment is detached.
+ if (getActivity() == null) {
+ return;
+ }
+
+ // Return early and show a dialog if dimensions are null (signaling a decoding error).
+ if (dimensions == null) {
+ showLoadWallpaperErrorDialog();
+ return;
+ }
+
+ mRawWallpaperSize = dimensions;
+ setUpExploreIntent(ImagePreviewFragment.this::initFullResView);
+ });
+
+ // Configure loading indicator with a MaterialProgressDrawable.
+ setUpLoadingIndicator();
+
+ return view;
+ }
+
+ protected void setUpLoadingIndicator() {
+ Context context = requireContext();
+ mProgressDrawable = new MaterialProgressDrawable(context.getApplicationContext(),
+ mLoadingIndicator);
+ mProgressDrawable.setAlpha(255);
+ mProgressDrawable.setBackgroundColor(getResources().getColor(R.color.material_white_100,
+ context.getTheme()));
+ mProgressDrawable.setColorSchemeColors(getAttrColor(
+ new ContextThemeWrapper(context, getDeviceDefaultTheme()),
+ android.R.attr.colorAccent));
+ mProgressDrawable.updateSizes(MaterialProgressDrawable.LARGE);
+ mLoadingIndicator.setImageDrawable(mProgressDrawable);
+
+ // We don't want to show the spinner every time we load an image if it loads quickly;
+ // instead, only start showing the spinner if loading the image has taken longer than half
+ // of a second.
+ mLoadingIndicator.postDelayed(() -> {
+ if (mFullResImageView != null && !mFullResImageView.hasImage()
+ && !mTestingModeEnabled) {
+ mLoadingIndicator.setVisibility(View.VISIBLE);
+ mLoadingIndicator.setAlpha(1f);
+ if (mProgressDrawable != null) {
+ mProgressDrawable.start();
+ }
+ }
+ }, 500);
+ }
+
+ protected int getDeviceDefaultTheme() {
+ return android.R.style.Theme_DeviceDefault;
+ }
+
+ @Override
+ public void onSet(int destination) {
+ setCurrentWallpaper(destination);
+ }
+
+ @Override
+ public void onClickTryAgain(@Destination int wallpaperDestination) {
+ setCurrentWallpaper(wallpaperDestination);
+ }
+
+ @Override
+ public void onClickOk() {
+ FragmentActivity activity = getActivity();
+ if (activity != null) {
+ activity.finish();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mProgressDrawable != null) {
+ mProgressDrawable.stop();
+ }
+ mFullResImageView.recycle();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
+ outState.putInt(KEY_BOTTOM_SHEET_STATE, bottomSheetBehavior.getState());
+ }
+
+ @Override
+ protected void setBottomSheetContentAlpha(float alpha) {
+ super.setBottomSheetContentAlpha(alpha);
+ mAttributionTitle.setAlpha(alpha);
+ mAttributionSubtitle1.setAlpha(alpha);
+ mAttributionSubtitle2.setAlpha(alpha);
+ }
+
+ private void populateAttributionPane() {
+ final Context context = getContext();
+
+ final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
+
+ List<String> attributions = mWallpaper.getAttributions(context);
+ if (attributions.size() > 0 && attributions.get(0) != null) {
+ mAttributionTitle.setText(attributions.get(0));
+ }
+
+ if (attributions.size() > 1 && attributions.get(1) != null) {
+ mAttributionSubtitle1.setVisibility(View.VISIBLE);
+ mAttributionSubtitle1.setText(attributions.get(1));
+ }
+
+ if (attributions.size() > 2 && attributions.get(2) != null) {
+ mAttributionSubtitle2.setVisibility(View.VISIBLE);
+ mAttributionSubtitle2.setText(attributions.get(2));
+ }
+
+ setUpSetWallpaperButton();
+
+ setUpExploreButton();
+
+ if (mExploreButton.getVisibility() == View.VISIBLE
+ && mSetWallpaperButton.getVisibility() == View.VISIBLE) {
+ mSpacer.setVisibility(View.VISIBLE);
+ } else {
+ mSpacer.setVisibility(View.GONE);
+ }
+
+ mBottomSheet.setVisibility(View.VISIBLE);
+
+ // Initialize the state of the BottomSheet based on the current state because if the initial
+ // and current state are the same, the state change listener won't fire and set the correct
+ // arrow asset and text alpha.
+ if (bottomSheetBehavior.getState() == STATE_EXPANDED) {
+ setPreviewChecked(false);
+ mAttributionTitle.setAlpha(1f);
+ mAttributionSubtitle1.setAlpha(1f);
+ mAttributionSubtitle2.setAlpha(1f);
+ } else {
+ setPreviewChecked(true);
+ mAttributionTitle.setAlpha(0f);
+ mAttributionSubtitle1.setAlpha(0f);
+ mAttributionSubtitle2.setAlpha(0f);
+ }
+
+ // Let the state change listener take care of animating a state change to the initial state
+ // if there's a state change.
+ bottomSheetBehavior.setState(mBottomSheetInitialState);
+ }
+
+ /**
+ * Initializes MosaicView by initializing tiling, setting a fallback page bitmap, and
+ * initializing a zoom-scroll observer and click listener.
+ */
+ private void initFullResView() {
+ mFullResImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
+
+ // Set a solid black "page bitmap" so MosaicView draws a black background while waiting
+ // for the image to load or a transparent one if a thumbnail already loaded.
+ Bitmap blackBitmap = Bitmap.createBitmap(1, 1, Config.ARGB_8888);
+ int color = (mLowResImageView.getDrawable() == null) ? Color.BLACK : Color.TRANSPARENT;
+ blackBitmap.setPixel(0, 0, color);
+ mFullResImageView.setImage(ImageSource.bitmap(blackBitmap));
+
+ // Then set a fallback "page bitmap" to cover the whole MosaicView, which is an actual
+ // (lower res) version of the image to be displayed.
+ Point targetPageBitmapSize = new Point(mRawWallpaperSize);
+ mWallpaperAsset.decodeBitmap(targetPageBitmapSize.x, targetPageBitmapSize.y,
+ pageBitmap -> {
+ // Check that the activity is still around since the decoding task started.
+ if (getActivity() == null) {
+ return;
+ }
+
+ // Some of these may be null depending on if the Fragment is paused, stopped,
+ // or destroyed.
+ if (mLoadingIndicator != null) {
+ mLoadingIndicator.setVisibility(View.GONE);
+ }
+ // The page bitmap may be null if there was a decoding error, so show an
+ // error dialog.
+ if (pageBitmap == null) {
+ showLoadWallpaperErrorDialog();
+ return;
+ }
+ if (mFullResImageView != null) {
+ // Set page bitmap.
+ mFullResImageView.setImage(ImageSource.bitmap(pageBitmap));
+
+ setDefaultWallpaperZoomAndScroll();
+ crossFadeInMosaicView();
+ }
+ if (mProgressDrawable != null) {
+ mProgressDrawable.stop();
+ }
+ getActivity().invalidateOptionsMenu();
+
+ populateAttributionPane();
+ });
+ }
+
+ /**
+ * Makes the MosaicView visible with an alpha fade-in animation while fading out the loading
+ * indicator.
+ */
+ private void crossFadeInMosaicView() {
+ long shortAnimationDuration = getResources().getInteger(
+ android.R.integer.config_shortAnimTime);
+
+ mFullResImageView.setAlpha(0f);
+ mFullResImageView.animate()
+ .alpha(1f)
+ .setDuration(shortAnimationDuration)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Clear the thumbnail bitmap reference to save memory since it's no longer
+ // visible.
+ if (mLowResImageView != null) {
+ mLowResImageView.setImageBitmap(null);
+ }
+ }
+ });
+
+ mLoadingIndicator.animate()
+ .alpha(0f)
+ .setDuration(shortAnimationDuration)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mLoadingIndicator != null) {
+ mLoadingIndicator.setVisibility(View.GONE);
+ }
+ }
+ });
+ }
+
+ /**
+ * Sets the default wallpaper zoom and scroll position based on a "crop surface"
+ * (with extra width to account for parallax) superimposed on the screen. Shows as much of the
+ * wallpaper as possible on the crop surface and align screen to crop surface such that the
+ * default preview matches what would be seen by the user in the left-most home screen.
+ *
+ * <p>This method is called once in the Fragment lifecycle after the wallpaper asset has loaded
+ * and rendered to the layout.
+ */
+ private void setDefaultWallpaperZoomAndScroll() {
+ // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface.
+ float defaultWallpaperZoom =
+ WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mDefaultCropSurfaceSize);
+ float minWallpaperZoom =
+ WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mScreenSize);
+
+ Point screenToCropSurfacePosition = WallpaperCropUtils.calculateCenterPosition(
+ mDefaultCropSurfaceSize, mScreenSize, true /* alignStart */, isRtl());
+ Point zoomedWallpaperSize = new Point(
+ Math.round(mRawWallpaperSize.x * defaultWallpaperZoom),
+ Math.round(mRawWallpaperSize.y * defaultWallpaperZoom));
+ Point cropSurfaceToWallpaperPosition = WallpaperCropUtils.calculateCenterPosition(
+ zoomedWallpaperSize, mDefaultCropSurfaceSize, false /* alignStart */, isRtl());
+
+ // Set min wallpaper zoom and max zoom on MosaicView widget.
+ mFullResImageView.setMaxScale(Math.max(DEFAULT_WALLPAPER_MAX_ZOOM, defaultWallpaperZoom));
+ mFullResImageView.setMinScale(minWallpaperZoom);
+
+ // Set center to composite positioning between scaled wallpaper and screen.
+ PointF centerPosition = new PointF(
+ mRawWallpaperSize.x / 2f,
+ mRawWallpaperSize.y / 2f);
+ centerPosition.offset(-(screenToCropSurfacePosition.x + cropSurfaceToWallpaperPosition.x),
+ -(screenToCropSurfacePosition.y + cropSurfaceToWallpaperPosition.y));
+
+ mFullResImageView.setScaleAndCenter(minWallpaperZoom, centerPosition);
+ }
+
+ private Rect calculateCropRect() {
+ // Calculate Rect of wallpaper in physical pixel terms (i.e., scaled to current zoom).
+ float wallpaperZoom = mFullResImageView.getScale();
+ int scaledWallpaperWidth = (int) (mRawWallpaperSize.x * wallpaperZoom);
+ int scaledWallpaperHeight = (int) (mRawWallpaperSize.y * wallpaperZoom);
+ Rect rect = new Rect();
+ mFullResImageView.visibleFileRect(rect);
+ int scrollX = (int) (rect.left * wallpaperZoom);
+ int scrollY = (int) (rect.top * wallpaperZoom);
+
+ rect.set(0, 0, scaledWallpaperWidth, scaledWallpaperHeight);
+
+ Display defaultDisplay = requireActivity().getWindowManager().getDefaultDisplay();
+ Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(defaultDisplay);
+ // Crop rect should start off as the visible screen and then include extra width and height
+ // if available within wallpaper at the current zoom.
+ Rect cropRect = new Rect(scrollX, scrollY, scrollX + screenSize.x, scrollY + screenSize.y);
+
+ Point defaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
+ getResources(), defaultDisplay);
+ int extraWidth = defaultCropSurfaceSize.x - screenSize.x;
+ int extraHeightTopAndBottom = (int) ((defaultCropSurfaceSize.y - screenSize.y) / 2f);
+
+ // Try to increase size of screenRect to include extra width depending on the layout
+ // direction.
+ if (isRtl()) {
+ cropRect.left = Math.max(cropRect.left - extraWidth, rect.left);
+ } else {
+ cropRect.right = Math.min(cropRect.right + extraWidth, rect.right);
+ }
+
+ // Try to increase the size of the cropRect to to include extra height.
+ int availableExtraHeightTop = cropRect.top - Math.max(
+ rect.top,
+ cropRect.top - extraHeightTopAndBottom);
+ int availableExtraHeightBottom = Math.min(
+ rect.bottom,
+ cropRect.bottom + extraHeightTopAndBottom) - cropRect.bottom;
+
+ int availableExtraHeightTopAndBottom =
+ Math.min(availableExtraHeightTop, availableExtraHeightBottom);
+ cropRect.top -= availableExtraHeightTopAndBottom;
+ cropRect.bottom += availableExtraHeightTopAndBottom;
+
+ return cropRect;
+ }
+
+ @Override
+ protected void setCurrentWallpaper(@Destination int destination) {
+ mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset,
+ destination, mFullResImageView.getScale(), calculateCropRect(),
+ new SetWallpaperCallback() {
+ @Override
+ public void onSuccess() {
+ finishActivityWithResultOk();
+ }
+
+ @Override
+ public void onError(@Nullable Throwable throwable) {
+ showSetWallpaperErrorDialog(destination);
+ }
+ });
+ }
+}
diff --git a/src/com/android/wallpaper/picker/PreviewFragment.java b/src/com/android/wallpaper/picker/PreviewFragment.java
index c698c8c..7310318 100755
--- a/src/com/android/wallpaper/picker/PreviewFragment.java
+++ b/src/com/android/wallpaper/picker/PreviewFragment.java
@@ -18,46 +18,33 @@
import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED;
import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
import android.content.res.Resources.NotFoundException;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.PorterDuff.Mode;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.Display;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.CheckBox;
-import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.CallSuper;
+import androidx.annotation.IdRes;
import androidx.annotation.IntDef;
+import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
@@ -66,9 +53,6 @@
import androidx.fragment.app.FragmentActivity;
import com.android.wallpaper.R;
-import com.android.wallpaper.asset.Asset;
-import com.android.wallpaper.asset.Asset.BitmapReceiver;
-import com.android.wallpaper.asset.Asset.DimensionsReceiver;
import com.android.wallpaper.compat.BuildCompat;
import com.android.wallpaper.model.LiveWallpaperInfo;
import com.android.wallpaper.model.WallpaperInfo;
@@ -78,17 +62,9 @@
import com.android.wallpaper.module.UserEventLogger;
import com.android.wallpaper.module.WallpaperPersister;
import com.android.wallpaper.module.WallpaperPersister.Destination;
-import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
import com.android.wallpaper.module.WallpaperPreferences;
import com.android.wallpaper.module.WallpaperSetter;
-import com.android.wallpaper.util.ScreenSizeCalculator;
-import com.android.wallpaper.util.WallpaperCropUtils;
-import com.android.wallpaper.widget.MaterialProgressDrawable;
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.MemoryCategory;
-import com.davemorrissey.labs.subscaleview.ImageSource;
-import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetBehavior.State;
@@ -99,20 +75,20 @@
* Fragment which displays the UI for previewing an individual wallpaper and its attribution
* information.
*/
-public class PreviewFragment extends Fragment implements
+public abstract class PreviewFragment extends Fragment implements
SetWallpaperDialogFragment.Listener, SetWallpaperErrorDialogFragment.Listener,
LoadWallpaperErrorDialogFragment.Listener {
/**
* User can view wallpaper and attributions in full screen, but "Set wallpaper" button is hidden.
*/
- public static final int MODE_VIEW_ONLY = 0;
+ static final int MODE_VIEW_ONLY = 0;
/**
* User can view wallpaper and attributions in full screen and click "Set wallpaper" to set the
* wallpaper with pan and crop position to the device.
*/
- public static final int MODE_CROP_AND_SET_WALLPAPER = 1;
+ static final int MODE_CROP_AND_SET_WALLPAPER = 1;
/**
* Possible preview modes for the fragment.
@@ -131,47 +107,33 @@
private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
"set_wallpaper_error_dialog";
private static final int UNUSED_REQUEST_CODE = 1;
- private static final float DEFAULT_WALLPAPER_MAX_ZOOM = 8f;
private static final String TAG = "PreviewFragment";
- private static final float PAGE_BITMAP_MAX_HEAP_RATIO = 0.25f;
- private static final String KEY_BOTTOM_SHEET_STATE = "key_bottom_sheet_state";
+ static final String KEY_BOTTOM_SHEET_STATE = "key_bottom_sheet_state";
@PreviewMode
- private int mPreviewMode;
+ protected int mPreviewMode;
/**
* When true, enables a test mode of operation -- in which certain UI features are disabled to
* allow for UI tests to run correctly. Works around issue in ProgressDialog currently where the
* dialog constantly keeps the UI thread alive and blocks a test forever.
*/
- private boolean mTestingModeEnabled;
+ protected boolean mTestingModeEnabled;
- protected SubsamplingScaleImageView mFullResImageView;
protected WallpaperInfo mWallpaper;
- private Asset mWallpaperAsset;
- private WallpaperSetter mWallpaperSetter;
- private UserEventLogger mUserEventLogger;
- private LinearLayout mBottomSheet;
- private TextView mAttributionTitle;
- private TextView mAttributionSubtitle1;
- private TextView mAttributionSubtitle2;
- private Button mAttributionExploreButton;
- private int mCurrentScreenOrientation;
- private Point mDefaultCropSurfaceSize;
- private Point mScreenSize;
- private Point mRawWallpaperSize; // Native size of wallpaper image.
- private ImageView mLoadingIndicator;
- private MaterialProgressDrawable mProgressDrawable;
- private ImageView mLowResImageView;
- private Button mSetWallpaperButton;
- private View mSpacer;
- private CheckBox mPreview;
+ protected WallpaperSetter mWallpaperSetter;
+ protected UserEventLogger mUserEventLogger;
+ protected LinearLayout mBottomSheet;
+ protected Button mExploreButton;
+ protected Button mSetWallpaperButton;
+
+ protected CheckBox mPreview;
@SuppressWarnings("RestrictTo")
@State
- private int mBottomSheetInitialState;
+ protected int mBottomSheetInitialState;
- private Intent mExploreIntent;
+ protected Intent mExploreIntent;
/**
* Staged error dialog fragments that were unable to be shown when the hosting activity didn't
@@ -180,40 +142,17 @@
private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
private LoadWallpaperErrorDialogFragment mStagedLoadWallpaperErrorDialogFragment;
- /**
- * Creates and returns new instance of {@link PreviewFragment} with the provided wallpaper set as
- * an argument.
- */
- public static PreviewFragment newInstance(
- WallpaperInfo wallpaperInfo, @PreviewMode int mode, boolean testingModeEnabled) {
- Bundle args = new Bundle();
- args.putParcelable(ARG_WALLPAPER, wallpaperInfo);
- args.putInt(ARG_PREVIEW_MODE, mode);
- args.putBoolean(ARG_TESTING_MODE_ENABLED, testingModeEnabled);
-
- PreviewFragment fragment = new PreviewFragment();
- fragment.setArguments(args);
- return fragment;
- }
-
- private static int getAttrColor(Context context, int attr) {
- TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
- int colorAccent = ta.getColor(0, 0);
- ta.recycle();
- return colorAccent;
- }
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Activity activity = getActivity();
- Context appContext = activity.getApplicationContext();
+ Context appContext = getContext().getApplicationContext();
Injector injector = InjectorProvider.getInjector();
mUserEventLogger = injector.getUserEventLogger(appContext);
mWallpaper = getArguments().getParcelable(ARG_WALLPAPER);
- mWallpaperAsset = mWallpaper.getAsset(appContext);
+
//noinspection ResourceType
mPreviewMode = getArguments().getInt(ARG_PREVIEW_MODE);
mTestingModeEnabled = getArguments().getBoolean(ARG_TESTING_MODE_ENABLED);
@@ -236,14 +175,18 @@
}
}
+ @LayoutRes
+ protected abstract int getLayoutResId();
+
@Override
+ @CallSuper
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_preview, container, false);
+ View view = inflater.inflate(getLayoutResId(), container, false);
// Set toolbar as the action bar.
Toolbar toolbar = view.findViewById(R.id.toolbar);
- AppCompatActivity activity = (AppCompatActivity) getActivity();
+ AppCompatActivity activity = (AppCompatActivity) requireActivity();
activity.setSupportActionBar(toolbar);
activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
activity.getSupportActionBar().setDisplayShowTitleEnabled(false);
@@ -270,18 +213,9 @@
R.dimen.preview_toolbar_set_wallpaper_button_end_padding),
/* bottom */ 0);
- mFullResImageView = view.findViewById(R.id.full_res_image);
- mLoadingIndicator = view.findViewById(R.id.loading_indicator);
-
- mBottomSheet = view.findViewById(R.id.bottom_sheet);
- mAttributionTitle = view.findViewById(R.id.preview_attribution_pane_title);
- mAttributionSubtitle1 = view.findViewById(R.id.preview_attribution_pane_subtitle1);
- mAttributionSubtitle2 = view.findViewById(R.id.preview_attribution_pane_subtitle2);
- mAttributionExploreButton = view.findViewById(
- R.id.preview_attribution_pane_explore_button);
- mLowResImageView = view.findViewById(R.id.low_res_image);
- mSetWallpaperButton = view.findViewById(R.id.preview_attribution_pane_set_wallpaper_button);
- mSpacer = view.findViewById(R.id.spacer);
+ mBottomSheet = view.findViewById(getBottomSheetResId());
+ mExploreButton = view.findViewById(getExploreButtonResId());
+ mSetWallpaperButton = view.findViewById(getSetWallpaperButtonResId());
// Workaround as we don't have access to bottomDialogCornerRadius, mBottomSheet radii are
// set to dialogCornerRadius by default.
@@ -294,84 +228,6 @@
bottomSheetBackground.setCornerRadii(radii);
mBottomSheet.setBackground(bottomSheetBackground);
- // Trim some memory from Glide to make room for the full-size image in this fragment.
- Glide.get(getActivity()).setMemoryCategory(MemoryCategory.LOW);
-
- mDefaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
- getResources(), getActivity().getWindowManager().getDefaultDisplay());
- mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize(
- getActivity().getWindowManager().getDefaultDisplay());
-
- // Load a low-res placeholder image if there's a thumbnail available from the asset that can be
- // shown to the user more quickly than the full-sized image.
- if (mWallpaperAsset.hasLowResDataSource()) {
- mWallpaperAsset.loadLowResDrawable(getActivity(), mLowResImageView, Color.BLACK,
- new WallpaperPreviewBitmapTransformation(getActivity().getApplicationContext(),
- isRtl()));
- }
-
- mWallpaperAsset.decodeRawDimensions(getActivity(), new DimensionsReceiver() {
- @Override
- public void onDimensionsDecoded(Point dimensions) {
- // Don't continue loading the wallpaper if the Fragment is detached.
- Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- // Return early and show a dialog if dimensions are null (signaling a decoding error).
- if (dimensions == null) {
- showLoadWallpaperErrorDialog();
- return;
- }
-
- mRawWallpaperSize = dimensions;
- ExploreIntentChecker intentChecker =
- InjectorProvider.getInjector().getExploreIntentChecker(activity);
- String actionUrl = mWallpaper.getActionUrl(activity);
- if (actionUrl != null && !actionUrl.isEmpty()) {
- Uri exploreUri = Uri.parse(mWallpaper.getActionUrl(activity));
-
- intentChecker.fetchValidActionViewIntent(exploreUri, exploreIntent -> {
- if (getActivity() == null) {
- return;
- }
-
- mExploreIntent = exploreIntent;
- initFullResView();
- });
- } else {
- initFullResView();
- }
- }
- });
-
- // Configure loading indicator with a MaterialProgressDrawable.
- mProgressDrawable = new MaterialProgressDrawable(getActivity().getApplicationContext(),
- mLoadingIndicator);
- mProgressDrawable.setAlpha(255);
- mProgressDrawable.setBackgroundColor(getResources().getColor(R.color.material_white_100,
- getContext().getTheme()));
- mProgressDrawable.setColorSchemeColors(getAttrColor(
- new ContextThemeWrapper(getContext(), getDeviceDefaultTheme()),
- android.R.attr.colorAccent));
- mProgressDrawable.updateSizes(MaterialProgressDrawable.LARGE);
- mLoadingIndicator.setImageDrawable(mProgressDrawable);
-
- // We don't want to show the spinner every time we load an image if it loads quickly; instead,
- // only start showing the spinner if loading the image has taken longer than half of a second.
- mLoadingIndicator.postDelayed(() -> {
- if (mFullResImageView != null && !mFullResImageView.hasImage()
- && !mTestingModeEnabled) {
- mLoadingIndicator.setVisibility(View.VISIBLE);
- mLoadingIndicator.setAlpha(1f);
- if (mProgressDrawable != null) {
- mProgressDrawable.start();
- }
- }
- }, 500);
-
-
mBottomSheetInitialState = (savedInstanceState == null)
? STATE_EXPANDED
: savedInstanceState.getInt(KEY_BOTTOM_SHEET_STATE,
@@ -381,6 +237,15 @@
return view;
}
+ @IdRes
+ protected abstract int getSetWallpaperButtonResId();
+
+ @IdRes
+ protected abstract int getBottomSheetResId();
+
+ @IdRes
+ protected abstract int getExploreButtonResId();
+
protected int getDeviceDefaultTheme() {
return android.R.style.Theme_DeviceDefault;
}
@@ -398,12 +263,12 @@
// allow committing fragment transactions.
if (mStagedLoadWallpaperErrorDialogFragment != null) {
mStagedLoadWallpaperErrorDialogFragment.show(
- getFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT);
+ requireFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT);
mStagedLoadWallpaperErrorDialogFragment = null;
}
if (mStagedSetWallpaperErrorDialogFragment != null) {
mStagedSetWallpaperErrorDialogFragment.show(
- getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
+ requireFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
mStagedSetWallpaperErrorDialogFragment = null;
}
}
@@ -428,20 +293,20 @@
// the IndividualPreviewActivity, the "My photos" selection (by way of
// TopLevelPickerActivity), or from a system "crop and set wallpaper" intent.
// Therefore, handle the Up button as a global Back.
- getActivity().onBackPressed();
+ requireActivity().onBackPressed();
return true;
}
return false;
}
- protected void setupPreviewMenu(Menu menu) {
+ private void setupPreviewMenu(Menu menu) {
mPreview = (CheckBox) menu.findItem(R.id.preview).getActionView();
mPreview.setChecked(mBottomSheetInitialState == STATE_COLLAPSED);
mPreview.setOnClickListener(this::setPreviewBehavior);
}
- private void setPreviewChecked(boolean checked) {
+ protected void setPreviewChecked(boolean checked) {
if (mPreview != null) {
mPreview.setChecked(checked);
int resId = checked ? R.string.expand_attribution_panel
@@ -461,6 +326,61 @@
}
}
+ protected void setUpSetWallpaperButton() {
+ if (mPreviewMode == MODE_VIEW_ONLY) {
+ mSetWallpaperButton.setVisibility(View.GONE);
+ } else {
+ mSetWallpaperButton.setVisibility(View.VISIBLE);
+ mSetWallpaperButton.setOnClickListener(this::onSetWallpaperClicked);
+ }
+ }
+
+ protected void setUpExploreButton() {
+ mExploreButton.setVisibility(View.GONE);
+ if (mExploreIntent == null) {
+ return;
+ }
+ Context context = requireContext();
+ mExploreButton.setVisibility(View.VISIBLE);
+ mExploreButton.setText(context.getString(
+ mWallpaper.getActionLabelRes(context)));
+
+ mExploreButton.setOnClickListener(view -> {
+ mUserEventLogger.logActionClicked(mWallpaper.getCollectionId(context),
+ mWallpaper.getActionLabelRes(context));
+
+ startActivity(mExploreIntent);
+ });
+ }
+
+ protected void setUpExploreIntent(@Nullable Runnable callback) {
+ Context context = getContext();
+ if (context == null) {
+ return;
+ }
+ ExploreIntentChecker intentChecker =
+ InjectorProvider.getInjector().getExploreIntentChecker(context);
+ String actionUrl = mWallpaper.getActionUrl(context);
+ if (actionUrl != null && !actionUrl.isEmpty()) {
+ Uri exploreUri = Uri.parse(mWallpaper.getActionUrl(context));
+
+ intentChecker.fetchValidActionViewIntent(exploreUri, exploreIntent -> {
+ if (getActivity() == null) {
+ return;
+ }
+
+ mExploreIntent = exploreIntent;
+ if (callback != null) {
+ callback.run();
+ }
+ });
+ } else {
+ if (callback != null) {
+ callback.run();
+ }
+ }
+ }
+
@Override
public void onSet(int destination) {
setCurrentWallpaper(destination);
@@ -483,10 +403,6 @@
public void onDestroy() {
super.onDestroy();
mWallpaperSetter.cleanUp();
- if (mProgressDrawable != null) {
- mProgressDrawable.stop();
- }
- mFullResImageView.recycle();
}
@Override
@@ -506,35 +422,6 @@
}
}
- /**
- * Returns a zoom level that is similar to the actual zoom, but that is exactly 0.5 ** n for some
- * integer n. This is useful for downsampling a bitmap--we want to see the bitmap at full detail,
- * or downsampled to 1 in every 2 pixels, or 1 in 4, and so on, depending on the zoom.
- */
- private static float getDownsampleZoom(float actualZoom) {
- if (actualZoom > 1) {
- // Very zoomed in, but we can't sample more than 1 pixel per pixel.
- return 1.0f;
- }
- float lower = 1.0f / roundUpToPower2((int) Math.ceil(1 / actualZoom));
- float upper = lower * 2;
- return nearestValue(actualZoom, lower, upper);
- }
-
- /**
- * Returns the integer rounded up to the next power of 2.
- */
- private static int roundUpToPower2(int value) {
- return 1 << (32 - Integer.numberOfLeadingZeros(value - 1));
- }
-
- /**
- * Returns the closer of two values a and b to the given value.
- */
- private static float nearestValue(float value, float a, float b) {
- return Math.abs(a - value) < Math.abs(b - value) ? a : b;
- }
-
private void setUpBottomSheetListeners() {
final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
@@ -554,6 +441,8 @@
case STATE_EXPANDED:
setPreviewChecked(false /* checked */);
break;
+ default:
+ Log.v(TAG, "Ignoring BottomSheet state: " + newState);
}
}
@@ -565,264 +454,13 @@
} else {
alpha = 1f - slideOffset;
}
- mAttributionTitle.setAlpha(alpha);
- mAttributionSubtitle1.setAlpha(alpha);
- mAttributionSubtitle2.setAlpha(alpha);
- mAttributionExploreButton.setAlpha(alpha);
+ setBottomSheetContentAlpha(alpha);
}
});
}
- private boolean isWallpaperLoaded() {
- return mFullResImageView.hasImage();
- }
-
- private void populateAttributionPane() {
- final Context context = getContext();
-
- final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
-
- List<String> attributions = mWallpaper.getAttributions(context);
- if (attributions.size() > 0 && attributions.get(0) != null) {
- mAttributionTitle.setText(attributions.get(0));
- }
-
- if (attributions.size() > 1 && attributions.get(1) != null) {
- mAttributionSubtitle1.setVisibility(View.VISIBLE);
- mAttributionSubtitle1.setText(attributions.get(1));
- }
-
- if (attributions.size() > 2 && attributions.get(2) != null) {
- mAttributionSubtitle2.setVisibility(View.VISIBLE);
- mAttributionSubtitle2.setText(attributions.get(2));
- }
-
- if (mPreviewMode == MODE_CROP_AND_SET_WALLPAPER) {
- mSetWallpaperButton.setVisibility(View.VISIBLE);
- mSetWallpaperButton.setOnClickListener(this::onSetWallpaperClicked);
- } else {
- mSetWallpaperButton.setVisibility(View.GONE);
- }
-
- String actionUrl = mWallpaper.getActionUrl(context);
-
- mAttributionExploreButton.setVisibility(View.GONE);
- if (actionUrl != null && !actionUrl.isEmpty()) {
- if (mExploreIntent != null) {
- mAttributionExploreButton.setVisibility(View.VISIBLE);
- mAttributionExploreButton.setText(context.getString(
- mWallpaper.getActionLabelRes(context)));
-
- mAttributionExploreButton.setOnClickListener(view -> {
- mUserEventLogger.logActionClicked(mWallpaper.getCollectionId(context),
- mWallpaper.getActionLabelRes(context));
-
- startActivity(mExploreIntent);
- });
- }
- }
-
- if (mAttributionExploreButton.getVisibility() == View.VISIBLE
- && mSetWallpaperButton.getVisibility() == View.VISIBLE) {
- mSpacer.setVisibility(View.VISIBLE);
- } else {
- mSpacer.setVisibility(View.GONE);
- }
-
- mBottomSheet.setVisibility(View.VISIBLE);
-
- // Initialize the state of the BottomSheet based on the current state because if the initial
- // and current state are the same, the state change listener won't fire and set the correct
- // arrow asset and text alpha.
- if (bottomSheetBehavior.getState() == STATE_EXPANDED) {
- setPreviewChecked(false);
- mAttributionTitle.setAlpha(1f);
- mAttributionSubtitle1.setAlpha(1f);
- mAttributionSubtitle2.setAlpha(1f);
- } else {
- setPreviewChecked(true);
- mAttributionTitle.setAlpha(0f);
- mAttributionSubtitle1.setAlpha(0f);
- mAttributionSubtitle2.setAlpha(0f);
- }
-
- // Let the state change listener take care of animating a state change to the initial state
- // if there's a state change.
- bottomSheetBehavior.setState(mBottomSheetInitialState);
- }
-
- /**
- * Initializes MosaicView by initializing tiling, setting a fallback page bitmap, and
- * initializing a zoom-scroll observer and click listener.
- */
- private void initFullResView() {
- mFullResImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
-
- // Set a solid black "page bitmap" so MosaicView draws a black background while waiting
- // for the image to load or a transparent one if a thumbnail already loaded.
- Bitmap blackBitmap = Bitmap.createBitmap(1, 1, Config.ARGB_8888);
- int color = (mLowResImageView.getDrawable() == null) ? Color.BLACK : Color.TRANSPARENT;
- blackBitmap.setPixel(0, 0, color);
- mFullResImageView.setImage(ImageSource.bitmap(blackBitmap));
-
- // Then set a fallback "page bitmap" to cover the whole MosaicView, which is an actual
- // (lower res) version of the image to be displayed.
- Point targetPageBitmapSize = new Point(mRawWallpaperSize);
- mWallpaperAsset.decodeBitmap(targetPageBitmapSize.x, targetPageBitmapSize.y,
- new BitmapReceiver() {
- @Override
- public void onBitmapDecoded(Bitmap pageBitmap) {
- // Check that the activity is still around since the decoding task started.
- if (getActivity() == null) {
- return;
- }
-
- // Some of these may be null depending on if the Fragment is paused, stopped,
- // or destroyed.
- if (mLoadingIndicator != null) {
- mLoadingIndicator.setVisibility(View.GONE);
- }
- // The page bitmap may be null if there was a decoding error, so show an
- // error dialog.
- if (pageBitmap == null) {
- showLoadWallpaperErrorDialog();
- return;
- }
- if (mFullResImageView != null) {
- // Set page bitmap.
- mFullResImageView.setImage(ImageSource.bitmap(pageBitmap));
-
- setDefaultWallpaperZoomAndScroll();
- crossFadeInMosaicView();
- }
- if (mProgressDrawable != null) {
- mProgressDrawable.stop();
- }
- getActivity().invalidateOptionsMenu();
-
- populateAttributionPane();
- }
- });
- }
-
- /**
- * Makes the MosaicView visible with an alpha fade-in animation while fading out the loading
- * indicator.
- */
- private void crossFadeInMosaicView() {
- long shortAnimationDuration = getResources().getInteger(
- android.R.integer.config_shortAnimTime);
-
- mFullResImageView.setAlpha(0f);
- mFullResImageView.animate()
- .alpha(1f)
- .setDuration(shortAnimationDuration)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Clear the thumbnail bitmap reference to save memory since it's no longer
- // visible.
- if (mLowResImageView != null) {
- mLowResImageView.setImageBitmap(null);
- }
- }
- });
-
- mLoadingIndicator.animate()
- .alpha(0f)
- .setDuration(shortAnimationDuration)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mLoadingIndicator != null) {
- mLoadingIndicator.setVisibility(View.GONE);
- }
- }
- });
- }
-
- /**
- * Sets the default wallpaper zoom and scroll position based on a "crop surface"
- * (with extra width to account for parallax) superimposed on the screen. Shows as much of the
- * wallpaper as possible on the crop surface and align screen to crop surface such that the
- * default preview matches what would be seen by the user in the left-most home screen.
- *
- * <p>This method is called once in the Fragment lifecycle after the wallpaper asset has loaded
- * and rendered to the layout.
- */
- private void setDefaultWallpaperZoomAndScroll() {
- // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface.
- float defaultWallpaperZoom =
- WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mDefaultCropSurfaceSize);
- float minWallpaperZoom =
- WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mScreenSize);
-
- Point screenToCropSurfacePosition = WallpaperCropUtils.calculateCenterPosition(
- mDefaultCropSurfaceSize, mScreenSize, true /* alignStart */, isRtl());
- Point zoomedWallpaperSize = new Point(
- Math.round(mRawWallpaperSize.x * defaultWallpaperZoom),
- Math.round(mRawWallpaperSize.y * defaultWallpaperZoom));
- Point cropSurfaceToWallpaperPosition = WallpaperCropUtils.calculateCenterPosition(
- zoomedWallpaperSize, mDefaultCropSurfaceSize, false /* alignStart */, isRtl());
-
- // Set min wallpaper zoom and max zoom on MosaicView widget.
- mFullResImageView.setMaxScale(Math.max(DEFAULT_WALLPAPER_MAX_ZOOM, defaultWallpaperZoom));
- mFullResImageView.setMinScale(minWallpaperZoom);
-
- // Set center to composite positioning between scaled wallpaper and screen.
- PointF centerPosition = new PointF(
- mRawWallpaperSize.x / 2f,
- mRawWallpaperSize.y / 2f);
- centerPosition.offset( - (screenToCropSurfacePosition.x + cropSurfaceToWallpaperPosition.x),
- - (screenToCropSurfacePosition.y + cropSurfaceToWallpaperPosition.y));
-
- mFullResImageView.setScaleAndCenter(minWallpaperZoom, centerPosition);
- }
-
- protected Rect calculateCropRect() {
- // Calculate Rect of wallpaper in physical pixel terms (i.e., scaled to current zoom).
- float wallpaperZoom = mFullResImageView.getScale();
- int scaledWallpaperWidth = (int) (mRawWallpaperSize.x * wallpaperZoom);
- int scaledWallpaperHeight = (int) (mRawWallpaperSize.y * wallpaperZoom);
- Rect rect = new Rect();
- mFullResImageView.visibleFileRect(rect);
- int scrollX = (int) (rect.left * wallpaperZoom);
- int scrollY = (int) (rect.top * wallpaperZoom);
-
- rect.set(0, 0, scaledWallpaperWidth, scaledWallpaperHeight);
- Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(
- getActivity().getWindowManager().getDefaultDisplay());
- // Crop rect should start off as the visible screen and then include extra width and height
- // if available within wallpaper at the current zoom.
- Rect cropRect = new Rect(scrollX, scrollY, scrollX + screenSize.x, scrollY + screenSize.y);
-
- Point defaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
- getResources(), getActivity().getWindowManager().getDefaultDisplay());
- int extraWidth = defaultCropSurfaceSize.x - screenSize.x;
- int extraHeightTopAndBottom = (int) ((defaultCropSurfaceSize.y - screenSize.y) / 2f);
-
- // Try to increase size of screenRect to include extra width depending on the layout
- // direction.
- if (isRtl()) {
- cropRect.left = Math.max(cropRect.left - extraWidth, rect.left);
- } else {
- cropRect.right = Math.min(cropRect.right + extraWidth, rect.right);
- }
-
- // Try to increase the size of the cropRect to to include extra height.
- int availableExtraHeightTop = cropRect.top - Math.max(
- rect.top,
- cropRect.top - extraHeightTopAndBottom);
- int availableExtraHeightBottom = Math.min(
- rect.bottom,
- cropRect.bottom + extraHeightTopAndBottom) - cropRect.bottom;
-
- int availableExtraHeightTopAndBottom =
- Math.min(availableExtraHeightTop, availableExtraHeightBottom);
- cropRect.top -= availableExtraHeightTopAndBottom;
- cropRect.bottom += availableExtraHeightTopAndBottom;
-
- return cropRect;
+ protected void setBottomSheetContentAlpha(float alpha) {
+ mExploreButton.setAlpha(alpha);
}
/**
@@ -830,35 +468,22 @@
*
* @param destination The wallpaper destination i.e. home vs. lockscreen vs. both.
*/
- private void setCurrentWallpaper(@Destination final int destination) {
- mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset,
- destination, mFullResImageView.getScale(), calculateCropRect(),
- new SetWallpaperCallback() {
- @Override
- public void onSuccess() {
- finishActivityWithResultOk();
- }
+ protected abstract void setCurrentWallpaper(@Destination int destination);
- @Override
- public void onError(@Nullable Throwable throwable) {
- showSetWallpaperErrorDialog(destination);
- }
- });
- }
-
- private void finishActivityWithResultOk() {
+ protected void finishActivityWithResultOk() {
+ Activity activity = requireActivity();
try {
- Toast.makeText(
- getActivity(), R.string.wallpaper_set_successfully_message, Toast.LENGTH_SHORT).show();
+ Toast.makeText(activity,
+ R.string.wallpaper_set_successfully_message, Toast.LENGTH_SHORT).show();
} catch (NotFoundException e) {
Log.e(TAG, "Could not show toast " + e);
}
- getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
- getActivity().setResult(Activity.RESULT_OK);
- getActivity().finish();
+ activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+ activity.setResult(Activity.RESULT_OK);
+ activity.finish();
}
- private void showSetWallpaperErrorDialog(@Destination int wallpaperDestination) {
+ protected void showSetWallpaperErrorDialog(@Destination int wallpaperDestination) {
SetWallpaperErrorDialogFragment newFragment = SetWallpaperErrorDialogFragment.newInstance(
R.string.set_wallpaper_error_message, wallpaperDestination);
newFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
@@ -866,9 +491,9 @@
// Show 'set wallpaper' error dialog now if it's safe to commit fragment transactions,
// otherwise stage it for later when the hosting activity is in a state to commit fragment
// transactions.
- BasePreviewActivity activity = (BasePreviewActivity) getActivity();
+ BasePreviewActivity activity = (BasePreviewActivity) requireActivity();
if (activity.isSafeToCommitFragmentTransaction()) {
- newFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
+ newFragment.show(requireFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
} else {
mStagedSetWallpaperErrorDialogFragment = newFragment;
}
@@ -878,17 +503,16 @@
* Shows 'load wallpaper' error dialog now or stage it to be shown when the hosting activity is
* in a state that allows committing fragment transactions.
*/
- private void showLoadWallpaperErrorDialog() {
+ protected void showLoadWallpaperErrorDialog() {
LoadWallpaperErrorDialogFragment dialogFragment =
LoadWallpaperErrorDialogFragment.newInstance();
- dialogFragment.setTargetFragment(PreviewFragment.this, UNUSED_REQUEST_CODE);
+ dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
// Show 'load wallpaper' error dialog now or stage it to be shown when the hosting
// activity is in a state that allows committing fragment transactions.
BasePreviewActivity activity = (BasePreviewActivity) getActivity();
if (activity != null && activity.isSafeToCommitFragmentTransaction()) {
- dialogFragment.show(PreviewFragment.this.getFragmentManager(),
- TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT);
+ dialogFragment.show(requireFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT);
} else {
mStagedLoadWallpaperErrorDialogFragment = dialogFragment;
}
@@ -903,59 +527,10 @@
}
/**
- * Gets the appropriate ActivityInfo orientation for the current configuration orientation to
- * enable locking screen rotation at API levels lower than 18.
- */
- @ActivityInfoScreenOrientation
- private int getCompatActivityInfoOrientation() {
- int configOrientation = getResources().getConfiguration().orientation;
- final Display display = getActivity().getWindowManager().getDefaultDisplay();
- int naturalOrientation = Configuration.ORIENTATION_LANDSCAPE;
- switch (display.getRotation()) {
- case Surface.ROTATION_0:
- case Surface.ROTATION_180:
- // We are currently in the same basic orientation as the natural orientation.
- naturalOrientation = configOrientation;
- break;
- case Surface.ROTATION_90:
- case Surface.ROTATION_270:
- // We are currently in the other basic orientation to the natural orientation.
- naturalOrientation = (configOrientation == Configuration.ORIENTATION_LANDSCAPE)
- ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
- break;
- default:
- // continue below
- }
-
- // Since the map starts at portrait, we need to offset if this device's natural orientation
- // is landscape.
- int indexOffset = 0;
- if (naturalOrientation == Configuration.ORIENTATION_LANDSCAPE) {
- indexOffset = 1;
- }
-
- switch ((display.getRotation() + indexOffset) % 4) {
- case 0:
- return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- case 1:
- return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
- case 2:
- return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
- case 3:
- return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
- default:
- Log.e(TAG, "Display rotation did not correspond to a valid ActivityInfo orientation"
- + "with display rotation: " + display.getRotation() + " and index offset: "
- + indexOffset + ".");
- return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
- }
- }
-
- /**
* Returns whether layout direction is RTL (or false for LTR). Since native RTL layout support
* was added in API 17, returns false for versions lower than 17.
*/
- private boolean isRtl() {
+ protected boolean isRtl() {
return getResources().getConfiguration().getLayoutDirection()
== View.LAYOUT_DIRECTION_RTL;
}
diff --git a/src_override/com/android/wallpaper/module/WallpapersInjector.java b/src_override/com/android/wallpaper/module/WallpapersInjector.java
index 8e3b3e9..5a39524 100755
--- a/src_override/com/android/wallpaper/module/WallpapersInjector.java
+++ b/src_override/com/android/wallpaper/module/WallpapersInjector.java
@@ -17,12 +17,12 @@
import android.content.Context;
+import androidx.fragment.app.Fragment;
+
import com.android.wallpaper.model.CategoryProvider;
import com.android.wallpaper.model.WallpaperInfo;
import com.android.wallpaper.monitor.PerformanceMonitor;
-import com.android.wallpaper.picker.PreviewFragment;
-
-import androidx.fragment.app.Fragment;
+import com.android.wallpaper.picker.ImagePreviewFragment;
/**
* A concrete, real implementation of the dependency provider.
@@ -69,7 +69,7 @@
WallpaperInfo wallpaperInfo,
int mode,
boolean testingModeEnabled) {
- return PreviewFragment.newInstance(wallpaperInfo, mode, testingModeEnabled);
+ return ImagePreviewFragment.newInstance(wallpaperInfo, mode, testingModeEnabled);
}
@Override