1 /* 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 */ 16 package com.android.wallpaper.picker; 17 18 import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED; 19 import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED; 20 21 import android.app.Activity; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.res.ColorStateList; 25 import android.content.res.Resources.NotFoundException; 26 import android.content.res.TypedArray; 27 import android.graphics.Insets; 28 import android.graphics.drawable.GradientDrawable; 29 import android.net.Uri; 30 import android.os.Bundle; 31 import android.util.Log; 32 import android.view.ContextThemeWrapper; 33 import android.view.LayoutInflater; 34 import android.view.Menu; 35 import android.view.MenuInflater; 36 import android.view.MenuItem; 37 import android.view.View; 38 import android.view.View.OnClickListener; 39 import android.view.ViewGroup; 40 import android.view.WindowInsets; 41 import android.widget.Button; 42 import android.widget.CheckBox; 43 import android.widget.TextView; 44 import android.widget.Toast; 45 46 import androidx.annotation.CallSuper; 47 import androidx.annotation.IdRes; 48 import androidx.annotation.IntDef; 49 import androidx.annotation.LayoutRes; 50 import androidx.annotation.Nullable; 51 import androidx.appcompat.app.AppCompatActivity; 52 import androidx.appcompat.widget.Toolbar; 53 import androidx.core.view.ViewCompat; 54 import androidx.core.widget.ContentLoadingProgressBar; 55 import androidx.fragment.app.Fragment; 56 import androidx.fragment.app.FragmentActivity; 57 58 import com.android.wallpaper.R; 59 import com.android.wallpaper.model.LiveWallpaperInfo; 60 import com.android.wallpaper.model.WallpaperInfo; 61 import com.android.wallpaper.module.ExploreIntentChecker; 62 import com.android.wallpaper.module.Injector; 63 import com.android.wallpaper.module.InjectorProvider; 64 import com.android.wallpaper.module.UserEventLogger; 65 import com.android.wallpaper.module.WallpaperPersister.Destination; 66 import com.android.wallpaper.module.WallpaperPreferences; 67 import com.android.wallpaper.module.WallpaperSetter; 68 69 import com.google.android.material.bottomsheet.BottomSheetBehavior; 70 import com.google.android.material.bottomsheet.BottomSheetBehavior.State; 71 72 import java.util.Date; 73 import java.util.List; 74 75 /** 76 * Base Fragment to display the UI for previewing an individual wallpaper 77 */ 78 public abstract class PreviewFragment extends Fragment implements 79 SetWallpaperDialogFragment.Listener, SetWallpaperErrorDialogFragment.Listener, 80 LoadWallpaperErrorDialogFragment.Listener { 81 82 /** 83 * User can view wallpaper and attributions in full screen, but "Set wallpaper" button is 84 * hidden. 85 */ 86 static final int MODE_VIEW_ONLY = 0; 87 88 /** 89 * User can view wallpaper and attributions in full screen and click "Set wallpaper" to set the 90 * wallpaper with pan and crop position to the device. 91 */ 92 static final int MODE_CROP_AND_SET_WALLPAPER = 1; 93 94 /** 95 * Possible preview modes for the fragment. 96 */ 97 @IntDef({ 98 MODE_VIEW_ONLY, 99 MODE_CROP_AND_SET_WALLPAPER}) 100 public @interface PreviewMode { 101 } 102 103 public static final String ARG_WALLPAPER = "wallpaper"; 104 public static final String ARG_PREVIEW_MODE = "preview_mode"; 105 public static final String ARG_TESTING_MODE_ENABLED = "testing_mode_enabled"; 106 107 /** 108 * Creates and returns new instance of {@link ImagePreviewFragment} with the provided wallpaper 109 * set as an argument. 110 */ newInstance( WallpaperInfo wallpaperInfo, @PreviewMode int mode, boolean testingModeEnabled)111 public static PreviewFragment newInstance( 112 WallpaperInfo wallpaperInfo, @PreviewMode int mode, boolean testingModeEnabled) { 113 114 boolean isLive = wallpaperInfo instanceof LiveWallpaperInfo; 115 116 Bundle args = new Bundle(); 117 args.putParcelable(ARG_WALLPAPER, wallpaperInfo); 118 args.putInt(ARG_PREVIEW_MODE, mode); 119 args.putBoolean(ARG_TESTING_MODE_ENABLED, testingModeEnabled); 120 121 PreviewFragment fragment = isLive ? new LivePreviewFragment() : new ImagePreviewFragment(); 122 fragment.setArguments(args); 123 return fragment; 124 } 125 126 private static final String TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT = 127 "load_wallpaper_error_dialog"; 128 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT = 129 "set_wallpaper_error_dialog"; 130 private static final int UNUSED_REQUEST_CODE = 1; 131 private static final String TAG = "PreviewFragment"; 132 static final String KEY_BOTTOM_SHEET_STATE = "key_bottom_sheet_state"; 133 134 @PreviewMode 135 protected int mPreviewMode; 136 137 /** 138 * When true, enables a test mode of operation -- in which certain UI features are disabled to 139 * allow for UI tests to run correctly. Works around issue in ProgressDialog currently where the 140 * dialog constantly keeps the UI thread alive and blocks a test forever. 141 */ 142 protected boolean mTestingModeEnabled; 143 144 protected WallpaperInfo mWallpaper; 145 protected WallpaperSetter mWallpaperSetter; 146 protected UserEventLogger mUserEventLogger; 147 protected ViewGroup mBottomSheet; 148 protected ContentLoadingProgressBar mLoadingProgressBar; 149 150 protected CheckBox mPreview; 151 152 @SuppressWarnings("RestrictTo") 153 @State 154 protected int mBottomSheetInitialState; 155 156 protected Intent mExploreIntent; 157 158 /** 159 * Staged error dialog fragments that were unable to be shown when the hosting activity didn't 160 * allow committing fragment transactions. 161 */ 162 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment; 163 private LoadWallpaperErrorDialogFragment mStagedLoadWallpaperErrorDialogFragment; 164 getAttrColor(Context context, int attr)165 protected static int getAttrColor(Context context, int attr) { 166 TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); 167 int colorAccent = ta.getColor(0, 0); 168 ta.recycle(); 169 return colorAccent; 170 } 171 172 @Override onCreate(Bundle savedInstanceState)173 public void onCreate(Bundle savedInstanceState) { 174 super.onCreate(savedInstanceState); 175 176 Activity activity = getActivity(); 177 Context appContext = getContext().getApplicationContext(); 178 Injector injector = InjectorProvider.getInjector(); 179 180 mUserEventLogger = injector.getUserEventLogger(appContext); 181 mWallpaper = getArguments().getParcelable(ARG_WALLPAPER); 182 183 //noinspection ResourceType 184 mPreviewMode = getArguments().getInt(ARG_PREVIEW_MODE); 185 mTestingModeEnabled = getArguments().getBoolean(ARG_TESTING_MODE_ENABLED); 186 mWallpaperSetter = new WallpaperSetter(injector.getWallpaperPersister(appContext), 187 injector.getPreferences(appContext), mUserEventLogger, mTestingModeEnabled); 188 189 setHasOptionsMenu(true); 190 191 List<String> attributions = getAttributions(activity); 192 if (attributions.size() > 0 && attributions.get(0) != null) { 193 activity.setTitle(attributions.get(0)); 194 } 195 } 196 197 @Override 198 @CallSuper onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)199 public View onCreateView(LayoutInflater inflater, ViewGroup container, 200 Bundle savedInstanceState) { 201 View view = inflater.inflate(getLayoutResId(), container, false); 202 203 // Set toolbar as the action bar. 204 Toolbar toolbar = view.findViewById(R.id.toolbar); 205 AppCompatActivity activity = (AppCompatActivity) requireActivity(); 206 activity.setSupportActionBar(toolbar); 207 activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); 208 activity.getSupportActionBar().setDisplayShowTitleEnabled(false); 209 210 toolbar.getNavigationIcon().setTint(getAttrColor(activity, android.R.attr.colorPrimary)); 211 toolbar.getNavigationIcon().setAutoMirrored(true); 212 213 ViewCompat.setPaddingRelative(toolbar, 214 /* start */ getResources().getDimensionPixelSize( 215 R.dimen.preview_toolbar_up_button_start_padding), 216 /* top */ 0, 217 /* end */ getResources().getDimensionPixelSize( 218 R.dimen.preview_toolbar_set_wallpaper_button_end_padding), 219 /* bottom */ 0); 220 221 mLoadingProgressBar = view.findViewById(getLoadingIndicatorResId()); 222 mLoadingProgressBar.show(); 223 224 mBottomSheet = view.findViewById(getBottomSheetResId()); 225 setUpBottomSheetView(mBottomSheet); 226 227 // Workaround as we don't have access to bottomDialogCornerRadius, mBottomSheet radii are 228 // set to dialogCornerRadius by default. 229 GradientDrawable bottomSheetBackground = (GradientDrawable) mBottomSheet.getBackground(); 230 float[] radii = bottomSheetBackground.getCornerRadii(); 231 for (int i = 0; i < radii.length; i++) { 232 radii[i]*=2f; 233 } 234 bottomSheetBackground = ((GradientDrawable)bottomSheetBackground.mutate()); 235 bottomSheetBackground.setCornerRadii(radii); 236 mBottomSheet.setBackground(bottomSheetBackground); 237 238 mBottomSheetInitialState = (savedInstanceState == null) ? STATE_EXPANDED 239 : savedInstanceState.getInt(KEY_BOTTOM_SHEET_STATE, STATE_EXPANDED); 240 setUpBottomSheetListeners(); 241 242 view.setOnApplyWindowInsetsListener((v, windowInsets) -> { 243 toolbar.setPadding(toolbar.getPaddingLeft(), 244 toolbar.getPaddingTop() + windowInsets.getSystemWindowInsetTop(), 245 toolbar.getPaddingRight(), toolbar.getBottom()); 246 mBottomSheet.setPadding(mBottomSheet.getPaddingLeft(), 247 mBottomSheet.getPaddingTop(), mBottomSheet.getPaddingRight(), 248 mBottomSheet.getPaddingBottom() + windowInsets.getSystemWindowInsetBottom()); 249 WindowInsets.Builder builder = new WindowInsets.Builder(windowInsets); 250 builder.setSystemWindowInsets(Insets.of(windowInsets.getSystemWindowInsetLeft(), 251 0, windowInsets.getStableInsetRight(), 0)); 252 return builder.build(); 253 }); 254 255 return view; 256 } 257 populateInfoPage(InfoPageController infoPage)258 protected void populateInfoPage(InfoPageController infoPage) { 259 Context context = requireContext(); 260 261 BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet); 262 263 List<String> attributions = getAttributions(context); 264 boolean showMetadata = shouldShowMetadataInPreview(); 265 CharSequence exploreLabel = getExploreButtonLabel(context); 266 267 infoPage.populate(attributions, showMetadata, this::onSetWallpaperClicked, 268 exploreLabel, 269 (showMetadata && mExploreIntent != null) ? this::onExploreClicked : null); 270 271 mBottomSheet.setVisibility(View.VISIBLE); 272 273 // Initialize the state of the BottomSheet based on the current state because if the initial 274 // and current state are the same, the state change listener won't fire and set the correct 275 // arrow asset and text alpha. 276 if (mBottomSheetInitialState == STATE_EXPANDED) { 277 setPreviewChecked(false); 278 infoPage.setContentAlpha(1f); 279 } else { 280 setPreviewChecked(true); 281 infoPage.setContentAlpha(0f); 282 } 283 284 bottomSheetBehavior.setState(mBottomSheetInitialState); 285 } 286 getAttributions(Context context)287 protected List<String> getAttributions(Context context) { 288 return mWallpaper.getAttributions(context); 289 } 290 shouldShowMetadataInPreview()291 protected boolean shouldShowMetadataInPreview() { 292 android.app.WallpaperInfo wallpaperComponent = mWallpaper.getWallpaperComponent(); 293 return wallpaperComponent == null || wallpaperComponent.getShowMetadataInPreview(); 294 } 295 296 @Nullable getExploreButtonLabel(Context context)297 protected abstract CharSequence getExploreButtonLabel(Context context); 298 299 @LayoutRes getLayoutResId()300 protected abstract int getLayoutResId(); 301 setUpBottomSheetView(ViewGroup bottomSheet)302 protected abstract void setUpBottomSheetView(ViewGroup bottomSheet); 303 304 @IdRes getBottomSheetResId()305 protected abstract int getBottomSheetResId(); 306 307 @IdRes getLoadingIndicatorResId()308 protected abstract int getLoadingIndicatorResId(); 309 getDeviceDefaultTheme()310 protected int getDeviceDefaultTheme() { 311 return android.R.style.Theme_DeviceDefault; 312 } 313 314 @Override onResume()315 public void onResume() { 316 super.onResume(); 317 318 WallpaperPreferences preferences = 319 InjectorProvider.getInjector().getPreferences(getActivity()); 320 preferences.setLastAppActiveTimestamp(new Date().getTime()); 321 322 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is 323 // one that was unable to be shown earlier when this fragment's hosting activity didn't 324 // allow committing fragment transactions. 325 if (mStagedLoadWallpaperErrorDialogFragment != null) { 326 mStagedLoadWallpaperErrorDialogFragment.show( 327 requireFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT); 328 mStagedLoadWallpaperErrorDialogFragment = null; 329 } 330 if (mStagedSetWallpaperErrorDialogFragment != null) { 331 mStagedSetWallpaperErrorDialogFragment.show( 332 requireFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT); 333 mStagedSetWallpaperErrorDialogFragment = null; 334 } 335 } 336 337 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)338 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 339 super.onCreateOptionsMenu(menu, inflater); 340 inflater.inflate(R.menu.preview_menu, menu); 341 } 342 343 @Override onPrepareOptionsMenu(Menu menu)344 public void onPrepareOptionsMenu(Menu menu) { 345 super.onPrepareOptionsMenu(menu); 346 setupPreviewMenu(menu); 347 } 348 349 @Override onOptionsItemSelected(MenuItem item)350 public boolean onOptionsItemSelected(MenuItem item) { 351 int id = item.getItemId(); 352 if (id == android.R.id.home) { 353 // The Preview screen has multiple entry points. It could be opened from either 354 // the IndividualPreviewActivity, the "My photos" selection (by way of 355 // TopLevelPickerActivity), or from a system "crop and set wallpaper" intent. 356 // Therefore, handle the Up button as a global Back. 357 requireActivity().onBackPressed(); 358 return true; 359 } 360 361 return false; 362 } 363 setupPreviewMenu(Menu menu)364 private void setupPreviewMenu(Menu menu) { 365 mPreview = (CheckBox) menu.findItem(R.id.preview).getActionView(); 366 mPreview.setChecked(mBottomSheetInitialState == STATE_COLLAPSED); 367 mPreview.setOnClickListener(this::setPreviewBehavior); 368 } 369 setPreviewChecked(boolean checked)370 protected void setPreviewChecked(boolean checked) { 371 if (mPreview != null) { 372 mPreview.setChecked(checked); 373 int resId = checked ? R.string.expand_attribution_panel 374 : R.string.collapse_attribution_panel; 375 mPreview.setContentDescription(getResources().getString(resId)); 376 } 377 } 378 setPreviewBehavior(View v)379 private void setPreviewBehavior(View v) { 380 CheckBox checkbox = (CheckBox) v; 381 BottomSheetBehavior<?> behavior = BottomSheetBehavior.from(mBottomSheet); 382 383 if (checkbox.isChecked()) { 384 behavior.setState(STATE_COLLAPSED); 385 } else { 386 behavior.setState(STATE_EXPANDED); 387 } 388 } 389 setUpExploreIntent(@ullable Runnable callback)390 protected void setUpExploreIntent(@Nullable Runnable callback) { 391 Context context = getContext(); 392 if (context == null) { 393 return; 394 } 395 String actionUrl = mWallpaper.getActionUrl(context); 396 if (actionUrl != null && !actionUrl.isEmpty()) { 397 Uri exploreUri = Uri.parse(mWallpaper.getActionUrl(context)); 398 ExploreIntentChecker intentChecker = 399 InjectorProvider.getInjector().getExploreIntentChecker(context); 400 401 intentChecker.fetchValidActionViewIntent(exploreUri, exploreIntent -> { 402 if (getActivity() == null) { 403 return; 404 } 405 406 mExploreIntent = exploreIntent; 407 if (callback != null) { 408 callback.run(); 409 } 410 }); 411 } else { 412 if (callback != null) { 413 callback.run(); 414 } 415 } 416 } 417 418 /** 419 * Configure loading indicator with a MaterialProgressDrawable. 420 */ setUpLoadingIndicator()421 protected void setUpLoadingIndicator() { 422 mLoadingProgressBar.setProgressTintList(ColorStateList.valueOf(getAttrColor( 423 new ContextThemeWrapper(requireContext(), getDeviceDefaultTheme()), 424 android.R.attr.colorAccent))); 425 mLoadingProgressBar.show(); 426 } 427 isLoaded()428 protected abstract boolean isLoaded(); 429 430 @Override onSet(int destination)431 public void onSet(int destination) { 432 setCurrentWallpaper(destination); 433 } 434 435 @Override onClickTryAgain(@estination int wallpaperDestination)436 public void onClickTryAgain(@Destination int wallpaperDestination) { 437 setCurrentWallpaper(wallpaperDestination); 438 } 439 440 @Override onClickOk()441 public void onClickOk() { 442 FragmentActivity activity = getActivity(); 443 if (activity != null) { 444 activity.finish(); 445 } 446 } 447 448 @Override onDestroy()449 public void onDestroy() { 450 super.onDestroy(); 451 mWallpaperSetter.cleanUp(); 452 } 453 454 @Override onSaveInstanceState(Bundle outState)455 public void onSaveInstanceState(Bundle outState) { 456 super.onSaveInstanceState(outState); 457 458 final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet); 459 outState.putInt(KEY_BOTTOM_SHEET_STATE, bottomSheetBehavior.getState()); 460 } 461 onSetWallpaperClicked(View button)462 protected void onSetWallpaperClicked(View button) { 463 mWallpaperSetter.requestDestination(getContext(), getFragmentManager(), this, 464 mWallpaper instanceof LiveWallpaperInfo); 465 } 466 onExploreClicked(View button)467 private void onExploreClicked(View button) { 468 if (getContext() == null) { 469 return; 470 } 471 Context context = getContext(); 472 mUserEventLogger.logActionClicked(mWallpaper.getCollectionId(context), 473 mWallpaper.getActionLabelRes(context)); 474 475 startActivity(mExploreIntent); 476 } 477 setUpBottomSheetListeners()478 private void setUpBottomSheetListeners() { 479 final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet); 480 481 bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { 482 @Override 483 public void onStateChanged(View bottomSheet, int newState) { 484 // Don't respond to lingering state change events occurring after the fragment has 485 // already been detached from the activity. Else, IllegalStateException may occur 486 // when trying to fetch resources. 487 if (getActivity() == null) { 488 return; 489 } 490 switch (newState) { 491 case STATE_COLLAPSED: 492 setPreviewChecked(true /* checked */); 493 break; 494 case STATE_EXPANDED: 495 setPreviewChecked(false /* checked */); 496 break; 497 default: 498 Log.v(TAG, "Ignoring BottomSheet state: " + newState); 499 } 500 } 501 502 @Override 503 public void onSlide(View bottomSheet, float slideOffset) { 504 float alpha; 505 if (slideOffset >= 0) { 506 alpha = slideOffset; 507 } else { 508 alpha = 1f - slideOffset; 509 } 510 setBottomSheetContentAlpha(alpha); 511 } 512 }); 513 } 514 setBottomSheetContentAlpha(float alpha)515 protected void setBottomSheetContentAlpha(float alpha) { 516 517 } 518 519 /** 520 * Sets current wallpaper to the device based on current zoom and scroll state. 521 * 522 * @param destination The wallpaper destination i.e. home vs. lockscreen vs. both. 523 */ setCurrentWallpaper(@estination int destination)524 protected abstract void setCurrentWallpaper(@Destination int destination); 525 finishActivityWithResultOk()526 protected void finishActivityWithResultOk() { 527 Activity activity = requireActivity(); 528 try { 529 Toast.makeText(activity, 530 R.string.wallpaper_set_successfully_message, Toast.LENGTH_SHORT).show(); 531 } catch (NotFoundException e) { 532 Log.e(TAG, "Could not show toast " + e); 533 } 534 activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out); 535 activity.setResult(Activity.RESULT_OK); 536 activity.finish(); 537 } 538 showSetWallpaperErrorDialog(@estination int wallpaperDestination)539 protected void showSetWallpaperErrorDialog(@Destination int wallpaperDestination) { 540 SetWallpaperErrorDialogFragment newFragment = SetWallpaperErrorDialogFragment.newInstance( 541 R.string.set_wallpaper_error_message, wallpaperDestination); 542 newFragment.setTargetFragment(this, UNUSED_REQUEST_CODE); 543 544 // Show 'set wallpaper' error dialog now if it's safe to commit fragment transactions, 545 // otherwise stage it for later when the hosting activity is in a state to commit fragment 546 // transactions. 547 BasePreviewActivity activity = (BasePreviewActivity) requireActivity(); 548 if (activity.isSafeToCommitFragmentTransaction()) { 549 newFragment.show(requireFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT); 550 } else { 551 mStagedSetWallpaperErrorDialogFragment = newFragment; 552 } 553 } 554 555 /** 556 * Shows 'load wallpaper' error dialog now or stage it to be shown when the hosting activity is 557 * in a state that allows committing fragment transactions. 558 */ showLoadWallpaperErrorDialog()559 protected void showLoadWallpaperErrorDialog() { 560 LoadWallpaperErrorDialogFragment dialogFragment = 561 LoadWallpaperErrorDialogFragment.newInstance(); 562 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE); 563 564 // Show 'load wallpaper' error dialog now or stage it to be shown when the hosting 565 // activity is in a state that allows committing fragment transactions. 566 BasePreviewActivity activity = (BasePreviewActivity) getActivity(); 567 if (activity != null && activity.isSafeToCommitFragmentTransaction()) { 568 dialogFragment.show(requireFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT); 569 } else { 570 mStagedLoadWallpaperErrorDialogFragment = dialogFragment; 571 } 572 } 573 574 /** 575 * Returns whether layout direction is RTL (or false for LTR). Since native RTL layout support 576 * was added in API 17, returns false for versions lower than 17. 577 */ isRtl()578 protected boolean isRtl() { 579 return getResources().getConfiguration().getLayoutDirection() 580 == View.LAYOUT_DIRECTION_RTL; 581 } 582 583 protected static class InfoPageController { 584 createView(LayoutInflater inflater)585 public static View createView(LayoutInflater inflater) { 586 return inflater.inflate(R.layout.preview_page_info, null /* root */); 587 } 588 589 private final int mPreviewMode; 590 private final View mInfoPage; 591 private final TextView mAttributionTitle; 592 private final TextView mAttributionSubtitle1; 593 private final TextView mAttributionSubtitle2; 594 private final Button mExploreButton; 595 private final Button mSetWallpaperButton; 596 private final View mSpacer; 597 InfoPageController(View infoPage, int previewMode)598 public InfoPageController(View infoPage, int previewMode) { 599 mInfoPage = infoPage; 600 mPreviewMode = previewMode; 601 602 mAttributionTitle = mInfoPage.findViewById(R.id.preview_attribution_pane_title); 603 mAttributionSubtitle1 = mInfoPage.findViewById(R.id.preview_attribution_pane_subtitle1); 604 mAttributionSubtitle2 = mInfoPage.findViewById(R.id.preview_attribution_pane_subtitle2); 605 mSpacer = mInfoPage.findViewById(R.id.spacer); 606 607 mExploreButton = mInfoPage.findViewById(R.id.preview_attribution_pane_explore_button); 608 mSetWallpaperButton = mInfoPage.findViewById( 609 R.id.preview_attribution_pane_set_wallpaper_button); 610 } 611 populate(List<String> attributions, boolean showMetadata, OnClickListener setWallpaperOnClickListener, CharSequence exploreButtonLabel, @Nullable OnClickListener exploreOnClickListener)612 public void populate(List<String> attributions, boolean showMetadata, 613 OnClickListener setWallpaperOnClickListener, 614 CharSequence exploreButtonLabel, 615 @Nullable OnClickListener exploreOnClickListener) { 616 if (attributions.size() > 0 && attributions.get(0) != null) { 617 mAttributionTitle.setText(attributions.get(0)); 618 } 619 620 if (showMetadata) { 621 if (attributions.size() > 1 && attributions.get(1) != null) { 622 mAttributionSubtitle1.setVisibility(View.VISIBLE); 623 mAttributionSubtitle1.setText(attributions.get(1)); 624 } 625 626 if (attributions.size() > 2 && attributions.get(2) != null) { 627 mAttributionSubtitle2.setVisibility(View.VISIBLE); 628 mAttributionSubtitle2.setText(attributions.get(2)); 629 } 630 } 631 setUpSetWallpaperButton(setWallpaperOnClickListener); 632 633 setUpExploreButton(exploreButtonLabel, exploreOnClickListener); 634 635 if (mExploreButton.getVisibility() == View.VISIBLE 636 && mSetWallpaperButton.getVisibility() == View.VISIBLE) { 637 mSpacer.setVisibility(View.VISIBLE); 638 } else { 639 mSpacer.setVisibility(View.GONE); 640 } 641 } 642 setContentAlpha(float alpha)643 public void setContentAlpha(float alpha) { 644 mSetWallpaperButton.setAlpha(alpha); 645 mExploreButton.setAlpha(alpha); 646 mAttributionTitle.setAlpha(alpha); 647 mAttributionSubtitle1.setAlpha(alpha); 648 mAttributionSubtitle2.setAlpha(alpha); 649 } 650 setUpSetWallpaperButton(OnClickListener setWallpaperOnClickListener)651 private void setUpSetWallpaperButton(OnClickListener setWallpaperOnClickListener) { 652 if (mPreviewMode == MODE_VIEW_ONLY) { 653 mSetWallpaperButton.setVisibility(View.GONE); 654 } else { 655 mSetWallpaperButton.setVisibility(View.VISIBLE); 656 mSetWallpaperButton.setOnClickListener(setWallpaperOnClickListener); 657 } 658 } 659 setUpExploreButton(CharSequence label, @Nullable OnClickListener exploreOnClickListener)660 private void setUpExploreButton(CharSequence label, 661 @Nullable OnClickListener exploreOnClickListener) { 662 mExploreButton.setVisibility(View.GONE); 663 if (exploreOnClickListener == null) { 664 return; 665 } 666 mExploreButton.setVisibility(View.VISIBLE); 667 mExploreButton.setText(label); 668 669 mExploreButton.setOnClickListener(exploreOnClickListener); 670 } 671 } 672 } 673