blob: e2b8ff3a9b200e6839fd9b300288e44030dd51b0 [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;
19import android.app.ProgressDialog;
Jon Miranda16ea1b12017-12-12 14:52:48 -080020import android.content.Context;
21import android.content.Intent;
Jon Miranda16ea1b12017-12-12 14:52:48 -080022import android.graphics.Color;
Santiago Etchebehere35891ea2019-07-26 15:21:42 -070023import android.graphics.Insets;
Jon Miranda16ea1b12017-12-12 14:52:48 -080024import android.graphics.Point;
25import 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;
45import androidx.appcompat.app.AlertDialog;
46import androidx.appcompat.widget.Toolbar;
47import androidx.fragment.app.Fragment;
48import androidx.fragment.app.FragmentManager;
49
Jon Miranda16ea1b12017-12-12 14:52:48 -080050import com.android.wallpaper.R;
51import com.android.wallpaper.asset.Asset;
Santiago Etchebehere35891ea2019-07-26 15:21:42 -070052import com.android.wallpaper.compat.BuildCompat;
Jon Miranda16ea1b12017-12-12 14:52:48 -080053import com.android.wallpaper.compat.ButtonDrawableSetterCompat;
Jon Miranda16ea1b12017-12-12 14:52:48 -080054import com.android.wallpaper.config.Flags;
55import com.android.wallpaper.model.Category;
Jon Miranda16ea1b12017-12-12 14:52:48 -080056import com.android.wallpaper.model.ImageWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080057import com.android.wallpaper.model.WallpaperInfo;
58import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
59import com.android.wallpaper.module.CurrentWallpaperInfoFactory.WallpaperInfoCallback;
60import com.android.wallpaper.module.DailyLoggingAlarmScheduler;
61import com.android.wallpaper.module.ExploreIntentChecker;
62import com.android.wallpaper.module.FormFactorChecker;
Jon Miranda16ea1b12017-12-12 14:52:48 -080063import com.android.wallpaper.module.Injector;
64import com.android.wallpaper.module.InjectorProvider;
65import com.android.wallpaper.module.NetworkStatusNotifier;
66import com.android.wallpaper.module.NetworkStatusNotifier.NetworkStatus;
67import com.android.wallpaper.module.UserEventLogger;
68import com.android.wallpaper.module.UserEventLogger.WallpaperSetFailureReason;
69import com.android.wallpaper.module.WallpaperPersister;
70import com.android.wallpaper.module.WallpaperPersister.Destination;
71import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
72import com.android.wallpaper.module.WallpaperPersister.WallpaperPosition;
73import com.android.wallpaper.module.WallpaperPreferences;
74import com.android.wallpaper.module.WallpaperPreferences.PresentationMode;
75import com.android.wallpaper.module.WallpaperRotationRefresher;
76import com.android.wallpaper.module.WallpaperRotationRefresher.Listener;
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;
80import com.android.wallpaper.util.ScreenSizeCalculator;
81import com.android.wallpaper.util.ThrowableAnalyzer;
82
Sunny Goyal8600a3f2018-08-15 12:48:01 -070083import com.google.android.material.bottomsheet.BottomSheetBehavior;
84import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback;
85import com.google.android.material.tabs.TabLayout;
86import com.google.android.material.tabs.TabLayout.OnTabSelectedListener;
87import com.google.android.material.tabs.TabLayout.Tab;
88
Jon Miranda16ea1b12017-12-12 14:52:48 -080089import java.util.List;
90
91/**
92 * Activity allowing users to select a category of wallpapers to choose from.
93 */
94public class TopLevelPickerActivity extends BaseActivity implements WallpapersUiContainer,
95 CurrentWallpaperBottomSheetPresenter, SetWallpaperErrorDialogFragment.Listener,
Santiago Etchebeherefab49612019-01-15 12:22:42 -080096 MyPhotosStarter, CategoryFragmentHost {
Jon Miranda16ea1b12017-12-12 14:52:48 -080097
98 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
99 "toplevel_set_wallpaper_error_dialog";
100
101 private static final String TAG = "TopLevelPicker";
102 private static final String KEY_SELECTED_CATEGORY_TAB = "selected_category_tab";
103
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800104 private WallpaperPickerDelegate mDelegate;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800105 private int mLastSelectedCategoryTabIndex;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800106 private UserEventLogger mUserEventLogger;
107 private NetworkStatusNotifier mNetworkStatusNotifier;
108 private NetworkStatusNotifier.Listener mNetworkStatusListener;
109 private WallpaperPersister mWallpaperPersister;
110 private boolean mWasCustomPhotoWallpaperSet;
111 @WallpaperPosition
112 private int mCustomPhotoWallpaperPosition;
113
114 /**
115 * Progress dialogs for "refresh daily wallpaper" and "set wallpaper" operations.
116 */
117 private ProgressDialog mRefreshWallpaperProgressDialog;
118 private ProgressDialog mSetWallpaperProgressDialog;
119
120 /**
121 * Designates a test mode of operation -- in which certain UI features are disabled to allow for
122 * UI tests to run correctly.
123 */
124 private boolean mTestingMode;
125
126 /**
127 * UI for the "currently set wallpaper" BottomSheet.
128 */
129 private LinearLayout mBottomSheet;
130 private ImageView mCurrentWallpaperImage;
131 private TextView mCurrentWallpaperPresentationMode;
132 private TextView mCurrentWallpaperTitle;
133 private TextView mCurrentWallpaperSubtitle;
134 private Button mCurrentWallpaperExploreButton;
135 private Button mCurrentWallpaperSkipWallpaperButton;
136 private FrameLayout mFragmentContainer;
137 private FrameLayout mLoadingIndicatorContainer;
138 private LinearLayout mWallpaperPositionOptions;
139
Jon Miranda16ea1b12017-12-12 14:52:48 -0800140 /**
141 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
142 * committing fragment transactions.
143 */
144 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
145
146 /**
147 * A wallpaper pending set to the device--we retain a reference to this in order to facilitate
148 * retry or re-crop operations.
149 */
150 private WallpaperInfo mPendingSetWallpaperInfo;
151
152 private static int getTextColorIdForWallpaperPositionButton(boolean isSelected) {
153 return isSelected ? R.color.accent_color : R.color.material_grey500;
154 }
155
156 @Override
157 protected void onCreate(Bundle savedInstanceState) {
158 super.onCreate(savedInstanceState);
159
Jon Miranda16ea1b12017-12-12 14:52:48 -0800160 mLastSelectedCategoryTabIndex = -1;
161
162 Injector injector = InjectorProvider.getInjector();
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800163 mDelegate = new WallpaperPickerDelegate(this, this, injector);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800164 mUserEventLogger = injector.getUserEventLogger(this);
165 mNetworkStatusNotifier = injector.getNetworkStatusNotifier(this);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800166 mWallpaperPersister = injector.getWallpaperPersister(this);
167 mWasCustomPhotoWallpaperSet = false;
168
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800169 @WallpaperSupportLevel int wallpaperSupportLevel = mDelegate.getWallpaperSupportLevel();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800170 if (wallpaperSupportLevel != WallpaperDisabledFragment.SUPPORTED_CAN_SET) {
171 setContentView(R.layout.activity_single_fragment);
172
173 FragmentManager fm = getSupportFragmentManager();
174 WallpaperDisabledFragment wallpaperDisabledFragment =
175 WallpaperDisabledFragment.newInstance(wallpaperSupportLevel);
176 fm.beginTransaction()
177 .add(R.id.fragment_container, wallpaperDisabledFragment)
178 .commit();
179 return;
180 }
181
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800182 if (mDelegate.getFormFactor() == FormFactorChecker.FORM_FACTOR_MOBILE) {
Ching-Sung Li5f689972019-05-20 17:16:57 +0800183 initializeMobile(true /* shouldForceRefresh */);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800184 } else { // DESKTOP
185 initializeDesktop(savedInstanceState);
186 }
187 }
188
189 @Override
190 protected void onResume() {
191 super.onResume();
Hyunyoung Song8077c862019-06-18 01:14:24 -0400192 boolean provisioned = Settings.Global.getInt(getContentResolver(),
193 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
194
195 mUserEventLogger.logResumed(provisioned, true);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800196 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
197 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
198 // committing fragment transactions.
199 if (mStagedSetWallpaperErrorDialogFragment != null) {
200 mStagedSetWallpaperErrorDialogFragment.show(
201 getSupportFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
202 mStagedSetWallpaperErrorDialogFragment = null;
203 }
204 }
205
206 @Override
Santiago Etchebeherebd536c62019-04-09 15:25:51 -0300207 protected void onStop() {
208 mUserEventLogger.logStopped();
209 super.onStop();
210 }
211
212 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800213 protected void onDestroy() {
214 super.onDestroy();
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800215 mDelegate.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800216 if (mNetworkStatusListener != null) {
217 mNetworkStatusNotifier.unregisterListener(mNetworkStatusListener);
218 }
219
220 if (mRefreshWallpaperProgressDialog != null) {
221 mRefreshWallpaperProgressDialog.dismiss();
222 }
223 if (mSetWallpaperProgressDialog != null) {
224 mSetWallpaperProgressDialog.dismiss();
225 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800226 }
227
228 @Override
229 public void requestCustomPhotoPicker(PermissionChangedListener listener) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800230 mDelegate.requestCustomPhotoPicker(listener);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800231 }
232
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800233 @Override
234 public void requestExternalStoragePermission(PermissionChangedListener listener) {
235 mDelegate.requestExternalStoragePermission(listener);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800236 }
237
238 /**
239 * Returns whether READ_EXTERNAL_STORAGE has been granted for the application.
240 */
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800241 public boolean isReadExternalStoragePermissionGranted() {
242 return mDelegate.isReadExternalStoragePermissionGranted();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800243 }
244
Ching-Sung Li5f689972019-05-20 17:16:57 +0800245 private void initializeMobile(boolean shouldForceRefresh) {
Santiago Etchebehere008368d2018-12-13 15:23:17 -0800246 setContentView(R.layout.activity_single_fragment);
Santiago Etchebehere35891ea2019-07-26 15:21:42 -0700247 getWindow().getDecorView().setSystemUiVisibility(
248 getWindow().getDecorView().getSystemUiVisibility()
249 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
250 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
251 findViewById(R.id.fragment_container)
252 .setOnApplyWindowInsetsListener((view, windowInsets) -> {
253 view.setPadding(view.getPaddingLeft(), windowInsets.getSystemWindowInsetTop(),
Chuck Liaoe9d79812020-02-20 18:03:55 +0800254 view.getPaddingRight(), view.getPaddingBottom());
Santiago Etchebehere35891ea2019-07-26 15:21:42 -0700255 // Consume only the top inset (status bar), to let other content in the Activity consume
256 // the nav bar (ie, by using "fitSystemWindows")
257 if (BuildCompat.isAtLeastQ()) {
258 WindowInsets.Builder builder = new WindowInsets.Builder(windowInsets);
259 builder.setSystemWindowInsets(Insets.of(windowInsets.getSystemWindowInsetLeft(),
260 0, windowInsets.getStableInsetRight(),
261 windowInsets.getSystemWindowInsetBottom()));
262 return builder.build();
263 } else {
264 return windowInsets.replaceSystemWindowInsets(
265 windowInsets.getSystemWindowInsetLeft(),
266 0, windowInsets.getStableInsetRight(),
267 windowInsets.getSystemWindowInsetBottom());
268 }
269 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800270
271 // Set toolbar as the action bar.
272 Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
273 setSupportActionBar(toolbar);
274
275 FragmentManager fm = getSupportFragmentManager();
276 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
277
Jon Miranda16ea1b12017-12-12 14:52:48 -0800278 if (fragment == null) {
279 // App launch specific logic: log the "app launched" event and set up daily logging.
280 mUserEventLogger.logAppLaunched();
281 DailyLoggingAlarmScheduler.setAlarm(getApplicationContext());
282
Santiago Etchebehereaa35e6e2019-01-25 10:30:11 -0800283 CategoryFragment newFragment = CategoryFragment.newInstance(
284 getString(R.string.wallpaper_app_name));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800285 fm.beginTransaction()
286 .add(R.id.fragment_container, newFragment)
287 .commit();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800288 }
289
Ching-Sung Li5f689972019-05-20 17:16:57 +0800290 mDelegate.initialize(shouldForceRefresh);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800291 }
292
293 private void initializeDesktop(Bundle savedInstanceState) {
294 setContentView(R.layout.activity_top_level_desktop);
295
296 mBottomSheet = (LinearLayout) findViewById(R.id.bottom_sheet);
297 mCurrentWallpaperImage = (ImageView) mBottomSheet.findViewById(R.id.current_wallpaper_image);
298 mCurrentWallpaperImage.getLayoutParams().width = getSingleWallpaperImageWidthPx();
299
300 mCurrentWallpaperPresentationMode =
301 (TextView) mBottomSheet.findViewById(R.id.current_wallpaper_presentation_mode);
302 mCurrentWallpaperTitle = (TextView) findViewById(R.id.current_wallpaper_title);
303 mCurrentWallpaperSubtitle = (TextView) findViewById(R.id.current_wallpaper_subtitle);
304 mCurrentWallpaperExploreButton = (Button) findViewById(
305 R.id.current_wallpaper_explore_button);
306 mCurrentWallpaperSkipWallpaperButton = (Button) findViewById(
307 R.id.current_wallpaper_skip_wallpaper_button);
308 mFragmentContainer = (FrameLayout) findViewById(R.id.fragment_container);
309 mLoadingIndicatorContainer = (FrameLayout) findViewById(R.id.loading_indicator_container);
310 mWallpaperPositionOptions = (LinearLayout) findViewById(
311 R.id.desktop_wallpaper_position_options);
312
313 final TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
314 tabLayout.addOnTabSelectedListener(new OnTabSelectedListener() {
315 @Override
316 public void onTabSelected(Tab tab) {
317 Category category = (Category) tab.getTag();
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800318 showCategoryDesktop(category.getCollectionId());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800319 mLastSelectedCategoryTabIndex = tabLayout.getSelectedTabPosition();
320 }
321
322 @Override
323 public void onTabUnselected(Tab tab) {
324 }
325
326 @Override
327 public void onTabReselected(Tab tab) {
328 Category category = (Category) tab.getTag();
329 // If offline, "My photos" may be the only visible category. In this case we want to allow
330 // re-selection so user can still select a photo as wallpaper while offline.
331 if (!category.isEnumerable()) {
332 onTabSelected(tab);
333 }
334 }
335 });
336
337 FragmentManager fm = getSupportFragmentManager();
338 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
339
340 if (fragment == null) {
341 // App launch specific logic: log the "app launched" event and set up daily logging.
342 mUserEventLogger.logAppLaunched();
343 DailyLoggingAlarmScheduler.setAlarm(getApplicationContext());
344 }
345
346 mNetworkStatusListener = new NetworkStatusNotifier.Listener() {
347 @Override
348 public void onNetworkChanged(@NetworkStatus int networkStatus) {
349 initializeDesktopBasedOnNetwork(networkStatus, savedInstanceState);
350 }
351 };
352 // Upon registering a listener, the onNetworkChanged method is immediately called with the
353 // initial network status.
354 mNetworkStatusNotifier.registerListener(mNetworkStatusListener);
355 }
356
357 private void initializeDesktopBasedOnNetwork(@NetworkStatus int networkStatus,
358 Bundle savedInstanceState) {
359 if (networkStatus == NetworkStatusNotifier.NETWORK_CONNECTED) {
360 initializeDesktopOnline(savedInstanceState);
361 } else {
362 initializeDesktopOffline();
363 }
364 }
365
366 private void initializeDesktopOnline(Bundle savedInstanceState) {
367 FragmentManager fm = getSupportFragmentManager();
368 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
369
370 // Require a category refresh if this is the first load of the app or if the app is now
371 // returning online after having been offline.
372 boolean forceCategoryRefresh = fragment == null || fragment instanceof OfflineDesktopFragment;
373
374 if (fragment != null) {
375 fm.beginTransaction()
376 .remove(fragment)
377 .commit();
378 }
379
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800380 mLastSelectedCategoryTabIndex = savedInstanceState != null
Jon Miranda16ea1b12017-12-12 14:52:48 -0800381 ? savedInstanceState.getInt(KEY_SELECTED_CATEGORY_TAB) : -1;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800382 mDelegate.populateCategories(forceCategoryRefresh);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800383
384 setDesktopLoading(true);
385 setUpBottomSheet();
386 refreshCurrentWallpapers(null /* refreshListener */);
387 }
388
389 private void initializeDesktopOffline() {
390 FragmentManager fm = getSupportFragmentManager();
391 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
392
393 if (fragment != null) {
394 fm.beginTransaction()
395 .remove(fragment)
396 .commit();
397 }
398 OfflineDesktopFragment newFragment = new OfflineDesktopFragment();
399 fm.beginTransaction()
400 .add(R.id.fragment_container, newFragment)
401 .commit();
402
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800403 // Reset the last selected category tab index to ensure the app doesn't try to reselect a
404 // tab for a category not yet repopulated.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800405 mLastSelectedCategoryTabIndex = -1;
406
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800407 mDelegate.populateCategories(true /* forceCategoryRefresh */);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800408
409 setDesktopLoading(false);
410 setCurrentWallpapersExpanded(false);
411 }
412
413 /**
414 * Sets the status of the loading indicator overlay in desktop mode.
415 *
416 * @param loading Whether an indeterminate loading indicator is displayed in place of the main
417 * fragment.
418 */
419 private void setDesktopLoading(boolean loading) {
420 if (loading) {
421 mLoadingIndicatorContainer.setVisibility(View.VISIBLE);
422 mFragmentContainer.setVisibility(View.GONE);
423 } else {
424 mLoadingIndicatorContainer.setVisibility(View.GONE);
425 mFragmentContainer.setVisibility(View.VISIBLE);
426 }
427 }
428
429 /**
430 * Returns the width (in physical px) to use for the "currently set wallpaper" thumbnail.
431 */
432 private int getSingleWallpaperImageWidthPx() {
433 Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(
434 getWindowManager().getDefaultDisplay());
435
436 int height = getResources().getDimensionPixelSize(
437 R.dimen.current_wallpaper_bottom_sheet_thumb_height);
438 return height * screenSize.x / screenSize.y;
439 }
440
441 /**
442 * Enables and populates the "Currently set" wallpaper BottomSheet.
443 */
444 private void setUpBottomSheet() {
445 mBottomSheet.setVisibility(View.VISIBLE);
446
Jon Miranda16ea1b12017-12-12 14:52:48 -0800447 if (Flags.skipDailyWallpaperButtonEnabled) {
448 // Add "next" icon to the Next Wallpaper button
449 Drawable nextWallpaperButtonDrawable = getResources().getDrawable(
Santiago Etchebehered24506c2018-04-05 17:02:42 -0700450 R.drawable.ic_refresh_18px);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800451
452 // This Drawable's state is shared across the app, so make a copy of it before applying a
453 // color tint as not to affect other clients elsewhere in the app.
454 nextWallpaperButtonDrawable =
455 nextWallpaperButtonDrawable.getConstantState().newDrawable().mutate();
456 // Color the "compass" icon with the accent color.
457 nextWallpaperButtonDrawable.setColorFilter(
458 getResources().getColor(R.color.accent_color), Mode.SRC_IN);
459 ButtonDrawableSetterCompat.setDrawableToButtonStart(
460 mCurrentWallpaperSkipWallpaperButton, nextWallpaperButtonDrawable);
461 }
462
463 final BottomSheetBehavior<LinearLayout> bottomSheetBehavior =
464 BottomSheetBehavior.from(mBottomSheet);
465 bottomSheetBehavior.setBottomSheetCallback(new BottomSheetCallback() {
466 @Override
467 public void onStateChanged(@NonNull View view, int i) {
468 }
469
470 @Override
471 public void onSlide(@NonNull View view, float slideOffset) {
472 float alpha;
473 if (slideOffset >= 0) {
474 alpha = slideOffset;
475 } else {
476 alpha = 1f - slideOffset;
477 }
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800478 LinearLayout bottomSheetContents = findViewById(R.id.bottom_sheet_contents);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800479 bottomSheetContents.setAlpha(alpha);
480 }
481 });
482 }
483
484 /**
485 * Enables a test mode of operation -- in which certain UI features are disabled to allow for
486 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
487 * constantly keeps the UI thread alive and blocks a test forever.
488 */
489 void setTestingMode(boolean testingMode) {
490 mTestingMode = testingMode;
491 }
492
493 /**
494 * Obtains the {@link WallpaperInfo} object(s) representing the wallpaper(s) currently set to the
495 * device from the {@link CurrentWallpaperInfoFactory} and displays them in the BottomSheet.
496 */
497 @Override
498 public void refreshCurrentWallpapers(@Nullable RefreshListener refreshListener) {
499 final Injector injector = InjectorProvider.getInjector();
500 final Context appContext = getApplicationContext();
501
502 CurrentWallpaperInfoFactory factory = injector.getCurrentWallpaperFactory(this);
503 factory.createCurrentWallpaperInfos(new WallpaperInfoCallback() {
504 @Override
505 public void onWallpaperInfoCreated(
506 final WallpaperInfo homeWallpaper,
507 @Nullable final WallpaperInfo lockWallpaper,
508 @PresentationMode final int presentationMode) {
509
510 if (isDestroyed()) {
511 return;
512 }
513
514 // Fetch the home wallpaper's thumbnail asset asynchronously to work around expensive
515 // method call to WallpaperManager#getWallpaperFile made from the CurrentWallpaperInfoVN
516 // getAsset() method.
517 AssetReceiver assetReceiver = (Asset thumbAsset) -> {
518 if (isDestroyed()) {
519 return;
520 }
521
522 homeWallpaper.getThumbAsset(appContext).loadDrawableWithTransition(
523 TopLevelPickerActivity.this,
524 mCurrentWallpaperImage,
525 200 /* transitionDurationMillis */,
526 () -> {
527 if (refreshListener != null) {
528 refreshListener.onCurrentWallpaperRefreshed();
529 }
530 },
531 Color.TRANSPARENT);
532 };
533 new FetchThumbAssetTask(appContext, homeWallpaper, assetReceiver).executeOnExecutor(
534 AsyncTask.THREAD_POOL_EXECUTOR);
535
536 mCurrentWallpaperPresentationMode.setText(
537 AttributionFormatter.getHumanReadableWallpaperPresentationMode(
538 TopLevelPickerActivity.this, presentationMode));
539
540 List<String> attributions = homeWallpaper.getAttributions(appContext);
541 if (attributions.size() > 0 && attributions.get(0) != null) {
542 mCurrentWallpaperTitle.setText(attributions.get(0));
543 }
544
545 mCurrentWallpaperSubtitle.setText(
546 AttributionFormatter.formatWallpaperSubtitle(appContext, homeWallpaper));
547
548 final String actionUrl = homeWallpaper.getActionUrl(appContext);
549 if (actionUrl != null && !actionUrl.isEmpty()) {
550 Uri exploreUri = Uri.parse(actionUrl);
551
552 ExploreIntentChecker intentChecker = injector.getExploreIntentChecker(appContext);
553 intentChecker.fetchValidActionViewIntent(exploreUri, (@Nullable Intent exploreIntent) -> {
554 if (exploreIntent != null && !isDestroyed()) {
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700555 // Set the icon for the button
556 Drawable exploreButtonDrawable = getResources().getDrawable(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700557 homeWallpaper.getActionIconRes(appContext));
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700558
559 // This Drawable's state is shared across the app, so make a copy of it
560 // before applying a color tint as not to affect other clients elsewhere
561 // in the app.
562 exploreButtonDrawable = exploreButtonDrawable.getConstantState()
563 .newDrawable().mutate();
564 // Color the "compass" icon with the accent color.
565 exploreButtonDrawable.setColorFilter(
566 getResources().getColor(R.color.accent_color), Mode.SRC_IN);
567
568 ButtonDrawableSetterCompat.setDrawableToButtonStart(
569 mCurrentWallpaperExploreButton, exploreButtonDrawable);
570 mCurrentWallpaperExploreButton.setText(getString(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700571 homeWallpaper.getActionLabelRes(appContext)));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800572 mCurrentWallpaperExploreButton.setVisibility(View.VISIBLE);
573 mCurrentWallpaperExploreButton.setOnClickListener(new OnClickListener() {
574 @Override
575 public void onClick(View v) {
Santiago Etchebeherece5613f2018-06-01 13:22:47 -0700576 mUserEventLogger.logActionClicked(
577 homeWallpaper.getCollectionId(appContext),
578 homeWallpaper.getActionLabelRes(appContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800579 startActivity(exploreIntent);
580 }
581 });
582 }
583 });
584 } else {
585 mCurrentWallpaperExploreButton.setVisibility(View.GONE);
586 }
587
588 // Hide the wallpaper position options UI if the current home wallpaper is not from
589 // "my photos".
590 String homeCollectionId = homeWallpaper.getCollectionId(TopLevelPickerActivity.this);
591 if (mWallpaperPositionOptions != null
592 && homeCollectionId != null // May be null if app is being used for the first time.
593 && !homeCollectionId.equals(getString(R.string.image_wallpaper_collection_id))) {
594 mWallpaperPositionOptions.setVisibility(View.GONE);
595 }
596
597 boolean showSkipWallpaperButton = Flags.skipDailyWallpaperButtonEnabled
598 && presentationMode == WallpaperPreferences.PRESENTATION_MODE_ROTATING;
599 if (showSkipWallpaperButton) {
600 mCurrentWallpaperSkipWallpaperButton.setVisibility(View.VISIBLE);
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800601 mCurrentWallpaperSkipWallpaperButton.setOnClickListener(
602 v -> refreshDailyWallpaper());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800603 } else {
604 mCurrentWallpaperSkipWallpaperButton.setVisibility(View.GONE);
605 }
606
607 if (refreshListener != null) {
608 refreshListener.onCurrentWallpaperRefreshed();
609 }
610 }
611 }, true /* forceRefresh */);
612 }
613
614 @Override
615 public void onSaveInstanceState(Bundle savedInstanceState) {
616 FormFactorChecker formFactorChecker = InjectorProvider.getInjector().getFormFactorChecker(this);
617 if (formFactorChecker.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP) {
618 TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
619
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800620 // tabLayout is only present when the main IndividualPickerFragment is present (as
621 // opposed to
Jon Miranda16ea1b12017-12-12 14:52:48 -0800622 // the WallpaperDisabledFragment), so need this null check.
623 if (tabLayout != null) {
624 savedInstanceState.putInt(KEY_SELECTED_CATEGORY_TAB, tabLayout.getSelectedTabPosition());
625 }
626 }
627
628 super.onSaveInstanceState(savedInstanceState);
629 }
630
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800631 @Override
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700632 @Nullable
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800633 public CategoryFragment getCategoryFragment() {
634 if (mDelegate.getFormFactor() != FormFactorChecker.FORM_FACTOR_MOBILE) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700635 return null;
636 }
637 FragmentManager fm = getSupportFragmentManager();
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800638 return (CategoryFragment) fm.findFragmentById(R.id.fragment_container);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700639 }
640
Jon Miranda16ea1b12017-12-12 14:52:48 -0800641 /**
642 * Populates the category tabs on DESKTOP form factor.
643 *
644 * @param selectedTabPosition The position of the tab to show as selected, or -1 if no particular
645 * tab should be selected (in which case: the tab of the category for the currently set
646 * wallpaper will be selected if enumerable; if not, the first enumerable category's tab will
647 * be selected).
648 */
649 private void populateCategoryTabs(int selectedTabPosition) {
650 final TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
651 tabLayout.removeAllTabs();
652
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800653 String currentlySetCollectionId = mDelegate.getPreferences().getHomeWallpaperCollectionId();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800654
655 Tab tabToSelect = null;
656 Tab firstEnumerableCategoryTab = null;
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800657 for (int i = 0; i < mDelegate.getCategoryProvider().getSize(); i++) {
658 Category category = mDelegate.getCategoryProvider().getCategory(i);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800659
660 Tab tab = tabLayout.newTab();
661 tab.setText(category.getTitle());
662 tab.setTag(category);
663 tabLayout.addTab(tab, false /* setSelected */);
664
665 if (firstEnumerableCategoryTab == null && category.isEnumerable()) {
666 firstEnumerableCategoryTab = tab;
667 }
668
669 boolean shouldSelectTab = (i == selectedTabPosition)
670 || (selectedTabPosition == -1
671 && tabToSelect == null
672 && category.isEnumerable()
673 && currentlySetCollectionId != null
674 && currentlySetCollectionId.equals(category.getCollectionId()));
675
676 if (shouldSelectTab) {
677 tabToSelect = tab;
678 }
679 }
680
681 // If the above loop did not identify a specific tab to select, then just select the tab for
682 // the first enumerable category.
683 if (tabToSelect == null) {
684 tabToSelect = firstEnumerableCategoryTab;
685 }
686
687 // There may be no enumerable tabs (e.g., offline case), so we need to null-check again.
688 if (tabToSelect != null) {
689 tabToSelect.select();
690 }
691 }
692
693 /**
694 * Refreshes the current wallpaper in a daily wallpaper rotation.
695 */
696 private void refreshDailyWallpaper() {
697 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
698 // causes Espresso to hang once the dialog is shown.
699 if (!mTestingMode) {
700 int themeResId;
701 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
702 themeResId = R.style.ProgressDialogThemePreL;
703 } else {
704 themeResId = R.style.LightDialogTheme;
705 }
706 mRefreshWallpaperProgressDialog = new ProgressDialog(this, themeResId);
707 mRefreshWallpaperProgressDialog.setTitle(null);
708 mRefreshWallpaperProgressDialog.setMessage(
709 getResources().getString(R.string.refreshing_daily_wallpaper_dialog_message));
710 mRefreshWallpaperProgressDialog.setIndeterminate(true);
711 mRefreshWallpaperProgressDialog.show();
712 }
713
714 WallpaperRotationRefresher wallpaperRotationRefresher =
715 InjectorProvider.getInjector().getWallpaperRotationRefresher();
716 wallpaperRotationRefresher.refreshWallpaper(this, new Listener() {
717 @Override
718 public void onRefreshed() {
719 if (isDestroyed()) {
720 return;
721 }
722
723 if (mRefreshWallpaperProgressDialog != null) {
724 mRefreshWallpaperProgressDialog.dismiss();
725 }
726
727 refreshCurrentWallpapers(null /* refreshListener */);
728 }
729
730 @Override
731 public void onError() {
732 if (mRefreshWallpaperProgressDialog != null) {
733 mRefreshWallpaperProgressDialog.dismiss();
734 }
735
736 AlertDialog errorDialog = new AlertDialog.Builder(
737 TopLevelPickerActivity.this, R.style.LightDialogTheme)
738 .setMessage(R.string.refresh_daily_wallpaper_failed_message)
739 .setPositiveButton(android.R.string.ok, null /* onClickListener */)
740 .create();
741 errorDialog.show();
742 }
743 });
744 }
745
746 @Override
747 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
748 super.onActivityResult(requestCode, resultCode, data);
749
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800750 if (requestCode == WallpaperPickerDelegate.SHOW_CATEGORY_REQUEST_CODE
751 && resultCode == Activity.RESULT_OK) {
Santiago Etchebeheree5e05842019-02-13 16:58:07 -0800752 if (mDelegate.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP) {
753 Uri imageUri = (data == null) ? null : data.getData();
754 if (imageUri != null) {
755 // User selected an image from the system picker, so launch the preview for that
756 // image.
757 ImageWallpaperInfo imageWallpaper = new ImageWallpaperInfo(imageUri);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800758 setCustomPhotoWallpaper(imageWallpaper);
759 return;
760 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800761 }
Santiago Etchebeheree5e05842019-02-13 16:58:07 -0800762 }
763 if (mDelegate.handleActivityResult(requestCode, resultCode, data)) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800764 finishActivityWithResultOk();
765 }
766 }
767
768 /**
769 * Shows the view-only preview activity for the given wallpaper.
770 */
771 public void showViewOnlyPreview(WallpaperInfo wallpaperInfo) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800772 mDelegate.showViewOnlyPreview(wallpaperInfo);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800773 }
774
775 @Override
776 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
777 @NonNull int[] grantResults) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800778 mDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800779 }
780
781 /**
782 * Shows the picker activity for the given category.
783 */
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800784 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800785 public void show(String collectionId) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800786 mDelegate.show(collectionId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800787 }
788
789 private void reselectLastTab() {
790 TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
791
792 // In the offline case, "My photos" could be the only category. Thus we need this check --
793 // to ensure that we don't try to select the "previously selected" category which was -1.
794 if (mLastSelectedCategoryTabIndex > -1) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800795 Tab tabToSelect = tabLayout.getTabAt(
796 mLastSelectedCategoryTabIndex);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800797 if (((Category) tabToSelect.getTag()).isEnumerable()) {
798 tabToSelect.select();
799 }
800 }
801 }
802
Jon Miranda16ea1b12017-12-12 14:52:48 -0800803 private void showCategoryDesktop(String collectionId) {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800804 Category category = mDelegate.findCategoryForCollectionId(collectionId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800805 if (category == null) {
806 return;
807 }
808
809 if (category.isEnumerable()) {
810 // Replace contained IndividualPickerFragment with a new instance for the given category.
811 final FragmentManager fm = getSupportFragmentManager();
812 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
813 if (fragment != null) {
814 fm.beginTransaction()
815 .remove(fragment)
816 .commit();
817 }
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800818 Injector injector = InjectorProvider.getInjector();
819 IndividualPickerFragment newFragment = injector.getIndividualPickerFragment(
820 collectionId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800821 fm.beginTransaction()
822 .add(R.id.fragment_container, newFragment)
823 .commit();
824 newFragment.setCurrentWallpaperBottomSheetPresenter(this);
825 newFragment.setWallpapersUiContainer(this);
826 } else {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800827 category.show(this, mDelegate.getPickerIntentFactory(),
828 WallpaperPickerDelegate.SHOW_CATEGORY_REQUEST_CODE);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800829
830 // Need to select the tab here in case we are coming back from a "My photos" in which case
831 // the tab would have been set to "My photos" while viewing a regular image category.
832 reselectLastTab();
833 }
834 }
835
836 private void finishActivityWithResultOk() {
837 overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
838 setResult(Activity.RESULT_OK);
839 finish();
840 }
841
Jon Miranda16ea1b12017-12-12 14:52:48 -0800842 @Override
843 public void setCurrentWallpapersExpanded(boolean expanded) {
844 final BottomSheetBehavior<LinearLayout> bottomSheetBehavior =
845 BottomSheetBehavior.from(mBottomSheet);
846 bottomSheetBehavior.setState(
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800847 expanded ? BottomSheetBehavior.STATE_EXPANDED
848 : BottomSheetBehavior.STATE_COLLAPSED);
849 }
850
851 @Override
852 public void doneFetchingCategories() {
853 populateCategoryTabs(mLastSelectedCategoryTabIndex);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800854 }
855
856 @Override
857 public void onWallpapersReady() {
858 setDesktopLoading(false);
859 setCurrentWallpapersExpanded(true);
860 }
861
862 @Override
Santiago Etchebeherefab49612019-01-15 12:22:42 -0800863 public MyPhotosStarter getMyPhotosStarter() {
864 return this;
865 }
866
867 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800868 public void onClickTryAgain(@Destination int unused) {
869 // Retry the set wallpaper operation with the default center-crop setting.
870 if (mPendingSetWallpaperInfo != null) {
871 setCustomPhotoWallpaper(mPendingSetWallpaperInfo);
872 }
873 }
874
875 /**
876 * Sets the provides wallpaper to the device with center-cropped and scaled to fit the device's
877 * default display.
878 */
879 private void setCustomPhotoWallpaper(final WallpaperInfo wallpaper) {
880 // Save this WallpaperInfo so we can retry this operation later if it fails.
881 mPendingSetWallpaperInfo = wallpaper;
882
883 showSettingWallpaperProgressDialog();
884
885 mWallpaperPersister.setIndividualWallpaperWithPosition(this, wallpaper,
886 WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP, new SetWallpaperCallback() {
887 @Override
888 public void onSuccess() {
889 dismissSettingWallpaperProgressDialog();
890 refreshCurrentWallpapers(null /* refreshListener */);
891
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800892 mDelegate.getPreferences().setPendingWallpaperSetStatus(
Jon Miranda16ea1b12017-12-12 14:52:48 -0800893 WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
894 mUserEventLogger.logWallpaperSet(
895 wallpaper.getCollectionId(getApplicationContext()),
896 wallpaper.getWallpaperId());
897 mUserEventLogger.logWallpaperSetResult(UserEventLogger.WALLPAPER_SET_RESULT_SUCCESS);
898
899 // The user may have closed the activity before the set wallpaper operation completed.
900 if (isDestroyed()) {
901 return;
902 }
903
904 // Show the wallpaper crop option selector and bind click event handlers.
905 mWallpaperPositionOptions.setVisibility(View.VISIBLE);
906
907 mWasCustomPhotoWallpaperSet = true;
908 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP;
909
910 initializeWallpaperPositionOptionClickHandlers(wallpaper);
911 }
912
913 @Override
914 public void onError(Throwable throwable) {
915 dismissSettingWallpaperProgressDialog();
916 showSetWallpaperErrorDialog();
917
Santiago Etchebehere635e96f2018-12-04 18:31:34 -0800918 mDelegate.getPreferences().setPendingWallpaperSetStatus(
Jon Miranda16ea1b12017-12-12 14:52:48 -0800919 WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
920 mUserEventLogger.logWallpaperSetResult(
921 UserEventLogger.WALLPAPER_SET_RESULT_FAILURE);
922 @WallpaperSetFailureReason int failureReason = ThrowableAnalyzer.isOOM(throwable)
923 ? UserEventLogger.WALLPAPER_SET_FAILURE_REASON_OOM
924 : UserEventLogger.WALLPAPER_SET_FAILURE_REASON_OTHER;
925 mUserEventLogger.logWallpaperSetFailureReason(failureReason);
926 Log.e(TAG, "Unable to set wallpaper from 'my photos'.");
927 }
928 });
929 }
930
931 /**
932 * Initializes the wallpaper position button click handlers to change the way the provided
933 * wallpaper is set to the device.
934 */
935 private void initializeWallpaperPositionOptionClickHandlers(final WallpaperInfo wallpaperInfo) {
936 Button centerCropOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_center_crop);
937 Button stretchOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_stretched);
938 Button centerOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_center);
939
940 // The "center crop" wallpaper position button is selected by default.
941 setCenterCropWallpaperPositionButtonSelected(centerCropOptionBtn, true /* isSelected */);
942 centerCropOptionBtn.setOnClickListener(new OnClickListener() {
943 @Override
944 public void onClick(View view) {
945 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
946 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP,
947 new SetWallpaperCallback() {
948 @Override
949 public void onSuccess() {
950 // The user may have closed the activity before the set wallpaper operation
951 // completed.
952 if (isDestroyed()) {
953 return;
954 }
955
956 refreshCurrentWallpapers(null /* refreshListener */);
957
958 setCenterCropWallpaperPositionButtonSelected(
959 centerCropOptionBtn, true /* isSelected */);
960 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
961 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
962
963 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP;
964 }
965
966 @Override
967 public void onError(@Nullable Throwable throwable) {
968 // no-op
969 }
970 });
971 }
972 });
973
974 // "Stretch" is not selected by default.
975 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
976 stretchOptionBtn.setOnClickListener(new OnClickListener() {
977 @Override
978 public void onClick(View view) {
979 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
980 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_STRETCH,
981 new SetWallpaperCallback() {
982 @Override
983 public void onSuccess() {
984 // The user may have closed the activity before the set wallpaper operation
985 // completed.
986 if (isDestroyed()) {
987 return;
988 }
989
990 refreshCurrentWallpapers(null /* refreshListener */);
991
992 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, true /* isSelected */);
993 setCenterCropWallpaperPositionButtonSelected(
994 centerCropOptionBtn, false /* isSelected */);
995 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
996
997 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_STRETCH;
998 }
999
1000 @Override
1001 public void onError(@Nullable Throwable throwable) {
1002 // no-op
1003 }
1004 });
1005 }
1006 });
1007
1008 // "Center" is not selected by default.
1009 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
1010 centerOptionBtn.setOnClickListener(new OnClickListener() {
1011 @Override
1012 public void onClick(View view) {
1013 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
1014 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_CENTER,
1015 new SetWallpaperCallback() {
1016 @Override
1017 public void onSuccess() {
1018 // The user may have closed the activity before the set wallpaper operation
1019 // completed.
1020 if (isDestroyed()) {
1021 return;
1022 }
1023
1024 refreshCurrentWallpapers(null /* refreshListener */);
1025
1026 setCenterWallpaperPositionButtonSelected(centerOptionBtn, true /* isSelected */);
1027 setCenterCropWallpaperPositionButtonSelected(
1028 centerCropOptionBtn, false /* isSelected */);
1029 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
1030
1031 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER;
1032 }
1033
1034 @Override
1035 public void onError(@Nullable Throwable throwable) {
1036 // no-op
1037 }
1038 });
1039 }
1040 });
1041 }
1042
1043 private void setCenterWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1044 int drawableId = isSelected ? R.drawable.center_blue : R.drawable.center_grey;
1045 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
1046 button.setTextColor(getColor(getTextColorIdForWallpaperPositionButton(isSelected)));
1047 }
1048
1049 private void setCenterCropWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1050 int drawableId = isSelected ? R.drawable.center_crop_blue : R.drawable.center_crop_grey;
1051 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
1052 button.setTextColor(getColor(getTextColorIdForWallpaperPositionButton(isSelected)));
1053 }
1054
1055 private void setStretchWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1056 int drawableId = isSelected ? R.drawable.stretch_blue : R.drawable.stretch_grey;
1057 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
1058 button.setTextColor(getColor(getTextColorIdForWallpaperPositionButton(isSelected)));
1059 }
1060
1061 private void showSettingWallpaperProgressDialog() {
Santiago Etchebehere635e96f2018-12-04 18:31:34 -08001062 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which
1063 // therefore causes Espresso to hang once the dialog is shown.
Jon Miranda16ea1b12017-12-12 14:52:48 -08001064 if (!mTestingMode) {
1065 int themeResId;
1066 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
1067 themeResId = R.style.ProgressDialogThemePreL;
1068 } else {
1069 themeResId = R.style.LightDialogTheme;
1070 }
1071 mSetWallpaperProgressDialog = new ProgressDialog(this, themeResId);
1072 mSetWallpaperProgressDialog.setTitle(null);
1073 mSetWallpaperProgressDialog.setMessage(
1074 getResources().getString(R.string.set_wallpaper_progress_message));
1075 mSetWallpaperProgressDialog.setIndeterminate(true);
1076 mSetWallpaperProgressDialog.show();
1077 }
1078 }
1079
1080 private void dismissSettingWallpaperProgressDialog() {
1081 if (mSetWallpaperProgressDialog != null) {
1082 mSetWallpaperProgressDialog.dismiss();
1083 }
1084 }
1085
1086 private void showSetWallpaperErrorDialog() {
1087 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
1088 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
1089
1090 if (isSafeToCommitFragmentTransaction()) {
1091 dialogFragment.show(getSupportFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
1092 } else {
1093 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
1094 }
1095 }
1096
1097 private interface AssetReceiver {
1098 void onAssetReceived(Asset asset);
1099 }
1100
1101 /**
1102 * An AsyncTask for asynchronously fetching the thumbnail asset for a given WallpaperInfo.
1103 * Used to work around expensive method call to WallpaperManager#getWallpaperFile made from the
1104 * CurrentWallpaperInfoVN getAsset() method.
1105 */
1106 private static class FetchThumbAssetTask extends AsyncTask<Void, Void, Asset> {
1107 private Context mAppContext;
1108 private WallpaperInfo mWallpaperInfo;
1109 private AssetReceiver mReceiver;
1110
1111 public FetchThumbAssetTask(Context appContext, WallpaperInfo wallpaperInfo,
1112 AssetReceiver receiver) {
1113 mAppContext = appContext;
1114 mWallpaperInfo = wallpaperInfo;
1115 mReceiver = receiver;
1116 }
1117
1118 @Override
1119 protected Asset doInBackground(Void... params) {
1120 return mWallpaperInfo.getThumbAsset(mAppContext);
1121 }
1122
1123 @Override
1124 protected void onPostExecute(Asset thumbAsset) {
1125 mReceiver.onAssetReceived(thumbAsset);
1126 }
1127 }
1128}