blob: 4cbde9de85c51f289a800b35dbda251f107702f0 [file] [log] [blame]
Jon Miranda16ea1b12017-12-12 14:52:48 -08001/*
2 * Copyright (C) 2017 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
Chuck Liaob3829fb2020-04-01 00:47:50 +080018import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED;
Wesley.CW Wang1eaa5b72020-07-02 17:49:58 +080019import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED;
Tracy Zhou942b7762020-04-01 00:35:16 -070020
Santiago Etchebehere635e96f2018-12-04 18:31:34 -080021import android.app.Activity;
Chuck Liaof646e052020-09-24 12:24:59 +080022import android.app.AlertDialog;
chihhangchuang32913262020-06-20 21:47:53 +080023import android.app.WallpaperColors;
Jon Miranda16ea1b12017-12-12 14:52:48 -080024import android.content.Intent;
25import android.content.pm.PackageManager;
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -070026import android.graphics.Rect;
Jon Miranda16ea1b12017-12-12 14:52:48 -080027import android.net.Uri;
Jon Miranda16ea1b12017-12-12 14:52:48 -080028import android.os.Bundle;
29import android.provider.Settings;
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -070030import android.service.wallpaper.WallpaperService;
Chuck Liaof6b4b192020-08-07 02:31:32 +080031import android.text.TextUtils;
Jon Miranda16ea1b12017-12-12 14:52:48 -080032import android.view.LayoutInflater;
Chuck Liaoa7215812021-04-13 22:33:43 +080033import android.view.MenuItem;
Tracy Zhou8b8b7032020-03-14 01:54:41 -070034import android.view.SurfaceView;
Jon Miranda16ea1b12017-12-12 14:52:48 -080035import android.view.View;
Jon Miranda16ea1b12017-12-12 14:52:48 -080036import android.view.ViewGroup;
37import android.widget.Button;
Jon Miranda16ea1b12017-12-12 14:52:48 -080038import android.widget.ImageView;
Jon Miranda16ea1b12017-12-12 14:52:48 -080039import android.widget.TextView;
40
Chuck Liaoe0d59452020-02-12 19:22:30 +080041import androidx.annotation.NonNull;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -080042import androidx.annotation.Nullable;
Chuck Liao4fb97852020-02-06 16:49:44 +080043import androidx.cardview.widget.CardView;
Chuck Liaoe0d59452020-02-12 19:22:30 +080044import androidx.viewpager.widget.PagerAdapter;
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -070045import androidx.viewpager.widget.ViewPager;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -080046
Jon Miranda16ea1b12017-12-12 14:52:48 -080047import com.android.wallpaper.R;
Jon Miranda16ea1b12017-12-12 14:52:48 -080048import com.android.wallpaper.model.Category;
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -070049import com.android.wallpaper.model.LiveWallpaperInfo;
Chuck Liaoa63f1bf2020-06-11 01:35:42 +080050import com.android.wallpaper.model.WallpaperCategory;
Jon Miranda16ea1b12017-12-12 14:52:48 -080051import com.android.wallpaper.model.WallpaperInfo;
52import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
53import com.android.wallpaper.module.CurrentWallpaperInfoFactory.WallpaperInfoCallback;
Jon Miranda16ea1b12017-12-12 14:52:48 -080054import com.android.wallpaper.module.InjectorProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -080055import com.android.wallpaper.module.UserEventLogger;
“Chuck7ef99722020-03-22 04:34:03 +080056import com.android.wallpaper.module.WallpaperPersister;
Jon Miranda16ea1b12017-12-12 14:52:48 -080057import com.android.wallpaper.module.WallpaperPreferences;
58import com.android.wallpaper.module.WallpaperPreferences.PresentationMode;
Chuck Liao1e0501f2020-02-17 18:20:54 +080059import com.android.wallpaper.picker.CategorySelectorFragment.CategorySelectorFragmentHost;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080060import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
61import com.android.wallpaper.picker.MyPhotosStarter.PermissionChangedListener;
“Chuck7ef99722020-03-22 04:34:03 +080062import com.android.wallpaper.picker.individual.IndividualPickerFragment;
Chuck Liaof40063f2020-03-03 18:35:24 +080063import com.android.wallpaper.picker.individual.IndividualPickerFragment.ThumbnailUpdater;
“Chuck7ef99722020-03-22 04:34:03 +080064import com.android.wallpaper.picker.individual.IndividualPickerFragment.WallpaperDestinationCallback;
Chuck Liaof6b4b192020-08-07 02:31:32 +080065import com.android.wallpaper.util.DeepLinkUtils;
Tianguang Zhangd3e4f632021-04-14 00:30:15 +020066import com.android.wallpaper.util.ResourceUtils;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070067import com.android.wallpaper.util.SizeCalculator;
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -070068import com.android.wallpaper.util.WallpaperConnection;
chihhangchuang32913262020-06-20 21:47:53 +080069import com.android.wallpaper.util.WallpaperConnection.WallpaperConnectionListener;
Santiago Etchebehered90db312020-08-06 18:20:52 -070070import com.android.wallpaper.util.WallpaperSurfaceCallback;
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +080071import com.android.wallpaper.widget.LockScreenPreviewer;
Chuck Liaoe0d59452020-02-12 19:22:30 +080072import com.android.wallpaper.widget.PreviewPager;
chihhangchuangeb719622020-05-20 18:58:19 +080073import com.android.wallpaper.widget.WallpaperColorsLoader;
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080074import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070075
Jon Miranda16ea1b12017-12-12 14:52:48 -080076import com.bumptech.glide.Glide;
77import com.bumptech.glide.MemoryCategory;
Chuck Liao87f45bc2020-02-14 19:46:36 +080078import com.google.android.material.bottomsheet.BottomSheetBehavior;
Jon Miranda16ea1b12017-12-12 14:52:48 -080079
80import java.util.ArrayList;
Chuck Liao2da47942020-05-22 00:31:22 +080081import java.util.Collections;
Jon Miranda16ea1b12017-12-12 14:52:48 -080082import java.util.Date;
83import java.util.List;
84
85/**
Santiago Etchebehere635e96f2018-12-04 18:31:34 -080086 * Displays the Main UI for picking a category of wallpapers to choose from.
Jon Miranda16ea1b12017-12-12 14:52:48 -080087 */
chihhangchuang3efb6832020-04-17 02:06:25 +080088public class CategoryFragment extends AppbarFragment
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080089 implements CategorySelectorFragmentHost, ThumbnailUpdater, WallpaperDestinationCallback,
Chuck Liaof6b4b192020-08-07 02:31:32 +080090 WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost,
91 IndividualPickerFragment.IndividualPickerFragmentHost {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -080092
93 /**
94 * Interface to be implemented by an Activity hosting a {@link CategoryFragment}
95 */
Santiago Etchebeherefab49612019-01-15 12:22:42 -080096 public interface CategoryFragmentHost extends MyPhotosStarterProvider {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -080097
98 void requestExternalStoragePermission(PermissionChangedListener listener);
99
100 boolean isReadExternalStoragePermissionGranted();
101
Ching-Sung Lief59efa2020-06-05 22:41:43 +0800102 void showViewOnlyPreview(WallpaperInfo wallpaperInfo, boolean isViewAsHome);
Chuck Liaoa63f1bf2020-06-11 01:35:42 +0800103
104 void show(String collectionId);
Chuck Liaoaee30432020-06-23 01:17:05 +0800105
Chuck Liaof6b4b192020-08-07 02:31:32 +0800106 void fetchCategories();
Chuck Liao3abf15b2020-12-17 22:33:02 +0800107
108 void cleanUp();
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800109 }
110
Santiago Etchebehere008368d2018-12-13 15:23:17 -0800111 public static CategoryFragment newInstance(CharSequence title) {
112 CategoryFragment fragment = new CategoryFragment();
chihhangchuang3efb6832020-04-17 02:06:25 +0800113 fragment.setArguments(AppbarFragment.createArguments(title));
Santiago Etchebehere008368d2018-12-13 15:23:17 -0800114 return fragment;
115 }
116
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800117 private static final String TAG = "CategoryFragment";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800118
Jon Miranda16ea1b12017-12-12 14:52:48 -0800119 private static final int SETTINGS_APP_INFO_REQUEST_CODE = 1;
120
121 private static final String PERMISSION_READ_WALLPAPER_INTERNAL =
122 "android.permission.READ_WALLPAPER_INTERNAL";
123
Tracy Zhou8b8b7032020-03-14 01:54:41 -0700124 private SurfaceView mWorkspaceSurface;
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700125 private WorkspaceSurfaceHolderCallback mWorkspaceSurfaceCallback;
Tracy Zhou942b7762020-04-01 00:35:16 -0700126 private SurfaceView mWallpaperSurface;
Santiago Etchebehered90db312020-08-06 18:20:52 -0700127 private WallpaperSurfaceCallback mWallpaperSurfaceCallback;
Chuck Liaoe0d59452020-02-12 19:22:30 +0800128 private PreviewPager mPreviewPager;
129 private List<View> mWallPaperPreviews;
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700130 private WallpaperConnection mWallpaperConnection;
Chuck Liao1e0501f2020-02-17 18:20:54 +0800131 private CategorySelectorFragment mCategorySelectorFragment;
“Chuck7ef99722020-03-22 04:34:03 +0800132 private IndividualPickerFragment mIndividualPickerFragment;
Chuck Liaof40063f2020-03-03 18:35:24 +0800133 private boolean mShowSelectedWallpaper;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800134 private BottomSheetBehavior<View> mBottomSheetBehavior;
chihhangchuangec6b90f2020-07-02 15:52:37 +0800135 // The index of Destination#DEST_HOME_SCREEN or Destination#DEST_LOCK_SCREEN
136 private int mWallpaperIndex;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800137
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800138 // The wallpaper information which is currently shown on the home preview.
139 private WallpaperInfo mHomePreviewWallpaperInfo;
140 // The wallpaper information which is currently shown on the lock preview.
141 private WallpaperInfo mLockPreviewWallpaperInfo;
142
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +0800143 private LockScreenPreviewer mLockScreenPreviewer;
Chuck Liaoaee30432020-06-23 01:17:05 +0800144 private View mRootContainer;
Tracy Zhou942b7762020-04-01 00:35:16 -0700145
Santiago Etchebehered90db312020-08-06 18:20:52 -0700146 private final Rect mPreviewLocalRect = new Rect();
147 private final Rect mPreviewGlobalRect = new Rect();
148 private final int[] mLivePreviewLocation = new int[2];
149
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800150 public CategoryFragment() {
Chuck Liao1e0501f2020-02-17 18:20:54 +0800151 mCategorySelectorFragment = new CategorySelectorFragment();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800152 }
153
154 @Override
155 public View onCreateView(LayoutInflater inflater, ViewGroup container,
156 Bundle savedInstanceState) {
157 View view = inflater.inflate(
chihhangchuang8fb80322020-04-29 13:40:16 +0800158 R.layout.fragment_category_picker, container, /* attachToRoot= */ false);
Chuck Liao989566d2020-01-10 20:57:50 +0800159
Chuck Liaoe0d59452020-02-12 19:22:30 +0800160 mWallPaperPreviews = new ArrayList<>();
161 CardView homePreviewCard = (CardView) inflater.inflate(
162 R.layout.wallpaper_preview_card, null);
Tracy Zhou8b8b7032020-03-14 01:54:41 -0700163 mWorkspaceSurface = homePreviewCard.findViewById(R.id.workspace_surface);
Tracy Zhou3eb234c2020-05-05 15:17:33 -0700164 mWorkspaceSurfaceCallback = new WorkspaceSurfaceHolderCallback(
165 mWorkspaceSurface, getContext());
Tracy Zhou942b7762020-04-01 00:35:16 -0700166 mWallpaperSurface = homePreviewCard.findViewById(R.id.wallpaper_surface);
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700167 mWallpaperSurfaceCallback = new WallpaperSurfaceCallback(getContext(), homePreviewCard,
Santiago Etchebehered90db312020-08-06 18:20:52 -0700168 mWallpaperSurface);
Chuck Liaoe0d59452020-02-12 19:22:30 +0800169 mWallPaperPreviews.add(homePreviewCard);
170
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800171 CardView lockscreenPreviewCard = (CardView) inflater.inflate(
172 R.layout.wallpaper_preview_card, null);
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800173 lockscreenPreviewCard.findViewById(R.id.workspace_surface).setVisibility(View.GONE);
174 lockscreenPreviewCard.findViewById(R.id.wallpaper_surface).setVisibility(View.GONE);
chihhangchuang287d4b72020-06-25 01:09:09 +0800175 ViewGroup lockPreviewContainer = lockscreenPreviewCard.findViewById(
176 R.id.lock_screen_preview_container);
177 lockPreviewContainer.setVisibility(View.VISIBLE);
Chihhang Chuang8a9cd7d2021-06-16 12:37:59 +0800178 mLockScreenPreviewer = new LockScreenPreviewer(getLifecycle(), getContext(),
chihhangchuang287d4b72020-06-25 01:09:09 +0800179 lockPreviewContainer);
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800180 mWallPaperPreviews.add(lockscreenPreviewCard);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800181
Chuck Liaoe0d59452020-02-12 19:22:30 +0800182 mPreviewPager = view.findViewById(R.id.wallpaper_preview_pager);
Chuck Liao2da47942020-05-22 00:31:22 +0800183 if (mPreviewPager.isRtl()) {
184 Collections.reverse(mWallPaperPreviews);
185 }
Chuck Liaoe0d59452020-02-12 19:22:30 +0800186 mPreviewPager.setAdapter(new PreviewPagerAdapter(mWallPaperPreviews));
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700187 mPreviewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700188 @Override
189 public void onPageScrolled(int position, float positionOffset,
190 int positionOffsetPixels) {
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800191 // For live wallpaper, show its thumbnail when scrolling.
Chuck Liaoa63b4272020-05-20 21:40:23 +0800192 if (mWallpaperConnection != null && mWallpaperConnection.isEngineReady()
193 && mHomePreviewWallpaperInfo instanceof LiveWallpaperInfo) {
Chuck Liao6a3bb9d2020-06-10 23:16:35 +0800194 if (positionOffset == 0.0f || positionOffset == 1.0f
195 || positionOffsetPixels == 0) {
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800196 // The page is not moved. Show live wallpaper.
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800197 mWallpaperSurface.setZOrderMediaOverlay(false);
198 } else {
199 // The page is moving. Show live wallpaper's thumbnail.
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800200 mWallpaperSurface.setZOrderMediaOverlay(true);
201 }
202 }
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700203 }
204
205 @Override
206 public void onPageSelected(int i) {
chihhangchuangec6b90f2020-07-02 15:52:37 +0800207 mWallpaperIndex = mPreviewPager.isRtl()
208 ? (mWallPaperPreviews.size() - 1) - i
209 : i;
“Chuck7ef99722020-03-22 04:34:03 +0800210 if (mIndividualPickerFragment != null && mIndividualPickerFragment.isVisible()) {
Chuck Liaod1a8a6c2020-07-03 00:03:07 +0800211 mIndividualPickerFragment.highlightAppliedWallpaper(mWallpaperIndex);
“Chuck7ef99722020-03-22 04:34:03 +0800212 }
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700213 }
214
215 @Override
216 public void onPageScrollStateChanged(int i) {
217 }
218 });
Chuck Liaoe0d59452020-02-12 19:22:30 +0800219 setupCurrentWallpaperPreview(view);
220
Chuck Liaoca97c572020-05-08 17:13:25 +0800221 ViewGroup fragmentContainer = view.findViewById(R.id.category_fragment_container);
Chuck Liaob7dc9052020-03-03 20:08:29 +0800222 mBottomSheetBehavior = BottomSheetBehavior.from(fragmentContainer);
Wesley.CW Wang1eaa5b72020-07-02 17:49:58 +0800223 mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
224 @Override
225 public void onStateChanged(@NonNull View bottomSheet, int newState) {
226 // Update preview pager's accessibility param since it will be blocked by the
227 // bottom sheet when expanded.
228 mPreviewPager.setImportantForAccessibility(newState == STATE_EXPANDED
229 ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
230 : View.IMPORTANT_FOR_ACCESSIBILITY_YES);
231 }
Chuck Liaob3829fb2020-04-01 00:47:50 +0800232
Wesley.CW Wang1eaa5b72020-07-02 17:49:58 +0800233 @Override
234 public void onSlide(@NonNull View bottomSheet, float slideOffset) {}
235 });
Chuck Liaoaee30432020-06-23 01:17:05 +0800236 mRootContainer = view.findViewById(R.id.root_container);
Santiago Etchebehere44815712020-03-23 17:37:26 -0700237 fragmentContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
238 @Override
239 public void onLayoutChange(View containerView, int left, int top, int right,
240 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
Chuck Liaoaee30432020-06-23 01:17:05 +0800241 int minimumHeight = mRootContainer.getHeight() - mPreviewPager.getMeasuredHeight();
Santiago Etchebehere44815712020-03-23 17:37:26 -0700242 mBottomSheetBehavior.setPeekHeight(minimumHeight);
243 containerView.setMinimumHeight(minimumHeight);
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700244 homePreviewCard.setRadius(SizeCalculator.getPreviewCornerRadius(
Santiago Etchebehere44815712020-03-23 17:37:26 -0700245 getActivity(), homePreviewCard.getMeasuredWidth()));
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700246 if (lockscreenPreviewCard != null) {
247 lockscreenPreviewCard
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700248 .setRadius(SizeCalculator.getPreviewCornerRadius(
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700249 getActivity(), lockPreviewContainer.getMeasuredWidth()));
Santiago Etchebehere44815712020-03-23 17:37:26 -0700250 }
Santiago Etchebehere44815712020-03-23 17:37:26 -0700251 }});
Chuck Liaofcd41062020-06-20 23:11:34 +0800252 fragmentContainer.setOnApplyWindowInsetsListener((v, windowInsets) -> {
253 v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(),
254 windowInsets.getSystemWindowInsetBottom());
255 return windowInsets;
Chuck Liaoca97c572020-05-08 17:13:25 +0800256 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800257
Kunhung Lid0174362021-04-05 15:31:41 +0800258 setUpToolbar(view);
Chuck Liao1e0501f2020-02-17 18:20:54 +0800259
260 getChildFragmentManager()
261 .beginTransaction()
262 .replace(R.id.category_fragment_container, mCategorySelectorFragment)
263 .commitNow();
Chuck Liaof6b4b192020-08-07 02:31:32 +0800264
265 // Deep link case
266 Intent intent = getActivity().getIntent();
267 String deepLinkCollectionId = DeepLinkUtils.getCollectionId(intent);
268 if (!TextUtils.isEmpty(deepLinkCollectionId)) {
269 mIndividualPickerFragment = InjectorProvider.getInjector()
270 .getIndividualPickerFragment(deepLinkCollectionId);
271 mIndividualPickerFragment.highlightAppliedWallpaper(mWallpaperIndex);
272 getChildFragmentManager()
273 .beginTransaction()
274 .replace(R.id.category_fragment_container, mIndividualPickerFragment)
275 .addToBackStack(null)
276 .commit();
277 getChildFragmentManager().executePendingTransactions();
278 intent.setData(null);
279 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800280 return view;
281 }
282
283 @Override
Tracy Zhou942b7762020-04-01 00:35:16 -0700284 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
Chuck Liaoaee30432020-06-23 01:17:05 +0800285 super.onViewCreated(view, savedInstanceState);
Tracy Zhou942b7762020-04-01 00:35:16 -0700286 updateWallpaperSurface();
287 updateWorkspaceSurface();
288 }
289
290 @Override
Santiago Etchebehere008368d2018-12-13 15:23:17 -0800291 public CharSequence getDefaultTitle() {
292 return getContext().getString(R.string.app_name);
293 }
294
295 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800296 public void onResume() {
297 super.onResume();
298
299 WallpaperPreferences preferences = InjectorProvider.getInjector().getPreferences(getActivity());
300 preferences.setLastAppActiveTimestamp(new Date().getTime());
301
302 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
303 // PreviewFragment.
304 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
305
Jon Miranda16ea1b12017-12-12 14:52:48 -0800306 // The wallpaper may have been set while this fragment was paused, so force refresh the current
307 // wallpapers and presentation mode.
Chuck Liaof40063f2020-03-03 18:35:24 +0800308 if (!mShowSelectedWallpaper) {
chihhangchuangcdabd8a2020-05-08 22:30:04 +0800309 refreshCurrentWallpapers(/* forceRefresh= */ true);
Chuck Liaof40063f2020-03-03 18:35:24 +0800310 }
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700311 if (mWallpaperConnection != null) {
312 mWallpaperConnection.setVisibility(true);
313 }
314 }
315
316 @Override
317 public void onPause() {
318 super.onPause();
319 if (mWallpaperConnection != null) {
320 mWallpaperConnection.setVisibility(false);
321 }
322 }
323
324 @Override
Kunhung Li8a1c6c32020-06-11 10:41:38 +0800325 public void onStop() {
326 super.onStop();
327 if (mWallpaperConnection != null) {
328 mWallpaperConnection.disconnect();
329 mWallpaperConnection = null;
330 }
331 }
332
333 @Override
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700334 public void onDestroyView() {
335 super.onDestroyView();
Tracy Zhou4421a3d2020-06-15 14:56:18 -0700336 mWallpaperSurfaceCallback.cleanUp();
Tracy Zhoua63c9862020-06-22 01:05:14 -0700337 mWorkspaceSurfaceCallback.cleanUp();
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700338 if (mWallpaperConnection != null) {
339 mWallpaperConnection.disconnect();
340 mWallpaperConnection = null;
341 }
Chuck Liaoe3f78452020-06-03 18:10:12 +0800342 mPreviewPager.setAdapter(null);
Chuck Liaoa46e6cc2020-06-06 00:36:08 +0800343 mWallPaperPreviews.forEach(view -> ((ViewGroup) view).removeAllViews());
344 mWallPaperPreviews.clear();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800345 }
346
347 @Override
348 public void onDestroy() {
349 super.onDestroy();
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700350 if (mWallpaperConnection != null) {
351 mWallpaperConnection.disconnect();
352 mWallpaperConnection = null;
353 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800354 }
355
356 @Override
357 public void onActivityResult(int requestCode, int resultCode, Intent data) {
358 if (requestCode == SETTINGS_APP_INFO_REQUEST_CODE) {
Chuck Liao1e0501f2020-02-17 18:20:54 +0800359 mCategorySelectorFragment.notifyDataSetChanged();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800360 }
361 }
362
Chuck Liao1e0501f2020-02-17 18:20:54 +0800363 @Override
364 public void requestCustomPhotoPicker(PermissionChangedListener listener) {
365 getFragmentHost().getMyPhotosStarter().requestCustomPhotoPicker(listener);
366 }
367
368 @Override
Chuck Liaoa63f1bf2020-06-11 01:35:42 +0800369 public void show(Category category) {
370 if (!(category instanceof WallpaperCategory)) {
371 getFragmentHost().show(category.getCollectionId());
372 return;
373 }
374 mIndividualPickerFragment = InjectorProvider.getInjector()
375 .getIndividualPickerFragment(category.getCollectionId());
chihhangchuangec6b90f2020-07-02 15:52:37 +0800376 mIndividualPickerFragment.highlightAppliedWallpaper(mWallpaperIndex);
Chuck Liaob3829fb2020-04-01 00:47:50 +0800377 mIndividualPickerFragment.setOnWallpaperSelectedListener(position -> {
378 // Scroll to the selected wallpaper and collapse the sheet if needed.
379 // Resize and scroll here because we want to let the RecyclerView's scrolling and
380 // BottomSheet's collapsing can be executed together instead of scrolling
381 // the RecyclerView after the BottomSheet is collapsed.
382 mIndividualPickerFragment.resizeLayout(mBottomSheetBehavior.getPeekHeight());
383 mIndividualPickerFragment.scrollToPosition(position);
384 if (mBottomSheetBehavior.getState() != STATE_COLLAPSED) {
Chuck Liaob3829fb2020-04-01 00:47:50 +0800385 mBottomSheetBehavior.setState(STATE_COLLAPSED);
386 }
387 });
Chuck Liaob1f1a3a2020-02-17 19:46:14 +0800388 getChildFragmentManager()
389 .beginTransaction()
“Chuck7ef99722020-03-22 04:34:03 +0800390 .replace(R.id.category_fragment_container, mIndividualPickerFragment)
Chuck Liaob1f1a3a2020-02-17 19:46:14 +0800391 .addToBackStack(null)
392 .commit();
393 getChildFragmentManager().executePendingTransactions();
394 }
395
Chuck Liaof40063f2020-03-03 18:35:24 +0800396 @Override
Chuck Liao58aca1c2021-03-17 01:20:55 +0800397 public boolean isHostToolbarShown() {
398 return true;
399 }
400
401 @Override
Chuck Liao0088bca2020-05-28 16:03:23 +0800402 public void setToolbarTitle(CharSequence title) {
403 setTitle(title);
404 }
405
406 @Override
Chuck Liaoa7215812021-04-13 22:33:43 +0800407 public void setToolbarMenu(int menuResId) {
408 setUpToolbarMenu(menuResId);
409 }
410
411 @Override
412 public void removeToolbarMenu() {
413 mToolbar.getMenu().clear();
414 }
415
416 @Override
Chuck Liaof6b4b192020-08-07 02:31:32 +0800417 public void moveToPreviousFragment() {
418 getChildFragmentManager().popBackStack();
419 }
420
421 @Override
422 public void fetchCategories() {
423 getFragmentHost().fetchCategories();
424 }
425
426 @Override
Chuck Liao3abf15b2020-12-17 22:33:02 +0800427 public void cleanUp() {
428 getFragmentHost().cleanUp();
429 }
430
431 @Override
Wesley.CW Wang27ff4262020-06-12 19:19:57 +0800432 public void expandBottomSheet() {
433 if (mBottomSheetBehavior.getState() != BottomSheetBehavior.STATE_EXPANDED) {
434 mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
435 }
436 }
437
438 @Override
439 public int getBottomSheetState() {
440 return mBottomSheetBehavior.getState();
441 }
442
443 @Override
Chuck Liaof40063f2020-03-03 18:35:24 +0800444 public void updateThumbnail(WallpaperInfo wallpaperInfo) {
445 new android.os.Handler().post(() -> {
446 // A config change may have destroyed the activity since the refresh started, so check
447 // for that.
448 if (getActivity() == null) {
449 return;
450 }
451
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800452 mHomePreviewWallpaperInfo = wallpaperInfo;
453 mLockPreviewWallpaperInfo = wallpaperInfo;
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700454 updateThumbnail(mHomePreviewWallpaperInfo,
455 mWallpaperSurfaceCallback.getHomeImageWallpaper(), true);
Chuck Liaof40063f2020-03-03 18:35:24 +0800456 mShowSelectedWallpaper = true;
457 });
458 }
459
460 @Override
461 public void restoreThumbnails() {
chihhangchuangcdabd8a2020-05-08 22:30:04 +0800462 refreshCurrentWallpapers(/* forceRefresh= */ true);
Chuck Liaof40063f2020-03-03 18:35:24 +0800463 mShowSelectedWallpaper = false;
464 }
465
“Chuck7ef99722020-03-22 04:34:03 +0800466 @Override
467 public void onDestinationSet(@WallpaperPersister.Destination int destination) {
468 if (destination == WallpaperPersister.DEST_BOTH) {
469 return;
470 }
471 mPreviewPager.switchPreviewPage(destination);
472 }
473
chihhangchuang7feb3752020-04-24 02:48:56 +0800474 @Override
Chuck Liaoa7215812021-04-13 22:33:43 +0800475 public boolean onMenuItemClick(MenuItem item) {
476 if (item.getItemId() == R.id.daily_rotation) {
477 if (mIndividualPickerFragment != null && mIndividualPickerFragment.isVisible()) {
478 mIndividualPickerFragment.showRotationDialog();
479 }
480 return true;
481 }
482 return super.onMenuItemClick(item);
483 }
484
Chuck Liaob1f1a3a2020-02-17 19:46:14 +0800485 /**
Chuck Liao8cbe49f2021-03-15 20:28:04 +0800486 * Gets the {@link CategorySelectorFragment} which is attached to {@link CategoryFragment}.
487 */
488 public CategorySelectorFragment getCategorySelectorFragment() {
489 return mCategorySelectorFragment;
490 }
491
492 /**
Chuck Liaob1f1a3a2020-02-17 19:46:14 +0800493 * Pops the child fragment from the stack if {@link CategoryFragment} is visible to the users.
494 *
495 * @return {@code true} if the child fragment is popped, {@code false} otherwise.
496 */
497 public boolean popChildFragment() {
498 return isVisible() && getChildFragmentManager().popBackStackImmediate();
Chuck Liao1e0501f2020-02-17 18:20:54 +0800499 }
500
Jon Miranda16ea1b12017-12-12 14:52:48 -0800501 private boolean canShowCurrentWallpaper() {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800502 Activity activity = getActivity();
503 CategoryFragmentHost host = getFragmentHost();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800504 PackageManager packageManager = activity.getPackageManager();
505 String packageName = activity.getPackageName();
506
507 boolean hasReadWallpaperInternal = packageManager.checkPermission(
508 PERMISSION_READ_WALLPAPER_INTERNAL, packageName) == PackageManager.PERMISSION_GRANTED;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800509 return hasReadWallpaperInternal || host.isReadExternalStoragePermissionGranted();
510 }
511
Chuck Liaoe0d59452020-02-12 19:22:30 +0800512 private void showCurrentWallpaper(View rootView, boolean show) {
Chuck Liaod975b0f2020-06-23 16:01:04 +0800513 // The category/wallpaper tiles page depends on the height of the preview pager.
514 // So if we want to hide the preview pager, we should use INVISIBLE instead of GONE.
Chuck Liaoe0d59452020-02-12 19:22:30 +0800515 rootView.findViewById(R.id.wallpaper_preview_pager)
Chuck Liaod975b0f2020-06-23 16:01:04 +0800516 .setVisibility(show ? View.VISIBLE : View.INVISIBLE);
Chuck Liaoe0d59452020-02-12 19:22:30 +0800517 rootView.findViewById(R.id.permission_needed)
518 .setVisibility(show ? View.GONE : View.VISIBLE);
519 }
520
521 private void setupCurrentWallpaperPreview(View rootView) {
522 if (canShowCurrentWallpaper()) {
523 showCurrentWallpaper(rootView, true);
524 } else {
525 showCurrentWallpaper(rootView, false);
526
527 Button mAllowAccessButton = rootView
528 .findViewById(R.id.permission_needed_allow_access_button);
529 mAllowAccessButton.setOnClickListener(view ->
530 getFragmentHost().requestExternalStoragePermission(
531 new PermissionChangedListener() {
532
533 @Override
534 public void onPermissionsGranted() {
535 showCurrentWallpaper(rootView, true);
Chuck Liao1e0501f2020-02-17 18:20:54 +0800536 mCategorySelectorFragment.notifyDataSetChanged();
Chuck Liaoe0d59452020-02-12 19:22:30 +0800537 }
538
539 @Override
540 public void onPermissionsDenied(boolean dontAskAgain) {
541 if (!dontAskAgain) {
542 return;
543 }
544 showPermissionNeededDialog();
545 }
546 })
547 );
548
549 // Replace explanation text with text containing the Wallpapers app name which replaces
550 // the placeholder.
551 String appName = getString(R.string.app_name);
552 String explanation = getString(R.string.permission_needed_explanation, appName);
553 TextView explanationView = rootView.findViewById(R.id.permission_needed_explanation);
554 explanationView.setText(explanation);
555 }
556 }
557
558 private void showPermissionNeededDialog() {
559 String permissionNeededMessage = getString(
560 R.string.permission_needed_explanation_go_to_settings);
561 AlertDialog dialog = new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme)
562 .setMessage(permissionNeededMessage)
563 .setPositiveButton(android.R.string.ok, /* onClickListener= */ null)
564 .setNegativeButton(
565 R.string.settings_button_label,
566 (dialogInterface, i) -> {
567 Intent appInfoIntent = new Intent();
568 appInfoIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
569 Uri uri = Uri.fromParts("package",
570 getActivity().getPackageName(), /* fragment= */ null);
571 appInfoIntent.setData(uri);
572 startActivityForResult(appInfoIntent, SETTINGS_APP_INFO_REQUEST_CODE);
573 })
574 .create();
575 dialog.show();
576 }
577
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800578 private CategoryFragmentHost getFragmentHost() {
579 return (CategoryFragmentHost) getActivity();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800580 }
581
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700582 private Intent getWallpaperIntent(android.app.WallpaperInfo info) {
583 return new Intent(WallpaperService.SERVICE_INTERFACE)
584 .setClassName(info.getPackageName(), info.getServiceName());
585 }
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800586
Jon Miranda16ea1b12017-12-12 14:52:48 -0800587 /**
chihhangchuangcdabd8a2020-05-08 22:30:04 +0800588 * Obtains the {@link WallpaperInfo} object(s) representing the wallpaper(s) currently set to
589 * the device from the {@link CurrentWallpaperInfoFactory}.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800590 */
chihhangchuangcdabd8a2020-05-08 22:30:04 +0800591 private void refreshCurrentWallpapers(boolean forceRefresh) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800592 CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector()
593 .getCurrentWallpaperFactory(getActivity().getApplicationContext());
594
595 factory.createCurrentWallpaperInfos(new WallpaperInfoCallback() {
596 @Override
597 public void onWallpaperInfoCreated(
598 final WallpaperInfo homeWallpaper,
599 @Nullable final WallpaperInfo lockWallpaper,
600 @PresentationMode final int presentationMode) {
601
602 // Update the metadata displayed on screen. Do this in a Handler so it is scheduled at the
603 // end of the message queue. This is necessary to ensure we do not remove or add data from
604 // the adapter while the layout is being computed. RecyclerView documentation therefore
605 // recommends performing such changes in a Handler.
606 new android.os.Handler().post(new Runnable() {
607 @Override
608 public void run() {
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700609 final Activity activity = getActivity();
610 // A config change may have destroyed the activity since the refresh
611 // started, so check for that.
612 if (activity == null) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800613 return;
614 }
615
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800616 mHomePreviewWallpaperInfo = homeWallpaper;
617 mLockPreviewWallpaperInfo =
618 lockWallpaper == null ? homeWallpaper : lockWallpaper;
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700619 updateThumbnail(mHomePreviewWallpaperInfo,
620 mWallpaperSurfaceCallback.getHomeImageWallpaper(), true);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800621 }
622 });
623 }
624 }, forceRefresh);
625 }
626
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800627 private void setUpLiveWallpaperPreview(WallpaperInfo homeWallpaper) {
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700628 Activity activity = getActivity();
629 if (activity == null) {
630 return;
631 }
632 if (mWallpaperConnection != null) {
633 mWallpaperConnection.disconnect();
634 }
Santiago Etchebehere44815712020-03-23 17:37:26 -0700635
Santiago Etchebehered90db312020-08-06 18:20:52 -0700636 if (WallpaperConnection.isPreviewAvailable()) {
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -0700637 ImageView previewView = mWallpaperSurfaceCallback.getHomeImageWallpaper();
Santiago Etchebehered90db312020-08-06 18:20:52 -0700638 mWallpaperConnection = new WallpaperConnection(
639 getWallpaperIntent(homeWallpaper.getWallpaperComponent()), activity,
640 new WallpaperConnectionListener() {
641 @Override
642 public void onWallpaperColorsChanged(WallpaperColors colors,
643 int displayId) {
644 if (mLockPreviewWallpaperInfo instanceof LiveWallpaperInfo) {
645 mLockScreenPreviewer.setColor(colors);
646 }
chihhangchuang32913262020-06-20 21:47:53 +0800647 }
Santiago Etchebehere7d8d3b62021-03-29 10:13:48 -0700648 }, mWallpaperSurface);
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700649
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700650
Santiago Etchebehered90db312020-08-06 18:20:52 -0700651 mWallpaperConnection.setVisibility(true);
652 previewView.post(() -> {
653 if (mWallpaperConnection != null && !mWallpaperConnection.connect()) {
654 mWallpaperConnection = null;
Santiago Etchebehered90db312020-08-06 18:20:52 -0700655 }
656 });
657 }
Santiago Etchebeheree945bbe2020-03-11 17:32:43 -0700658 }
659
Chuck Liaof40063f2020-03-03 18:35:24 +0800660 private void updateThumbnail(WallpaperInfo wallpaperInfo, ImageView thumbnailView,
661 boolean isHomeWallpaper) {
662 if (wallpaperInfo == null) {
663 return;
664 }
665
666 if (thumbnailView == null) {
667 return;
668 }
669
670 Activity activity = getActivity();
671 if (activity == null) {
672 return;
673 }
674
675 UserEventLogger eventLogger = InjectorProvider.getInjector().getUserEventLogger(activity);
Tracy Zhou942b7762020-04-01 00:35:16 -0700676
Ching-Sung Lie23979a2020-04-23 19:04:48 +0800677 boolean renderInImageWallpaperSurface =
678 !(wallpaperInfo instanceof LiveWallpaperInfo) && isHomeWallpaper;
Santiago Etchebehered90db312020-08-06 18:20:52 -0700679 ImageView imageView = renderInImageWallpaperSurface
680 ? mWallpaperSurfaceCallback.getHomeImageWallpaper() : thumbnailView;
Kunhung Lifc4f7632020-06-15 09:50:49 +0800681 if (imageView != null) {
682 wallpaperInfo.getThumbAsset(activity.getApplicationContext())
683 .loadPreviewImage(activity, imageView,
Tianguang Zhangd3e4f632021-04-14 00:30:15 +0200684 ResourceUtils.getColorAttr(
685 getActivity(), android.R.attr.colorSecondary));
Kunhung Lifc4f7632020-06-15 09:50:49 +0800686 }
687
Chuck Liaof40063f2020-03-03 18:35:24 +0800688 if (isHomeWallpaper) {
689 if (wallpaperInfo instanceof LiveWallpaperInfo) {
Santiago Etchebehered90db312020-08-06 18:20:52 -0700690 if (mWallpaperSurfaceCallback.getHomeImageWallpaper() != null) {
Kunhung Lifc4f7632020-06-15 09:50:49 +0800691 wallpaperInfo.getThumbAsset(activity.getApplicationContext()).loadPreviewImage(
Santiago Etchebehered90db312020-08-06 18:20:52 -0700692 activity, mWallpaperSurfaceCallback.getHomeImageWallpaper(),
Tianguang Zhangd3e4f632021-04-14 00:30:15 +0200693 ResourceUtils.getColorAttr(
694 getActivity(), android.R.attr.colorSecondary));
Kunhung Lifc4f7632020-06-15 09:50:49 +0800695 }
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800696 setUpLiveWallpaperPreview(wallpaperInfo);
Chuck Liaof40063f2020-03-03 18:35:24 +0800697 } else {
698 if (mWallpaperConnection != null) {
699 mWallpaperConnection.disconnect();
700 mWallpaperConnection = null;
701 }
702 }
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800703 } else {
704 // lock screen wallpaper
Santiago Etchebehere7d8d3b62021-03-29 10:13:48 -0700705 if (!(wallpaperInfo instanceof LiveWallpaperInfo)
706 || !WallpaperConnection.isPreviewAvailable()) {
chihhangchuang32913262020-06-20 21:47:53 +0800707 // Load wallpaper color from thumbnail for static wallpaper.
708 WallpaperColorsLoader.getWallpaperColors(
chihhangchuang90f902c2020-06-03 16:14:40 +0800709 activity,
710 wallpaperInfo.getThumbAsset(activity),
chihhangchuang287d4b72020-06-25 01:09:09 +0800711 mLockScreenPreviewer::setColor);
chihhangchuang32913262020-06-20 21:47:53 +0800712 }
Chuck Liaof40063f2020-03-03 18:35:24 +0800713 }
Chuck Liao2a06c0b2020-04-30 15:07:09 +0800714
chihhangchuangb4ec0222020-06-08 16:29:21 +0800715 ((View) thumbnailView.getParent()).setOnClickListener(view -> {
Ching-Sung Lief59efa2020-06-05 22:41:43 +0800716 getFragmentHost().showViewOnlyPreview(wallpaperInfo, isHomeWallpaper);
Chuck Liaof40063f2020-03-03 18:35:24 +0800717 eventLogger.logCurrentWallpaperPreviewed();
718 });
719 }
720
Tracy Zhou942b7762020-04-01 00:35:16 -0700721 private void updateWallpaperSurface() {
722 mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback);
Santiago Etchebehere7d8d3b62021-03-29 10:13:48 -0700723 mWallpaperSurface.setZOrderMediaOverlay(true);
Tracy Zhou8b8b7032020-03-14 01:54:41 -0700724 }
725
Tracy Zhou942b7762020-04-01 00:35:16 -0700726 private void updateWorkspaceSurface() {
727 mWorkspaceSurface.setZOrderMediaOverlay(true);
728 mWorkspaceSurface.getHolder().addCallback(mWorkspaceSurfaceCallback);
729 }
730
Chuck Liaoa46e6cc2020-06-06 00:36:08 +0800731 private static class PreviewPagerAdapter extends PagerAdapter {
Chuck Liaoe0d59452020-02-12 19:22:30 +0800732
733 private List<View> mPages;
734
735 PreviewPagerAdapter(List<View> pages) {
736 mPages = pages;
737 }
738
739 @Override
740 public void destroyItem(@NonNull ViewGroup container, int position,
741 @NonNull Object object) {
742 container.removeView((View) object);
743 }
744
745 @NonNull
746 @Override
747 public Object instantiateItem(@NonNull ViewGroup container, int position) {
748 View view = mPages.get(position);
749 container.addView(view);
750 return view;
751 }
752
753 @Override
754 public int getCount() {
755 return mPages.size();
756 }
757
758 @Override
759 public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
760 return view == o;
761 }
762 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800763}