1 /* 2 * Copyright (C) 2022 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 */ 16 package com.android.wallpaper.picker; 17 18 import static android.view.View.VISIBLE; 19 20 import static com.android.wallpaper.util.LaunchSourceUtils.LAUNCH_SOURCE_LAUNCHER; 21 import static com.android.wallpaper.util.LaunchSourceUtils.LAUNCH_SOURCE_SETTINGS_HOMEPAGE; 22 import static com.android.wallpaper.util.LaunchSourceUtils.WALLPAPER_LAUNCH_SOURCE; 23 24 import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED; 25 import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN; 26 27 import android.animation.Animator; 28 import android.animation.AnimatorListenerAdapter; 29 import android.annotation.SuppressLint; 30 import android.app.Activity; 31 import android.app.AlertDialog; 32 import android.app.WallpaperColors; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.content.res.Resources.NotFoundException; 36 import android.graphics.drawable.Drawable; 37 import android.os.Bundle; 38 import android.util.Log; 39 import android.view.LayoutInflater; 40 import android.view.SurfaceView; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.view.animation.Interpolator; 44 import android.view.animation.PathInterpolator; 45 import android.widget.CompoundButton; 46 import android.widget.FrameLayout; 47 import android.widget.ProgressBar; 48 import android.widget.Toast; 49 import android.widget.Toolbar; 50 51 import androidx.annotation.CallSuper; 52 import androidx.annotation.IntDef; 53 import androidx.annotation.Nullable; 54 import androidx.appcompat.content.res.AppCompatResources; 55 import androidx.core.content.res.ResourcesCompat; 56 import androidx.core.view.AccessibilityDelegateCompat; 57 import androidx.core.view.ViewCompat; 58 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; 59 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; 60 import androidx.fragment.app.Fragment; 61 import androidx.fragment.app.FragmentActivity; 62 import androidx.lifecycle.ViewModelProvider; 63 64 import com.android.customization.model.color.WallpaperColorResources; 65 import com.android.wallpaper.R; 66 import com.android.wallpaper.model.LiveWallpaperInfo; 67 import com.android.wallpaper.model.SetWallpaperViewModel; 68 import com.android.wallpaper.model.WallpaperInfo; 69 import com.android.wallpaper.module.Injector; 70 import com.android.wallpaper.module.InjectorProvider; 71 import com.android.wallpaper.module.UserEventLogger; 72 import com.android.wallpaper.module.WallpaperPersister.Destination; 73 import com.android.wallpaper.module.WallpaperSetter; 74 import com.android.wallpaper.util.PreviewUtils; 75 import com.android.wallpaper.util.ResourceUtils; 76 import com.android.wallpaper.widget.DuoTabs; 77 import com.android.wallpaper.widget.FloatingSheet; 78 import com.android.wallpaper.widget.WallpaperControlButtonGroup; 79 import com.android.wallpaper.widget.floatingsheetcontent.WallpaperInfoContent; 80 81 import com.google.android.material.bottomsheet.BottomSheetBehavior; 82 import com.google.android.material.transition.MaterialSharedAxis; 83 84 /** 85 * Base Fragment to display the UI for previewing an individual wallpaper. 86 */ 87 public abstract class PreviewFragment extends Fragment implements WallpaperColorThemePreview { 88 89 public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f); 90 91 /** 92 * User can view wallpaper and attributions in full screen, but "Set wallpaper" button is 93 * hidden. 94 */ 95 public static final int MODE_VIEW_ONLY = 0; 96 97 /** 98 * User can view wallpaper and attributions in full screen and click "Set wallpaper" to set the 99 * wallpaper with pan and crop position to the device. 100 */ 101 public static final int MODE_CROP_AND_SET_WALLPAPER = 1; 102 103 /** 104 * Possible preview modes for the fragment. 105 */ 106 @IntDef({ 107 MODE_VIEW_ONLY, 108 MODE_CROP_AND_SET_WALLPAPER}) 109 public @interface PreviewMode { 110 } 111 112 public static final String ARG_IS_NEW_TASK = "is_new_task"; 113 public static final String ARG_IS_ASSET_ID_PRESENT = "is_asset_id_present"; 114 public static final String ARG_WALLPAPER = "wallpaper"; 115 public static final String ARG_VIEW_AS_HOME = "view_as_home"; 116 117 private static final String TAG = "PreviewFragment"; 118 119 protected WallpaperInfo mWallpaper; 120 protected WallpaperSetter mWallpaperSetter; 121 protected ViewModelProvider mViewModelProvider; 122 protected WallpaperColors mWallpaperColors; 123 protected UserEventLogger mUserEventLogger; 124 private SetWallpaperViewModel mSetWallpaperViewModel; 125 126 // UI 127 private SurfaceView mWorkspaceSurface; 128 private WorkspaceSurfaceHolderCallback mWorkspaceSurfaceCallback; 129 private SurfaceView mLockSurface; 130 private WorkspaceSurfaceHolderCallback mLockSurfaceCallback; 131 private View mHideFloatingSheetTouchLayout; 132 private DuoTabs mOverlayTabs; 133 private @DuoTabs.Tab int mInitSelectedTab; 134 private View mExitFullPreviewButton; 135 protected View mSetWallpaperButton; 136 protected FrameLayout mSetWallpaperButtonContainer; 137 protected View mPreviewScrim; 138 protected Toolbar mToolbar; 139 protected WallpaperControlButtonGroup mWallpaperControlButtonGroup; 140 protected FloatingSheet mFloatingSheet; 141 protected TouchForwardingLayout mTouchForwardingLayout; 142 143 protected ProgressBar mProgressBar; 144 145 protected boolean mIsViewAsHome; 146 147 /** 148 * We create an instance of WallpaperInfo from CurrentWallpaperInfo when a user taps on 149 * the preview of a wallpapers in the wallpaper picker main screen. However, there are 150 * other instances as well in which an instance of the specific WallpaperInfo is created. This 151 * variable is used in order to identify whether the instance created has an assetId or not. 152 * This is needed for restricting the destination where a wallpaper can be set after editing 153 * it. 154 */ 155 protected boolean mIsAssetIdPresent; 156 157 /** 158 * True if the activity of this fragment is launched with {@link Intent#FLAG_ACTIVITY_NEW_TASK}. 159 */ 160 private boolean mIsNewTask; 161 162 // The system "short" animation time duration, in milliseconds. This 163 // duration is ideal for subtle animations or animations that occur 164 // very frequently. 165 private int mShortAnimTimeMillis; 166 167 private final BottomSheetBehavior.BottomSheetCallback mStandardFloatingSheetCallback = 168 new BottomSheetBehavior.BottomSheetCallback() { 169 @Override 170 public void onStateChanged(@androidx.annotation.NonNull View bottomSheet, 171 int newState) { 172 if (newState == STATE_EXPANDED) { 173 mHideFloatingSheetTouchLayout.setVisibility(View.VISIBLE); 174 mTouchForwardingLayout.setVisibility(View.GONE); 175 } 176 if (newState == STATE_HIDDEN) { 177 mWallpaperControlButtonGroup.deselectAllFloatingSheetControlButtons(); 178 mHideFloatingSheetTouchLayout.setVisibility(View.GONE); 179 mTouchForwardingLayout.setVisibility(VISIBLE); 180 mTouchForwardingLayout.requestFocus(); 181 } 182 } 183 184 @Override 185 public void onSlide(@androidx.annotation.NonNull View bottomSheet, 186 float slideOffset) { 187 } 188 }; 189 190 protected final BottomSheetBehavior.BottomSheetCallback 191 mShowOverlayOnHideFloatingSheetCallback = 192 new BottomSheetBehavior.BottomSheetCallback() { 193 @Override 194 public void onStateChanged(@androidx.annotation.NonNull View bottomSheet, 195 int newState) { 196 if (newState == STATE_HIDDEN) { 197 hideScreenPreviewOverlay(/* hide= */false); 198 } 199 } 200 201 @Override 202 public void onSlide(@androidx.annotation.NonNull View bottomSheet, 203 float slideOffset) { 204 } 205 }; 206 207 /** 208 * Sets current wallpaper to the device based on current zoom and scroll state. 209 * 210 * @param destination The wallpaper destination i.e. home vs. lockscreen vs. both. 211 */ setWallpaper(@estination int destination)212 protected abstract void setWallpaper(@Destination int destination); 213 214 @Override onCreate(Bundle savedInstanceState)215 public void onCreate(Bundle savedInstanceState) { 216 super.onCreate(savedInstanceState); 217 Bundle args = requireArguments(); 218 mWallpaper = args.getParcelable(ARG_WALLPAPER); 219 mIsViewAsHome = args.getBoolean(ARG_VIEW_AS_HOME); 220 mIsAssetIdPresent = args.getBoolean(ARG_IS_ASSET_ID_PRESENT); 221 mIsNewTask = args.getBoolean(ARG_IS_NEW_TASK); 222 mInitSelectedTab = mIsViewAsHome ? DuoTabs.TAB_SECONDARY : DuoTabs.TAB_PRIMARY; 223 Context appContext = requireContext().getApplicationContext(); 224 Injector injector = InjectorProvider.getInjector(); 225 226 mUserEventLogger = injector.getUserEventLogger(appContext); 227 mWallpaperSetter = new WallpaperSetter(injector.getWallpaperPersister(appContext), 228 injector.getPreferences(appContext), mUserEventLogger, 229 injector.getCurrentWallpaperInfoFactory(appContext), false); 230 mViewModelProvider = new ViewModelProvider(requireActivity()); 231 mSetWallpaperViewModel = mViewModelProvider.get(SetWallpaperViewModel.class); 232 mSetWallpaperViewModel.getStatus().observe(requireActivity(), setWallpaperStatus -> { 233 switch (setWallpaperStatus) { 234 case SUCCESS: 235 onSetWallpaperSuccess(); 236 break; 237 case ERROR: 238 showSetWallpaperErrorDialog(); 239 break; 240 default: 241 // Do nothing when UNKNOWN or PENDING 242 } 243 }); 244 245 mShortAnimTimeMillis = getResources().getInteger(android.R.integer.config_shortAnimTime); 246 setEnterTransition(new MaterialSharedAxis(MaterialSharedAxis.X, /* forward */ true)); 247 setReturnTransition(new MaterialSharedAxis(MaterialSharedAxis.X, /* forward */ false)); 248 setExitTransition(new MaterialSharedAxis(MaterialSharedAxis.X, /* forward */ true)); 249 setReenterTransition(new MaterialSharedAxis(MaterialSharedAxis.X, /* forward */ false)); 250 251 } 252 253 @Override 254 @CallSuper onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)255 public View onCreateView(LayoutInflater inflater, ViewGroup container, 256 Bundle savedInstanceState) { 257 View view = inflater.inflate(R.layout.fragment_wallpaper_preview, container, false); 258 // Progress indicator 259 mProgressBar = view.findViewById(R.id.action_progress); 260 // Toolbar 261 mToolbar = view.findViewById(R.id.toolbar); 262 setUpToolbar(); 263 // TouchForwardingLayout 264 mTouchForwardingLayout = view.findViewById(R.id.touch_forwarding_layout); 265 mTouchForwardingLayout.setOnClickAccessibilityDescription( 266 R.string.hide_preview_controls_action); 267 // Preview overlay 268 mWorkspaceSurface = view.findViewById(R.id.workspace_surface); 269 mWorkspaceSurfaceCallback = new WorkspaceSurfaceHolderCallback( 270 mWorkspaceSurface, 271 new PreviewUtils( 272 requireContext(), 273 getString(R.string.grid_control_metadata_name)), 274 shouldApplyWallpaperColors()); 275 // Hide the work space's bottom row initially to avoid overlapping with the overlay tabs. 276 mWorkspaceSurfaceCallback.setHideBottomRow(true); 277 mLockSurface = view.findViewById(R.id.lock_screen_overlay_surface); 278 mLockSurfaceCallback = new WorkspaceSurfaceHolderCallback( 279 mLockSurface, 280 new PreviewUtils( 281 requireContext().getApplicationContext(), 282 null, 283 getString(R.string.lock_screen_preview_provider_authority)), 284 shouldApplyWallpaperColors()); 285 setUpScreenPreviewOverlay(); 286 // Set wallpaper button 287 mSetWallpaperButtonContainer = view.findViewById(R.id.button_set_wallpaper_container); 288 mSetWallpaperButton = view.findViewById(R.id.button_set_wallpaper); 289 mSetWallpaperButtonContainer.setOnClickListener( 290 v -> showDestinationSelectionDialogForWallpaper(mWallpaper)); 291 // Overlay tabs 292 mOverlayTabs = view.findViewById(R.id.overlay_tabs); 293 mOverlayTabs.setTabText(getString(R.string.lock_screen_message), 294 getString(R.string.home_screen_message)); 295 mOverlayTabs.setOnTabSelectedListener(this::updateScreenPreviewOverlay); 296 mOverlayTabs.selectTab(mInitSelectedTab); 297 // Floating sheet and button control group 298 mFloatingSheet = view.findViewById(R.id.floating_sheet); 299 mHideFloatingSheetTouchLayout = view.findViewById(R.id.hide_floating_sheet_touch_layout); 300 mWallpaperControlButtonGroup = view.findViewById(R.id.wallpaper_control_button_group); 301 setUpFloatingSheet(requireContext()); 302 mWallpaperControlButtonGroup.showButton(WallpaperControlButtonGroup.INFORMATION, 303 getFloatingSheetControlButtonChangeListener(WallpaperControlButtonGroup.INFORMATION, 304 FloatingSheet.INFORMATION)); 305 mPreviewScrim = view.findViewById(R.id.preview_scrim); 306 mExitFullPreviewButton = view.findViewById(R.id.exit_full_preview_button); 307 mExitFullPreviewButton.setOnClickListener(v -> toggleWallpaperPreviewControl()); 308 return view; 309 } 310 setUpToolbar()311 private void setUpToolbar() { 312 Activity activity = getActivity(); 313 if (activity == null) { 314 return; 315 } 316 mToolbar.setTitle(R.string.preview); 317 mToolbar.setTitleTextColor(getResources().getColor(R.color.preview_toolbar_text_light)); 318 mToolbar.setBackgroundResource(android.R.color.transparent); 319 activity.getWindow().setStatusBarColor( 320 getResources().getColor(android.R.color.transparent)); 321 activity.getWindow().setNavigationBarColor( 322 getResources().getColor(android.R.color.transparent)); 323 324 // The hosting activity needs to implement AppbarFragment.AppbarFragmentHost 325 AppbarFragment.AppbarFragmentHost host = (AppbarFragment.AppbarFragmentHost) activity; 326 if (host.isUpArrowSupported()) { 327 mToolbar.setNavigationIcon(getToolbarBackIcon()); 328 mToolbar.setNavigationContentDescription(R.string.bottom_action_bar_back); 329 mToolbar.setNavigationOnClickListener(view -> { 330 host.onUpArrowPressed(); 331 }); 332 } 333 } 334 335 @Nullable getToolbarBackIcon()336 private Drawable getToolbarBackIcon() { 337 Drawable backIcon = ResourcesCompat.getDrawable(getResources(), 338 R.drawable.material_ic_arrow_back_black_24, 339 null); 340 if (backIcon == null) { 341 return null; 342 } 343 backIcon.setAutoMirrored(true); 344 backIcon.setTint(getResources().getColor(R.color.preview_toolbar_text_light)); 345 return backIcon; 346 } 347 setUpScreenPreviewOverlay()348 private void setUpScreenPreviewOverlay() { 349 int placeHolderColor = ResourceUtils.getColorAttr(requireContext(), 350 android.R.attr.colorBackground); 351 mWorkspaceSurface.setResizeBackgroundColor(placeHolderColor); 352 mWorkspaceSurface.setZOrderMediaOverlay(true); 353 mWorkspaceSurface.getHolder().addCallback(mWorkspaceSurfaceCallback); 354 mLockSurface.setResizeBackgroundColor(placeHolderColor); 355 mLockSurface.setZOrderMediaOverlay(true); 356 mLockSurface.getHolder().addCallback(mLockSurfaceCallback); 357 } 358 359 @SuppressLint("ClickableViewAccessibility") setUpFloatingSheet(Context context)360 private void setUpFloatingSheet(Context context) { 361 setHideFloatingSheetLayoutAccessibilityAction(); 362 mHideFloatingSheetTouchLayout.setContentDescription( 363 getString(R.string.preview_screen_description)); 364 mHideFloatingSheetTouchLayout.setOnClickListener(v -> mFloatingSheet.collapse()); 365 mHideFloatingSheetTouchLayout.setVisibility(View.GONE); 366 mFloatingSheet.addFloatingSheetCallback(mStandardFloatingSheetCallback); 367 mFloatingSheet.addFloatingSheetCallback(mShowOverlayOnHideFloatingSheetCallback); 368 mFloatingSheet.putFloatingSheetContent(FloatingSheet.INFORMATION, 369 new WallpaperInfoContent(context, mWallpaper)); 370 } 371 getFloatingSheetControlButtonChangeListener( @allpaperControlButtonGroup.WallpaperControlType int wallpaperType, @FloatingSheet.Companion.FloatingSheetContentType int floatingSheetType)372 protected CompoundButton.OnCheckedChangeListener getFloatingSheetControlButtonChangeListener( 373 @WallpaperControlButtonGroup.WallpaperControlType int wallpaperType, 374 @FloatingSheet.Companion.FloatingSheetContentType int floatingSheetType) { 375 return (buttonView, isChecked) -> { 376 if (isChecked) { 377 mWallpaperControlButtonGroup.deselectOtherFloatingSheetControlButtons( 378 wallpaperType); 379 if (mFloatingSheet.isFloatingSheetCollapsed()) { 380 hideScreenPreviewOverlay(/* hide= */true); 381 mFloatingSheet.updateContentView(floatingSheetType); 382 mFloatingSheet.expand(); 383 } else { 384 mFloatingSheet.updateContentViewWithAnimation(floatingSheetType); 385 } 386 } else { 387 if (!mWallpaperControlButtonGroup.isFloatingSheetControlButtonSelected()) { 388 mFloatingSheet.collapse(); 389 } 390 } 391 }; 392 } 393 394 private void setHideFloatingSheetLayoutAccessibilityAction() { 395 ViewCompat.setAccessibilityDelegate(mHideFloatingSheetTouchLayout, 396 new AccessibilityDelegateCompat() { 397 @Override 398 public void onInitializeAccessibilityNodeInfo(View host, 399 AccessibilityNodeInfoCompat info) { 400 super.onInitializeAccessibilityNodeInfo(host, info); 401 CharSequence description = host.getResources().getString( 402 R.string.hide_wallpaper_info_action); 403 AccessibilityActionCompat clickAction = new AccessibilityActionCompat( 404 AccessibilityNodeInfoCompat.ACTION_CLICK, description); 405 info.addAction(clickAction); 406 } 407 }); 408 } 409 410 @Override 411 public void onDestroy() { 412 super.onDestroy(); 413 if (mWallpaperSetter != null) { 414 mWallpaperSetter.cleanUp(); 415 } 416 if (mWorkspaceSurfaceCallback != null) { 417 mWorkspaceSurfaceCallback.cleanUp(); 418 } 419 if (mLockSurfaceCallback != null) { 420 mLockSurfaceCallback.cleanUp(); 421 } 422 } 423 424 protected void onWallpaperColorsChanged(@Nullable WallpaperColors colors) { 425 // Early return to not block the instrumentation test. 426 if (InjectorProvider.getInjector().isInstrumentationTest()) { 427 return; 428 } 429 if (!shouldApplyWallpaperColors()) { 430 return; 431 } 432 mWallpaperColors = colors; 433 Context context = getContext(); 434 if (context == null || colors == null) { 435 return; 436 } 437 // Apply the wallpaper color resources to the fragment context. So the views created by 438 // the context will apply the given wallpaper color. 439 new WallpaperColorResources(colors).apply(context); 440 mSetWallpaperButton.setBackground(null); 441 mSetWallpaperButton.setBackgroundResource(R.drawable.set_wallpaper_button_background); 442 mExitFullPreviewButton.setForeground( 443 AppCompatResources.getDrawable(context, R.drawable.exit_full_preview_cross)); 444 mWallpaperControlButtonGroup.updateBackgroundColor(); 445 mOverlayTabs.updateBackgroundColor(); 446 // Update the color theme for the home screen overlay 447 updateWorkspacePreview(mWorkspaceSurface, mWorkspaceSurfaceCallback, colors, 448 /* hideBottomRow= */ mOverlayTabs.getVisibility() == VISIBLE); 449 // Update the color theme for the lock screen overlay 450 updateWorkspacePreview(mLockSurface, mLockSurfaceCallback, colors, 451 /* hideBottomRow= */ mOverlayTabs.getVisibility() == VISIBLE); 452 mFloatingSheet.setColor(context); 453 } 454 455 private void updateScreenPreviewOverlay(@DuoTabs.Tab int tab) { 456 if (mWorkspaceSurface != null) { 457 mWorkspaceSurface.setVisibility( 458 tab == DuoTabs.TAB_SECONDARY ? View.VISIBLE : View.INVISIBLE); 459 mWorkspaceSurface.setZOrderMediaOverlay(tab == DuoTabs.TAB_SECONDARY); 460 } 461 if (mLockSurface != null) { 462 mLockSurface.setVisibility( 463 tab == DuoTabs.TAB_PRIMARY ? View.VISIBLE : View.INVISIBLE); 464 mLockSurface.setZOrderMediaOverlay(tab == DuoTabs.TAB_PRIMARY); 465 } 466 } 467 468 protected void toggleWallpaperPreviewControl() { 469 boolean wasVisible = mPreviewScrim.getVisibility() == VISIBLE; 470 mTouchForwardingLayout.setOnClickAccessibilityDescription( 471 wasVisible ? R.string.show_preview_controls_action 472 : R.string.hide_preview_controls_action); 473 animateWallpaperPreviewControl(wasVisible); 474 } 475 476 private void animateWallpaperPreviewControl(boolean hide) { 477 // When hiding the preview control, we should show the workspace bottom row components 478 hideBottomRow(!hide); 479 mPreviewScrim.animate() 480 .alpha(hide ? 0f : 1f) 481 .setDuration(mShortAnimTimeMillis) 482 .setListener(new ViewAnimatorListener(mPreviewScrim, hide)); 483 mWallpaperControlButtonGroup.animate().alpha(hide ? 0f : 1f) 484 .setDuration(mShortAnimTimeMillis) 485 .setListener(new ViewAnimatorListener(mWallpaperControlButtonGroup, hide)); 486 mOverlayTabs.animate().alpha(hide ? 0f : 1f) 487 .setDuration(mShortAnimTimeMillis) 488 .setListener(new ViewAnimatorListener(mOverlayTabs, hide)); 489 mSetWallpaperButtonContainer.animate().alpha(hide ? 0f : 1f) 490 .setDuration(mShortAnimTimeMillis) 491 .setListener(new ViewAnimatorListener(mSetWallpaperButtonContainer, hide)); 492 mToolbar.animate().alpha(hide ? 0f : 1f) 493 .setDuration(mShortAnimTimeMillis) 494 .setListener(new ViewAnimatorListener(mToolbar, hide)); 495 // The show and hide of the button is the opposite of the wallpaper preview control 496 mExitFullPreviewButton.animate().alpha(!hide ? 0f : 1f) 497 .setDuration(mShortAnimTimeMillis) 498 .setListener(new ViewAnimatorListener(mExitFullPreviewButton, !hide)); 499 } 500 501 private void hideBottomRow(boolean hide) { 502 if (mWorkspaceSurfaceCallback != null) { 503 Bundle data = new Bundle(); 504 data.putBoolean(WorkspaceSurfaceHolderCallback.KEY_HIDE_BOTTOM_ROW, hide); 505 mWorkspaceSurfaceCallback.send(WorkspaceSurfaceHolderCallback.MESSAGE_ID_UPDATE_PREVIEW, 506 data); 507 } 508 } 509 510 protected void hideScreenPreviewOverlay(boolean hide) { 511 mPreviewScrim.setVisibility(hide ? View.INVISIBLE : View.VISIBLE); 512 mOverlayTabs.setVisibility(hide ? View.INVISIBLE : View.VISIBLE); 513 boolean isLockSelected = mOverlayTabs.getSelectedTab() == DuoTabs.TAB_PRIMARY; 514 if (isLockSelected) { 515 mLockSurface.setVisibility(hide ? View.INVISIBLE : View.VISIBLE); 516 mLockSurface.setZOrderMediaOverlay(!hide); 517 } else { 518 mWorkspaceSurface.setVisibility(hide ? View.INVISIBLE : View.VISIBLE); 519 mWorkspaceSurface.setZOrderMediaOverlay(!hide); 520 } 521 } 522 523 protected void onSetWallpaperSuccess() { 524 Activity activity = getActivity(); 525 if (activity == null) { 526 return; 527 } 528 try { 529 Toast.makeText(activity, R.string.wallpaper_set_successfully_message, 530 Toast.LENGTH_SHORT).show(); 531 } catch (NotFoundException e) { 532 Log.e(TAG, "Could not show toast " + e); 533 } 534 activity.setResult(Activity.RESULT_OK); 535 finishActivityWithFadeTransition(); 536 537 // Start activity to go back to main screen. 538 if (mIsNewTask) { 539 Intent intent = new Intent(requireActivity(), TrampolinePickerActivity.class); 540 intent.putExtra(WALLPAPER_LAUNCH_SOURCE, 541 mIsViewAsHome ? LAUNCH_SOURCE_LAUNCHER : LAUNCH_SOURCE_SETTINGS_HOMEPAGE); 542 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); 543 startActivity(intent); 544 } 545 } 546 547 protected void finishActivityWithFadeTransition() { 548 Activity activity = getActivity(); 549 if (activity == null) { 550 return; 551 } 552 activity.finish(); 553 } 554 555 private void showDestinationSelectionDialogForWallpaper(WallpaperInfo wallpaperInfo) { 556 557 // This logic is implemented for the editing of live wallpapers. The purpose is to 558 // restrict users to set the edited creative wallpaper only to the destination from 559 // where they originally started the editing process. For instance, if they began editing 560 // by clicking on the homescreen preview, they would be allowed to set the wallpaper on the 561 // homescreen and both the homescreen and lockscreen. On the other hand, if they initiated 562 // editing by clicking on the lockscreen preview, they would only be allowed to set the 563 // wallpaper on the lockscreen and both the homescreen and lockscreen. It's essential to 564 // note that this restriction only applies when the editing process is started by tapping 565 // on the preview available on the wallpaper picker home page. 566 boolean isLockOption = true; 567 boolean isHomeOption = true; 568 if (wallpaperInfo instanceof LiveWallpaperInfo) { 569 if (!mIsAssetIdPresent) { 570 isHomeOption = mIsViewAsHome; 571 isLockOption = !mIsViewAsHome; 572 } 573 } 574 575 mWallpaperSetter.requestDestination(getActivity(), getParentFragmentManager(), 576 destination -> { 577 mSetWallpaperViewModel.setDestination(destination); 578 setWallpaper(destination); 579 }, 580 wallpaperInfo instanceof LiveWallpaperInfo, isHomeOption, isLockOption); 581 } 582 583 protected void showSetWallpaperErrorDialog() { 584 new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme) 585 .setMessage(R.string.set_wallpaper_error_message) 586 .setPositiveButton(R.string.try_again, (dialogInterface, i) -> 587 setWallpaper(mSetWallpaperViewModel.getDestination()) 588 ) 589 .setNegativeButton(android.R.string.cancel, null) 590 .create() 591 .show(); 592 } 593 594 protected void showLoadWallpaperErrorDialog() { 595 new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme) 596 .setMessage(R.string.load_wallpaper_error_message) 597 .setPositiveButton(android.R.string.ok, 598 (dialogInterface, i) -> finishFragmentActivity()) 599 .setOnDismissListener(dialog -> finishFragmentActivity()) 600 .create() 601 .show(); 602 } 603 604 private void finishFragmentActivity() { 605 FragmentActivity activity = getActivity(); 606 if (activity != null) { 607 activity.finish(); 608 } 609 } 610 611 private static class ViewAnimatorListener extends AnimatorListenerAdapter { 612 final View mView; 613 final boolean mHide; 614 615 private ViewAnimatorListener(View view, boolean hide) { 616 mView = view; 617 mHide = hide; 618 } 619 620 @Override 621 public void onAnimationStart(Animator animation) { 622 mView.setVisibility(VISIBLE); 623 } 624 625 @Override 626 public void onAnimationEnd(Animator animation) { 627 mView.setVisibility(mHide ? View.INVISIBLE : VISIBLE); 628 } 629 } 630 } 631