blob: 1fdd463f0d802edec6b8ee571a7d9fe25a47b163 [file] [log] [blame]
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -07001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.wallpaper.picker;
17
Chihhang Chuang15169282021-06-07 20:25:18 +080018import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
19import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -070020import static android.view.View.MeasureSpec.EXACTLY;
21import static android.view.View.MeasureSpec.makeMeasureSpec;
22
23import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
24import static com.android.wallpaper.widget.BottomActionBar.BottomAction.EDIT;
25import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
26
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070027import android.animation.Animator;
28import android.animation.AnimatorListenerAdapter;
29import android.app.Activity;
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +080030import android.app.WallpaperColors;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070031import android.content.Context;
Santiago Etchebeheref172aef2020-07-04 18:48:35 -070032import android.content.res.Resources;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070033import android.graphics.Bitmap;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070034import android.graphics.Color;
35import android.graphics.Point;
36import android.graphics.PointF;
37import android.graphics.Rect;
38import android.os.Bundle;
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080039import android.util.Log;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070040import android.view.LayoutInflater;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -070041import android.view.Surface;
42import android.view.SurfaceControlViewHost;
43import android.view.SurfaceHolder;
44import android.view.SurfaceView;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070045import android.view.View;
46import android.view.ViewGroup;
Santiago Etchebehere8557c462020-12-02 18:41:40 -080047import android.view.ViewGroup.LayoutParams;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070048import android.widget.ImageView;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070049
Chuck Liaofc1b8c92020-05-21 20:54:15 +080050import androidx.annotation.NonNull;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070051import androidx.annotation.Nullable;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -070052import androidx.cardview.widget.CardView;
Tracy Zhou3eb234c2020-05-05 15:17:33 -070053import androidx.constraintlayout.widget.ConstraintLayout;
54import androidx.constraintlayout.widget.ConstraintSet;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070055import androidx.fragment.app.FragmentActivity;
56
57import com.android.wallpaper.R;
58import com.android.wallpaper.asset.Asset;
Chuck Liaodc424e82020-09-04 00:17:44 +080059import com.android.wallpaper.asset.CurrentWallpaperAssetVN;
“Chuckffd832c2020-03-22 02:15:58 +080060import com.android.wallpaper.model.WallpaperInfo;
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080061import com.android.wallpaper.module.BitmapCropper;
62import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070063import com.android.wallpaper.module.WallpaperPersister.Destination;
64import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
Tianguang Zhang893187f2021-05-07 22:14:04 +020065import com.android.wallpaper.util.FullScreenAnimation;
Tianguang Zhangd3e4f632021-04-14 00:30:15 +020066import com.android.wallpaper.util.ResourceUtils;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070067import com.android.wallpaper.util.ScreenSizeCalculator;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070068import com.android.wallpaper.util.SizeCalculator;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070069import com.android.wallpaper.util.WallpaperCropUtils;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -070070import com.android.wallpaper.widget.BottomActionBar;
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +080071import com.android.wallpaper.widget.BottomActionBar.AccessibilityCallback;
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +080072import com.android.wallpaper.widget.LockScreenPreviewer;
Tracy Zhou35997422020-05-01 13:42:06 -070073import com.android.wallpaper.widget.WallpaperInfoView;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070074
75import com.bumptech.glide.Glide;
76import com.bumptech.glide.MemoryCategory;
77import com.davemorrissey.labs.subscaleview.ImageSource;
78import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070079
chihhangchuang923e29b2020-07-02 19:09:36 +080080import java.util.Locale;
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -070081import java.util.concurrent.ExecutionException;
82import java.util.concurrent.Future;
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080083import java.util.concurrent.atomic.AtomicInteger;
chihhangchuang923e29b2020-07-02 19:09:36 +080084
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070085/**
Santiago Etchebehere4ecb6aa2019-11-07 11:48:58 -080086 * Fragment which displays the UI for previewing an individual static wallpaper and its attribution
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070087 * information.
88 */
Chihhang Chuangc9c108a2021-04-28 18:35:14 +080089public class ImagePreviewFragment extends PreviewFragment {
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070090
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080091 private static final String TAG = "ImagePreviewFragment";
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070092 private static final float DEFAULT_WALLPAPER_MAX_ZOOM = 8f;
93
Tracy Zhou4421a3d2020-06-15 14:56:18 -070094 private final WallpaperSurfaceCallback mWallpaperSurfaceCallback =
95 new WallpaperSurfaceCallback();
Tracy Zhouc5a6bad2020-05-20 23:28:22 -070096
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070097 private SubsamplingScaleImageView mFullResImageView;
98 private Asset mWallpaperAsset;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070099 private Point mScreenSize;
100 private Point mRawWallpaperSize; // Native size of wallpaper image.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700101 private ImageView mLowResImageView;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700102 private TouchForwardingLayout mTouchForwardingLayout;
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700103 private ConstraintLayout mContainer;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700104 private SurfaceView mWallpaperSurface;
Tracy Zhou35997422020-05-01 13:42:06 -0700105 private WallpaperInfoView mWallpaperInfoView;
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800106 private AtomicInteger mImageScaleChangeCounter = new AtomicInteger(0);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700107
Santiago Etchebehere62287292021-04-30 15:53:22 -0700108 protected SurfaceView mWorkspaceSurface;
Santiago Etchebeheredbdfb662021-04-26 10:25:13 -0700109 protected WorkspaceSurfaceHolderCallback mWorkspaceSurfaceCallback;
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800110 protected ViewGroup mLockPreviewContainer;
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +0800111 protected LockScreenPreviewer mLockScreenPreviewer;
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700112 private Future<Integer> mPlaceholderColorFuture;
Chihhang Chuang57daaa92021-04-20 21:40:01 +0800113
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700114 @Override
115 public void onCreate(Bundle savedInstanceState) {
116 super.onCreate(savedInstanceState);
117 mWallpaperAsset = mWallpaper.getAsset(requireContext().getApplicationContext());
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700118 mPlaceholderColorFuture = mWallpaper.computePlaceholderColor(requireContext());
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700119 }
120
121 @Override
122 protected int getLayoutResId() {
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700123 return R.layout.fragment_image_preview_v2;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700124 }
125
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700126 @Override
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700127 public View onCreateView(LayoutInflater inflater, ViewGroup container,
Michel Comin Escudeb3e1fef2019-11-22 11:41:53 -0800128 Bundle savedInstanceState) {
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700129 View view = super.onCreateView(inflater, container, savedInstanceState);
130
131 Activity activity = requireActivity();
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700132 mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize(
133 activity.getWindowManager().getDefaultDisplay());
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700134
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700135 mContainer = view.findViewById(R.id.container);
136 mTouchForwardingLayout = mContainer.findViewById(R.id.touch_forwarding_layout);
Chihhang Chuang60a5bc22021-05-14 23:00:01 +0800137 mTouchForwardingLayout.setForwardingEnabled(true);
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700138
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700139 // Set aspect ratio on the preview card dynamically.
140 ConstraintSet set = new ConstraintSet();
141 set.clone(mContainer);
chihhangchuang923e29b2020-07-02 19:09:36 +0800142 String ratio = String.format(Locale.US, "%d:%d", mScreenSize.x, mScreenSize.y);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700143 set.setDimensionRatio(mTouchForwardingLayout.getId(), ratio);
144 set.applyTo(mContainer);
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700145
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700146 mWorkspaceSurface = mContainer.findViewById(R.id.workspace_surface);
Santiago Etchebeheredbdfb662021-04-26 10:25:13 -0700147 mWorkspaceSurfaceCallback = createWorkspaceSurfaceCallback(mWorkspaceSurface);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700148 mWallpaperSurface = mContainer.findViewById(R.id.wallpaper_surface);
chihhangchuang287d4b72020-06-25 01:09:09 +0800149 mLockPreviewContainer = mContainer.findViewById(R.id.lock_screen_preview_container);
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700150 int placeHolderColor = ResourceUtils.getColorAttr(getContext(),
151 android.R.attr.colorBackground);
152 mWorkspaceSurface.setResizeBackgroundColor(placeHolderColor);
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +0800153 mLockScreenPreviewer = new LockScreenPreviewer(getLifecycle(), getContext(),
chihhangchuang287d4b72020-06-25 01:09:09 +0800154 mLockPreviewContainer);
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +0800155 mLockScreenPreviewer.setDateViewVisibility(!mFullScreenAnimation.isFullScreen());
156 mFullScreenAnimation.setFullScreenStatusListener(
157 isFullScreen -> mLockScreenPreviewer.setDateViewVisibility(!isFullScreen));
Chihhang Chuang17821eb2021-06-21 15:31:07 +0800158 setUpTabs(view.findViewById(R.id.separated_tabs));
Tracy Zhoue4a5f262020-05-26 17:40:19 -0700159
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700160 view.measure(makeMeasureSpec(mScreenSize.x, EXACTLY),
161 makeMeasureSpec(mScreenSize.y, EXACTLY));
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700162 ((CardView) mWorkspaceSurface.getParent())
163 .setRadius(SizeCalculator.getPreviewCornerRadius(
164 activity, mContainer.getMeasuredWidth()));
Chihhang Chuang57daaa92021-04-20 21:40:01 +0800165
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800166 renderImageWallpaper();
167 renderWorkspaceSurface();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700168
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700169 // Trim some memory from Glide to make room for the full-size image in this fragment.
170 Glide.get(activity).setMemoryCategory(MemoryCategory.LOW);
Tianguang Zhang893187f2021-05-07 22:14:04 +0200171
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700172 return view;
173 }
174
Santiago Etchebehere4ecb6aa2019-11-07 11:48:58 -0800175 @Override
Chuck Liaofc1b8c92020-05-21 20:54:15 +0800176 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
177 super.onViewCreated(view, savedInstanceState);
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800178 }
179
Chihhang Chuangc9c108a2021-04-28 18:35:14 +0800180 protected void onWallpaperColorsChanged(@Nullable WallpaperColors colors) {
Chihhang Chuang1e8bece2021-05-17 15:08:06 +0800181 // Make it enabled since the buttons are disabled while wallpaper is moving.
182 mBottomActionBar.enableActionButtonsWithBottomSheet(true);
183
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800184 mLockScreenPreviewer.setColor(colors);
Tianguang Zhang893187f2021-05-07 22:14:04 +0200185
186 mFullScreenAnimation.setFullScreenTextColor(
187 colors == null || (colors.getColorHints()
188 & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
189 ? FullScreenAnimation.FullScreenTextColor.LIGHT
190 : FullScreenAnimation.FullScreenTextColor.DARK);
Santiago Etchebehere4ecb6aa2019-11-07 11:48:58 -0800191 }
192
Santiago Etchebeheredc55b192019-11-08 13:56:52 -0800193 @Override
194 protected boolean isLoaded() {
195 return mFullResImageView != null && mFullResImageView.hasImage();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700196 }
197
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700198 @Override
199 public void onClickOk() {
200 FragmentActivity activity = getActivity();
201 if (activity != null) {
202 activity.finish();
203 }
204 }
205
206 @Override
207 public void onDestroy() {
208 super.onDestroy();
Chihhang Chuang57daaa92021-04-20 21:40:01 +0800209
210 if (mFullResImageView != null) {
211 mFullResImageView.recycle();
212 }
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700213
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +0800214 if (mLockScreenPreviewer != null) {
215 mLockScreenPreviewer.release();
216 }
217
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700218 mWallpaperSurfaceCallback.cleanUp();
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700219 mWorkspaceSurfaceCallback.cleanUp();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700220 }
221
222 @Override
Tracy Zhoue4a5f262020-05-26 17:40:19 -0700223 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700224 super.onBottomActionBarReady(bottomActionBar);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700225 mWallpaperInfoView = (WallpaperInfoView)
226 LayoutInflater.from(getContext()).inflate(
227 R.layout.wallpaper_info_view, /* root= */null);
228 mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
229 mBottomActionBar.showActionsOnly(INFORMATION, EDIT, APPLY);
Tianguang Zhang893187f2021-05-07 22:14:04 +0200230
Chihhang Chuangca7c8252020-06-17 17:32:46 +0800231 mBottomActionBar.setActionClickListener(APPLY, this::onSetWallpaperClicked);
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +0800232
Chihhang Chuang17821eb2021-06-21 15:31:07 +0800233 View separatedTabsContainer = getView().findViewById(R.id.separated_tabs_container);
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +0800234 // Update target view's accessibility param since it will be blocked by the bottom sheet
235 // when expanded.
236 mBottomActionBar.setAccessibilityCallback(new AccessibilityCallback() {
237 @Override
238 public void onBottomSheetCollapsed() {
Chihhang Chuang15169282021-06-07 20:25:18 +0800239 mContainer.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
Chihhang Chuang17821eb2021-06-21 15:31:07 +0800240 separatedTabsContainer.setImportantForAccessibility(
241 IMPORTANT_FOR_ACCESSIBILITY_YES);
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +0800242 }
243
244 @Override
245 public void onBottomSheetExpanded() {
Wesley.CW Wangb8e51a22020-07-02 18:40:31 +0800246 mContainer.setImportantForAccessibility(
Chihhang Chuang15169282021-06-07 20:25:18 +0800247 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
Chihhang Chuang17821eb2021-06-21 15:31:07 +0800248 separatedTabsContainer.setImportantForAccessibility(
Chihhang Chuang15169282021-06-07 20:25:18 +0800249 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
250
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +0800251 }
252 });
253
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700254 mBottomActionBar.show();
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800255 // Loads wallpaper info and populate into view.
256 setUpExploreIntentAndLabel(this::populateWallpaperInfo);
257 // To avoid applying the wallpaper when the wallpaper's not parsed.
chihhangchuang13cb1fe2020-06-30 16:46:59 +0800258 mBottomActionBar.disableActions();
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800259 // If the wallpaper is parsed, enable the bottom action bar.
260 if (mRawWallpaperSize != null) {
261 mBottomActionBar.enableActions();
262 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700263 }
264
265 /**
266 * Initializes MosaicView by initializing tiling, setting a fallback page bitmap, and
267 * initializing a zoom-scroll observer and click listener.
268 */
269 private void initFullResView() {
Chihhang Chuang9c1d4e42021-05-14 23:53:16 +0800270 if (mRawWallpaperSize == null || mFullResImageView == null) {
271 return;
272 }
273
Tracy Zhoudee07892020-06-16 20:14:30 -0700274 // Minimum scale will only be respected under this scale type.
275 mFullResImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM);
276 // When we set a minimum scale bigger than the scale with which the full image is shown,
277 // disallow user to pan outside the view we show the wallpaper in.
278 mFullResImageView.setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700279
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700280 // Then set a fallback "page bitmap" to cover the whole MosaicView, which is an actual
281 // (lower res) version of the image to be displayed.
282 Point targetPageBitmapSize = new Point(mRawWallpaperSize);
283 mWallpaperAsset.decodeBitmap(targetPageBitmapSize.x, targetPageBitmapSize.y,
284 pageBitmap -> {
285 // Check that the activity is still around since the decoding task started.
286 if (getActivity() == null) {
287 return;
288 }
289
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700290 // The page bitmap may be null if there was a decoding error, so show an
291 // error dialog.
292 if (pageBitmap == null) {
293 showLoadWallpaperErrorDialog();
294 return;
295 }
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700296 // Some of these may be null depending on if the Fragment is paused, stopped,
297 // or destroyed.
298 mWallpaperSurface.setBackgroundColor(Color.TRANSPARENT);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700299 if (mFullResImageView != null) {
300 // Set page bitmap.
301 mFullResImageView.setImage(ImageSource.bitmap(pageBitmap));
302
Chuck Liaodc424e82020-09-04 00:17:44 +0800303 setDefaultWallpaperZoomAndScroll(
304 mWallpaperAsset instanceof CurrentWallpaperAssetVN);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700305 crossFadeInMosaicView();
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800306 mFullResImageView.setOnStateChangedListener(
307 new SubsamplingScaleImageView.DefaultOnStateChangedListener() {
308 @Override
309 public void onCenterChanged(PointF newCenter, int origin) {
310 super.onCenterChanged(newCenter, origin);
Chihhang Chuang1e8bece2021-05-17 15:08:06 +0800311 // Disallow bottom sheet to popup when wallpaper is moving
312 // by user dragging.
313 mBottomActionBar.enableActionButtonsWithBottomSheet(false);
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800314 mImageScaleChangeCounter.incrementAndGet();
315 mFullResImageView.postDelayed(() -> {
316 if (mImageScaleChangeCounter.decrementAndGet() == 0) {
317 recalculateColors();
318 }
319 }, /* delayMillis= */ 100);
320 }
321 });
Chihhang Chuangc9c108a2021-04-28 18:35:14 +0800322 mFullResImageView.post(this::recalculateColors);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700323 }
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800324 });
Chihhang Chuang1e8bece2021-05-17 15:08:06 +0800325
326 mFullResImageView.setOnTouchListener((v, ev) -> {
327 // Consume the touch event for collapsing bottom sheet while it is expanded or
328 // dragging (not collapsed).
329 if (mBottomActionBar != null && !mBottomActionBar.isBottomSheetCollapsed()) {
330 mBottomActionBar.collapseBottomSheetIfExpanded();
331 return true;
332 }
333 return false;
334 });
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800335 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700336
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800337 private void recalculateColors() {
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800338 Context context = getContext();
339 if (context == null) {
340 Log.e(TAG, "Got null context, skip recalculating colors");
341 return;
342 }
343
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800344 BitmapCropper bitmapCropper = InjectorProvider.getInjector().getBitmapCropper();
345 bitmapCropper.cropAndScaleBitmap(mWallpaperAsset, mFullResImageView.getScale(),
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800346 calculateCropRect(context), /* adjustForRtl= */ false,
347 new BitmapCropper.Callback() {
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800348 @Override
349 public void onBitmapCropped(Bitmap croppedBitmap) {
Santiago Etchebehere40ce6d22021-06-08 18:41:35 -0700350 boolean shouldRecycle = false;
351 if (croppedBitmap.getConfig() == Bitmap.Config.HARDWARE) {
352 croppedBitmap = croppedBitmap.copy(Bitmap.Config.ARGB_8888, false);
353 shouldRecycle = true;
354 }
355 WallpaperColors colors = WallpaperColors.fromBitmap(croppedBitmap);
356 if (shouldRecycle) {
357 croppedBitmap.recycle();
358 }
359 onWallpaperColorsChanged(colors);
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800360 }
361
362 @Override
363 public void onError(@Nullable Throwable e) {
364 Log.w(TAG, "Recalculate colors, crop and scale bitmap failed.", e);
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700365 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700366 });
367 }
368
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800369 private void populateWallpaperInfo() {
370 if (mWallpaperInfoView != null && mWallpaper != null) {
371 mWallpaperInfoView.populateWallpaperInfo(
372 mWallpaper,
373 mActionLabel,
374 WallpaperInfoHelper.shouldShowExploreButton(
375 getContext(), mExploreIntent),
376 this::onExploreClicked);
377 }
378 }
379
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700380 /**
381 * Makes the MosaicView visible with an alpha fade-in animation while fading out the loading
382 * indicator.
383 */
384 private void crossFadeInMosaicView() {
385 long shortAnimationDuration = getResources().getInteger(
386 android.R.integer.config_shortAnimTime);
387
388 mFullResImageView.setAlpha(0f);
389 mFullResImageView.animate()
390 .alpha(1f)
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700391 .setInterpolator(ALPHA_OUT)
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700392 .setDuration(shortAnimationDuration)
393 .setListener(new AnimatorListenerAdapter() {
394 @Override
395 public void onAnimationEnd(Animator animation) {
396 // Clear the thumbnail bitmap reference to save memory since it's no longer
397 // visible.
398 if (mLowResImageView != null) {
399 mLowResImageView.setImageBitmap(null);
400 }
401 }
402 });
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700403 }
404
405 /**
Michel Comin Escudeb3e1fef2019-11-22 11:41:53 -0800406 * Sets the default wallpaper zoom and scroll position based on a "crop surface" (with extra
407 * width to account for parallax) superimposed on the screen. Shows as much of the wallpaper as
408 * possible on the crop surface and align screen to crop surface such that the default preview
409 * matches what would be seen by the user in the left-most home screen.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700410 *
411 * <p>This method is called once in the Fragment lifecycle after the wallpaper asset has loaded
412 * and rendered to the layout.
Chuck Liaodc424e82020-09-04 00:17:44 +0800413 *
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800414 * @param offsetToStart {@code true} if we want to offset the visible rectangle to the start
415 * side of the raw wallpaper; {@code false} otherwise.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700416 */
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800417 private void setDefaultWallpaperZoomAndScroll(boolean offsetToStart) {
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700418 // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface.
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800419 int cropWidth = mWallpaperSurface.getMeasuredWidth();
420 int cropHeight = mWallpaperSurface.getMeasuredHeight();
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700421 Point crop = new Point(cropWidth, cropHeight);
422 Rect visibleRawWallpaperRect =
423 WallpaperCropUtils.calculateVisibleRect(mRawWallpaperSize, crop);
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800424 if (offsetToStart) {
425 if (WallpaperCropUtils.isRtl(requireContext())) {
426 visibleRawWallpaperRect.offsetTo(mRawWallpaperSize.x
427 - visibleRawWallpaperRect.width(), visibleRawWallpaperRect.top);
428 } else {
429 visibleRawWallpaperRect.offsetTo(/* newLeft= */ 0, visibleRawWallpaperRect.top);
430 }
Chuck Liaodc424e82020-09-04 00:17:44 +0800431 }
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -0700432
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800433 final PointF centerPosition = new PointF(visibleRawWallpaperRect.centerX(),
434 visibleRawWallpaperRect.centerY());
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -0700435
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700436 Point visibleRawWallpaperSize = new Point(visibleRawWallpaperRect.width(),
437 visibleRawWallpaperRect.height());
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700438
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700439 final float defaultWallpaperZoom = WallpaperCropUtils.calculateMinZoom(
440 visibleRawWallpaperSize, crop);
441 final float minWallpaperZoom = defaultWallpaperZoom;
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700442
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700443
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700444 // Set min wallpaper zoom and max zoom on MosaicView widget.
445 mFullResImageView.setMaxScale(Math.max(DEFAULT_WALLPAPER_MAX_ZOOM, defaultWallpaperZoom));
446 mFullResImageView.setMinScale(minWallpaperZoom);
447
448 // Set center to composite positioning between scaled wallpaper and screen.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700449 mFullResImageView.setScaleAndCenter(minWallpaperZoom, centerPosition);
450 }
451
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800452 private Rect calculateCropRect(Context context) {
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700453 float wallpaperZoom = mFullResImageView.getScale();
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800454 Context appContext = context.getApplicationContext();
Tracy Zhou67fd7e32020-05-01 18:35:36 -0700455
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700456 Rect visibleFileRect = new Rect();
457 mFullResImageView.visibleFileRect(visibleFileRect);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700458
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800459 int cropWidth = mWallpaperSurface.getMeasuredWidth();
460 int cropHeight = mWallpaperSurface.getMeasuredHeight();
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700461 int maxCrop = Math.max(cropWidth, cropHeight);
462 int minCrop = Math.min(cropWidth, cropHeight);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700463 Point hostViewSize = new Point(cropWidth, cropHeight);
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700464
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800465 Resources res = appContext.getResources();
Santiago Etchebeherebcd6bb62020-07-09 12:09:21 -0700466 Point cropSurfaceSize = WallpaperCropUtils.calculateCropSurfaceSize(res, maxCrop, minCrop);
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700467
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700468 return WallpaperCropUtils.calculateCropRect(appContext, hostViewSize,
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700469 cropSurfaceSize, mRawWallpaperSize, visibleFileRect, wallpaperZoom);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700470 }
471
472 @Override
473 protected void setCurrentWallpaper(@Destination int destination) {
474 mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset,
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800475 destination, mFullResImageView.getScale(), calculateCropRect(getContext()),
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700476 new SetWallpaperCallback() {
477 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800478 public void onSuccess(WallpaperInfo wallpaperInfo) {
Ching-Sung Lib5e9bd72020-06-15 14:10:55 +0800479 finishActivity(/* success= */ true);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700480 }
481
482 @Override
483 public void onError(@Nullable Throwable throwable) {
484 showSetWallpaperErrorDialog(destination);
485 }
486 });
487 }
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700488
489 private void renderWorkspaceSurface() {
490 mWorkspaceSurface.setZOrderMediaOverlay(true);
491 mWorkspaceSurface.getHolder().addCallback(mWorkspaceSurfaceCallback);
492 }
493
494 private void renderImageWallpaper() {
495 mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback);
496 }
497
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700498 private class WallpaperSurfaceCallback implements SurfaceHolder.Callback {
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700499 private Surface mLastSurface;
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700500 private SurfaceControlViewHost mHost;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700501
502 @Override
503 public void surfaceCreated(SurfaceHolder holder) {
504 if (mLastSurface != holder.getSurface()) {
505 mLastSurface = holder.getSurface();
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700506 if (mFullResImageView != null) {
507 mFullResImageView.recycle();
508 }
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800509 Context context = getContext();
510 View wallpaperPreviewContainer = LayoutInflater.from(context).inflate(
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700511 R.layout.fullscreen_wallpaper_preview, null);
512 mFullResImageView = wallpaperPreviewContainer.findViewById(R.id.full_res_image);
513 mLowResImageView = wallpaperPreviewContainer.findViewById(R.id.low_res_image);
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700514 mWallpaperAsset.decodeRawDimensions(getActivity(), dimensions -> {
515 // Don't continue loading the wallpaper if the Fragment is detached.
516 if (getActivity() == null) {
517 return;
518 }
519
520 // Return early and show a dialog if dimensions are null (signaling a decoding
521 // error).
522 if (dimensions == null) {
523 showLoadWallpaperErrorDialog();
524 return;
525 }
526
527 // To avoid applying the wallpaper when it's not parsed. Now it's parsed, enable
528 // the bottom action bar to allow applying the wallpaper.
529 if (mBottomActionBar != null) {
530 mBottomActionBar.enableActions();
531 }
532
533 mRawWallpaperSize = dimensions;
534 initFullResView();
535 });
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800536 // Scale the mWallpaperSurface based on system zoom's scale so that the wallpaper is
537 // rendered in a larger surface than what preview shows, simulating the behavior of
538 // the actual wallpaper surface.
539 float scale = WallpaperCropUtils.getSystemWallpaperMaximumScale(context);
540 int origWidth = mWallpaperSurface.getWidth();
541 int width = (int) (origWidth * scale);
542 int origHeight = mWallpaperSurface.getHeight();
543 int height = (int) (origHeight * scale);
544 int left = (origWidth - width) / 2;
545 int top = (origHeight - height) / 2;
546
547 if (WallpaperCropUtils.isRtl(context)) {
548 left *= -1;
549 }
550
551 LayoutParams params = mWallpaperSurface.getLayoutParams();
552 params.width = width;
553 params.height = height;
554 mWallpaperSurface.setX(left);
555 mWallpaperSurface.setY(top);
556 mWallpaperSurface.setLayoutParams(params);
557 mWallpaperSurface.requestLayout();
558
Chuck Liaoca1d1292020-06-19 01:29:53 +0800559 // Load a low-res placeholder image if there's a thumbnail available from the asset
560 // that can be shown to the user more quickly than the full-sized image.
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700561 Activity activity = requireActivity();
562 int placeHolderColor = ResourceUtils.getColorAttr(activity,
563 android.R.attr.colorBackground);
564 if (mPlaceholderColorFuture.isDone()) {
565 try {
566 placeHolderColor = mWallpaper.computePlaceholderColor(context).get();
567 } catch (InterruptedException | ExecutionException e) {
568 // Do nothing
569 }
Chuck Liaoca1d1292020-06-19 01:29:53 +0800570 }
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700571 mWallpaperSurface.setResizeBackgroundColor(placeHolderColor);
572 mWallpaperSurface.setBackgroundColor(placeHolderColor);
573
574 mWallpaperAsset.loadLowResDrawable(activity, mLowResImageView, placeHolderColor,
575 new WallpaperPreviewBitmapTransformation(
576 activity.getApplicationContext(), isRtl()));
577
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700578 wallpaperPreviewContainer.measure(
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800579 makeMeasureSpec(width, EXACTLY),
580 makeMeasureSpec(height, EXACTLY));
581 wallpaperPreviewContainer.layout(0, 0, width, height);
Ching-Sung Lib9404622020-05-18 22:38:45 +0800582 mTouchForwardingLayout.setTargetView(mFullResImageView);
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700583
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700584 cleanUp();
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800585 mHost = new SurfaceControlViewHost(context,
586 context.getDisplay(), mWallpaperSurface.getHostToken());
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700587 mHost.setView(wallpaperPreviewContainer, wallpaperPreviewContainer.getWidth(),
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700588 wallpaperPreviewContainer.getHeight());
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700589 mWallpaperSurface.setChildSurfacePackage(mHost.getSurfacePackage());
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700590 }
591 }
592
593 @Override
594 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }
595
596 @Override
597 public void surfaceDestroyed(SurfaceHolder holder) { }
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700598
599 public void cleanUp() {
600 if (mHost != null) {
601 mHost.release();
602 mHost = null;
603 }
604 }
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700605 }
606
Santiago Etchebehere3e452912021-06-07 18:10:30 -0700607 @Override
608 protected void updateScreenPreview(boolean isHomeSelected) {
chihhangchuang287d4b72020-06-25 01:09:09 +0800609 mWorkspaceSurface.setVisibility(isHomeSelected ? View.VISIBLE : View.INVISIBLE);
Santiago Etchebehere3e452912021-06-07 18:10:30 -0700610
chihhangchuang287d4b72020-06-25 01:09:09 +0800611 mLockPreviewContainer.setVisibility(isHomeSelected ? View.INVISIBLE : View.VISIBLE);
Tianguang Zhang893187f2021-05-07 22:14:04 +0200612
613 mFullScreenAnimation.setIsHomeSelected(isHomeSelected);
Tracy Zhou67fd7e32020-05-01 18:35:36 -0700614 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700615}