blob: 191a39f7676fc5e3bc359306fd8d2c3c86eee0a6 [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
Jon Miranda16ea1b12017-12-12 14:52:48 -080018import android.app.Activity;
Chuck Liaof646e052020-09-24 12:24:59 +080019import android.app.AlertDialog;
Jon Miranda16ea1b12017-12-12 14:52:48 -080020import android.app.ProgressDialog;
Jon Miranda16ea1b12017-12-12 14:52:48 -080021import android.content.Context;
22import android.content.Intent;
Jon Miranda16ea1b12017-12-12 14:52:48 -080023import android.graphics.Color;
Santiago Etchebehere35891ea2019-07-26 15:21:42 -070024import android.graphics.Insets;
Jon Miranda16ea1b12017-12-12 14:52:48 -080025import android.graphics.PorterDuff.Mode;
26import android.graphics.drawable.Drawable;
27import android.net.Uri;
28import android.os.AsyncTask;
29import android.os.Build.VERSION;
30import android.os.Build.VERSION_CODES;
31import android.os.Bundle;
Hyunyoung Song8077c862019-06-18 01:14:24 -040032import android.provider.Settings;
Jon Miranda16ea1b12017-12-12 14:52:48 -080033import android.util.Log;
34import android.view.View;
35import android.view.View.OnClickListener;
Santiago Etchebehere35891ea2019-07-26 15:21:42 -070036import android.view.WindowInsets;
Jon Miranda16ea1b12017-12-12 14:52:48 -080037import android.widget.Button;
38import android.widget.FrameLayout;
39import android.widget.ImageView;
40import android.widget.LinearLayout;
41import android.widget.TextView;
42
Santiago Etchebehere635e96f2018-12-04 18:31:34 -080043import androidx.annotation.NonNull;
44import androidx.annotation.Nullable;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -080045import androidx.appcompat.widget.Toolbar;
46import androidx.fragment.app.Fragment;
47import androidx.fragment.app.FragmentManager;
48
Jon Miranda16ea1b12017-12-12 14:52:48 -080049import com.android.wallpaper.R;
50import com.android.wallpaper.asset.Asset;
51import com.android.wallpaper.compat.ButtonDrawableSetterCompat;
Jon Miranda16ea1b12017-12-12 14:52:48 -080052import com.android.wallpaper.config.Flags;
53import com.android.wallpaper.model.Category;
Jon Miranda16ea1b12017-12-12 14:52:48 -080054import com.android.wallpaper.model.ImageWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080055import com.android.wallpaper.model.WallpaperInfo;
56import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
57import com.android.wallpaper.module.CurrentWallpaperInfoFactory.WallpaperInfoCallback;
58import com.android.wallpaper.module.DailyLoggingAlarmScheduler;
59import com.android.wallpaper.module.ExploreIntentChecker;
60import com.android.wallpaper.module.FormFactorChecker;
Jon Miranda16ea1b12017-12-12 14:52:48 -080061import com.android.wallpaper.module.Injector;
62import com.android.wallpaper.module.InjectorProvider;
63import com.android.wallpaper.module.NetworkStatusNotifier;
64import com.android.wallpaper.module.NetworkStatusNotifier.NetworkStatus;
65import com.android.wallpaper.module.UserEventLogger;
66import com.android.wallpaper.module.UserEventLogger.WallpaperSetFailureReason;
67import com.android.wallpaper.module.WallpaperPersister;
68import com.android.wallpaper.module.WallpaperPersister.Destination;
69import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
70import com.android.wallpaper.module.WallpaperPersister.WallpaperPosition;
71import com.android.wallpaper.module.WallpaperPreferences;
72import com.android.wallpaper.module.WallpaperPreferences.PresentationMode;
73import com.android.wallpaper.module.WallpaperRotationRefresher;
74import com.android.wallpaper.module.WallpaperRotationRefresher.Listener;
Kunhung Lic6701492021-01-28 17:35:56 +080075import com.android.wallpaper.picker.AppbarFragment.AppbarFragmentHost;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -080076import com.android.wallpaper.picker.CategoryFragment.CategoryFragmentHost;
Jon Miranda16ea1b12017-12-12 14:52:48 -080077import com.android.wallpaper.picker.WallpaperDisabledFragment.WallpaperSupportLevel;
Jon Miranda16ea1b12017-12-12 14:52:48 -080078import com.android.wallpaper.picker.individual.IndividualPickerFragment;
Kunhung Li14648702021-04-15 13:45:10 +080079import com.android.wallpaper.util.ActivityUtils;
Tianguang Zhangd3e4f632021-04-14 00:30:15 +020080import com.android.wallpaper.util.ResourceUtils;
Jon Miranda16ea1b12017-12-12 14:52:48 -080081import com.android.wallpaper.util.ScreenSizeCalculator;
82import com.android.wallpaper.util.ThrowableAnalyzer;
83
Sunny Goyal8600a3f2018-08-15 12:48:01 -070084import com.google.android.material.bottomsheet.BottomSheetBehavior;
85import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback;
86import com.google.android.material.tabs.TabLayout;
87import com.google.android.material.tabs.TabLayout.OnTabSelectedListener;
88import com.google.android.material.tabs.TabLayout.Tab;
89
Jon Miranda16ea1b12017-12-12 14:52:48 -080090import java.util.List;
91
92/**
93 * Activity allowing users to select a category of wallpapers to choose from.
94 */
95public class TopLevelPickerActivity extends BaseActivity implements WallpapersUiContainer,
96 CurrentWallpaperBottomSheetPresenter, SetWallpaperErrorDialogFragment.Listener,
Chuck Liao5572b982021-04-13 23:48:14 +080097 MyPhotosStarter, AppbarFragmentHost, CategoryFragmentHost {
Jon Miranda16ea1b12017-12-12 14:52:48 -080098
99 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
100 "toplevel_set_wallpaper_error_dialog";
101
102 private static final String TAG = "TopLevelPicker";
103 private static final String KEY_SELECTED_CATEGORY_TAB = "selected_category_tab";
104
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800105 private WallpaperPickerDelegate mDelegate;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800106 private int mLastSelectedCategoryTabIndex;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800107 private UserEventLogger mUserEventLogger;
108 private NetworkStatusNotifier mNetworkStatusNotifier;
109 private NetworkStatusNotifier.Listener mNetworkStatusListener;
110 private WallpaperPersister mWallpaperPersister;
Kunhung Li166842e2020-05-13 19:19:06 +0800111 private WallpaperPreferences mWallpaperPreferences;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800112 private boolean mWasCustomPhotoWallpaperSet;
113 @WallpaperPosition
114 private int mCustomPhotoWallpaperPosition;
115
116 /**
117 * Progress dialogs for "refresh daily wallpaper" and "set wallpaper" operations.
118 */
119 private ProgressDialog mRefreshWallpaperProgressDialog;
120 private ProgressDialog mSetWallpaperProgressDialog;
121
122 /**
123 * Designates a test mode of operation -- in which certain UI features are disabled to allow for
124 * UI tests to run correctly.
125 */
126 private boolean mTestingMode;
127
128 /**
129 * UI for the "currently set wallpaper" BottomSheet.
130 */
131 private LinearLayout mBottomSheet;
132 private ImageView mCurrentWallpaperImage;
133 private TextView mCurrentWallpaperPresentationMode;
134 private TextView mCurrentWallpaperTitle;
135 private TextView mCurrentWallpaperSubtitle;
136 private Button mCurrentWallpaperExploreButton;
137 private Button mCurrentWallpaperSkipWallpaperButton;
138 private FrameLayout mFragmentContainer;
139 private FrameLayout mLoadingIndicatorContainer;
140 private LinearLayout mWallpaperPositionOptions;
141
Jon Miranda16ea1b12017-12-12 14:52:48 -0800142 /**
143 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
144 * committing fragment transactions.
145 */
146 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
147
148 /**
149 * A wallpaper pending set to the device--we retain a reference to this in order to facilitate
150 * retry or re-crop operations.
151 */
152 private WallpaperInfo mPendingSetWallpaperInfo;
153
Tianguang Zhangd3e4f632021-04-14 00:30:15 +0200154 private int getTextColorForWallpaperPositionButton(boolean isSelected) {
155 int textColorId = isSelected
156 ? android.R.attr.colorAccent
157 : android.R.attr.textColorTertiary;
158 return ResourceUtils.getColorAttr(this, textColorId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800159 }
160
161 @Override
162 protected void onCreate(Bundle savedInstanceState) {
163 super.onCreate(savedInstanceState);
164
Jon Miranda16ea1b12017-12-12 14:52:48 -0800165 mLastSelectedCategoryTabIndex = -1;
166
167 Injector injector = InjectorProvider.getInjector();
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800168 mDelegate = new WallpaperPickerDelegate(this, this, injector);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800169 mUserEventLogger = injector.getUserEventLogger(this);
170 mNetworkStatusNotifier = injector.getNetworkStatusNotifier(this);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800171 mWallpaperPersister = injector.getWallpaperPersister(this);
Kunhung Li83f72a82020-06-02 11:05:10 +0800172 mWallpaperPreferences = injector.getPreferences(this);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800173 mWasCustomPhotoWallpaperSet = false;
174
Chuck Liaoe2d35f52020-09-22 21:20:47 +0800175 mDelegate.getCategoryProvider().resetIfNeeded();
176
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800177 @WallpaperSupportLevel int wallpaperSupportLevel = mDelegate.getWallpaperSupportLevel();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800178 if (wallpaperSupportLevel != WallpaperDisabledFragment.SUPPORTED_CAN_SET) {
Chuck Liao8ec38e02020-02-26 20:59:32 +0800179 setContentView(R.layout.activity_top_level_picker);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800180
181 FragmentManager fm = getSupportFragmentManager();
182 WallpaperDisabledFragment wallpaperDisabledFragment =
183 WallpaperDisabledFragment.newInstance(wallpaperSupportLevel);
184 fm.beginTransaction()
185 .add(R.id.fragment_container, wallpaperDisabledFragment)
186 .commit();
187 return;
188 }
189
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800190 if (mDelegate.getFormFactor() == FormFactorChecker.FORM_FACTOR_MOBILE) {
Ching-Sung Li5f689972019-05-20 17:16:57 +0800191 initializeMobile(true /* shouldForceRefresh */);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800192 } else { // DESKTOP
193 initializeDesktop(savedInstanceState);
194 }
195 }
196
197 @Override
198 protected void onResume() {
199 super.onResume();
Hyunyoung Song8077c862019-06-18 01:14:24 -0400200 boolean provisioned = Settings.Global.getInt(getContentResolver(),
201 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
202
203 mUserEventLogger.logResumed(provisioned, true);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800204 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
205 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
206 // committing fragment transactions.
207 if (mStagedSetWallpaperErrorDialogFragment != null) {
208 mStagedSetWallpaperErrorDialogFragment.show(
209 getSupportFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
210 mStagedSetWallpaperErrorDialogFragment = null;
211 }
212 }
213
214 @Override
Santiago Etchebeherebd536c62019-04-09 15:25:51 -0300215 protected void onStop() {
216 mUserEventLogger.logStopped();
217 super.onStop();
218 }
219
220 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800221 protected void onDestroy() {
222 super.onDestroy();
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800223 mDelegate.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800224 if (mNetworkStatusListener != null) {
225 mNetworkStatusNotifier.unregisterListener(mNetworkStatusListener);
226 }
227
228 if (mRefreshWallpaperProgressDialog != null) {
229 mRefreshWallpaperProgressDialog.dismiss();
230 }
231 if (mSetWallpaperProgressDialog != null) {
232 mSetWallpaperProgressDialog.dismiss();
233 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800234 }
235
236 @Override
Chuck Liaob1f1a3a2020-02-17 19:46:14 +0800237 public void onBackPressed() {
Chuck Liaof24418e2020-05-10 02:29:44 +0800238 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
Chuck Liao8cbe49f2021-03-15 20:28:04 +0800239 if (fragment != null && fragment.getChildFragmentManager().popBackStackImmediate()) {
Chuck Liaob1f1a3a2020-02-17 19:46:14 +0800240 return;
241 }
242 super.onBackPressed();
243 }
244
245 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800246 public void requestCustomPhotoPicker(PermissionChangedListener listener) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800247 mDelegate.requestCustomPhotoPicker(listener);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800248 }
249
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800250 @Override
251 public void requestExternalStoragePermission(PermissionChangedListener listener) {
252 mDelegate.requestExternalStoragePermission(listener);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800253 }
254
255 /**
256 * Returns whether READ_EXTERNAL_STORAGE has been granted for the application.
257 */
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800258 public boolean isReadExternalStoragePermissionGranted() {
259 return mDelegate.isReadExternalStoragePermissionGranted();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800260 }
261
Ching-Sung Li5f689972019-05-20 17:16:57 +0800262 private void initializeMobile(boolean shouldForceRefresh) {
Chuck Liao8ec38e02020-02-26 20:59:32 +0800263 setContentView(R.layout.activity_top_level_picker);
Kunhung Li14648702021-04-15 13:45:10 +0800264 if (ActivityUtils.isSUWMode(getBaseContext())) {
265 findViewById(R.id.fragment_main).setFitsSystemWindows(/* fitSystemWindows= */ true);
266 }
Santiago Etchebehere35891ea2019-07-26 15:21:42 -0700267 getWindow().getDecorView().setSystemUiVisibility(
268 getWindow().getDecorView().getSystemUiVisibility()
269 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
270 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
Chuck Liao829d4d22020-05-07 17:00:10 +0800271 View fragmentContainer = findViewById(R.id.fragment_container);
272 fragmentContainer.setOnApplyWindowInsetsListener((view, windowInsets) -> {
Santiago Etchebehere35891ea2019-07-26 15:21:42 -0700273 view.setPadding(view.getPaddingLeft(), windowInsets.getSystemWindowInsetTop(),
Chuck Liaoe9d79812020-02-20 18:03:55 +0800274 view.getPaddingRight(), view.getPaddingBottom());
Santiago Etchebehere35891ea2019-07-26 15:21:42 -0700275 // Consume only the top inset (status bar), to let other content in the Activity consume
276 // the nav bar (ie, by using "fitSystemWindows")
Colin Cross1b7f2082021-05-21 11:15:20 -0700277 if (VERSION.SDK_INT >= VERSION_CODES.Q) {
Santiago Etchebehere35891ea2019-07-26 15:21:42 -0700278 WindowInsets.Builder builder = new WindowInsets.Builder(windowInsets);
279 builder.setSystemWindowInsets(Insets.of(windowInsets.getSystemWindowInsetLeft(),
280 0, windowInsets.getStableInsetRight(),
281 windowInsets.getSystemWindowInsetBottom()));
282 return builder.build();
283 } else {
284 return windowInsets.replaceSystemWindowInsets(
285 windowInsets.getSystemWindowInsetLeft(),
286 0, windowInsets.getStableInsetRight(),
287 windowInsets.getSystemWindowInsetBottom());
288 }
289 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800290
291 // Set toolbar as the action bar.
292 Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
293 setSupportActionBar(toolbar);
294
295 FragmentManager fm = getSupportFragmentManager();
296 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
297
Jon Miranda16ea1b12017-12-12 14:52:48 -0800298 if (fragment == null) {
Wesley.CW Wang484cdf32020-09-25 19:18:51 +0800299 // App launch specific logic: log the "app launch source" event.
300 mUserEventLogger.logAppLaunched(getIntent());
Kunhung Li166842e2020-05-13 19:19:06 +0800301 mWallpaperPreferences.incrementAppLaunched();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800302 DailyLoggingAlarmScheduler.setAlarm(getApplicationContext());
303
Santiago Etchebehereaa35e6e2019-01-25 10:30:11 -0800304 CategoryFragment newFragment = CategoryFragment.newInstance(
305 getString(R.string.wallpaper_app_name));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800306 fm.beginTransaction()
307 .add(R.id.fragment_container, newFragment)
308 .commit();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800309 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800310 }
311
312 private void initializeDesktop(Bundle savedInstanceState) {
313 setContentView(R.layout.activity_top_level_desktop);
314
315 mBottomSheet = (LinearLayout) findViewById(R.id.bottom_sheet);
316 mCurrentWallpaperImage = (ImageView) mBottomSheet.findViewById(R.id.current_wallpaper_image);
317 mCurrentWallpaperImage.getLayoutParams().width = getSingleWallpaperImageWidthPx();
318
319 mCurrentWallpaperPresentationMode =
320 (TextView) mBottomSheet.findViewById(R.id.current_wallpaper_presentation_mode);
321 mCurrentWallpaperTitle = (TextView) findViewById(R.id.current_wallpaper_title);
322 mCurrentWallpaperSubtitle = (TextView) findViewById(R.id.current_wallpaper_subtitle);
323 mCurrentWallpaperExploreButton = (Button) findViewById(
324 R.id.current_wallpaper_explore_button);
325 mCurrentWallpaperSkipWallpaperButton = (Button) findViewById(
326 R.id.current_wallpaper_skip_wallpaper_button);
327 mFragmentContainer = (FrameLayout) findViewById(R.id.fragment_container);
328 mLoadingIndicatorContainer = (FrameLayout) findViewById(R.id.loading_indicator_container);
329 mWallpaperPositionOptions = (LinearLayout) findViewById(
330 R.id.desktop_wallpaper_position_options);
331
332 final TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
333 tabLayout.addOnTabSelectedListener(new OnTabSelectedListener() {
334 @Override
335 public void onTabSelected(Tab tab) {
336 Category category = (Category) tab.getTag();
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800337 showCategoryDesktop(category.getCollectionId());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800338 mLastSelectedCategoryTabIndex = tabLayout.getSelectedTabPosition();
339 }
340
341 @Override
342 public void onTabUnselected(Tab tab) {
343 }
344
345 @Override
346 public void onTabReselected(Tab tab) {
347 Category category = (Category) tab.getTag();
348 // If offline, "My photos" may be the only visible category. In this case we want to allow
349 // re-selection so user can still select a photo as wallpaper while offline.
350 if (!category.isEnumerable()) {
351 onTabSelected(tab);
352 }
353 }
354 });
355
356 FragmentManager fm = getSupportFragmentManager();
357 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
358
359 if (fragment == null) {
Wesley.CW Wang484cdf32020-09-25 19:18:51 +0800360 // App launch specific logic: log the "app launch source" event.
361 mUserEventLogger.logAppLaunched(getIntent());
Kunhung Li166842e2020-05-13 19:19:06 +0800362 mWallpaperPreferences.incrementAppLaunched();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800363 DailyLoggingAlarmScheduler.setAlarm(getApplicationContext());
364 }
365
366 mNetworkStatusListener = new NetworkStatusNotifier.Listener() {
367 @Override
368 public void onNetworkChanged(@NetworkStatus int networkStatus) {
369 initializeDesktopBasedOnNetwork(networkStatus, savedInstanceState);
370 }
371 };
372 // Upon registering a listener, the onNetworkChanged method is immediately called with the
373 // initial network status.
374 mNetworkStatusNotifier.registerListener(mNetworkStatusListener);
375 }
376
377 private void initializeDesktopBasedOnNetwork(@NetworkStatus int networkStatus,
378 Bundle savedInstanceState) {
379 if (networkStatus == NetworkStatusNotifier.NETWORK_CONNECTED) {
380 initializeDesktopOnline(savedInstanceState);
381 } else {
382 initializeDesktopOffline();
383 }
384 }
385
386 private void initializeDesktopOnline(Bundle savedInstanceState) {
387 FragmentManager fm = getSupportFragmentManager();
388 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
389
390 // Require a category refresh if this is the first load of the app or if the app is now
391 // returning online after having been offline.
392 boolean forceCategoryRefresh = fragment == null || fragment instanceof OfflineDesktopFragment;
393
394 if (fragment != null) {
395 fm.beginTransaction()
396 .remove(fragment)
397 .commit();
398 }
399
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800400 mLastSelectedCategoryTabIndex = savedInstanceState != null
Jon Miranda16ea1b12017-12-12 14:52:48 -0800401 ? savedInstanceState.getInt(KEY_SELECTED_CATEGORY_TAB) : -1;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800402 mDelegate.populateCategories(forceCategoryRefresh);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800403
404 setDesktopLoading(true);
405 setUpBottomSheet();
406 refreshCurrentWallpapers(null /* refreshListener */);
407 }
408
409 private void initializeDesktopOffline() {
410 FragmentManager fm = getSupportFragmentManager();
411 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
412
413 if (fragment != null) {
414 fm.beginTransaction()
415 .remove(fragment)
416 .commit();
417 }
418 OfflineDesktopFragment newFragment = new OfflineDesktopFragment();
419 fm.beginTransaction()
420 .add(R.id.fragment_container, newFragment)
421 .commit();
422
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800423 // Reset the last selected category tab index to ensure the app doesn't try to reselect a
424 // tab for a category not yet repopulated.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800425 mLastSelectedCategoryTabIndex = -1;
426
Ching-Sung Li15dcdf52021-06-11 15:55:21 +0800427 mDelegate.populateCategories(/* forceRefresh= */ true);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800428
429 setDesktopLoading(false);
430 setCurrentWallpapersExpanded(false);
431 }
432
433 /**
434 * Sets the status of the loading indicator overlay in desktop mode.
435 *
436 * @param loading Whether an indeterminate loading indicator is displayed in place of the main
437 * fragment.
438 */
439 private void setDesktopLoading(boolean loading) {
440 if (loading) {
441 mLoadingIndicatorContainer.setVisibility(View.VISIBLE);
442 mFragmentContainer.setVisibility(View.GONE);
443 } else {
444 mLoadingIndicatorContainer.setVisibility(View.GONE);
445 mFragmentContainer.setVisibility(View.VISIBLE);
446 }
447 }
448
449 /**
450 * Returns the width (in physical px) to use for the "currently set wallpaper" thumbnail.
451 */
452 private int getSingleWallpaperImageWidthPx() {
chihhangchuangc8442a72020-05-07 14:26:32 +0800453 final float screenAspectRatio =
454 ScreenSizeCalculator.getInstance().getScreenAspectRatio(this);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800455
456 int height = getResources().getDimensionPixelSize(
457 R.dimen.current_wallpaper_bottom_sheet_thumb_height);
chihhangchuangc8442a72020-05-07 14:26:32 +0800458 return (int) (height / screenAspectRatio);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800459 }
460
461 /**
462 * Enables and populates the "Currently set" wallpaper BottomSheet.
463 */
464 private void setUpBottomSheet() {
465 mBottomSheet.setVisibility(View.VISIBLE);
466
Jon Miranda16ea1b12017-12-12 14:52:48 -0800467 if (Flags.skipDailyWallpaperButtonEnabled) {
468 // Add "next" icon to the Next Wallpaper button
469 Drawable nextWallpaperButtonDrawable = getResources().getDrawable(
Santiago Etchebehered24506c2018-04-05 17:02:42 -0700470 R.drawable.ic_refresh_18px);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800471
472 // This Drawable's state is shared across the app, so make a copy of it before applying a
473 // color tint as not to affect other clients elsewhere in the app.
474 nextWallpaperButtonDrawable =
475 nextWallpaperButtonDrawable.getConstantState().newDrawable().mutate();
476 // Color the "compass" icon with the accent color.
477 nextWallpaperButtonDrawable.setColorFilter(
Tianguang Zhangd3e4f632021-04-14 00:30:15 +0200478 ResourceUtils.getColorAttr(this,
479 android.R.attr.colorAccent), Mode.SRC_IN);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800480 ButtonDrawableSetterCompat.setDrawableToButtonStart(
481 mCurrentWallpaperSkipWallpaperButton, nextWallpaperButtonDrawable);
482 }
483
484 final BottomSheetBehavior<LinearLayout> bottomSheetBehavior =
485 BottomSheetBehavior.from(mBottomSheet);
486 bottomSheetBehavior.setBottomSheetCallback(new BottomSheetCallback() {
487 @Override
488 public void onStateChanged(@NonNull View view, int i) {
489 }
490
491 @Override
492 public void onSlide(@NonNull View view, float slideOffset) {
493 float alpha;
494 if (slideOffset >= 0) {
495 alpha = slideOffset;
496 } else {
497 alpha = 1f - slideOffset;
498 }
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800499 LinearLayout bottomSheetContents = findViewById(R.id.bottom_sheet_contents);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800500 bottomSheetContents.setAlpha(alpha);
501 }
502 });
503 }
504
505 /**
506 * Enables a test mode of operation -- in which certain UI features are disabled to allow for
507 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
508 * constantly keeps the UI thread alive and blocks a test forever.
509 */
510 void setTestingMode(boolean testingMode) {
511 mTestingMode = testingMode;
512 }
513
514 /**
515 * Obtains the {@link WallpaperInfo} object(s) representing the wallpaper(s) currently set to the
516 * device from the {@link CurrentWallpaperInfoFactory} and displays them in the BottomSheet.
517 */
518 @Override
519 public void refreshCurrentWallpapers(@Nullable RefreshListener refreshListener) {
520 final Injector injector = InjectorProvider.getInjector();
521 final Context appContext = getApplicationContext();
522
523 CurrentWallpaperInfoFactory factory = injector.getCurrentWallpaperFactory(this);
524 factory.createCurrentWallpaperInfos(new WallpaperInfoCallback() {
525 @Override
526 public void onWallpaperInfoCreated(
527 final WallpaperInfo homeWallpaper,
528 @Nullable final WallpaperInfo lockWallpaper,
529 @PresentationMode final int presentationMode) {
530
531 if (isDestroyed()) {
532 return;
533 }
534
535 // Fetch the home wallpaper's thumbnail asset asynchronously to work around expensive
536 // method call to WallpaperManager#getWallpaperFile made from the CurrentWallpaperInfoVN
537 // getAsset() method.
538 AssetReceiver assetReceiver = (Asset thumbAsset) -> {
539 if (isDestroyed()) {
540 return;
541 }
542
543 homeWallpaper.getThumbAsset(appContext).loadDrawableWithTransition(
544 TopLevelPickerActivity.this,
545 mCurrentWallpaperImage,
546 200 /* transitionDurationMillis */,
547 () -> {
548 if (refreshListener != null) {
549 refreshListener.onCurrentWallpaperRefreshed();
550 }
551 },
552 Color.TRANSPARENT);
553 };
554 new FetchThumbAssetTask(appContext, homeWallpaper, assetReceiver).executeOnExecutor(
555 AsyncTask.THREAD_POOL_EXECUTOR);
556
557 mCurrentWallpaperPresentationMode.setText(
558 AttributionFormatter.getHumanReadableWallpaperPresentationMode(
559 TopLevelPickerActivity.this, presentationMode));
560
561 List<String> attributions = homeWallpaper.getAttributions(appContext);
562 if (attributions.size() > 0 && attributions.get(0) != null) {
563 mCurrentWallpaperTitle.setText(attributions.get(0));
564 }
565
566 mCurrentWallpaperSubtitle.setText(
567 AttributionFormatter.formatWallpaperSubtitle(appContext, homeWallpaper));
568
569 final String actionUrl = homeWallpaper.getActionUrl(appContext);
570 if (actionUrl != null && !actionUrl.isEmpty()) {
571 Uri exploreUri = Uri.parse(actionUrl);
572
573 ExploreIntentChecker intentChecker = injector.getExploreIntentChecker(appContext);
574 intentChecker.fetchValidActionViewIntent(exploreUri, (@Nullable Intent exploreIntent) -> {
575 if (exploreIntent != null && !isDestroyed()) {
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700576 // Set the icon for the button
577 Drawable exploreButtonDrawable = getResources().getDrawable(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700578 homeWallpaper.getActionIconRes(appContext));
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700579
580 // This Drawable's state is shared across the app, so make a copy of it
581 // before applying a color tint as not to affect other clients elsewhere
582 // in the app.
583 exploreButtonDrawable = exploreButtonDrawable.getConstantState()
584 .newDrawable().mutate();
585 // Color the "compass" icon with the accent color.
586 exploreButtonDrawable.setColorFilter(
Tianguang Zhangd3e4f632021-04-14 00:30:15 +0200587 ResourceUtils.getColorAttr(TopLevelPickerActivity.this,
588 android.R.attr.colorAccent), Mode.SRC_IN);
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700589
590 ButtonDrawableSetterCompat.setDrawableToButtonStart(
591 mCurrentWallpaperExploreButton, exploreButtonDrawable);
592 mCurrentWallpaperExploreButton.setText(getString(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700593 homeWallpaper.getActionLabelRes(appContext)));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800594 mCurrentWallpaperExploreButton.setVisibility(View.VISIBLE);
595 mCurrentWallpaperExploreButton.setOnClickListener(new OnClickListener() {
596 @Override
597 public void onClick(View v) {
Santiago Etchebeherece5613f2018-06-01 13:22:47 -0700598 mUserEventLogger.logActionClicked(
599 homeWallpaper.getCollectionId(appContext),
600 homeWallpaper.getActionLabelRes(appContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800601 startActivity(exploreIntent);
602 }
603 });
604 }
605 });
606 } else {
607 mCurrentWallpaperExploreButton.setVisibility(View.GONE);
608 }
609
610 // Hide the wallpaper position options UI if the current home wallpaper is not from
611 // "my photos".
612 String homeCollectionId = homeWallpaper.getCollectionId(TopLevelPickerActivity.this);
613 if (mWallpaperPositionOptions != null
614 && homeCollectionId != null // May be null if app is being used for the first time.
615 && !homeCollectionId.equals(getString(R.string.image_wallpaper_collection_id))) {
616 mWallpaperPositionOptions.setVisibility(View.GONE);
617 }
618
619 boolean showSkipWallpaperButton = Flags.skipDailyWallpaperButtonEnabled
620 && presentationMode == WallpaperPreferences.PRESENTATION_MODE_ROTATING;
621 if (showSkipWallpaperButton) {
622 mCurrentWallpaperSkipWallpaperButton.setVisibility(View.VISIBLE);
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800623 mCurrentWallpaperSkipWallpaperButton.setOnClickListener(
624 v -> refreshDailyWallpaper());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800625 } else {
626 mCurrentWallpaperSkipWallpaperButton.setVisibility(View.GONE);
627 }
628
629 if (refreshListener != null) {
630 refreshListener.onCurrentWallpaperRefreshed();
631 }
632 }
633 }, true /* forceRefresh */);
634 }
635
636 @Override
637 public void onSaveInstanceState(Bundle savedInstanceState) {
638 FormFactorChecker formFactorChecker = InjectorProvider.getInjector().getFormFactorChecker(this);
639 if (formFactorChecker.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP) {
640 TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
641
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800642 // tabLayout is only present when the main IndividualPickerFragment is present (as
643 // opposed to
Jon Miranda16ea1b12017-12-12 14:52:48 -0800644 // the WallpaperDisabledFragment), so need this null check.
645 if (tabLayout != null) {
646 savedInstanceState.putInt(KEY_SELECTED_CATEGORY_TAB, tabLayout.getSelectedTabPosition());
647 }
648 }
649
650 super.onSaveInstanceState(savedInstanceState);
651 }
652
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800653 @Override
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700654 @Nullable
Chuck Liao8cbe49f2021-03-15 20:28:04 +0800655 public CategorySelectorFragment getCategorySelectorFragment() {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800656 if (mDelegate.getFormFactor() != FormFactorChecker.FORM_FACTOR_MOBILE) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700657 return null;
658 }
659 FragmentManager fm = getSupportFragmentManager();
Chuck Liao8cbe49f2021-03-15 20:28:04 +0800660 return ((CategoryFragment) fm.findFragmentById(
661 R.id.fragment_container)).getCategorySelectorFragment();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700662 }
663
Jon Miranda16ea1b12017-12-12 14:52:48 -0800664 /**
665 * Populates the category tabs on DESKTOP form factor.
666 *
667 * @param selectedTabPosition The position of the tab to show as selected, or -1 if no particular
668 * tab should be selected (in which case: the tab of the category for the currently set
669 * wallpaper will be selected if enumerable; if not, the first enumerable category's tab will
670 * be selected).
671 */
672 private void populateCategoryTabs(int selectedTabPosition) {
673 final TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
674 tabLayout.removeAllTabs();
675
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800676 String currentlySetCollectionId = mDelegate.getPreferences().getHomeWallpaperCollectionId();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800677
678 Tab tabToSelect = null;
679 Tab firstEnumerableCategoryTab = null;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800680 for (int i = 0; i < mDelegate.getCategoryProvider().getSize(); i++) {
681 Category category = mDelegate.getCategoryProvider().getCategory(i);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800682
683 Tab tab = tabLayout.newTab();
684 tab.setText(category.getTitle());
685 tab.setTag(category);
686 tabLayout.addTab(tab, false /* setSelected */);
687
688 if (firstEnumerableCategoryTab == null && category.isEnumerable()) {
689 firstEnumerableCategoryTab = tab;
690 }
691
692 boolean shouldSelectTab = (i == selectedTabPosition)
693 || (selectedTabPosition == -1
694 && tabToSelect == null
695 && category.isEnumerable()
696 && currentlySetCollectionId != null
697 && currentlySetCollectionId.equals(category.getCollectionId()));
698
699 if (shouldSelectTab) {
700 tabToSelect = tab;
701 }
702 }
703
704 // If the above loop did not identify a specific tab to select, then just select the tab for
705 // the first enumerable category.
706 if (tabToSelect == null) {
707 tabToSelect = firstEnumerableCategoryTab;
708 }
709
710 // There may be no enumerable tabs (e.g., offline case), so we need to null-check again.
711 if (tabToSelect != null) {
712 tabToSelect.select();
713 }
714 }
715
716 /**
717 * Refreshes the current wallpaper in a daily wallpaper rotation.
718 */
719 private void refreshDailyWallpaper() {
720 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
721 // causes Espresso to hang once the dialog is shown.
722 if (!mTestingMode) {
723 int themeResId;
724 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
725 themeResId = R.style.ProgressDialogThemePreL;
726 } else {
727 themeResId = R.style.LightDialogTheme;
728 }
729 mRefreshWallpaperProgressDialog = new ProgressDialog(this, themeResId);
730 mRefreshWallpaperProgressDialog.setTitle(null);
731 mRefreshWallpaperProgressDialog.setMessage(
732 getResources().getString(R.string.refreshing_daily_wallpaper_dialog_message));
733 mRefreshWallpaperProgressDialog.setIndeterminate(true);
734 mRefreshWallpaperProgressDialog.show();
735 }
736
737 WallpaperRotationRefresher wallpaperRotationRefresher =
738 InjectorProvider.getInjector().getWallpaperRotationRefresher();
739 wallpaperRotationRefresher.refreshWallpaper(this, new Listener() {
740 @Override
741 public void onRefreshed() {
742 if (isDestroyed()) {
743 return;
744 }
745
746 if (mRefreshWallpaperProgressDialog != null) {
747 mRefreshWallpaperProgressDialog.dismiss();
748 }
749
750 refreshCurrentWallpapers(null /* refreshListener */);
751 }
752
753 @Override
754 public void onError() {
755 if (mRefreshWallpaperProgressDialog != null) {
756 mRefreshWallpaperProgressDialog.dismiss();
757 }
758
759 AlertDialog errorDialog = new AlertDialog.Builder(
760 TopLevelPickerActivity.this, R.style.LightDialogTheme)
761 .setMessage(R.string.refresh_daily_wallpaper_failed_message)
762 .setPositiveButton(android.R.string.ok, null /* onClickListener */)
763 .create();
764 errorDialog.show();
765 }
766 });
767 }
768
769 @Override
770 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
771 super.onActivityResult(requestCode, resultCode, data);
772
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800773 if (requestCode == WallpaperPickerDelegate.SHOW_CATEGORY_REQUEST_CODE
774 && resultCode == Activity.RESULT_OK) {
Santiago Etchebeheree5e05842019-02-13 16:58:07 -0800775 if (mDelegate.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP) {
776 Uri imageUri = (data == null) ? null : data.getData();
777 if (imageUri != null) {
778 // User selected an image from the system picker, so launch the preview for that
779 // image.
780 ImageWallpaperInfo imageWallpaper = new ImageWallpaperInfo(imageUri);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800781 setCustomPhotoWallpaper(imageWallpaper);
782 return;
783 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800784 }
Santiago Etchebeheree5e05842019-02-13 16:58:07 -0800785 }
786 if (mDelegate.handleActivityResult(requestCode, resultCode, data)) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800787 finishActivityWithResultOk();
788 }
789 }
790
791 /**
792 * Shows the view-only preview activity for the given wallpaper.
793 */
Ching-Sung Lief59efa2020-06-05 22:41:43 +0800794 public void showViewOnlyPreview(WallpaperInfo wallpaperInfo, boolean isViewAsHome) {
795 mDelegate.showViewOnlyPreview(wallpaperInfo, isViewAsHome);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800796 }
797
798 @Override
Chuck Liaoa63f1bf2020-06-11 01:35:42 +0800799 public void show(String collectionId) {
800 mDelegate.show(collectionId);
801 }
802
803 @Override
Chuck Liaof6b4b192020-08-07 02:31:32 +0800804 public void fetchCategories() {
805 mDelegate.initialize(!mDelegate.getCategoryProvider().isCategoriesFetched());
806 }
807
808 @Override
Chuck Liao3abf15b2020-12-17 22:33:02 +0800809 public void cleanUp() {
810 mDelegate.cleanUp();
811 }
812
813 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800814 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
815 @NonNull int[] grantResults) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800816 mDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800817 }
818
Jon Miranda16ea1b12017-12-12 14:52:48 -0800819 private void reselectLastTab() {
820 TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
821
822 // In the offline case, "My photos" could be the only category. Thus we need this check --
823 // to ensure that we don't try to select the "previously selected" category which was -1.
824 if (mLastSelectedCategoryTabIndex > -1) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800825 Tab tabToSelect = tabLayout.getTabAt(
826 mLastSelectedCategoryTabIndex);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800827 if (((Category) tabToSelect.getTag()).isEnumerable()) {
828 tabToSelect.select();
829 }
830 }
831 }
832
Jon Miranda16ea1b12017-12-12 14:52:48 -0800833 private void showCategoryDesktop(String collectionId) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800834 Category category = mDelegate.findCategoryForCollectionId(collectionId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800835 if (category == null) {
836 return;
837 }
838
839 if (category.isEnumerable()) {
840 // Replace contained IndividualPickerFragment with a new instance for the given category.
841 final FragmentManager fm = getSupportFragmentManager();
842 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
843 if (fragment != null) {
844 fm.beginTransaction()
845 .remove(fragment)
846 .commit();
847 }
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800848 Injector injector = InjectorProvider.getInjector();
849 IndividualPickerFragment newFragment = injector.getIndividualPickerFragment(
850 collectionId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800851 fm.beginTransaction()
852 .add(R.id.fragment_container, newFragment)
853 .commit();
854 newFragment.setCurrentWallpaperBottomSheetPresenter(this);
855 newFragment.setWallpapersUiContainer(this);
856 } else {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800857 category.show(this, mDelegate.getPickerIntentFactory(),
858 WallpaperPickerDelegate.SHOW_CATEGORY_REQUEST_CODE);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800859
860 // Need to select the tab here in case we are coming back from a "My photos" in which case
861 // the tab would have been set to "My photos" while viewing a regular image category.
862 reselectLastTab();
863 }
864 }
865
866 private void finishActivityWithResultOk() {
867 overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
868 setResult(Activity.RESULT_OK);
869 finish();
870 }
871
Jon Miranda16ea1b12017-12-12 14:52:48 -0800872 @Override
873 public void setCurrentWallpapersExpanded(boolean expanded) {
874 final BottomSheetBehavior<LinearLayout> bottomSheetBehavior =
875 BottomSheetBehavior.from(mBottomSheet);
876 bottomSheetBehavior.setState(
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800877 expanded ? BottomSheetBehavior.STATE_EXPANDED
878 : BottomSheetBehavior.STATE_COLLAPSED);
879 }
880
881 @Override
882 public void doneFetchingCategories() {
883 populateCategoryTabs(mLastSelectedCategoryTabIndex);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800884 }
885
886 @Override
887 public void onWallpapersReady() {
888 setDesktopLoading(false);
889 setCurrentWallpapersExpanded(true);
890 }
891
892 @Override
Santiago Etchebeherefab49612019-01-15 12:22:42 -0800893 public MyPhotosStarter getMyPhotosStarter() {
894 return this;
895 }
896
897 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800898 public void onClickTryAgain(@Destination int unused) {
899 // Retry the set wallpaper operation with the default center-crop setting.
900 if (mPendingSetWallpaperInfo != null) {
901 setCustomPhotoWallpaper(mPendingSetWallpaperInfo);
902 }
903 }
904
905 /**
906 * Sets the provides wallpaper to the device with center-cropped and scaled to fit the device's
907 * default display.
908 */
909 private void setCustomPhotoWallpaper(final WallpaperInfo wallpaper) {
910 // Save this WallpaperInfo so we can retry this operation later if it fails.
911 mPendingSetWallpaperInfo = wallpaper;
912
913 showSettingWallpaperProgressDialog();
914
915 mWallpaperPersister.setIndividualWallpaperWithPosition(this, wallpaper,
916 WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP, new SetWallpaperCallback() {
917 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800918 public void onSuccess(WallpaperInfo wallpaperInfo) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800919 dismissSettingWallpaperProgressDialog();
920 refreshCurrentWallpapers(null /* refreshListener */);
921
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800922 mDelegate.getPreferences().setPendingWallpaperSetStatus(
Jon Miranda16ea1b12017-12-12 14:52:48 -0800923 WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
924 mUserEventLogger.logWallpaperSet(
925 wallpaper.getCollectionId(getApplicationContext()),
926 wallpaper.getWallpaperId());
927 mUserEventLogger.logWallpaperSetResult(UserEventLogger.WALLPAPER_SET_RESULT_SUCCESS);
928
929 // The user may have closed the activity before the set wallpaper operation completed.
930 if (isDestroyed()) {
931 return;
932 }
933
934 // Show the wallpaper crop option selector and bind click event handlers.
935 mWallpaperPositionOptions.setVisibility(View.VISIBLE);
936
937 mWasCustomPhotoWallpaperSet = true;
938 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP;
939
940 initializeWallpaperPositionOptionClickHandlers(wallpaper);
941 }
942
943 @Override
944 public void onError(Throwable throwable) {
945 dismissSettingWallpaperProgressDialog();
946 showSetWallpaperErrorDialog();
947
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800948 mDelegate.getPreferences().setPendingWallpaperSetStatus(
Jon Miranda16ea1b12017-12-12 14:52:48 -0800949 WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
950 mUserEventLogger.logWallpaperSetResult(
951 UserEventLogger.WALLPAPER_SET_RESULT_FAILURE);
952 @WallpaperSetFailureReason int failureReason = ThrowableAnalyzer.isOOM(throwable)
953 ? UserEventLogger.WALLPAPER_SET_FAILURE_REASON_OOM
954 : UserEventLogger.WALLPAPER_SET_FAILURE_REASON_OTHER;
955 mUserEventLogger.logWallpaperSetFailureReason(failureReason);
956 Log.e(TAG, "Unable to set wallpaper from 'my photos'.");
957 }
958 });
959 }
960
961 /**
962 * Initializes the wallpaper position button click handlers to change the way the provided
963 * wallpaper is set to the device.
964 */
965 private void initializeWallpaperPositionOptionClickHandlers(final WallpaperInfo wallpaperInfo) {
966 Button centerCropOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_center_crop);
967 Button stretchOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_stretched);
968 Button centerOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_center);
969
970 // The "center crop" wallpaper position button is selected by default.
971 setCenterCropWallpaperPositionButtonSelected(centerCropOptionBtn, true /* isSelected */);
972 centerCropOptionBtn.setOnClickListener(new OnClickListener() {
973 @Override
974 public void onClick(View view) {
975 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
976 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP,
977 new SetWallpaperCallback() {
978 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800979 public void onSuccess(WallpaperInfo wallpaperInfo) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800980 // The user may have closed the activity before the set wallpaper operation
981 // completed.
982 if (isDestroyed()) {
983 return;
984 }
985
986 refreshCurrentWallpapers(null /* refreshListener */);
987
988 setCenterCropWallpaperPositionButtonSelected(
989 centerCropOptionBtn, true /* isSelected */);
990 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
991 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
992
993 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP;
994 }
995
996 @Override
997 public void onError(@Nullable Throwable throwable) {
998 // no-op
999 }
1000 });
1001 }
1002 });
1003
1004 // "Stretch" is not selected by default.
1005 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
1006 stretchOptionBtn.setOnClickListener(new OnClickListener() {
1007 @Override
1008 public void onClick(View view) {
1009 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
1010 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_STRETCH,
1011 new SetWallpaperCallback() {
1012 @Override
“Chuckffd832c2020-03-22 02:15:58 +08001013 public void onSuccess(WallpaperInfo wallpaperInfo) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001014 // The user may have closed the activity before the set wallpaper operation
1015 // completed.
1016 if (isDestroyed()) {
1017 return;
1018 }
1019
1020 refreshCurrentWallpapers(null /* refreshListener */);
1021
1022 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, true /* isSelected */);
1023 setCenterCropWallpaperPositionButtonSelected(
1024 centerCropOptionBtn, false /* isSelected */);
1025 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
1026
1027 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_STRETCH;
1028 }
1029
1030 @Override
1031 public void onError(@Nullable Throwable throwable) {
1032 // no-op
1033 }
1034 });
1035 }
1036 });
1037
1038 // "Center" is not selected by default.
1039 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
1040 centerOptionBtn.setOnClickListener(new OnClickListener() {
1041 @Override
1042 public void onClick(View view) {
1043 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
1044 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_CENTER,
1045 new SetWallpaperCallback() {
1046 @Override
“Chuckffd832c2020-03-22 02:15:58 +08001047 public void onSuccess(WallpaperInfo wallpaperInfo) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001048 // The user may have closed the activity before the set wallpaper operation
1049 // completed.
1050 if (isDestroyed()) {
1051 return;
1052 }
1053
1054 refreshCurrentWallpapers(null /* refreshListener */);
1055
1056 setCenterWallpaperPositionButtonSelected(centerOptionBtn, true /* isSelected */);
1057 setCenterCropWallpaperPositionButtonSelected(
1058 centerCropOptionBtn, false /* isSelected */);
1059 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
1060
1061 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER;
1062 }
1063
1064 @Override
1065 public void onError(@Nullable Throwable throwable) {
1066 // no-op
1067 }
1068 });
1069 }
1070 });
1071 }
1072
1073 private void setCenterWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1074 int drawableId = isSelected ? R.drawable.center_blue : R.drawable.center_grey;
1075 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
Tianguang Zhangd3e4f632021-04-14 00:30:15 +02001076 button.setTextColor(getTextColorForWallpaperPositionButton(isSelected));
Jon Miranda16ea1b12017-12-12 14:52:48 -08001077 }
1078
1079 private void setCenterCropWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1080 int drawableId = isSelected ? R.drawable.center_crop_blue : R.drawable.center_crop_grey;
1081 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
Tianguang Zhangd3e4f632021-04-14 00:30:15 +02001082 button.setTextColor(getTextColorForWallpaperPositionButton(isSelected));
Jon Miranda16ea1b12017-12-12 14:52:48 -08001083 }
1084
1085 private void setStretchWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1086 int drawableId = isSelected ? R.drawable.stretch_blue : R.drawable.stretch_grey;
1087 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
Tianguang Zhangd3e4f632021-04-14 00:30:15 +02001088 button.setTextColor(getTextColorForWallpaperPositionButton(isSelected));
Jon Miranda16ea1b12017-12-12 14:52:48 -08001089 }
1090
1091 private void showSettingWallpaperProgressDialog() {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -08001092 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which
1093 // therefore causes Espresso to hang once the dialog is shown.
Jon Miranda16ea1b12017-12-12 14:52:48 -08001094 if (!mTestingMode) {
1095 int themeResId;
1096 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
1097 themeResId = R.style.ProgressDialogThemePreL;
1098 } else {
1099 themeResId = R.style.LightDialogTheme;
1100 }
1101 mSetWallpaperProgressDialog = new ProgressDialog(this, themeResId);
1102 mSetWallpaperProgressDialog.setTitle(null);
1103 mSetWallpaperProgressDialog.setMessage(
1104 getResources().getString(R.string.set_wallpaper_progress_message));
1105 mSetWallpaperProgressDialog.setIndeterminate(true);
1106 mSetWallpaperProgressDialog.show();
1107 }
1108 }
1109
1110 private void dismissSettingWallpaperProgressDialog() {
1111 if (mSetWallpaperProgressDialog != null) {
1112 mSetWallpaperProgressDialog.dismiss();
1113 }
1114 }
1115
1116 private void showSetWallpaperErrorDialog() {
1117 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
1118 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
1119
1120 if (isSafeToCommitFragmentTransaction()) {
1121 dialogFragment.show(getSupportFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
1122 } else {
1123 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
1124 }
1125 }
1126
chihhangchuang3efb6832020-04-17 02:06:25 +08001127 @Override
Kunhung Lic6701492021-01-28 17:35:56 +08001128 public void onUpArrowPressed() {
1129 onBackPressed();
1130 }
1131
1132 @Override
1133 public boolean isUpArrowSupported() {
Kunhung Li14648702021-04-15 13:45:10 +08001134 return !ActivityUtils.isSUWMode(getBaseContext());
Kunhung Lic6701492021-01-28 17:35:56 +08001135 }
1136
Jon Miranda16ea1b12017-12-12 14:52:48 -08001137 private interface AssetReceiver {
1138 void onAssetReceived(Asset asset);
1139 }
1140
1141 /**
1142 * An AsyncTask for asynchronously fetching the thumbnail asset for a given WallpaperInfo.
1143 * Used to work around expensive method call to WallpaperManager#getWallpaperFile made from the
1144 * CurrentWallpaperInfoVN getAsset() method.
1145 */
1146 private static class FetchThumbAssetTask extends AsyncTask<Void, Void, Asset> {
1147 private Context mAppContext;
1148 private WallpaperInfo mWallpaperInfo;
1149 private AssetReceiver mReceiver;
1150
1151 public FetchThumbAssetTask(Context appContext, WallpaperInfo wallpaperInfo,
1152 AssetReceiver receiver) {
1153 mAppContext = appContext;
1154 mWallpaperInfo = wallpaperInfo;
1155 mReceiver = receiver;
1156 }
1157
1158 @Override
1159 protected Asset doInBackground(Void... params) {
1160 return mWallpaperInfo.getThumbAsset(mAppContext);
1161 }
1162
1163 @Override
1164 protected void onPostExecute(Asset thumbAsset) {
1165 mReceiver.onAssetReceived(thumbAsset);
1166 }
1167 }
1168}