blob: 089c93b20e6dfdb8ba1693d461c936b48dd5a995 [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
Tracy Zhoua19cbdd2020-04-26 12:12:11 -070018import static android.view.View.MeasureSpec.EXACTLY;
19import static android.view.View.MeasureSpec.makeMeasureSpec;
20
21import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
22import static com.android.wallpaper.widget.BottomActionBar.BottomAction.EDIT;
23import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
24
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070025import android.animation.Animator;
26import android.animation.AnimatorListenerAdapter;
27import android.app.Activity;
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +080028import android.app.WallpaperColors;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070029import android.content.Context;
Santiago Etchebeheref172aef2020-07-04 18:48:35 -070030import android.content.res.Resources;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070031import android.graphics.Bitmap;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070032import android.graphics.Color;
33import android.graphics.Point;
34import android.graphics.PointF;
35import android.graphics.Rect;
36import android.os.Bundle;
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080037import android.util.Log;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070038import android.view.LayoutInflater;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -070039import android.view.Surface;
40import android.view.SurfaceControlViewHost;
41import android.view.SurfaceHolder;
42import android.view.SurfaceView;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070043import android.view.View;
44import android.view.ViewGroup;
Santiago Etchebehere8557c462020-12-02 18:41:40 -080045import android.view.ViewGroup.LayoutParams;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070046import android.widget.ImageView;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070047
Chuck Liaofc1b8c92020-05-21 20:54:15 +080048import androidx.annotation.NonNull;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070049import androidx.annotation.Nullable;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -070050import androidx.cardview.widget.CardView;
Tracy Zhou3eb234c2020-05-05 15:17:33 -070051import androidx.constraintlayout.widget.ConstraintLayout;
52import androidx.constraintlayout.widget.ConstraintSet;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070053import androidx.fragment.app.FragmentActivity;
54
55import com.android.wallpaper.R;
56import com.android.wallpaper.asset.Asset;
Chuck Liaodc424e82020-09-04 00:17:44 +080057import com.android.wallpaper.asset.CurrentWallpaperAssetVN;
“Chuckffd832c2020-03-22 02:15:58 +080058import com.android.wallpaper.model.WallpaperInfo;
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080059import com.android.wallpaper.module.BitmapCropper;
60import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070061import com.android.wallpaper.module.WallpaperPersister.Destination;
62import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
Tianguang Zhang893187f2021-05-07 22:14:04 +020063import com.android.wallpaper.util.FullScreenAnimation;
Tianguang Zhangd3e4f632021-04-14 00:30:15 +020064import com.android.wallpaper.util.ResourceUtils;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070065import com.android.wallpaper.util.ScreenSizeCalculator;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070066import com.android.wallpaper.util.SizeCalculator;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070067import com.android.wallpaper.util.WallpaperCropUtils;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -070068import com.android.wallpaper.widget.BottomActionBar;
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +080069import com.android.wallpaper.widget.BottomActionBar.AccessibilityCallback;
Chihhang Chuanga70d90e2021-04-09 15:42:27 +080070import com.android.wallpaper.widget.LockScreenPreviewer2;
Tracy Zhou35997422020-05-01 13:42:06 -070071import com.android.wallpaper.widget.WallpaperInfoView;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070072
73import com.bumptech.glide.Glide;
74import com.bumptech.glide.MemoryCategory;
75import com.davemorrissey.labs.subscaleview.ImageSource;
76import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
chihhangchuang45d94132020-07-08 16:18:37 +080077import com.google.android.material.tabs.TabLayout;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070078
chihhangchuang923e29b2020-07-02 19:09:36 +080079import java.util.Locale;
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080080import java.util.Optional;
81import java.util.concurrent.atomic.AtomicInteger;
chihhangchuang923e29b2020-07-02 19:09:36 +080082
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070083/**
Santiago Etchebehere4ecb6aa2019-11-07 11:48:58 -080084 * Fragment which displays the UI for previewing an individual static wallpaper and its attribution
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070085 * information.
86 */
Chihhang Chuangc9c108a2021-04-28 18:35:14 +080087public class ImagePreviewFragment extends PreviewFragment {
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070088
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +080089 private static final String TAG = "ImagePreviewFragment";
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070090 private static final float DEFAULT_WALLPAPER_MAX_ZOOM = 8f;
91
Tracy Zhou4421a3d2020-06-15 14:56:18 -070092 private final WallpaperSurfaceCallback mWallpaperSurfaceCallback =
93 new WallpaperSurfaceCallback();
Tracy Zhouc5a6bad2020-05-20 23:28:22 -070094
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070095 private SubsamplingScaleImageView mFullResImageView;
96 private Asset mWallpaperAsset;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070097 private Point mScreenSize;
98 private Point mRawWallpaperSize; // Native size of wallpaper image.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -070099 private ImageView mLowResImageView;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700100 private TouchForwardingLayout mTouchForwardingLayout;
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700101 private ConstraintLayout mContainer;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700102 private SurfaceView mWallpaperSurface;
Tracy Zhou35997422020-05-01 13:42:06 -0700103 private WallpaperInfoView mWallpaperInfoView;
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800104 private Optional<Integer> mLastSelectedTabPositionOptional = Optional.empty();
105 private AtomicInteger mImageScaleChangeCounter = new AtomicInteger(0);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700106
Santiago Etchebehere62287292021-04-30 15:53:22 -0700107 protected SurfaceView mWorkspaceSurface;
Santiago Etchebeheredbdfb662021-04-26 10:25:13 -0700108 protected WorkspaceSurfaceHolderCallback mWorkspaceSurfaceCallback;
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800109 protected ViewGroup mLockPreviewContainer;
110 protected LockScreenPreviewer2 mLockScreenPreviewer;
Chihhang Chuang57daaa92021-04-20 21:40:01 +0800111
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700112 @Override
113 public void onCreate(Bundle savedInstanceState) {
114 super.onCreate(savedInstanceState);
115 mWallpaperAsset = mWallpaper.getAsset(requireContext().getApplicationContext());
116 }
117
118 @Override
119 protected int getLayoutResId() {
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700120 return R.layout.fragment_image_preview_v2;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700121 }
122
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700123 @Override
Santiago Etchebeheredc55b192019-11-08 13:56:52 -0800124 protected int getLoadingIndicatorResId() {
125 return R.id.loading_indicator;
126 }
127
128 @Override
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700129 public View onCreateView(LayoutInflater inflater, ViewGroup container,
Michel Comin Escudeb3e1fef2019-11-22 11:41:53 -0800130 Bundle savedInstanceState) {
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700131 View view = super.onCreateView(inflater, container, savedInstanceState);
132
133 Activity activity = requireActivity();
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700134 mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize(
135 activity.getWindowManager().getDefaultDisplay());
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700136
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700137 // TODO: Consider moving some part of this to the base class when live preview is ready.
138 view.findViewById(R.id.low_res_image).setVisibility(View.GONE);
139 view.findViewById(R.id.full_res_image).setVisibility(View.GONE);
140 mLoadingProgressBar.hide();
141 mContainer = view.findViewById(R.id.container);
142 mTouchForwardingLayout = mContainer.findViewById(R.id.touch_forwarding_layout);
Chihhang Chuang60a5bc22021-05-14 23:00:01 +0800143 mTouchForwardingLayout.setForwardingEnabled(true);
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700144
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700145 // Set aspect ratio on the preview card dynamically.
146 ConstraintSet set = new ConstraintSet();
147 set.clone(mContainer);
chihhangchuang923e29b2020-07-02 19:09:36 +0800148 String ratio = String.format(Locale.US, "%d:%d", mScreenSize.x, mScreenSize.y);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700149 set.setDimensionRatio(mTouchForwardingLayout.getId(), ratio);
150 set.applyTo(mContainer);
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700151
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700152 mWorkspaceSurface = mContainer.findViewById(R.id.workspace_surface);
Santiago Etchebeheredbdfb662021-04-26 10:25:13 -0700153 mWorkspaceSurfaceCallback = createWorkspaceSurfaceCallback(mWorkspaceSurface);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700154 mWallpaperSurface = mContainer.findViewById(R.id.wallpaper_surface);
chihhangchuang287d4b72020-06-25 01:09:09 +0800155 mLockPreviewContainer = mContainer.findViewById(R.id.lock_screen_preview_container);
Chihhang Chuanga70d90e2021-04-09 15:42:27 +0800156 mLockScreenPreviewer = new LockScreenPreviewer2(getLifecycle(), getContext(),
chihhangchuang287d4b72020-06-25 01:09:09 +0800157 mLockPreviewContainer);
Chuck Liaofc1b8c92020-05-21 20:54:15 +0800158
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800159 setUpTabs(view.findViewById(R.id.pill_tabs));
Tracy Zhoue4a5f262020-05-26 17:40:19 -0700160
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700161 view.measure(makeMeasureSpec(mScreenSize.x, EXACTLY),
162 makeMeasureSpec(mScreenSize.y, EXACTLY));
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700163 ((CardView) mWorkspaceSurface.getParent())
164 .setRadius(SizeCalculator.getPreviewCornerRadius(
165 activity, mContainer.getMeasuredWidth()));
Chihhang Chuang57daaa92021-04-20 21:40:01 +0800166
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800167 renderImageWallpaper();
168 renderWorkspaceSurface();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700169
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700170 // Trim some memory from Glide to make room for the full-size image in this fragment.
171 Glide.get(activity).setMemoryCategory(MemoryCategory.LOW);
Santiago Etchebeheref4549ed2019-11-13 14:18:57 -0800172 setUpLoadingIndicator();
Tianguang Zhang893187f2021-05-07 22:14:04 +0200173
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700174 return view;
175 }
176
Santiago Etchebehere4ecb6aa2019-11-07 11:48:58 -0800177 @Override
Chuck Liaofc1b8c92020-05-21 20:54:15 +0800178 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
179 super.onViewCreated(view, savedInstanceState);
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800180 mWallpaperAsset.decodeRawDimensions(getActivity(), dimensions -> {
181 // Don't continue loading the wallpaper if the Fragment is detached.
182 if (getActivity() == null) {
183 return;
184 }
185
186 // Return early and show a dialog if dimensions are null (signaling a decoding error).
187 if (dimensions == null) {
188 showLoadWallpaperErrorDialog();
189 return;
190 }
191
192 // To avoid applying the wallpaper when it's not parsed. Now it's parsed, enable the
193 // bottom action bar to allow applying the wallpaper.
194 if (mBottomActionBar != null) {
195 mBottomActionBar.enableActions();
196 }
197
198 mRawWallpaperSize = dimensions;
199 initFullResView();
200 });
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800201 }
202
Chihhang Chuangc9c108a2021-04-28 18:35:14 +0800203 protected void onWallpaperColorsChanged(@Nullable WallpaperColors colors) {
Chihhang Chuang1e8bece2021-05-17 15:08:06 +0800204 // Make it enabled since the buttons are disabled while wallpaper is moving.
205 mBottomActionBar.enableActionButtonsWithBottomSheet(true);
206
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800207 mLockScreenPreviewer.setColor(colors);
Tianguang Zhang893187f2021-05-07 22:14:04 +0200208
209 mFullScreenAnimation.setFullScreenTextColor(
210 colors == null || (colors.getColorHints()
211 & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
212 ? FullScreenAnimation.FullScreenTextColor.LIGHT
213 : FullScreenAnimation.FullScreenTextColor.DARK);
Santiago Etchebehere4ecb6aa2019-11-07 11:48:58 -0800214 }
215
Santiago Etchebeheredc55b192019-11-08 13:56:52 -0800216 @Override
217 protected boolean isLoaded() {
218 return mFullResImageView != null && mFullResImageView.hasImage();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700219 }
220
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700221 @Override
222 public void onClickOk() {
223 FragmentActivity activity = getActivity();
224 if (activity != null) {
225 activity.finish();
226 }
227 }
228
229 @Override
230 public void onDestroy() {
231 super.onDestroy();
Santiago Etchebeheref4549ed2019-11-13 14:18:57 -0800232 if (mLoadingProgressBar != null) {
233 mLoadingProgressBar.hide();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700234 }
Chihhang Chuang57daaa92021-04-20 21:40:01 +0800235
236 if (mFullResImageView != null) {
237 mFullResImageView.recycle();
238 }
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700239
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700240 mWallpaperSurfaceCallback.cleanUp();
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700241 mWorkspaceSurfaceCallback.cleanUp();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700242 }
243
244 @Override
Tracy Zhoue4a5f262020-05-26 17:40:19 -0700245 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700246 super.onBottomActionBarReady(bottomActionBar);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700247 mWallpaperInfoView = (WallpaperInfoView)
248 LayoutInflater.from(getContext()).inflate(
249 R.layout.wallpaper_info_view, /* root= */null);
250 mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
251 mBottomActionBar.showActionsOnly(INFORMATION, EDIT, APPLY);
Tianguang Zhang893187f2021-05-07 22:14:04 +0200252
Chihhang Chuangca7c8252020-06-17 17:32:46 +0800253 mBottomActionBar.setActionClickListener(APPLY, this::onSetWallpaperClicked);
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +0800254
255 // Update target view's accessibility param since it will be blocked by the bottom sheet
256 // when expanded.
257 mBottomActionBar.setAccessibilityCallback(new AccessibilityCallback() {
258 @Override
259 public void onBottomSheetCollapsed() {
Wesley.CW Wangb8e51a22020-07-02 18:40:31 +0800260 mContainer.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +0800261 }
262
263 @Override
264 public void onBottomSheetExpanded() {
Wesley.CW Wangb8e51a22020-07-02 18:40:31 +0800265 mContainer.setImportantForAccessibility(
266 View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
Wesley.CW Wang61c28ec2020-06-19 18:58:50 +0800267 }
268 });
269
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700270 mBottomActionBar.show();
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800271 // Loads wallpaper info and populate into view.
272 setUpExploreIntentAndLabel(this::populateWallpaperInfo);
273 // To avoid applying the wallpaper when the wallpaper's not parsed.
chihhangchuang13cb1fe2020-06-30 16:46:59 +0800274 mBottomActionBar.disableActions();
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800275 // If the wallpaper is parsed, enable the bottom action bar.
276 if (mRawWallpaperSize != null) {
277 mBottomActionBar.enableActions();
278 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700279 }
280
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800281 protected void setUpTabs(TabLayout tabs) {
282 tabs.addTab(tabs.newTab().setText(getContext().getString(R.string.home_screen_message)));
283 tabs.addTab(tabs.newTab().setText(getContext().getString(R.string.lock_screen_message)));
284 tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
285 @Override
286 public void onTabSelected(TabLayout.Tab tab) {
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800287 mLastSelectedTabPositionOptional = Optional.of(tab.getPosition());
288 updateScreenPreview(/* isHomeSelected= */ tab.getPosition() == 0);
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800289 }
290
291 @Override
292 public void onTabUnselected(TabLayout.Tab tab) {}
293
294 @Override
295 public void onTabReselected(TabLayout.Tab tab) {}
296 });
297
298 // The TabLayout only contains below tabs
299 // 0. Home tab
300 // 1. Lock tab
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800301 int tabPosition = mLastSelectedTabPositionOptional.orElseGet(() -> mViewAsHome ? 0 : 1);
302 tabs.getTabAt(tabPosition).select();
303 updateScreenPreview(/* isHomeSelected= */ tabPosition == 0);
Chihhang Chuang58bd0ca2021-04-27 00:41:13 +0800304 }
305
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700306 /**
307 * Initializes MosaicView by initializing tiling, setting a fallback page bitmap, and
308 * initializing a zoom-scroll observer and click listener.
309 */
310 private void initFullResView() {
Chihhang Chuang9c1d4e42021-05-14 23:53:16 +0800311 if (mRawWallpaperSize == null || mFullResImageView == null) {
312 return;
313 }
314
Tracy Zhoudee07892020-06-16 20:14:30 -0700315 // Minimum scale will only be respected under this scale type.
316 mFullResImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM);
317 // When we set a minimum scale bigger than the scale with which the full image is shown,
318 // disallow user to pan outside the view we show the wallpaper in.
319 mFullResImageView.setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700320
321 // Set a solid black "page bitmap" so MosaicView draws a black background while waiting
322 // for the image to load or a transparent one if a thumbnail already loaded.
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700323 Bitmap backgroundBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
Tianguang Zhangd3e4f632021-04-14 00:30:15 +0200324 int preColor = ResourceUtils.getColorAttr(getActivity(), android.R.attr.colorSecondary);
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700325 int color = (mLowResImageView.getDrawable() == null) ? preColor : Color.TRANSPARENT;
326 backgroundBitmap.setPixel(0, 0, color);
327 mFullResImageView.setImage(ImageSource.bitmap(backgroundBitmap));
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700328
329 // Then set a fallback "page bitmap" to cover the whole MosaicView, which is an actual
330 // (lower res) version of the image to be displayed.
331 Point targetPageBitmapSize = new Point(mRawWallpaperSize);
332 mWallpaperAsset.decodeBitmap(targetPageBitmapSize.x, targetPageBitmapSize.y,
333 pageBitmap -> {
334 // Check that the activity is still around since the decoding task started.
335 if (getActivity() == null) {
336 return;
337 }
338
339 // Some of these may be null depending on if the Fragment is paused, stopped,
340 // or destroyed.
Santiago Etchebeheref4549ed2019-11-13 14:18:57 -0800341 if (mLoadingProgressBar != null) {
342 mLoadingProgressBar.hide();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700343 }
344 // The page bitmap may be null if there was a decoding error, so show an
345 // error dialog.
346 if (pageBitmap == null) {
347 showLoadWallpaperErrorDialog();
348 return;
349 }
350 if (mFullResImageView != null) {
351 // Set page bitmap.
352 mFullResImageView.setImage(ImageSource.bitmap(pageBitmap));
353
Chuck Liaodc424e82020-09-04 00:17:44 +0800354 setDefaultWallpaperZoomAndScroll(
355 mWallpaperAsset instanceof CurrentWallpaperAssetVN);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700356 crossFadeInMosaicView();
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800357 mFullResImageView.setOnStateChangedListener(
358 new SubsamplingScaleImageView.DefaultOnStateChangedListener() {
359 @Override
360 public void onCenterChanged(PointF newCenter, int origin) {
361 super.onCenterChanged(newCenter, origin);
Chihhang Chuang1e8bece2021-05-17 15:08:06 +0800362 // Disallow bottom sheet to popup when wallpaper is moving
363 // by user dragging.
364 mBottomActionBar.enableActionButtonsWithBottomSheet(false);
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800365 mImageScaleChangeCounter.incrementAndGet();
366 mFullResImageView.postDelayed(() -> {
367 if (mImageScaleChangeCounter.decrementAndGet() == 0) {
368 recalculateColors();
369 }
370 }, /* delayMillis= */ 100);
371 }
372 });
Chihhang Chuangc9c108a2021-04-28 18:35:14 +0800373 mFullResImageView.post(this::recalculateColors);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700374 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700375 getActivity().invalidateOptionsMenu();
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800376 });
Chihhang Chuang1e8bece2021-05-17 15:08:06 +0800377
378 mFullResImageView.setOnTouchListener((v, ev) -> {
379 // Consume the touch event for collapsing bottom sheet while it is expanded or
380 // dragging (not collapsed).
381 if (mBottomActionBar != null && !mBottomActionBar.isBottomSheetCollapsed()) {
382 mBottomActionBar.collapseBottomSheetIfExpanded();
383 return true;
384 }
385 return false;
386 });
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800387 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700388
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800389 private void recalculateColors() {
390 BitmapCropper bitmapCropper = InjectorProvider.getInjector().getBitmapCropper();
391 bitmapCropper.cropAndScaleBitmap(mWallpaperAsset, mFullResImageView.getScale(),
392 calculateCropRect(), /* adjustForRtl= */ false, new BitmapCropper.Callback() {
393 @Override
394 public void onBitmapCropped(Bitmap croppedBitmap) {
Chihhang Chuangc9c108a2021-04-28 18:35:14 +0800395 onWallpaperColorsChanged(WallpaperColors.fromBitmap(croppedBitmap));
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800396 }
397
398 @Override
399 public void onError(@Nullable Throwable e) {
400 Log.w(TAG, "Recalculate colors, crop and scale bitmap failed.", e);
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700401 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700402 });
403 }
404
Chihhang Chuang4f97a7d2021-04-27 18:27:54 +0800405 private void populateWallpaperInfo() {
406 if (mWallpaperInfoView != null && mWallpaper != null) {
407 mWallpaperInfoView.populateWallpaperInfo(
408 mWallpaper,
409 mActionLabel,
410 WallpaperInfoHelper.shouldShowExploreButton(
411 getContext(), mExploreIntent),
412 this::onExploreClicked);
413 }
414 }
415
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700416 /**
417 * Makes the MosaicView visible with an alpha fade-in animation while fading out the loading
418 * indicator.
419 */
420 private void crossFadeInMosaicView() {
421 long shortAnimationDuration = getResources().getInteger(
422 android.R.integer.config_shortAnimTime);
423
424 mFullResImageView.setAlpha(0f);
425 mFullResImageView.animate()
426 .alpha(1f)
427 .setDuration(shortAnimationDuration)
428 .setListener(new AnimatorListenerAdapter() {
429 @Override
430 public void onAnimationEnd(Animator animation) {
431 // Clear the thumbnail bitmap reference to save memory since it's no longer
432 // visible.
433 if (mLowResImageView != null) {
434 mLowResImageView.setImageBitmap(null);
435 }
436 }
437 });
438
Santiago Etchebeheref4549ed2019-11-13 14:18:57 -0800439 mLoadingProgressBar.animate()
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700440 .alpha(0f)
441 .setDuration(shortAnimationDuration)
442 .setListener(new AnimatorListenerAdapter() {
443 @Override
444 public void onAnimationEnd(Animator animation) {
Santiago Etchebeheref4549ed2019-11-13 14:18:57 -0800445 if (mLoadingProgressBar != null) {
446 mLoadingProgressBar.hide();
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700447 }
448 }
449 });
450 }
451
452 /**
Michel Comin Escudeb3e1fef2019-11-22 11:41:53 -0800453 * Sets the default wallpaper zoom and scroll position based on a "crop surface" (with extra
454 * width to account for parallax) superimposed on the screen. Shows as much of the wallpaper as
455 * possible on the crop surface and align screen to crop surface such that the default preview
456 * matches what would be seen by the user in the left-most home screen.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700457 *
458 * <p>This method is called once in the Fragment lifecycle after the wallpaper asset has loaded
459 * and rendered to the layout.
Chuck Liaodc424e82020-09-04 00:17:44 +0800460 *
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800461 * @param offsetToStart {@code true} if we want to offset the visible rectangle to the start
462 * side of the raw wallpaper; {@code false} otherwise.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700463 */
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800464 private void setDefaultWallpaperZoomAndScroll(boolean offsetToStart) {
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700465 // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface.
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800466 int cropWidth = mWallpaperSurface.getMeasuredWidth();
467 int cropHeight = mWallpaperSurface.getMeasuredHeight();
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700468 Point crop = new Point(cropWidth, cropHeight);
469 Rect visibleRawWallpaperRect =
470 WallpaperCropUtils.calculateVisibleRect(mRawWallpaperSize, crop);
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800471 if (offsetToStart) {
472 if (WallpaperCropUtils.isRtl(requireContext())) {
473 visibleRawWallpaperRect.offsetTo(mRawWallpaperSize.x
474 - visibleRawWallpaperRect.width(), visibleRawWallpaperRect.top);
475 } else {
476 visibleRawWallpaperRect.offsetTo(/* newLeft= */ 0, visibleRawWallpaperRect.top);
477 }
Chuck Liaodc424e82020-09-04 00:17:44 +0800478 }
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -0700479
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800480 final PointF centerPosition = new PointF(visibleRawWallpaperRect.centerX(),
481 visibleRawWallpaperRect.centerY());
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -0700482
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700483 Point visibleRawWallpaperSize = new Point(visibleRawWallpaperRect.width(),
484 visibleRawWallpaperRect.height());
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700485
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700486 final float defaultWallpaperZoom = WallpaperCropUtils.calculateMinZoom(
487 visibleRawWallpaperSize, crop);
488 final float minWallpaperZoom = defaultWallpaperZoom;
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700489
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700490
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700491 // Set min wallpaper zoom and max zoom on MosaicView widget.
492 mFullResImageView.setMaxScale(Math.max(DEFAULT_WALLPAPER_MAX_ZOOM, defaultWallpaperZoom));
493 mFullResImageView.setMinScale(minWallpaperZoom);
494
495 // Set center to composite positioning between scaled wallpaper and screen.
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700496 mFullResImageView.setScaleAndCenter(minWallpaperZoom, centerPosition);
497 }
498
499 private Rect calculateCropRect() {
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700500 float wallpaperZoom = mFullResImageView.getScale();
Chuck Liao5182a312020-03-04 20:04:18 +0800501 Context context = requireContext().getApplicationContext();
Tracy Zhou67fd7e32020-05-01 18:35:36 -0700502
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700503 Rect visibleFileRect = new Rect();
504 mFullResImageView.visibleFileRect(visibleFileRect);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700505
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800506 int cropWidth = mWallpaperSurface.getMeasuredWidth();
507 int cropHeight = mWallpaperSurface.getMeasuredHeight();
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700508 int maxCrop = Math.max(cropWidth, cropHeight);
509 int minCrop = Math.min(cropWidth, cropHeight);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700510 Point hostViewSize = new Point(cropWidth, cropHeight);
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700511
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700512 Resources res = context.getResources();
Santiago Etchebeherebcd6bb62020-07-09 12:09:21 -0700513 Point cropSurfaceSize = WallpaperCropUtils.calculateCropSurfaceSize(res, maxCrop, minCrop);
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700514
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700515 Rect cropRect = WallpaperCropUtils.calculateCropRect(context, hostViewSize,
Santiago Etchebeheref172aef2020-07-04 18:48:35 -0700516 cropSurfaceSize, mRawWallpaperSize, visibleFileRect, wallpaperZoom);
Tracy Zhouecfd6eb2020-05-27 23:56:35 -0700517 return cropRect;
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700518 }
519
520 @Override
521 protected void setCurrentWallpaper(@Destination int destination) {
522 mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset,
523 destination, mFullResImageView.getScale(), calculateCropRect(),
524 new SetWallpaperCallback() {
525 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800526 public void onSuccess(WallpaperInfo wallpaperInfo) {
Ching-Sung Lib5e9bd72020-06-15 14:10:55 +0800527 finishActivity(/* success= */ true);
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700528 }
529
530 @Override
531 public void onError(@Nullable Throwable throwable) {
532 showSetWallpaperErrorDialog(destination);
533 }
534 });
535 }
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700536
537 private void renderWorkspaceSurface() {
538 mWorkspaceSurface.setZOrderMediaOverlay(true);
539 mWorkspaceSurface.getHolder().addCallback(mWorkspaceSurfaceCallback);
540 }
541
542 private void renderImageWallpaper() {
543 mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback);
544 }
545
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700546 private class WallpaperSurfaceCallback implements SurfaceHolder.Callback {
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700547 private Surface mLastSurface;
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700548 private SurfaceControlViewHost mHost;
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700549
550 @Override
551 public void surfaceCreated(SurfaceHolder holder) {
552 if (mLastSurface != holder.getSurface()) {
553 mLastSurface = holder.getSurface();
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700554 if (mFullResImageView != null) {
555 mFullResImageView.recycle();
556 }
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800557 Context context = getContext();
558 View wallpaperPreviewContainer = LayoutInflater.from(context).inflate(
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700559 R.layout.fullscreen_wallpaper_preview, null);
560 mFullResImageView = wallpaperPreviewContainer.findViewById(R.id.full_res_image);
561 mLowResImageView = wallpaperPreviewContainer.findViewById(R.id.low_res_image);
Chihhang Chuang9c1d4e42021-05-14 23:53:16 +0800562 initFullResView();
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800563 // Scale the mWallpaperSurface based on system zoom's scale so that the wallpaper is
564 // rendered in a larger surface than what preview shows, simulating the behavior of
565 // the actual wallpaper surface.
566 float scale = WallpaperCropUtils.getSystemWallpaperMaximumScale(context);
567 int origWidth = mWallpaperSurface.getWidth();
568 int width = (int) (origWidth * scale);
569 int origHeight = mWallpaperSurface.getHeight();
570 int height = (int) (origHeight * scale);
571 int left = (origWidth - width) / 2;
572 int top = (origHeight - height) / 2;
573
574 if (WallpaperCropUtils.isRtl(context)) {
575 left *= -1;
576 }
577
578 LayoutParams params = mWallpaperSurface.getLayoutParams();
579 params.width = width;
580 params.height = height;
581 mWallpaperSurface.setX(left);
582 mWallpaperSurface.setY(top);
583 mWallpaperSurface.setLayoutParams(params);
584 mWallpaperSurface.requestLayout();
585
Chuck Liaoca1d1292020-06-19 01:29:53 +0800586 // Load a low-res placeholder image if there's a thumbnail available from the asset
587 // that can be shown to the user more quickly than the full-sized image.
588 if (mWallpaperAsset.hasLowResDataSource()) {
589 Activity activity = requireActivity();
590 mWallpaperAsset.loadLowResDrawable(activity, mLowResImageView, Color.BLACK,
591 new WallpaperPreviewBitmapTransformation(
592 activity.getApplicationContext(), isRtl()));
593 }
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700594 wallpaperPreviewContainer.measure(
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800595 makeMeasureSpec(width, EXACTLY),
596 makeMeasureSpec(height, EXACTLY));
597 wallpaperPreviewContainer.layout(0, 0, width, height);
Ching-Sung Lib9404622020-05-18 22:38:45 +0800598 mTouchForwardingLayout.setTargetView(mFullResImageView);
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700599
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700600 cleanUp();
Santiago Etchebehere8557c462020-12-02 18:41:40 -0800601 mHost = new SurfaceControlViewHost(context,
602 context.getDisplay(), mWallpaperSurface.getHostToken());
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700603 mHost.setView(wallpaperPreviewContainer, wallpaperPreviewContainer.getWidth(),
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700604 wallpaperPreviewContainer.getHeight());
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700605 mWallpaperSurface.setChildSurfacePackage(mHost.getSurfacePackage());
Tracy Zhoua19cbdd2020-04-26 12:12:11 -0700606 }
607 }
608
609 @Override
610 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }
611
612 @Override
613 public void surfaceDestroyed(SurfaceHolder holder) { }
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700614
615 public void cleanUp() {
616 if (mHost != null) {
617 mHost.release();
618 mHost = null;
619 }
620 }
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700621 }
622
Wesley.CW Wangf21486b2020-06-02 18:30:32 +0800623 private void updateScreenPreview(boolean isHomeSelected) {
chihhangchuang287d4b72020-06-25 01:09:09 +0800624 mWorkspaceSurface.setVisibility(isHomeSelected ? View.VISIBLE : View.INVISIBLE);
625 mLockPreviewContainer.setVisibility(isHomeSelected ? View.INVISIBLE : View.VISIBLE);
Tianguang Zhang893187f2021-05-07 22:14:04 +0200626
627 mFullScreenAnimation.setIsHomeSelected(isHomeSelected);
Tracy Zhou67fd7e32020-05-01 18:35:36 -0700628 }
Santiago Etchebehere6d3d1e62019-09-26 11:11:11 -0700629}