blob: 21d5979d21abd73e4884e075a38106de1a0ea486 [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;
James O'Learyd6458392021-06-21 09:39:51 -040034import android.graphics.BitmapFactory;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070035import android.graphics.Color;
James O'Learyd6458392021-06-21 09:39:51 -040036import android.graphics.ColorSpace;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070037import android.graphics.Point;
38import android.graphics.PointF;
39import android.graphics.Rect;
40import android.os.Bundle;
Santiago Etchebehere537a0d32021-06-30 14:55:50 +020041import android.os.Handler;
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080042import android.util.Log;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070043import android.view.LayoutInflater;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -070044import android.view.Surface;
45import android.view.SurfaceControlViewHost;
46import android.view.SurfaceHolder;
47import android.view.SurfaceView;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070048import android.view.View;
49import android.view.ViewGroup;
Santiago Etchebehere8557c462020-12-02 18:41:40 -080050import android.view.ViewGroup.LayoutParams;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070051import android.widget.ImageView;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070052
Chuck Liaofc1b8c92020-05-21 20:54:15 +080053import androidx.annotation.NonNull;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070054import androidx.annotation.Nullable;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -070055import androidx.cardview.widget.CardView;
Tracy Zhou3eb234c2020-05-05 15:17:33 -070056import androidx.constraintlayout.widget.ConstraintLayout;
57import androidx.constraintlayout.widget.ConstraintSet;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070058import androidx.fragment.app.FragmentActivity;
59
60import com.android.wallpaper.R;
61import com.android.wallpaper.asset.Asset;
Chuck Liaodc424e82020-09-04 00:17:44 +080062import com.android.wallpaper.asset.CurrentWallpaperAssetVN;
“Chuckffd832c2020-03-22 02:15:58 +080063import com.android.wallpaper.model.WallpaperInfo;
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080064import com.android.wallpaper.module.BitmapCropper;
65import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070066import com.android.wallpaper.module.WallpaperPersister.Destination;
67import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
Tianguang Zhang893187f2021-05-07 22:14:04 +020068import com.android.wallpaper.util.FullScreenAnimation;
Tianguang Zhangd3e4f632021-04-14 00:30:15 +020069import com.android.wallpaper.util.ResourceUtils;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070070import com.android.wallpaper.util.ScreenSizeCalculator;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070071import com.android.wallpaper.util.SizeCalculator;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070072import com.android.wallpaper.util.WallpaperCropUtils;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -070073import com.android.wallpaper.widget.BottomActionBar;
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +080074import com.android.wallpaper.widget.BottomActionBar.AccessibilityCallback;
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +080075import com.android.wallpaper.widget.LockScreenPreviewer;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070076
77import com.bumptech.glide.Glide;
78import com.bumptech.glide.MemoryCategory;
79import com.davemorrissey.labs.subscaleview.ImageSource;
80import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070081
James O'Learyd6458392021-06-21 09:39:51 -040082import java.io.ByteArrayOutputStream;
chihhangchuang923e29b2020-07-02 19:09:36 +080083import java.util.Locale;
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -070084import java.util.concurrent.ExecutionException;
Santiago Etchebehere537a0d32021-06-30 14:55:50 +020085import java.util.concurrent.Executor;
86import java.util.concurrent.Executors;
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -070087import java.util.concurrent.Future;
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080088import java.util.concurrent.atomic.AtomicInteger;
chihhangchuang923e29b2020-07-02 19:09:36 +080089
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070090/**
Santiago Etchebehere4ecb6aa2019-11-07 11:48:58 -080091 * Fragment which displays the UI for previewing an individual static wallpaper and its attribution
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070092 * information.
93 */
Chihhang Chuangc9c108a2021-04-28 18:35:14 +080094public class ImagePreviewFragment extends PreviewFragment {
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070095
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080096 private static final String TAG = "ImagePreviewFragment";
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070097 private static final float DEFAULT_WALLPAPER_MAX_ZOOM = 8f;
Santiago Etchebehere537a0d32021-06-30 14:55:50 +020098 private static final Executor sExecutor = Executors.newCachedThreadPool();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070099
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700100 private final WallpaperSurfaceCallback mWallpaperSurfaceCallback =
101 new WallpaperSurfaceCallback();
Tracy Zhouc5a6bad2020-05-20 23:28:22 -0700102
Santiago Etchebehere537a0d32021-06-30 14:55:50 +0200103 private final AtomicInteger mImageScaleChangeCounter = new AtomicInteger(0);
104 private final AtomicInteger mRecalculateColorCounter = new AtomicInteger(0);
105
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700106 private SubsamplingScaleImageView mFullResImageView;
107 private Asset mWallpaperAsset;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700108 private Point mScreenSize;
109 private Point mRawWallpaperSize; // Native size of wallpaper image.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700110 private ImageView mLowResImageView;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700111 private TouchForwardingLayout mTouchForwardingLayout;
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700112 private ConstraintLayout mContainer;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700113 private SurfaceView mWallpaperSurface;
Santiago Etchebeherec320c5f2021-05-14 17:35:57 -0700114 private WallpaperColors mWallpaperColors;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700115
Santiago Etchebehere62287292021-04-30 15:53:22 -0700116 protected SurfaceView mWorkspaceSurface;
Santiago Etchebeheredbdfb662021-04-26 10:25:13 -0700117 protected WorkspaceSurfaceHolderCallback mWorkspaceSurfaceCallback;
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800118 protected ViewGroup mLockPreviewContainer;
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +0800119 protected LockScreenPreviewer mLockScreenPreviewer;
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700120 private Future<Integer> mPlaceholderColorFuture;
Chihhang Chuang57daaa92021-04-20 21:40:01 +0800121
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700122 @Override
123 public void onCreate(Bundle savedInstanceState) {
124 super.onCreate(savedInstanceState);
125 mWallpaperAsset = mWallpaper.getAsset(requireContext().getApplicationContext());
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700126 mPlaceholderColorFuture = mWallpaper.computePlaceholderColor(requireContext());
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700127 }
128
129 @Override
130 protected int getLayoutResId() {
Ching-Sung Lid2f46be2021-06-22 20:55:28 +0800131 return R.layout.fragment_image_preview;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700132 }
133
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700134 @Override
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700135 public View onCreateView(LayoutInflater inflater, ViewGroup container,
Michel Comin Escudeb3e1fef2019-11-22 11:41:53 -0800136 Bundle savedInstanceState) {
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700137 View view = super.onCreateView(inflater, container, savedInstanceState);
138
139 Activity activity = requireActivity();
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700140 mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize(
141 activity.getWindowManager().getDefaultDisplay());
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700142
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700143 mContainer = view.findViewById(R.id.container);
144 mTouchForwardingLayout = mContainer.findViewById(R.id.touch_forwarding_layout);
Chihhang Chuang60a5bc22021-05-14 23:00:01 +0800145 mTouchForwardingLayout.setForwardingEnabled(true);
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700146
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700147 // Set aspect ratio on the preview card dynamically.
148 ConstraintSet set = new ConstraintSet();
149 set.clone(mContainer);
chihhangchuang923e29b2020-07-02 19:09:36 +0800150 String ratio = String.format(Locale.US, "%d:%d", mScreenSize.x, mScreenSize.y);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700151 set.setDimensionRatio(mTouchForwardingLayout.getId(), ratio);
152 set.applyTo(mContainer);
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700153
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700154 mWorkspaceSurface = mContainer.findViewById(R.id.workspace_surface);
Santiago Etchebeheredbdfb662021-04-26 10:25:13 -0700155 mWorkspaceSurfaceCallback = createWorkspaceSurfaceCallback(mWorkspaceSurface);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700156 mWallpaperSurface = mContainer.findViewById(R.id.wallpaper_surface);
chihhangchuang287d4b72020-06-25 01:09:09 +0800157 mLockPreviewContainer = mContainer.findViewById(R.id.lock_screen_preview_container);
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700158 int placeHolderColor = ResourceUtils.getColorAttr(getContext(),
159 android.R.attr.colorBackground);
160 mWorkspaceSurface.setResizeBackgroundColor(placeHolderColor);
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +0800161 mLockScreenPreviewer = new LockScreenPreviewer(getLifecycle(), getContext(),
chihhangchuang287d4b72020-06-25 01:09:09 +0800162 mLockPreviewContainer);
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +0800163 mLockScreenPreviewer.setDateViewVisibility(!mFullScreenAnimation.isFullScreen());
164 mFullScreenAnimation.setFullScreenStatusListener(
165 isFullScreen -> mLockScreenPreviewer.setDateViewVisibility(!isFullScreen));
Chihhang Chuang17821eb2021-06-21 15:31:07 +0800166 setUpTabs(view.findViewById(R.id.separated_tabs));
Tracy Zhoue4a5f262020-05-26 17:40:19 -0700167
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700168 view.measure(makeMeasureSpec(mScreenSize.x, EXACTLY),
169 makeMeasureSpec(mScreenSize.y, EXACTLY));
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700170 ((CardView) mWorkspaceSurface.getParent())
171 .setRadius(SizeCalculator.getPreviewCornerRadius(
172 activity, mContainer.getMeasuredWidth()));
Chihhang Chuang57daaa92021-04-20 21:40:01 +0800173
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800174 renderImageWallpaper();
175 renderWorkspaceSurface();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700176
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700177 // Trim some memory from Glide to make room for the full-size image in this fragment.
178 Glide.get(activity).setMemoryCategory(MemoryCategory.LOW);
Tianguang Zhang893187f2021-05-07 22:14:04 +0200179
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700180 return view;
181 }
182
Santiago Etchebehere4ecb6aa2019-11-07 11:48:58 -0800183 @Override
Chuck Liaofc1b8c92020-05-21 20:54:15 +0800184 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
185 super.onViewCreated(view, savedInstanceState);
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800186 }
187
Chihhang Chuangc9c108a2021-04-28 18:35:14 +0800188 protected void onWallpaperColorsChanged(@Nullable WallpaperColors colors) {
Chihhang Chuang1e8bece2021-05-17 15:08:06 +0800189 // Make it enabled since the buttons are disabled while wallpaper is moving.
190 mBottomActionBar.enableActionButtonsWithBottomSheet(true);
191
Santiago Etchebeherec320c5f2021-05-14 17:35:57 -0700192 mWallpaperColors = colors;
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800193 mLockScreenPreviewer.setColor(colors);
Tianguang Zhang893187f2021-05-07 22:14:04 +0200194
195 mFullScreenAnimation.setFullScreenTextColor(
196 colors == null || (colors.getColorHints()
197 & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
198 ? FullScreenAnimation.FullScreenTextColor.LIGHT
199 : FullScreenAnimation.FullScreenTextColor.DARK);
Santiago Etchebehere4ecb6aa2019-11-07 11:48:58 -0800200 }
201
Santiago Etchebeheredc55b192019-11-08 13:56:52 -0800202 @Override
203 protected boolean isLoaded() {
204 return mFullResImageView != null && mFullResImageView.hasImage();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700205 }
206
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700207 @Override
208 public void onClickOk() {
209 FragmentActivity activity = getActivity();
210 if (activity != null) {
211 activity.finish();
212 }
213 }
214
215 @Override
216 public void onDestroy() {
217 super.onDestroy();
Chihhang Chuang57daaa92021-04-20 21:40:01 +0800218
219 if (mFullResImageView != null) {
220 mFullResImageView.recycle();
221 }
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700222
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +0800223 if (mLockScreenPreviewer != null) {
224 mLockScreenPreviewer.release();
225 }
226
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700227 mWallpaperSurfaceCallback.cleanUp();
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700228 mWorkspaceSurfaceCallback.cleanUp();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700229 }
230
231 @Override
Tracy Zhoue4a5f262020-05-26 17:40:19 -0700232 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700233 super.onBottomActionBarReady(bottomActionBar);
Chihhang Chuangf97f1592021-06-24 16:51:04 +0800234 mBottomActionBar.bindBottomSheetContentWithAction(
235 new WallpaperInfoContent(getContext()), INFORMATION);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700236 mBottomActionBar.showActionsOnly(INFORMATION, EDIT, APPLY);
Tianguang Zhang893187f2021-05-07 22:14:04 +0200237
Chihhang Chuangca7c8252020-06-17 17:32:46 +0800238 mBottomActionBar.setActionClickListener(APPLY, this::onSetWallpaperClicked);
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +0800239
Chihhang Chuang17821eb2021-06-21 15:31:07 +0800240 View separatedTabsContainer = getView().findViewById(R.id.separated_tabs_container);
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +0800241 // Update target view's accessibility param since it will be blocked by the bottom sheet
242 // when expanded.
243 mBottomActionBar.setAccessibilityCallback(new AccessibilityCallback() {
244 @Override
245 public void onBottomSheetCollapsed() {
Chihhang Chuang15169282021-06-07 20:25:18 +0800246 mContainer.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
Chihhang Chuang17821eb2021-06-21 15:31:07 +0800247 separatedTabsContainer.setImportantForAccessibility(
248 IMPORTANT_FOR_ACCESSIBILITY_YES);
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +0800249 }
250
251 @Override
252 public void onBottomSheetExpanded() {
Wesley.CW Wangb8e51a22020-07-02 18:40:31 +0800253 mContainer.setImportantForAccessibility(
Chihhang Chuang15169282021-06-07 20:25:18 +0800254 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
Chihhang Chuang17821eb2021-06-21 15:31:07 +0800255 separatedTabsContainer.setImportantForAccessibility(
Chihhang Chuang15169282021-06-07 20:25:18 +0800256 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
257
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +0800258 }
259 });
260
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700261 mBottomActionBar.show();
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800262 // To avoid applying the wallpaper when the wallpaper's not parsed.
chihhangchuang13cb1fe2020-06-30 16:46:59 +0800263 mBottomActionBar.disableActions();
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800264 // If the wallpaper is parsed, enable the bottom action bar.
265 if (mRawWallpaperSize != null) {
266 mBottomActionBar.enableActions();
267 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700268 }
269
270 /**
271 * Initializes MosaicView by initializing tiling, setting a fallback page bitmap, and
272 * initializing a zoom-scroll observer and click listener.
273 */
274 private void initFullResView() {
Chihhang Chuang9c1d4e42021-05-14 23:53:16 +0800275 if (mRawWallpaperSize == null || mFullResImageView == null) {
276 return;
277 }
278
Tracy Zhoudee07892020-06-16 20:14:30 -0700279 // Minimum scale will only be respected under this scale type.
280 mFullResImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM);
281 // When we set a minimum scale bigger than the scale with which the full image is shown,
282 // disallow user to pan outside the view we show the wallpaper in.
283 mFullResImageView.setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700284
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700285 // Then set a fallback "page bitmap" to cover the whole MosaicView, which is an actual
286 // (lower res) version of the image to be displayed.
287 Point targetPageBitmapSize = new Point(mRawWallpaperSize);
288 mWallpaperAsset.decodeBitmap(targetPageBitmapSize.x, targetPageBitmapSize.y,
289 pageBitmap -> {
290 // Check that the activity is still around since the decoding task started.
291 if (getActivity() == null) {
292 return;
293 }
294
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700295 // The page bitmap may be null if there was a decoding error, so show an
296 // error dialog.
297 if (pageBitmap == null) {
298 showLoadWallpaperErrorDialog();
299 return;
300 }
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700301 // Some of these may be null depending on if the Fragment is paused, stopped,
302 // or destroyed.
303 mWallpaperSurface.setBackgroundColor(Color.TRANSPARENT);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700304 if (mFullResImageView != null) {
305 // Set page bitmap.
306 mFullResImageView.setImage(ImageSource.bitmap(pageBitmap));
307
Chuck Liaodc424e82020-09-04 00:17:44 +0800308 setDefaultWallpaperZoomAndScroll(
309 mWallpaperAsset instanceof CurrentWallpaperAssetVN);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700310 crossFadeInMosaicView();
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800311 mFullResImageView.setOnStateChangedListener(
312 new SubsamplingScaleImageView.DefaultOnStateChangedListener() {
313 @Override
314 public void onCenterChanged(PointF newCenter, int origin) {
315 super.onCenterChanged(newCenter, origin);
Chihhang Chuang1e8bece2021-05-17 15:08:06 +0800316 // Disallow bottom sheet to popup when wallpaper is moving
317 // by user dragging.
318 mBottomActionBar.enableActionButtonsWithBottomSheet(false);
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800319 mImageScaleChangeCounter.incrementAndGet();
320 mFullResImageView.postDelayed(() -> {
321 if (mImageScaleChangeCounter.decrementAndGet() == 0) {
322 recalculateColors();
323 }
324 }, /* delayMillis= */ 100);
325 }
326 });
Chihhang Chuangc9c108a2021-04-28 18:35:14 +0800327 mFullResImageView.post(this::recalculateColors);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700328 }
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800329 });
Chihhang Chuang1e8bece2021-05-17 15:08:06 +0800330
331 mFullResImageView.setOnTouchListener((v, ev) -> {
332 // Consume the touch event for collapsing bottom sheet while it is expanded or
333 // dragging (not collapsed).
334 if (mBottomActionBar != null && !mBottomActionBar.isBottomSheetCollapsed()) {
335 mBottomActionBar.collapseBottomSheetIfExpanded();
336 return true;
337 }
338 return false;
339 });
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800340 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700341
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800342 private void recalculateColors() {
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800343 Context context = getContext();
344 if (context == null) {
345 Log.e(TAG, "Got null context, skip recalculating colors");
346 return;
347 }
348
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800349 BitmapCropper bitmapCropper = InjectorProvider.getInjector().getBitmapCropper();
350 bitmapCropper.cropAndScaleBitmap(mWallpaperAsset, mFullResImageView.getScale(),
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800351 calculateCropRect(context), /* adjustForRtl= */ false,
352 new BitmapCropper.Callback() {
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800353 @Override
354 public void onBitmapCropped(Bitmap croppedBitmap) {
Santiago Etchebehere537a0d32021-06-30 14:55:50 +0200355 mRecalculateColorCounter.incrementAndGet();
356 sExecutor.execute(() -> {
357 boolean shouldRecycle = false;
358 ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
359 Bitmap cropped = croppedBitmap;
360 if (cropped.compress(Bitmap.CompressFormat.PNG, 100, tmpOut)) {
361 byte[] outByteArray = tmpOut.toByteArray();
362 BitmapFactory.Options options = new BitmapFactory.Options();
363 options.inPreferredColorSpace =
364 ColorSpace.get(ColorSpace.Named.SRGB);
365 cropped = BitmapFactory.decodeByteArray(outByteArray, 0,
366 outByteArray.length);
367 }
368 if (cropped.getConfig() == Bitmap.Config.HARDWARE) {
369 cropped = cropped.copy(Bitmap.Config.ARGB_8888, false);
370 shouldRecycle = true;
371 }
372 WallpaperColors colors = WallpaperColors.fromBitmap(cropped);
373 if (shouldRecycle) {
374 cropped.recycle();
375 }
376 if (mRecalculateColorCounter.decrementAndGet() == 0) {
377 Handler.getMain().post(() -> onWallpaperColorsChanged(colors));
378 }
379 });
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800380 }
381
382 @Override
383 public void onError(@Nullable Throwable e) {
384 Log.w(TAG, "Recalculate colors, crop and scale bitmap failed.", e);
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700385 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700386 });
387 }
388
389 /**
390 * Makes the MosaicView visible with an alpha fade-in animation while fading out the loading
391 * indicator.
392 */
393 private void crossFadeInMosaicView() {
394 long shortAnimationDuration = getResources().getInteger(
395 android.R.integer.config_shortAnimTime);
396
397 mFullResImageView.setAlpha(0f);
398 mFullResImageView.animate()
399 .alpha(1f)
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700400 .setInterpolator(ALPHA_OUT)
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700401 .setDuration(shortAnimationDuration)
402 .setListener(new AnimatorListenerAdapter() {
403 @Override
404 public void onAnimationEnd(Animator animation) {
405 // Clear the thumbnail bitmap reference to save memory since it's no longer
406 // visible.
407 if (mLowResImageView != null) {
408 mLowResImageView.setImageBitmap(null);
409 }
410 }
411 });
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700412 }
413
414 /**
Michel Comin Escudeb3e1fef2019-11-22 11:41:53 -0800415 * Sets the default wallpaper zoom and scroll position based on a "crop surface" (with extra
416 * width to account for parallax) superimposed on the screen. Shows as much of the wallpaper as
417 * possible on the crop surface and align screen to crop surface such that the default preview
418 * matches what would be seen by the user in the left-most home screen.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700419 *
420 * <p>This method is called once in the Fragment lifecycle after the wallpaper asset has loaded
421 * and rendered to the layout.
Chuck Liaodc424e82020-09-04 00:17:44 +0800422 *
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800423 * @param offsetToStart {@code true} if we want to offset the visible rectangle to the start
424 * side of the raw wallpaper; {@code false} otherwise.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700425 */
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800426 private void setDefaultWallpaperZoomAndScroll(boolean offsetToStart) {
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700427 // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface.
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800428 int cropWidth = mWallpaperSurface.getMeasuredWidth();
429 int cropHeight = mWallpaperSurface.getMeasuredHeight();
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700430 Point crop = new Point(cropWidth, cropHeight);
431 Rect visibleRawWallpaperRect =
432 WallpaperCropUtils.calculateVisibleRect(mRawWallpaperSize, crop);
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800433 if (offsetToStart) {
434 if (WallpaperCropUtils.isRtl(requireContext())) {
435 visibleRawWallpaperRect.offsetTo(mRawWallpaperSize.x
436 - visibleRawWallpaperRect.width(), visibleRawWallpaperRect.top);
437 } else {
438 visibleRawWallpaperRect.offsetTo(/* newLeft= */ 0, visibleRawWallpaperRect.top);
439 }
Chuck Liaodc424e82020-09-04 00:17:44 +0800440 }
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -0700441
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800442 final PointF centerPosition = new PointF(visibleRawWallpaperRect.centerX(),
443 visibleRawWallpaperRect.centerY());
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -0700444
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700445 Point visibleRawWallpaperSize = new Point(visibleRawWallpaperRect.width(),
446 visibleRawWallpaperRect.height());
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700447
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700448 final float defaultWallpaperZoom = WallpaperCropUtils.calculateMinZoom(
449 visibleRawWallpaperSize, crop);
450 final float minWallpaperZoom = defaultWallpaperZoom;
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700451
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700452
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700453 // Set min wallpaper zoom and max zoom on MosaicView widget.
454 mFullResImageView.setMaxScale(Math.max(DEFAULT_WALLPAPER_MAX_ZOOM, defaultWallpaperZoom));
455 mFullResImageView.setMinScale(minWallpaperZoom);
456
457 // Set center to composite positioning between scaled wallpaper and screen.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700458 mFullResImageView.setScaleAndCenter(minWallpaperZoom, centerPosition);
459 }
460
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800461 private Rect calculateCropRect(Context context) {
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700462 float wallpaperZoom = mFullResImageView.getScale();
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800463 Context appContext = context.getApplicationContext();
Tracy Zhou67fd7e32020-05-01 18:35:36 -0700464
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700465 Rect visibleFileRect = new Rect();
466 mFullResImageView.visibleFileRect(visibleFileRect);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700467
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800468 int cropWidth = mWallpaperSurface.getMeasuredWidth();
469 int cropHeight = mWallpaperSurface.getMeasuredHeight();
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700470 int maxCrop = Math.max(cropWidth, cropHeight);
471 int minCrop = Math.min(cropWidth, cropHeight);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700472 Point hostViewSize = new Point(cropWidth, cropHeight);
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700473
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800474 Resources res = appContext.getResources();
Santiago Etchebeherebcd6bb62020-07-09 12:09:21 -0700475 Point cropSurfaceSize = WallpaperCropUtils.calculateCropSurfaceSize(res, maxCrop, minCrop);
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700476
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700477 return WallpaperCropUtils.calculateCropRect(appContext, hostViewSize,
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700478 cropSurfaceSize, mRawWallpaperSize, visibleFileRect, wallpaperZoom);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700479 }
480
481 @Override
482 protected void setCurrentWallpaper(@Destination int destination) {
483 mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset,
Chihhang Chuang5d94cb62021-05-25 18:10:22 +0800484 destination, mFullResImageView.getScale(), calculateCropRect(getContext()),
Santiago Etchebeherec320c5f2021-05-14 17:35:57 -0700485 mWallpaperColors, new SetWallpaperCallback() {
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700486 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800487 public void onSuccess(WallpaperInfo wallpaperInfo) {
Ching-Sung Lib5e9bd72020-06-15 14:10:55 +0800488 finishActivity(/* success= */ true);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700489 }
490
491 @Override
492 public void onError(@Nullable Throwable throwable) {
493 showSetWallpaperErrorDialog(destination);
494 }
495 });
496 }
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700497
498 private void renderWorkspaceSurface() {
499 mWorkspaceSurface.setZOrderMediaOverlay(true);
500 mWorkspaceSurface.getHolder().addCallback(mWorkspaceSurfaceCallback);
501 }
502
503 private void renderImageWallpaper() {
504 mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback);
505 }
506
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700507 private class WallpaperSurfaceCallback implements SurfaceHolder.Callback {
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700508 private Surface mLastSurface;
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700509 private SurfaceControlViewHost mHost;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700510
511 @Override
512 public void surfaceCreated(SurfaceHolder holder) {
513 if (mLastSurface != holder.getSurface()) {
514 mLastSurface = holder.getSurface();
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700515 if (mFullResImageView != null) {
516 mFullResImageView.recycle();
517 }
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800518 Context context = getContext();
519 View wallpaperPreviewContainer = LayoutInflater.from(context).inflate(
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700520 R.layout.fullscreen_wallpaper_preview, null);
521 mFullResImageView = wallpaperPreviewContainer.findViewById(R.id.full_res_image);
522 mLowResImageView = wallpaperPreviewContainer.findViewById(R.id.low_res_image);
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700523 mWallpaperAsset.decodeRawDimensions(getActivity(), dimensions -> {
524 // Don't continue loading the wallpaper if the Fragment is detached.
525 if (getActivity() == null) {
526 return;
527 }
528
529 // Return early and show a dialog if dimensions are null (signaling a decoding
530 // error).
531 if (dimensions == null) {
532 showLoadWallpaperErrorDialog();
533 return;
534 }
535
536 // To avoid applying the wallpaper when it's not parsed. Now it's parsed, enable
537 // the bottom action bar to allow applying the wallpaper.
538 if (mBottomActionBar != null) {
539 mBottomActionBar.enableActions();
540 }
541
542 mRawWallpaperSize = dimensions;
543 initFullResView();
544 });
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800545 // Scale the mWallpaperSurface based on system zoom's scale so that the wallpaper is
546 // rendered in a larger surface than what preview shows, simulating the behavior of
547 // the actual wallpaper surface.
548 float scale = WallpaperCropUtils.getSystemWallpaperMaximumScale(context);
549 int origWidth = mWallpaperSurface.getWidth();
550 int width = (int) (origWidth * scale);
551 int origHeight = mWallpaperSurface.getHeight();
552 int height = (int) (origHeight * scale);
553 int left = (origWidth - width) / 2;
554 int top = (origHeight - height) / 2;
555
556 if (WallpaperCropUtils.isRtl(context)) {
557 left *= -1;
558 }
559
560 LayoutParams params = mWallpaperSurface.getLayoutParams();
561 params.width = width;
562 params.height = height;
563 mWallpaperSurface.setX(left);
564 mWallpaperSurface.setY(top);
565 mWallpaperSurface.setLayoutParams(params);
566 mWallpaperSurface.requestLayout();
567
Chuck Liaoca1d1292020-06-19 01:29:53 +0800568 // Load a low-res placeholder image if there's a thumbnail available from the asset
569 // that can be shown to the user more quickly than the full-sized image.
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700570 Activity activity = requireActivity();
571 int placeHolderColor = ResourceUtils.getColorAttr(activity,
572 android.R.attr.colorBackground);
573 if (mPlaceholderColorFuture.isDone()) {
574 try {
575 placeHolderColor = mWallpaper.computePlaceholderColor(context).get();
576 } catch (InterruptedException | ExecutionException e) {
577 // Do nothing
578 }
Chuck Liaoca1d1292020-06-19 01:29:53 +0800579 }
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700580 mWallpaperSurface.setResizeBackgroundColor(placeHolderColor);
581 mWallpaperSurface.setBackgroundColor(placeHolderColor);
582
583 mWallpaperAsset.loadLowResDrawable(activity, mLowResImageView, placeHolderColor,
Ching-Sung Lid2f46be2021-06-22 20:55:28 +0800584 mPreviewBitmapTransformation);
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700585
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700586 wallpaperPreviewContainer.measure(
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800587 makeMeasureSpec(width, EXACTLY),
588 makeMeasureSpec(height, EXACTLY));
589 wallpaperPreviewContainer.layout(0, 0, width, height);
Ching-Sung Lib9404622020-05-18 22:38:45 +0800590 mTouchForwardingLayout.setTargetView(mFullResImageView);
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700591
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700592 cleanUp();
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800593 mHost = new SurfaceControlViewHost(context,
594 context.getDisplay(), mWallpaperSurface.getHostToken());
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700595 mHost.setView(wallpaperPreviewContainer, wallpaperPreviewContainer.getWidth(),
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700596 wallpaperPreviewContainer.getHeight());
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700597 mWallpaperSurface.setChildSurfacePackage(mHost.getSurfacePackage());
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700598 }
599 }
600
601 @Override
602 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }
603
604 @Override
605 public void surfaceDestroyed(SurfaceHolder holder) { }
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700606
607 public void cleanUp() {
608 if (mHost != null) {
609 mHost.release();
610 mHost = null;
611 }
612 }
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700613 }
614
Santiago Etchebehere3e452912021-06-07 18:10:30 -0700615 @Override
616 protected void updateScreenPreview(boolean isHomeSelected) {
chihhangchuang287d4b72020-06-25 01:09:09 +0800617 mWorkspaceSurface.setVisibility(isHomeSelected ? View.VISIBLE : View.INVISIBLE);
Santiago Etchebehere3e452912021-06-07 18:10:30 -0700618
chihhangchuang287d4b72020-06-25 01:09:09 +0800619 mLockPreviewContainer.setVisibility(isHomeSelected ? View.INVISIBLE : View.VISIBLE);
Tianguang Zhang893187f2021-05-07 22:14:04 +0200620
621 mFullScreenAnimation.setIsHomeSelected(isHomeSelected);
Tracy Zhou67fd7e32020-05-01 18:35:36 -0700622 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700623}