blob: 390daf3143e840e8fc8c490d8cb4bde13b4835b4 [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;
Santiago Etchebehere35891ea2019-07-26 15:21:42 -070051import com.android.wallpaper.compat.BuildCompat;
Jon Miranda16ea1b12017-12-12 14:52:48 -080052import com.android.wallpaper.compat.ButtonDrawableSetterCompat;
Jon Miranda16ea1b12017-12-12 14:52:48 -080053import com.android.wallpaper.config.Flags;
54import com.android.wallpaper.model.Category;
Jon Miranda16ea1b12017-12-12 14:52:48 -080055import com.android.wallpaper.model.ImageWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080056import com.android.wallpaper.model.WallpaperInfo;
57import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
58import com.android.wallpaper.module.CurrentWallpaperInfoFactory.WallpaperInfoCallback;
59import com.android.wallpaper.module.DailyLoggingAlarmScheduler;
60import com.android.wallpaper.module.ExploreIntentChecker;
61import com.android.wallpaper.module.FormFactorChecker;
Jon Miranda16ea1b12017-12-12 14:52:48 -080062import com.android.wallpaper.module.Injector;
63import com.android.wallpaper.module.InjectorProvider;
64import com.android.wallpaper.module.NetworkStatusNotifier;
65import com.android.wallpaper.module.NetworkStatusNotifier.NetworkStatus;
66import com.android.wallpaper.module.UserEventLogger;
67import com.android.wallpaper.module.UserEventLogger.WallpaperSetFailureReason;
68import com.android.wallpaper.module.WallpaperPersister;
69import com.android.wallpaper.module.WallpaperPersister.Destination;
70import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
71import com.android.wallpaper.module.WallpaperPersister.WallpaperPosition;
72import com.android.wallpaper.module.WallpaperPreferences;
73import com.android.wallpaper.module.WallpaperPreferences.PresentationMode;
74import com.android.wallpaper.module.WallpaperRotationRefresher;
75import com.android.wallpaper.module.WallpaperRotationRefresher.Listener;
Kunhung Lic6701492021-01-28 17:35:56 +080076import com.android.wallpaper.picker.AppbarFragment.AppbarFragmentHost;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -080077import com.android.wallpaper.picker.CategoryFragment.CategoryFragmentHost;
Jon Miranda16ea1b12017-12-12 14:52:48 -080078import com.android.wallpaper.picker.WallpaperDisabledFragment.WallpaperSupportLevel;
Jon Miranda16ea1b12017-12-12 14:52:48 -080079import com.android.wallpaper.picker.individual.IndividualPickerFragment;
Kunhung Li14648702021-04-15 13:45:10 +080080import com.android.wallpaper.util.ActivityUtils;
Tianguang Zhangd3e4f632021-04-14 00:30:15 +020081import com.android.wallpaper.util.ResourceUtils;
Jon Miranda16ea1b12017-12-12 14:52:48 -080082import com.android.wallpaper.util.ScreenSizeCalculator;
83import com.android.wallpaper.util.ThrowableAnalyzer;
84
Sunny Goyal8600a3f2018-08-15 12:48:01 -070085import com.google.android.material.bottomsheet.BottomSheetBehavior;
86import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback;
87import com.google.android.material.tabs.TabLayout;
88import com.google.android.material.tabs.TabLayout.OnTabSelectedListener;
89import com.google.android.material.tabs.TabLayout.Tab;
90
Jon Miranda16ea1b12017-12-12 14:52:48 -080091import java.util.List;
92
93/**
94 * Activity allowing users to select a category of wallpapers to choose from.
95 */
96public class TopLevelPickerActivity extends BaseActivity implements WallpapersUiContainer,
97 CurrentWallpaperBottomSheetPresenter, SetWallpaperErrorDialogFragment.Listener,
Chuck Liao5572b982021-04-13 23:48:14 +080098 MyPhotosStarter, AppbarFragmentHost, CategoryFragmentHost {
Jon Miranda16ea1b12017-12-12 14:52:48 -080099
100 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
101 "toplevel_set_wallpaper_error_dialog";
102
103 private static final String TAG = "TopLevelPicker";
104 private static final String KEY_SELECTED_CATEGORY_TAB = "selected_category_tab";
105
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800106 private WallpaperPickerDelegate mDelegate;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800107 private int mLastSelectedCategoryTabIndex;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800108 private UserEventLogger mUserEventLogger;
109 private NetworkStatusNotifier mNetworkStatusNotifier;
110 private NetworkStatusNotifier.Listener mNetworkStatusListener;
111 private WallpaperPersister mWallpaperPersister;
Kunhung Li166842e2020-05-13 19:19:06 +0800112 private WallpaperPreferences mWallpaperPreferences;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800113 private boolean mWasCustomPhotoWallpaperSet;
114 @WallpaperPosition
115 private int mCustomPhotoWallpaperPosition;
116
117 /**
118 * Progress dialogs for "refresh daily wallpaper" and "set wallpaper" operations.
119 */
120 private ProgressDialog mRefreshWallpaperProgressDialog;
121 private ProgressDialog mSetWallpaperProgressDialog;
122
123 /**
124 * Designates a test mode of operation -- in which certain UI features are disabled to allow for
125 * UI tests to run correctly.
126 */
127 private boolean mTestingMode;
128
129 /**
130 * UI for the "currently set wallpaper" BottomSheet.
131 */
132 private LinearLayout mBottomSheet;
133 private ImageView mCurrentWallpaperImage;
134 private TextView mCurrentWallpaperPresentationMode;
135 private TextView mCurrentWallpaperTitle;
136 private TextView mCurrentWallpaperSubtitle;
137 private Button mCurrentWallpaperExploreButton;
138 private Button mCurrentWallpaperSkipWallpaperButton;
139 private FrameLayout mFragmentContainer;
140 private FrameLayout mLoadingIndicatorContainer;
141 private LinearLayout mWallpaperPositionOptions;
142
Jon Miranda16ea1b12017-12-12 14:52:48 -0800143 /**
144 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
145 * committing fragment transactions.
146 */
147 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
148
149 /**
150 * A wallpaper pending set to the device--we retain a reference to this in order to facilitate
151 * retry or re-crop operations.
152 */
153 private WallpaperInfo mPendingSetWallpaperInfo;
154
Tianguang Zhangd3e4f632021-04-14 00:30:15 +0200155 private int getTextColorForWallpaperPositionButton(boolean isSelected) {
156 int textColorId = isSelected
157 ? android.R.attr.colorAccent
158 : android.R.attr.textColorTertiary;
159 return ResourceUtils.getColorAttr(this, textColorId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800160 }
161
162 @Override
163 protected void onCreate(Bundle savedInstanceState) {
164 super.onCreate(savedInstanceState);
165
Jon Miranda16ea1b12017-12-12 14:52:48 -0800166 mLastSelectedCategoryTabIndex = -1;
167
168 Injector injector = InjectorProvider.getInjector();
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800169 mDelegate = new WallpaperPickerDelegate(this, this, injector);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800170 mUserEventLogger = injector.getUserEventLogger(this);
171 mNetworkStatusNotifier = injector.getNetworkStatusNotifier(this);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800172 mWallpaperPersister = injector.getWallpaperPersister(this);
Kunhung Li83f72a82020-06-02 11:05:10 +0800173 mWallpaperPreferences = injector.getPreferences(this);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800174 mWasCustomPhotoWallpaperSet = false;
175
Chuck Liaoe2d35f52020-09-22 21:20:47 +0800176 mDelegate.getCategoryProvider().resetIfNeeded();
177
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800178 @WallpaperSupportLevel int wallpaperSupportLevel = mDelegate.getWallpaperSupportLevel();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800179 if (wallpaperSupportLevel != WallpaperDisabledFragment.SUPPORTED_CAN_SET) {
Chuck Liao8ec38e02020-02-26 20:59:32 +0800180 setContentView(R.layout.activity_top_level_picker);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800181
182 FragmentManager fm = getSupportFragmentManager();
183 WallpaperDisabledFragment wallpaperDisabledFragment =
184 WallpaperDisabledFragment.newInstance(wallpaperSupportLevel);
185 fm.beginTransaction()
186 .add(R.id.fragment_container, wallpaperDisabledFragment)
187 .commit();
188 return;
189 }
190
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800191 if (mDelegate.getFormFactor() == FormFactorChecker.FORM_FACTOR_MOBILE) {
Ching-Sung Li5f689972019-05-20 17:16:57 +0800192 initializeMobile(true /* shouldForceRefresh */);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800193 } else { // DESKTOP
194 initializeDesktop(savedInstanceState);
195 }
196 }
197
198 @Override
199 protected void onResume() {
200 super.onResume();
Hyunyoung Song8077c862019-06-18 01:14:24 -0400201 boolean provisioned = Settings.Global.getInt(getContentResolver(),
202 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
203
204 mUserEventLogger.logResumed(provisioned, true);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800205 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
206 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
207 // committing fragment transactions.
208 if (mStagedSetWallpaperErrorDialogFragment != null) {
209 mStagedSetWallpaperErrorDialogFragment.show(
210 getSupportFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
211 mStagedSetWallpaperErrorDialogFragment = null;
212 }
213 }
214
215 @Override
Santiago Etchebeherebd536c62019-04-09 15:25:51 -0300216 protected void onStop() {
217 mUserEventLogger.logStopped();
218 super.onStop();
219 }
220
221 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800222 protected void onDestroy() {
223 super.onDestroy();
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800224 mDelegate.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800225 if (mNetworkStatusListener != null) {
226 mNetworkStatusNotifier.unregisterListener(mNetworkStatusListener);
227 }
228
229 if (mRefreshWallpaperProgressDialog != null) {
230 mRefreshWallpaperProgressDialog.dismiss();
231 }
232 if (mSetWallpaperProgressDialog != null) {
233 mSetWallpaperProgressDialog.dismiss();
234 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800235 }
236
237 @Override
Chuck Liaob1f1a3a2020-02-17 19:46:14 +0800238 public void onBackPressed() {
Chuck Liaof24418e2020-05-10 02:29:44 +0800239 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
Chuck Liao8cbe49f2021-03-15 20:28:04 +0800240 if (fragment != null && fragment.getChildFragmentManager().popBackStackImmediate()) {
Chuck Liaob1f1a3a2020-02-17 19:46:14 +0800241 return;
242 }
243 super.onBackPressed();
244 }
245
246 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800247 public void requestCustomPhotoPicker(PermissionChangedListener listener) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800248 mDelegate.requestCustomPhotoPicker(listener);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800249 }
250
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800251 @Override
252 public void requestExternalStoragePermission(PermissionChangedListener listener) {
253 mDelegate.requestExternalStoragePermission(listener);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800254 }
255
256 /**
257 * Returns whether READ_EXTERNAL_STORAGE has been granted for the application.
258 */
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800259 public boolean isReadExternalStoragePermissionGranted() {
260 return mDelegate.isReadExternalStoragePermissionGranted();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800261 }
262
Ching-Sung Li5f689972019-05-20 17:16:57 +0800263 private void initializeMobile(boolean shouldForceRefresh) {
Chuck Liao8ec38e02020-02-26 20:59:32 +0800264 setContentView(R.layout.activity_top_level_picker);
Kunhung Li14648702021-04-15 13:45:10 +0800265 if (ActivityUtils.isSUWMode(getBaseContext())) {
266 findViewById(R.id.fragment_main).setFitsSystemWindows(/* fitSystemWindows= */ true);
267 }
Santiago Etchebehere35891ea2019-07-26 15:21:42 -0700268 getWindow().getDecorView().setSystemUiVisibility(
269 getWindow().getDecorView().getSystemUiVisibility()
270 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
271 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
Chuck Liao829d4d22020-05-07 17:00:10 +0800272 View fragmentContainer = findViewById(R.id.fragment_container);
273 fragmentContainer.setOnApplyWindowInsetsListener((view, windowInsets) -> {
Santiago Etchebehere35891ea2019-07-26 15:21:42 -0700274 view.setPadding(view.getPaddingLeft(), windowInsets.getSystemWindowInsetTop(),
Chuck Liaoe9d79812020-02-20 18:03:55 +0800275 view.getPaddingRight(), view.getPaddingBottom());
Santiago Etchebehere35891ea2019-07-26 15:21:42 -0700276 // Consume only the top inset (status bar), to let other content in the Activity consume
277 // the nav bar (ie, by using "fitSystemWindows")
278 if (BuildCompat.isAtLeastQ()) {
279 WindowInsets.Builder builder = new WindowInsets.Builder(windowInsets);
280 builder.setSystemWindowInsets(Insets.of(windowInsets.getSystemWindowInsetLeft(),
281 0, windowInsets.getStableInsetRight(),
282 windowInsets.getSystemWindowInsetBottom()));
283 return builder.build();
284 } else {
285 return windowInsets.replaceSystemWindowInsets(
286 windowInsets.getSystemWindowInsetLeft(),
287 0, windowInsets.getStableInsetRight(),
288 windowInsets.getSystemWindowInsetBottom());
289 }
290 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800291
292 // Set toolbar as the action bar.
293 Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
294 setSupportActionBar(toolbar);
295
296 FragmentManager fm = getSupportFragmentManager();
297 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
298
Jon Miranda16ea1b12017-12-12 14:52:48 -0800299 if (fragment == null) {
Wesley.CW Wang484cdf32020-09-25 19:18:51 +0800300 // App launch specific logic: log the "app launch source" event.
301 mUserEventLogger.logAppLaunched(getIntent());
Kunhung Li166842e2020-05-13 19:19:06 +0800302 mWallpaperPreferences.incrementAppLaunched();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800303 DailyLoggingAlarmScheduler.setAlarm(getApplicationContext());
304
Santiago Etchebehereaa35e6e2019-01-25 10:30:11 -0800305 CategoryFragment newFragment = CategoryFragment.newInstance(
306 getString(R.string.wallpaper_app_name));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800307 fm.beginTransaction()
308 .add(R.id.fragment_container, newFragment)
309 .commit();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800310 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800311 }
312
313 private void initializeDesktop(Bundle savedInstanceState) {
314 setContentView(R.layout.activity_top_level_desktop);
315
316 mBottomSheet = (LinearLayout) findViewById(R.id.bottom_sheet);
317 mCurrentWallpaperImage = (ImageView) mBottomSheet.findViewById(R.id.current_wallpaper_image);
318 mCurrentWallpaperImage.getLayoutParams().width = getSingleWallpaperImageWidthPx();
319
320 mCurrentWallpaperPresentationMode =
321 (TextView) mBottomSheet.findViewById(R.id.current_wallpaper_presentation_mode);
322 mCurrentWallpaperTitle = (TextView) findViewById(R.id.current_wallpaper_title);
323 mCurrentWallpaperSubtitle = (TextView) findViewById(R.id.current_wallpaper_subtitle);
324 mCurrentWallpaperExploreButton = (Button) findViewById(
325 R.id.current_wallpaper_explore_button);
326 mCurrentWallpaperSkipWallpaperButton = (Button) findViewById(
327 R.id.current_wallpaper_skip_wallpaper_button);
328 mFragmentContainer = (FrameLayout) findViewById(R.id.fragment_container);
329 mLoadingIndicatorContainer = (FrameLayout) findViewById(R.id.loading_indicator_container);
330 mWallpaperPositionOptions = (LinearLayout) findViewById(
331 R.id.desktop_wallpaper_position_options);
332
333 final TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
334 tabLayout.addOnTabSelectedListener(new OnTabSelectedListener() {
335 @Override
336 public void onTabSelected(Tab tab) {
337 Category category = (Category) tab.getTag();
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800338 showCategoryDesktop(category.getCollectionId());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800339 mLastSelectedCategoryTabIndex = tabLayout.getSelectedTabPosition();
340 }
341
342 @Override
343 public void onTabUnselected(Tab tab) {
344 }
345
346 @Override
347 public void onTabReselected(Tab tab) {
348 Category category = (Category) tab.getTag();
349 // If offline, "My photos" may be the only visible category. In this case we want to allow
350 // re-selection so user can still select a photo as wallpaper while offline.
351 if (!category.isEnumerable()) {
352 onTabSelected(tab);
353 }
354 }
355 });
356
357 FragmentManager fm = getSupportFragmentManager();
358 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
359
360 if (fragment == null) {
Wesley.CW Wang484cdf32020-09-25 19:18:51 +0800361 // App launch specific logic: log the "app launch source" event.
362 mUserEventLogger.logAppLaunched(getIntent());
Kunhung Li166842e2020-05-13 19:19:06 +0800363 mWallpaperPreferences.incrementAppLaunched();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800364 DailyLoggingAlarmScheduler.setAlarm(getApplicationContext());
365 }
366
367 mNetworkStatusListener = new NetworkStatusNotifier.Listener() {
368 @Override
369 public void onNetworkChanged(@NetworkStatus int networkStatus) {
370 initializeDesktopBasedOnNetwork(networkStatus, savedInstanceState);
371 }
372 };
373 // Upon registering a listener, the onNetworkChanged method is immediately called with the
374 // initial network status.
375 mNetworkStatusNotifier.registerListener(mNetworkStatusListener);
376 }
377
378 private void initializeDesktopBasedOnNetwork(@NetworkStatus int networkStatus,
379 Bundle savedInstanceState) {
380 if (networkStatus == NetworkStatusNotifier.NETWORK_CONNECTED) {
381 initializeDesktopOnline(savedInstanceState);
382 } else {
383 initializeDesktopOffline();
384 }
385 }
386
387 private void initializeDesktopOnline(Bundle savedInstanceState) {
388 FragmentManager fm = getSupportFragmentManager();
389 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
390
391 // Require a category refresh if this is the first load of the app or if the app is now
392 // returning online after having been offline.
393 boolean forceCategoryRefresh = fragment == null || fragment instanceof OfflineDesktopFragment;
394
395 if (fragment != null) {
396 fm.beginTransaction()
397 .remove(fragment)
398 .commit();
399 }
400
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800401 mLastSelectedCategoryTabIndex = savedInstanceState != null
Jon Miranda16ea1b12017-12-12 14:52:48 -0800402 ? savedInstanceState.getInt(KEY_SELECTED_CATEGORY_TAB) : -1;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800403 mDelegate.populateCategories(forceCategoryRefresh);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800404
405 setDesktopLoading(true);
406 setUpBottomSheet();
407 refreshCurrentWallpapers(null /* refreshListener */);
408 }
409
410 private void initializeDesktopOffline() {
411 FragmentManager fm = getSupportFragmentManager();
412 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
413
414 if (fragment != null) {
415 fm.beginTransaction()
416 .remove(fragment)
417 .commit();
418 }
419 OfflineDesktopFragment newFragment = new OfflineDesktopFragment();
420 fm.beginTransaction()
421 .add(R.id.fragment_container, newFragment)
422 .commit();
423
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800424 // Reset the last selected category tab index to ensure the app doesn't try to reselect a
425 // tab for a category not yet repopulated.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800426 mLastSelectedCategoryTabIndex = -1;
427
Ching-Sung Li15dcdf52021-06-11 15:55:21 +0800428 mDelegate.populateCategories(/* forceRefresh= */ true);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800429
430 setDesktopLoading(false);
431 setCurrentWallpapersExpanded(false);
432 }
433
434 /**
435 * Sets the status of the loading indicator overlay in desktop mode.
436 *
437 * @param loading Whether an indeterminate loading indicator is displayed in place of the main
438 * fragment.
439 */
440 private void setDesktopLoading(boolean loading) {
441 if (loading) {
442 mLoadingIndicatorContainer.setVisibility(View.VISIBLE);
443 mFragmentContainer.setVisibility(View.GONE);
444 } else {
445 mLoadingIndicatorContainer.setVisibility(View.GONE);
446 mFragmentContainer.setVisibility(View.VISIBLE);
447 }
448 }
449
450 /**
451 * Returns the width (in physical px) to use for the "currently set wallpaper" thumbnail.
452 */
453 private int getSingleWallpaperImageWidthPx() {
chihhangchuangc8442a72020-05-07 14:26:32 +0800454 final float screenAspectRatio =
455 ScreenSizeCalculator.getInstance().getScreenAspectRatio(this);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800456
457 int height = getResources().getDimensionPixelSize(
458 R.dimen.current_wallpaper_bottom_sheet_thumb_height);
chihhangchuangc8442a72020-05-07 14:26:32 +0800459 return (int) (height / screenAspectRatio);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800460 }
461
462 /**
463 * Enables and populates the "Currently set" wallpaper BottomSheet.
464 */
465 private void setUpBottomSheet() {
466 mBottomSheet.setVisibility(View.VISIBLE);
467
Jon Miranda16ea1b12017-12-12 14:52:48 -0800468 if (Flags.skipDailyWallpaperButtonEnabled) {
469 // Add "next" icon to the Next Wallpaper button
470 Drawable nextWallpaperButtonDrawable = getResources().getDrawable(
Santiago Etchebehered24506c2018-04-05 17:02:42 -0700471 R.drawable.ic_refresh_18px);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800472
473 // This Drawable's state is shared across the app, so make a copy of it before applying a
474 // color tint as not to affect other clients elsewhere in the app.
475 nextWallpaperButtonDrawable =
476 nextWallpaperButtonDrawable.getConstantState().newDrawable().mutate();
477 // Color the "compass" icon with the accent color.
478 nextWallpaperButtonDrawable.setColorFilter(
Tianguang Zhangd3e4f632021-04-14 00:30:15 +0200479 ResourceUtils.getColorAttr(this,
480 android.R.attr.colorAccent), Mode.SRC_IN);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800481 ButtonDrawableSetterCompat.setDrawableToButtonStart(
482 mCurrentWallpaperSkipWallpaperButton, nextWallpaperButtonDrawable);
483 }
484
485 final BottomSheetBehavior<LinearLayout> bottomSheetBehavior =
486 BottomSheetBehavior.from(mBottomSheet);
487 bottomSheetBehavior.setBottomSheetCallback(new BottomSheetCallback() {
488 @Override
489 public void onStateChanged(@NonNull View view, int i) {
490 }
491
492 @Override
493 public void onSlide(@NonNull View view, float slideOffset) {
494 float alpha;
495 if (slideOffset >= 0) {
496 alpha = slideOffset;
497 } else {
498 alpha = 1f - slideOffset;
499 }
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800500 LinearLayout bottomSheetContents = findViewById(R.id.bottom_sheet_contents);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800501 bottomSheetContents.setAlpha(alpha);
502 }
503 });
504 }
505
506 /**
507 * Enables a test mode of operation -- in which certain UI features are disabled to allow for
508 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
509 * constantly keeps the UI thread alive and blocks a test forever.
510 */
511 void setTestingMode(boolean testingMode) {
512 mTestingMode = testingMode;
513 }
514
515 /**
516 * Obtains the {@link WallpaperInfo} object(s) representing the wallpaper(s) currently set to the
517 * device from the {@link CurrentWallpaperInfoFactory} and displays them in the BottomSheet.
518 */
519 @Override
520 public void refreshCurrentWallpapers(@Nullable RefreshListener refreshListener) {
521 final Injector injector = InjectorProvider.getInjector();
522 final Context appContext = getApplicationContext();
523
524 CurrentWallpaperInfoFactory factory = injector.getCurrentWallpaperFactory(this);
525 factory.createCurrentWallpaperInfos(new WallpaperInfoCallback() {
526 @Override
527 public void onWallpaperInfoCreated(
528 final WallpaperInfo homeWallpaper,
529 @Nullable final WallpaperInfo lockWallpaper,
530 @PresentationMode final int presentationMode) {
531
532 if (isDestroyed()) {
533 return;
534 }
535
536 // Fetch the home wallpaper's thumbnail asset asynchronously to work around expensive
537 // method call to WallpaperManager#getWallpaperFile made from the CurrentWallpaperInfoVN
538 // getAsset() method.
539 AssetReceiver assetReceiver = (Asset thumbAsset) -> {
540 if (isDestroyed()) {
541 return;
542 }
543
544 homeWallpaper.getThumbAsset(appContext).loadDrawableWithTransition(
545 TopLevelPickerActivity.this,
546 mCurrentWallpaperImage,
547 200 /* transitionDurationMillis */,
548 () -> {
549 if (refreshListener != null) {
550 refreshListener.onCurrentWallpaperRefreshed();
551 }
552 },
553 Color.TRANSPARENT);
554 };
555 new FetchThumbAssetTask(appContext, homeWallpaper, assetReceiver).executeOnExecutor(
556 AsyncTask.THREAD_POOL_EXECUTOR);
557
558 mCurrentWallpaperPresentationMode.setText(
559 AttributionFormatter.getHumanReadableWallpaperPresentationMode(
560 TopLevelPickerActivity.this, presentationMode));
561
562 List<String> attributions = homeWallpaper.getAttributions(appContext);
563 if (attributions.size() > 0 && attributions.get(0) != null) {
564 mCurrentWallpaperTitle.setText(attributions.get(0));
565 }
566
567 mCurrentWallpaperSubtitle.setText(
568 AttributionFormatter.formatWallpaperSubtitle(appContext, homeWallpaper));
569
570 final String actionUrl = homeWallpaper.getActionUrl(appContext);
571 if (actionUrl != null && !actionUrl.isEmpty()) {
572 Uri exploreUri = Uri.parse(actionUrl);
573
574 ExploreIntentChecker intentChecker = injector.getExploreIntentChecker(appContext);
575 intentChecker.fetchValidActionViewIntent(exploreUri, (@Nullable Intent exploreIntent) -> {
576 if (exploreIntent != null && !isDestroyed()) {
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700577 // Set the icon for the button
578 Drawable exploreButtonDrawable = getResources().getDrawable(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700579 homeWallpaper.getActionIconRes(appContext));
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700580
581 // This Drawable's state is shared across the app, so make a copy of it
582 // before applying a color tint as not to affect other clients elsewhere
583 // in the app.
584 exploreButtonDrawable = exploreButtonDrawable.getConstantState()
585 .newDrawable().mutate();
586 // Color the "compass" icon with the accent color.
587 exploreButtonDrawable.setColorFilter(
Tianguang Zhangd3e4f632021-04-14 00:30:15 +0200588 ResourceUtils.getColorAttr(TopLevelPickerActivity.this,
589 android.R.attr.colorAccent), Mode.SRC_IN);
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700590
591 ButtonDrawableSetterCompat.setDrawableToButtonStart(
592 mCurrentWallpaperExploreButton, exploreButtonDrawable);
593 mCurrentWallpaperExploreButton.setText(getString(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700594 homeWallpaper.getActionLabelRes(appContext)));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800595 mCurrentWallpaperExploreButton.setVisibility(View.VISIBLE);
596 mCurrentWallpaperExploreButton.setOnClickListener(new OnClickListener() {
597 @Override
598 public void onClick(View v) {
Santiago Etchebeherece5613f2018-06-01 13:22:47 -0700599 mUserEventLogger.logActionClicked(
600 homeWallpaper.getCollectionId(appContext),
601 homeWallpaper.getActionLabelRes(appContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800602 startActivity(exploreIntent);
603 }
604 });
605 }
606 });
607 } else {
608 mCurrentWallpaperExploreButton.setVisibility(View.GONE);
609 }
610
611 // Hide the wallpaper position options UI if the current home wallpaper is not from
612 // "my photos".
613 String homeCollectionId = homeWallpaper.getCollectionId(TopLevelPickerActivity.this);
614 if (mWallpaperPositionOptions != null
615 && homeCollectionId != null // May be null if app is being used for the first time.
616 && !homeCollectionId.equals(getString(R.string.image_wallpaper_collection_id))) {
617 mWallpaperPositionOptions.setVisibility(View.GONE);
618 }
619
620 boolean showSkipWallpaperButton = Flags.skipDailyWallpaperButtonEnabled
621 && presentationMode == WallpaperPreferences.PRESENTATION_MODE_ROTATING;
622 if (showSkipWallpaperButton) {
623 mCurrentWallpaperSkipWallpaperButton.setVisibility(View.VISIBLE);
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800624 mCurrentWallpaperSkipWallpaperButton.setOnClickListener(
625 v -> refreshDailyWallpaper());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800626 } else {
627 mCurrentWallpaperSkipWallpaperButton.setVisibility(View.GONE);
628 }
629
630 if (refreshListener != null) {
631 refreshListener.onCurrentWallpaperRefreshed();
632 }
633 }
634 }, true /* forceRefresh */);
635 }
636
637 @Override
638 public void onSaveInstanceState(Bundle savedInstanceState) {
639 FormFactorChecker formFactorChecker = InjectorProvider.getInjector().getFormFactorChecker(this);
640 if (formFactorChecker.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP) {
641 TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
642
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800643 // tabLayout is only present when the main IndividualPickerFragment is present (as
644 // opposed to
Jon Miranda16ea1b12017-12-12 14:52:48 -0800645 // the WallpaperDisabledFragment), so need this null check.
646 if (tabLayout != null) {
647 savedInstanceState.putInt(KEY_SELECTED_CATEGORY_TAB, tabLayout.getSelectedTabPosition());
648 }
649 }
650
651 super.onSaveInstanceState(savedInstanceState);
652 }
653
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800654 @Override
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700655 @Nullable
Chuck Liao8cbe49f2021-03-15 20:28:04 +0800656 public CategorySelectorFragment getCategorySelectorFragment() {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800657 if (mDelegate.getFormFactor() != FormFactorChecker.FORM_FACTOR_MOBILE) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700658 return null;
659 }
660 FragmentManager fm = getSupportFragmentManager();
Chuck Liao8cbe49f2021-03-15 20:28:04 +0800661 return ((CategoryFragment) fm.findFragmentById(
662 R.id.fragment_container)).getCategorySelectorFragment();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700663 }
664
Jon Miranda16ea1b12017-12-12 14:52:48 -0800665 /**
666 * Populates the category tabs on DESKTOP form factor.
667 *
668 * @param selectedTabPosition The position of the tab to show as selected, or -1 if no particular
669 * tab should be selected (in which case: the tab of the category for the currently set
670 * wallpaper will be selected if enumerable; if not, the first enumerable category's tab will
671 * be selected).
672 */
673 private void populateCategoryTabs(int selectedTabPosition) {
674 final TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
675 tabLayout.removeAllTabs();
676
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800677 String currentlySetCollectionId = mDelegate.getPreferences().getHomeWallpaperCollectionId();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800678
679 Tab tabToSelect = null;
680 Tab firstEnumerableCategoryTab = null;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800681 for (int i = 0; i < mDelegate.getCategoryProvider().getSize(); i++) {
682 Category category = mDelegate.getCategoryProvider().getCategory(i);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800683
684 Tab tab = tabLayout.newTab();
685 tab.setText(category.getTitle());
686 tab.setTag(category);
687 tabLayout.addTab(tab, false /* setSelected */);
688
689 if (firstEnumerableCategoryTab == null && category.isEnumerable()) {
690 firstEnumerableCategoryTab = tab;
691 }
692
693 boolean shouldSelectTab = (i == selectedTabPosition)
694 || (selectedTabPosition == -1
695 && tabToSelect == null
696 && category.isEnumerable()
697 && currentlySetCollectionId != null
698 && currentlySetCollectionId.equals(category.getCollectionId()));
699
700 if (shouldSelectTab) {
701 tabToSelect = tab;
702 }
703 }
704
705 // If the above loop did not identify a specific tab to select, then just select the tab for
706 // the first enumerable category.
707 if (tabToSelect == null) {
708 tabToSelect = firstEnumerableCategoryTab;
709 }
710
711 // There may be no enumerable tabs (e.g., offline case), so we need to null-check again.
712 if (tabToSelect != null) {
713 tabToSelect.select();
714 }
715 }
716
717 /**
718 * Refreshes the current wallpaper in a daily wallpaper rotation.
719 */
720 private void refreshDailyWallpaper() {
721 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
722 // causes Espresso to hang once the dialog is shown.
723 if (!mTestingMode) {
724 int themeResId;
725 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
726 themeResId = R.style.ProgressDialogThemePreL;
727 } else {
728 themeResId = R.style.LightDialogTheme;
729 }
730 mRefreshWallpaperProgressDialog = new ProgressDialog(this, themeResId);
731 mRefreshWallpaperProgressDialog.setTitle(null);
732 mRefreshWallpaperProgressDialog.setMessage(
733 getResources().getString(R.string.refreshing_daily_wallpaper_dialog_message));
734 mRefreshWallpaperProgressDialog.setIndeterminate(true);
735 mRefreshWallpaperProgressDialog.show();
736 }
737
738 WallpaperRotationRefresher wallpaperRotationRefresher =
739 InjectorProvider.getInjector().getWallpaperRotationRefresher();
740 wallpaperRotationRefresher.refreshWallpaper(this, new Listener() {
741 @Override
742 public void onRefreshed() {
743 if (isDestroyed()) {
744 return;
745 }
746
747 if (mRefreshWallpaperProgressDialog != null) {
748 mRefreshWallpaperProgressDialog.dismiss();
749 }
750
751 refreshCurrentWallpapers(null /* refreshListener */);
752 }
753
754 @Override
755 public void onError() {
756 if (mRefreshWallpaperProgressDialog != null) {
757 mRefreshWallpaperProgressDialog.dismiss();
758 }
759
760 AlertDialog errorDialog = new AlertDialog.Builder(
761 TopLevelPickerActivity.this, R.style.LightDialogTheme)
762 .setMessage(R.string.refresh_daily_wallpaper_failed_message)
763 .setPositiveButton(android.R.string.ok, null /* onClickListener */)
764 .create();
765 errorDialog.show();
766 }
767 });
768 }
769
770 @Override
771 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
772 super.onActivityResult(requestCode, resultCode, data);
773
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800774 if (requestCode == WallpaperPickerDelegate.SHOW_CATEGORY_REQUEST_CODE
775 && resultCode == Activity.RESULT_OK) {
Santiago Etchebeheree5e05842019-02-13 16:58:07 -0800776 if (mDelegate.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP) {
777 Uri imageUri = (data == null) ? null : data.getData();
778 if (imageUri != null) {
779 // User selected an image from the system picker, so launch the preview for that
780 // image.
781 ImageWallpaperInfo imageWallpaper = new ImageWallpaperInfo(imageUri);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800782 setCustomPhotoWallpaper(imageWallpaper);
783 return;
784 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800785 }
Santiago Etchebeheree5e05842019-02-13 16:58:07 -0800786 }
787 if (mDelegate.handleActivityResult(requestCode, resultCode, data)) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800788 finishActivityWithResultOk();
789 }
790 }
791
792 /**
793 * Shows the view-only preview activity for the given wallpaper.
794 */
Ching-Sung Lief59efa2020-06-05 22:41:43 +0800795 public void showViewOnlyPreview(WallpaperInfo wallpaperInfo, boolean isViewAsHome) {
796 mDelegate.showViewOnlyPreview(wallpaperInfo, isViewAsHome);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800797 }
798
799 @Override
Chuck Liaoa63f1bf2020-06-11 01:35:42 +0800800 public void show(String collectionId) {
801 mDelegate.show(collectionId);
802 }
803
804 @Override
Chuck Liaof6b4b192020-08-07 02:31:32 +0800805 public void fetchCategories() {
806 mDelegate.initialize(!mDelegate.getCategoryProvider().isCategoriesFetched());
807 }
808
809 @Override
Chuck Liao3abf15b2020-12-17 22:33:02 +0800810 public void cleanUp() {
811 mDelegate.cleanUp();
812 }
813
814 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800815 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
816 @NonNull int[] grantResults) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800817 mDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800818 }
819
Jon Miranda16ea1b12017-12-12 14:52:48 -0800820 private void reselectLastTab() {
821 TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
822
823 // In the offline case, "My photos" could be the only category. Thus we need this check --
824 // to ensure that we don't try to select the "previously selected" category which was -1.
825 if (mLastSelectedCategoryTabIndex > -1) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800826 Tab tabToSelect = tabLayout.getTabAt(
827 mLastSelectedCategoryTabIndex);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800828 if (((Category) tabToSelect.getTag()).isEnumerable()) {
829 tabToSelect.select();
830 }
831 }
832 }
833
Jon Miranda16ea1b12017-12-12 14:52:48 -0800834 private void showCategoryDesktop(String collectionId) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800835 Category category = mDelegate.findCategoryForCollectionId(collectionId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800836 if (category == null) {
837 return;
838 }
839
840 if (category.isEnumerable()) {
841 // Replace contained IndividualPickerFragment with a new instance for the given category.
842 final FragmentManager fm = getSupportFragmentManager();
843 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
844 if (fragment != null) {
845 fm.beginTransaction()
846 .remove(fragment)
847 .commit();
848 }
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800849 Injector injector = InjectorProvider.getInjector();
850 IndividualPickerFragment newFragment = injector.getIndividualPickerFragment(
851 collectionId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800852 fm.beginTransaction()
853 .add(R.id.fragment_container, newFragment)
854 .commit();
855 newFragment.setCurrentWallpaperBottomSheetPresenter(this);
856 newFragment.setWallpapersUiContainer(this);
857 } else {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800858 category.show(this, mDelegate.getPickerIntentFactory(),
859 WallpaperPickerDelegate.SHOW_CATEGORY_REQUEST_CODE);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800860
861 // Need to select the tab here in case we are coming back from a "My photos" in which case
862 // the tab would have been set to "My photos" while viewing a regular image category.
863 reselectLastTab();
864 }
865 }
866
867 private void finishActivityWithResultOk() {
868 overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
869 setResult(Activity.RESULT_OK);
870 finish();
871 }
872
Jon Miranda16ea1b12017-12-12 14:52:48 -0800873 @Override
874 public void setCurrentWallpapersExpanded(boolean expanded) {
875 final BottomSheetBehavior<LinearLayout> bottomSheetBehavior =
876 BottomSheetBehavior.from(mBottomSheet);
877 bottomSheetBehavior.setState(
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800878 expanded ? BottomSheetBehavior.STATE_EXPANDED
879 : BottomSheetBehavior.STATE_COLLAPSED);
880 }
881
882 @Override
883 public void doneFetchingCategories() {
884 populateCategoryTabs(mLastSelectedCategoryTabIndex);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800885 }
886
887 @Override
888 public void onWallpapersReady() {
889 setDesktopLoading(false);
890 setCurrentWallpapersExpanded(true);
891 }
892
893 @Override
Santiago Etchebeherefab49612019-01-15 12:22:42 -0800894 public MyPhotosStarter getMyPhotosStarter() {
895 return this;
896 }
897
898 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800899 public void onClickTryAgain(@Destination int unused) {
900 // Retry the set wallpaper operation with the default center-crop setting.
901 if (mPendingSetWallpaperInfo != null) {
902 setCustomPhotoWallpaper(mPendingSetWallpaperInfo);
903 }
904 }
905
906 /**
907 * Sets the provides wallpaper to the device with center-cropped and scaled to fit the device's
908 * default display.
909 */
910 private void setCustomPhotoWallpaper(final WallpaperInfo wallpaper) {
911 // Save this WallpaperInfo so we can retry this operation later if it fails.
912 mPendingSetWallpaperInfo = wallpaper;
913
914 showSettingWallpaperProgressDialog();
915
916 mWallpaperPersister.setIndividualWallpaperWithPosition(this, wallpaper,
917 WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP, new SetWallpaperCallback() {
918 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800919 public void onSuccess(WallpaperInfo wallpaperInfo) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800920 dismissSettingWallpaperProgressDialog();
921 refreshCurrentWallpapers(null /* refreshListener */);
922
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800923 mDelegate.getPreferences().setPendingWallpaperSetStatus(
Jon Miranda16ea1b12017-12-12 14:52:48 -0800924 WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
925 mUserEventLogger.logWallpaperSet(
926 wallpaper.getCollectionId(getApplicationContext()),
927 wallpaper.getWallpaperId());
928 mUserEventLogger.logWallpaperSetResult(UserEventLogger.WALLPAPER_SET_RESULT_SUCCESS);
929
930 // The user may have closed the activity before the set wallpaper operation completed.
931 if (isDestroyed()) {
932 return;
933 }
934
935 // Show the wallpaper crop option selector and bind click event handlers.
936 mWallpaperPositionOptions.setVisibility(View.VISIBLE);
937
938 mWasCustomPhotoWallpaperSet = true;
939 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP;
940
941 initializeWallpaperPositionOptionClickHandlers(wallpaper);
942 }
943
944 @Override
945 public void onError(Throwable throwable) {
946 dismissSettingWallpaperProgressDialog();
947 showSetWallpaperErrorDialog();
948
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800949 mDelegate.getPreferences().setPendingWallpaperSetStatus(
Jon Miranda16ea1b12017-12-12 14:52:48 -0800950 WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
951 mUserEventLogger.logWallpaperSetResult(
952 UserEventLogger.WALLPAPER_SET_RESULT_FAILURE);
953 @WallpaperSetFailureReason int failureReason = ThrowableAnalyzer.isOOM(throwable)
954 ? UserEventLogger.WALLPAPER_SET_FAILURE_REASON_OOM
955 : UserEventLogger.WALLPAPER_SET_FAILURE_REASON_OTHER;
956 mUserEventLogger.logWallpaperSetFailureReason(failureReason);
957 Log.e(TAG, "Unable to set wallpaper from 'my photos'.");
958 }
959 });
960 }
961
962 /**
963 * Initializes the wallpaper position button click handlers to change the way the provided
964 * wallpaper is set to the device.
965 */
966 private void initializeWallpaperPositionOptionClickHandlers(final WallpaperInfo wallpaperInfo) {
967 Button centerCropOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_center_crop);
968 Button stretchOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_stretched);
969 Button centerOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_center);
970
971 // The "center crop" wallpaper position button is selected by default.
972 setCenterCropWallpaperPositionButtonSelected(centerCropOptionBtn, true /* isSelected */);
973 centerCropOptionBtn.setOnClickListener(new OnClickListener() {
974 @Override
975 public void onClick(View view) {
976 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
977 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP,
978 new SetWallpaperCallback() {
979 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800980 public void onSuccess(WallpaperInfo wallpaperInfo) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800981 // The user may have closed the activity before the set wallpaper operation
982 // completed.
983 if (isDestroyed()) {
984 return;
985 }
986
987 refreshCurrentWallpapers(null /* refreshListener */);
988
989 setCenterCropWallpaperPositionButtonSelected(
990 centerCropOptionBtn, true /* isSelected */);
991 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
992 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
993
994 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP;
995 }
996
997 @Override
998 public void onError(@Nullable Throwable throwable) {
999 // no-op
1000 }
1001 });
1002 }
1003 });
1004
1005 // "Stretch" is not selected by default.
1006 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
1007 stretchOptionBtn.setOnClickListener(new OnClickListener() {
1008 @Override
1009 public void onClick(View view) {
1010 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
1011 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_STRETCH,
1012 new SetWallpaperCallback() {
1013 @Override
“Chuckffd832c2020-03-22 02:15:58 +08001014 public void onSuccess(WallpaperInfo wallpaperInfo) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001015 // The user may have closed the activity before the set wallpaper operation
1016 // completed.
1017 if (isDestroyed()) {
1018 return;
1019 }
1020
1021 refreshCurrentWallpapers(null /* refreshListener */);
1022
1023 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, true /* isSelected */);
1024 setCenterCropWallpaperPositionButtonSelected(
1025 centerCropOptionBtn, false /* isSelected */);
1026 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
1027
1028 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_STRETCH;
1029 }
1030
1031 @Override
1032 public void onError(@Nullable Throwable throwable) {
1033 // no-op
1034 }
1035 });
1036 }
1037 });
1038
1039 // "Center" is not selected by default.
1040 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
1041 centerOptionBtn.setOnClickListener(new OnClickListener() {
1042 @Override
1043 public void onClick(View view) {
1044 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
1045 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_CENTER,
1046 new SetWallpaperCallback() {
1047 @Override
“Chuckffd832c2020-03-22 02:15:58 +08001048 public void onSuccess(WallpaperInfo wallpaperInfo) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001049 // The user may have closed the activity before the set wallpaper operation
1050 // completed.
1051 if (isDestroyed()) {
1052 return;
1053 }
1054
1055 refreshCurrentWallpapers(null /* refreshListener */);
1056
1057 setCenterWallpaperPositionButtonSelected(centerOptionBtn, true /* isSelected */);
1058 setCenterCropWallpaperPositionButtonSelected(
1059 centerCropOptionBtn, false /* isSelected */);
1060 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
1061
1062 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER;
1063 }
1064
1065 @Override
1066 public void onError(@Nullable Throwable throwable) {
1067 // no-op
1068 }
1069 });
1070 }
1071 });
1072 }
1073
1074 private void setCenterWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1075 int drawableId = isSelected ? R.drawable.center_blue : R.drawable.center_grey;
1076 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
Tianguang Zhangd3e4f632021-04-14 00:30:15 +02001077 button.setTextColor(getTextColorForWallpaperPositionButton(isSelected));
Jon Miranda16ea1b12017-12-12 14:52:48 -08001078 }
1079
1080 private void setCenterCropWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1081 int drawableId = isSelected ? R.drawable.center_crop_blue : R.drawable.center_crop_grey;
1082 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
Tianguang Zhangd3e4f632021-04-14 00:30:15 +02001083 button.setTextColor(getTextColorForWallpaperPositionButton(isSelected));
Jon Miranda16ea1b12017-12-12 14:52:48 -08001084 }
1085
1086 private void setStretchWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1087 int drawableId = isSelected ? R.drawable.stretch_blue : R.drawable.stretch_grey;
1088 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
Tianguang Zhangd3e4f632021-04-14 00:30:15 +02001089 button.setTextColor(getTextColorForWallpaperPositionButton(isSelected));
Jon Miranda16ea1b12017-12-12 14:52:48 -08001090 }
1091
1092 private void showSettingWallpaperProgressDialog() {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -08001093 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which
1094 // therefore causes Espresso to hang once the dialog is shown.
Jon Miranda16ea1b12017-12-12 14:52:48 -08001095 if (!mTestingMode) {
1096 int themeResId;
1097 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
1098 themeResId = R.style.ProgressDialogThemePreL;
1099 } else {
1100 themeResId = R.style.LightDialogTheme;
1101 }
1102 mSetWallpaperProgressDialog = new ProgressDialog(this, themeResId);
1103 mSetWallpaperProgressDialog.setTitle(null);
1104 mSetWallpaperProgressDialog.setMessage(
1105 getResources().getString(R.string.set_wallpaper_progress_message));
1106 mSetWallpaperProgressDialog.setIndeterminate(true);
1107 mSetWallpaperProgressDialog.show();
1108 }
1109 }
1110
1111 private void dismissSettingWallpaperProgressDialog() {
1112 if (mSetWallpaperProgressDialog != null) {
1113 mSetWallpaperProgressDialog.dismiss();
1114 }
1115 }
1116
1117 private void showSetWallpaperErrorDialog() {
1118 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
1119 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
1120
1121 if (isSafeToCommitFragmentTransaction()) {
1122 dialogFragment.show(getSupportFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
1123 } else {
1124 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
1125 }
1126 }
1127
chihhangchuang3efb6832020-04-17 02:06:25 +08001128 @Override
Kunhung Lic6701492021-01-28 17:35:56 +08001129 public void onUpArrowPressed() {
1130 onBackPressed();
1131 }
1132
1133 @Override
1134 public boolean isUpArrowSupported() {
Kunhung Li14648702021-04-15 13:45:10 +08001135 return !ActivityUtils.isSUWMode(getBaseContext());
Kunhung Lic6701492021-01-28 17:35:56 +08001136 }
1137
Jon Miranda16ea1b12017-12-12 14:52:48 -08001138 private interface AssetReceiver {
1139 void onAssetReceived(Asset asset);
1140 }
1141
1142 /**
1143 * An AsyncTask for asynchronously fetching the thumbnail asset for a given WallpaperInfo.
1144 * Used to work around expensive method call to WallpaperManager#getWallpaperFile made from the
1145 * CurrentWallpaperInfoVN getAsset() method.
1146 */
1147 private static class FetchThumbAssetTask extends AsyncTask<Void, Void, Asset> {
1148 private Context mAppContext;
1149 private WallpaperInfo mWallpaperInfo;
1150 private AssetReceiver mReceiver;
1151
1152 public FetchThumbAssetTask(Context appContext, WallpaperInfo wallpaperInfo,
1153 AssetReceiver receiver) {
1154 mAppContext = appContext;
1155 mWallpaperInfo = wallpaperInfo;
1156 mReceiver = receiver;
1157 }
1158
1159 @Override
1160 protected Asset doInBackground(Void... params) {
1161 return mWallpaperInfo.getThumbAsset(mAppContext);
1162 }
1163
1164 @Override
1165 protected void onPostExecute(Asset thumbAsset) {
1166 mReceiver.onAssetReceived(thumbAsset);
1167 }
1168 }
1169}