1 /* 2 * Copyright (C) 2009 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 17 package com.android.wallpaper.livepicker; 18 19 import android.app.Activity; 20 import android.app.AlertDialog; 21 import android.app.WallpaperColors; 22 import android.app.WallpaperInfo; 23 import android.app.WallpaperManager; 24 import android.content.ActivityNotFoundException; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.DialogInterface; 28 import android.content.Intent; 29 import android.content.ServiceConnection; 30 import android.content.pm.ActivityInfo; 31 import android.content.pm.PackageManager; 32 import android.content.res.Resources.NotFoundException; 33 import android.graphics.Outline; 34 import android.graphics.Rect; 35 import android.graphics.RectF; 36 import android.graphics.drawable.Drawable; 37 import android.net.Uri; 38 import android.os.Bundle; 39 import android.os.IBinder; 40 import android.os.ParcelFileDescriptor; 41 import android.os.RemoteException; 42 import android.service.wallpaper.IWallpaperConnection; 43 import android.service.wallpaper.IWallpaperEngine; 44 import android.service.wallpaper.IWallpaperService; 45 import android.service.wallpaper.WallpaperService; 46 import android.service.wallpaper.WallpaperSettingsActivity; 47 import android.text.TextUtils; 48 import android.util.Log; 49 import android.util.Pair; 50 import android.view.ContextThemeWrapper; 51 import android.view.Menu; 52 import android.view.MenuItem; 53 import android.view.MotionEvent; 54 import android.view.View; 55 import android.view.ViewGroup; 56 import android.view.ViewOutlineProvider; 57 import android.view.WindowManager.LayoutParams; 58 import android.view.animation.AnimationUtils; 59 import android.widget.ArrayAdapter; 60 import android.widget.Button; 61 import android.widget.CheckBox; 62 import android.widget.TextView; 63 import android.widget.Toolbar; 64 65 import androidx.annotation.NonNull; 66 import androidx.annotation.Nullable; 67 import androidx.annotation.VisibleForTesting; 68 import androidx.lifecycle.LiveData; 69 import androidx.slice.Slice; 70 import androidx.slice.widget.SliceLiveData; 71 import androidx.slice.widget.SliceView; 72 import androidx.viewpager.widget.PagerAdapter; 73 import androidx.viewpager.widget.ViewPager; 74 75 import com.google.android.material.bottomsheet.BottomSheetBehavior; 76 import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback; 77 import com.google.android.material.tabs.TabLayout; 78 79 import java.io.IOException; 80 import java.util.ArrayList; 81 import java.util.List; 82 83 public class LiveWallpaperPreview extends Activity { 84 static final String EXTRA_LIVE_WALLPAPER_INFO = "android.live_wallpaper.info"; 85 86 private static final String LOG_TAG = "LiveWallpaperPreview"; 87 88 private static final boolean SHOW_FAKE_DATA = false; 89 90 private WallpaperManager mWallpaperManager; 91 private WallpaperConnection mWallpaperConnection; 92 93 private Intent mWallpaperIntent; 94 private Intent mSettingsIntent; 95 private Intent mDeleteIntent; 96 97 private View mLoading; 98 private View mViewBottomPane; 99 private BottomSheetBehavior mBottomSheetBehavior; 100 private ViewPager mViewPager; 101 private TabLayout mTabLayout; 102 private CheckBox mPreview; 103 104 protected final List<Pair<String, View>> mPages = new ArrayList<>(); 105 private SliceView mSliceViewSettings; 106 private LiveData<Slice> mLiveDataSettings; 107 108 @Override onCreate(Bundle savedInstanceState)109 protected void onCreate(Bundle savedInstanceState) { 110 super.onCreate(savedInstanceState); 111 init(); 112 } 113 init()114 protected void init() { 115 WallpaperInfo info = getIntent().getParcelableExtra(EXTRA_LIVE_WALLPAPER_INFO); 116 if (info == null) { 117 finish(); 118 return; 119 } 120 initUI(info, null /* deleteAction */); 121 } 122 initUI(WallpaperInfo info, @Nullable String deleteAction)123 protected void initUI(WallpaperInfo info, @Nullable String deleteAction) { 124 getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE 125 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 126 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); 127 setContentView(R.layout.live_wallpaper_preview); 128 mLoading = findViewById(R.id.loading); 129 130 final String packageName = info.getPackageName(); 131 mWallpaperIntent = new Intent(WallpaperService.SERVICE_INTERFACE) 132 .setClassName(info.getPackageName(), info.getServiceName()); 133 134 final String settingsActivity = info.getSettingsActivity(); 135 if (settingsActivity != null) { 136 mSettingsIntent = new Intent(); 137 mSettingsIntent.setComponent(new ComponentName(packageName, settingsActivity)); 138 mSettingsIntent.putExtra(WallpaperSettingsActivity.EXTRA_PREVIEW_MODE, true); 139 final PackageManager pm = getPackageManager(); 140 final ActivityInfo activityInfo = mSettingsIntent.resolveActivityInfo(pm, 0); 141 if (activityInfo == null) { 142 Log.e(LOG_TAG, "Couldn't find settings activity: " + settingsActivity); 143 mSettingsIntent = null; 144 } 145 } 146 147 final Toolbar toolbar = findViewById(R.id.toolbar); 148 setActionBar(toolbar); 149 getActionBar().setDisplayHomeAsUpEnabled(true); 150 getActionBar().setDisplayShowTitleEnabled(false); 151 152 final Drawable backArrow = getDrawable(R.drawable.ic_arrow_back_white_24dp); 153 backArrow.setAutoMirrored(true); 154 toolbar.setNavigationIcon(backArrow); 155 156 mWallpaperManager = WallpaperManager.getInstance(this); 157 mWallpaperConnection = new WallpaperConnection(mWallpaperIntent, info); 158 getWindow().getDecorView().post(new Runnable() { 159 public void run() { 160 if (!mWallpaperConnection.connect()) { 161 mWallpaperConnection = null; 162 } 163 } 164 }); 165 166 if (!TextUtils.isEmpty(deleteAction)) { 167 mDeleteIntent = new Intent(deleteAction); 168 mDeleteIntent.setPackage(info.getPackageName()); 169 mDeleteIntent.putExtra(EXTRA_LIVE_WALLPAPER_INFO, info); 170 } 171 172 initInfoPage(info); 173 initSettingsPage(info); 174 populateBottomPane(); 175 } 176 initInfoPage(WallpaperInfo info)177 private void initInfoPage(WallpaperInfo info) { 178 final View pageInfo = getLayoutInflater().inflate(R.layout.page_info, null /* root */); 179 final TextView attributionTitle = pageInfo.findViewById( 180 R.id.preview_attribution_pane_title); 181 final TextView attributionAuthor = pageInfo.findViewById( 182 R.id.preview_attribution_pane_author); 183 final TextView attributionDescription = pageInfo.findViewById( 184 R.id.preview_attribution_pane_description); 185 final Button attributionExploreButton = pageInfo.findViewById( 186 R.id.preview_attribution_pane_explore_button); 187 final View spacer = pageInfo.findViewById(R.id.spacer); 188 final Button setWallpaperButton = pageInfo.findViewById( 189 R.id.preview_attribution_pane_set_wallpaper_button); 190 191 setWallpaperButton.setOnClickListener(this::setLiveWallpaper); 192 mPages.add(Pair.create(getString(R.string.tab_info), pageInfo)); 193 194 if (SHOW_FAKE_DATA) { 195 attributionTitle.setText("Diorama, Yosemite"); 196 attributionTitle.setVisibility(View.VISIBLE); 197 attributionAuthor.setText("Live Earth Collection - Android Earth"); 198 attributionAuthor.setVisibility(View.VISIBLE); 199 attributionDescription.setText("Lorem ipsum dolor sit amet, consectetur adipiscing" 200 + " elit. Sed imperdiet et mauris molestie laoreet. Proin volutpat elit nec" 201 + " magna tempus, ac aliquet lectus volutpat."); 202 attributionDescription.setVisibility(View.VISIBLE); 203 attributionExploreButton.setText("Explore"); 204 attributionExploreButton.setVisibility(View.VISIBLE); 205 spacer.setVisibility(View.VISIBLE); 206 return; 207 } 208 209 final PackageManager pm = getPackageManager(); 210 211 // Set attribution title 212 final CharSequence title = info.loadLabel(pm); 213 if (!TextUtils.isEmpty(title)) { 214 attributionTitle.setText(title); 215 attributionTitle.setVisibility(View.VISIBLE); 216 } 217 218 // Don't show other meta data if attribute showMetadataInPreview is set to False 219 if (!info.getShowMetadataInPreview()) { 220 return; 221 } 222 223 // Set attribution author 224 try { 225 final CharSequence author = info.loadAuthor(pm); 226 if (!TextUtils.isEmpty(author)) { 227 attributionAuthor.setText(author); 228 attributionAuthor.setVisibility(View.VISIBLE); 229 } 230 } catch (NotFoundException e) { 231 // It's expected if the live wallpaper doesn't provide this information 232 } 233 234 // Set attribution description 235 try { 236 final CharSequence description = info.loadDescription(pm); 237 if (!TextUtils.isEmpty(description)) { 238 attributionDescription.setText(description); 239 attributionDescription.setVisibility(View.VISIBLE); 240 } 241 } catch (NotFoundException e) { 242 // It's expected if the live wallpaper doesn't provide this information 243 } 244 245 // Set context information 246 try { 247 final Uri contextUri = info.loadContextUri(pm); 248 if (contextUri != null) { 249 final Intent intent = new Intent(Intent.ACTION_VIEW, contextUri); 250 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 251 attributionExploreButton.setOnClickListener(v -> { 252 try { 253 startActivity(intent); 254 } catch (ActivityNotFoundException e) { 255 Log.e(LOG_TAG, "Couldn't find activity for context link.", e); 256 } 257 }); 258 attributionExploreButton.setVisibility(View.VISIBLE); 259 spacer.setVisibility(View.VISIBLE); 260 261 // Update context description string if it's provided 262 final CharSequence contextDescription = info.loadContextDescription(pm); 263 if (!TextUtils.isEmpty(contextDescription)) { 264 attributionExploreButton.setText(contextDescription); 265 } 266 } 267 } catch (NotFoundException e) { 268 // It's expected if the wallpaper doesn't provide this information 269 } 270 } 271 272 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) getSettingsSliceUri(@onNull WallpaperInfo info)273 protected Uri getSettingsSliceUri(@NonNull WallpaperInfo info) { 274 return info.getSettingsSliceUri(); 275 } 276 initSettingsPage(WallpaperInfo info)277 private void initSettingsPage(WallpaperInfo info) { 278 final Uri uriSettingsSlice = getSettingsSliceUri(info); 279 if (uriSettingsSlice == null) { 280 return; 281 } 282 283 final View pageSettings = getLayoutInflater().inflate(R.layout.page_settings, 284 null /* root */); 285 final Button setWallpaperButton = pageSettings.findViewById( 286 R.id.preview_attribution_pane_set_wallpaper_button); 287 288 mSliceViewSettings = pageSettings.findViewById(R.id.settings_slice); 289 mSliceViewSettings.setMode(SliceView.MODE_LARGE); 290 mSliceViewSettings.setScrollable(false); 291 292 // Set LiveData for SliceView 293 mLiveDataSettings = SliceLiveData.fromUri(this /* context */, uriSettingsSlice); 294 mLiveDataSettings.observeForever(mSliceViewSettings); 295 296 setWallpaperButton.setOnClickListener(this::setLiveWallpaper); 297 298 mPages.add(Pair.create(getResources().getString(R.string.tab_customize), pageSettings)); 299 } 300 populateBottomPane()301 private void populateBottomPane() { 302 mViewBottomPane = findViewById(R.id.bottom_pane); 303 mViewPager = findViewById(R.id.viewpager); 304 mTabLayout = findViewById(R.id.tablayout); 305 306 mBottomSheetBehavior = BottomSheetBehavior.from(mViewBottomPane); 307 mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); 308 309 // Create PagerAdapter 310 final PagerAdapter pagerAdapter = new PagerAdapter() { 311 @NonNull 312 @Override 313 public Object instantiateItem(ViewGroup container, int position) { 314 final View page = mPages.get(position).second; 315 container.addView(page); 316 return page; 317 } 318 319 @Override 320 public void destroyItem(@NonNull ViewGroup container, int position, 321 @NonNull Object object) { 322 if (object instanceof View) { 323 container.removeView((View) object); 324 } 325 } 326 327 @Override 328 public int getCount() { 329 return mPages.size(); 330 } 331 332 @Override 333 public CharSequence getPageTitle(int position) { 334 try { 335 return mPages.get(position).first; 336 } catch (IndexOutOfBoundsException e) { 337 return null; 338 } 339 } 340 341 @Override 342 public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { 343 return (view == object); 344 } 345 }; 346 347 // Add OnPageChangeListener to re-measure ViewPager's height 348 mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { 349 @Override 350 public void onPageSelected(int position) { 351 mViewPager.requestLayout(); 352 } 353 }); 354 355 // Set PagerAdapter 356 mViewPager.setAdapter(pagerAdapter); 357 358 // Make TabLayout visible if there are more than one page 359 if (mPages.size() > 1) { 360 mTabLayout.setVisibility(View.VISIBLE); 361 mTabLayout.setupWithViewPager(mViewPager); 362 } 363 364 // Initializes a rounded rectangle outline and clips the upper corners to be rounded. 365 mViewBottomPane.setOutlineProvider(new ViewOutlineProvider() { 366 private final int radius = getResources().getDimensionPixelSize( 367 R.dimen.preview_viewpager_round_radius); 368 369 @Override 370 public void getOutline(View view, Outline outline) { 371 outline.setRoundRect(0 /* left */, 0 /* top */, view.getWidth(), 372 view.getHeight() + radius, radius); 373 } 374 }); 375 mViewBottomPane.setClipToOutline(true); 376 } 377 378 @Override onCreateOptionsMenu(Menu menu)379 public boolean onCreateOptionsMenu(Menu menu) { 380 getMenuInflater().inflate(R.menu.menu_preview, menu); 381 setupPreviewMenu(menu); 382 menu.findItem(R.id.configure).setVisible(mSettingsIntent != null); 383 menu.findItem(R.id.delete_wallpaper).setVisible(mDeleteIntent != null); 384 return super.onCreateOptionsMenu(menu); 385 } 386 setupPreviewMenu(Menu menu)387 private void setupPreviewMenu(Menu menu) { 388 mPreview = (CheckBox) menu.findItem(R.id.preview).getActionView(); 389 mPreview.setOnClickListener(this::setPreviewBehavior); 390 391 BottomSheetCallback callback = new BottomSheetCallback() { 392 @Override 393 public void onStateChanged(View bottomSheet, int newState) { 394 switch (newState) { 395 case BottomSheetBehavior.STATE_COLLAPSED: 396 setPreviewChecked(true /* checked */); 397 break; 398 case BottomSheetBehavior.STATE_EXPANDED: 399 setPreviewChecked(false /* checked */); 400 break; 401 } 402 } 403 404 @Override 405 public void onSlide(View bottomSheet, float slideOffset) { 406 mTabLayout.setAlpha(slideOffset); 407 mViewPager.setAlpha(slideOffset); 408 } 409 }; 410 mBottomSheetBehavior.setBottomSheetCallback(callback); 411 412 int state = mBottomSheetBehavior.getState(); 413 callback.onStateChanged(mViewBottomPane, state); 414 switch (state) { 415 case BottomSheetBehavior.STATE_COLLAPSED: 416 callback.onSlide(mViewBottomPane, 0f); 417 break; 418 case BottomSheetBehavior.STATE_EXPANDED: 419 callback.onSlide(mViewBottomPane, 1f); 420 break; 421 } 422 } 423 setPreviewChecked(boolean checked)424 private void setPreviewChecked(boolean checked) { 425 if (mPreview != null) { 426 mPreview.setChecked(checked); 427 int resId = checked ? R.string.expand_attribution_panel 428 : R.string.collapse_attribution_panel; 429 mPreview.setContentDescription(getResources().getString(resId)); 430 } 431 } 432 setPreviewBehavior(final View v)433 private void setPreviewBehavior(final View v) { 434 CheckBox checkbox = (CheckBox) v; 435 if (checkbox.isChecked()) { 436 mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); 437 } else { 438 mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); 439 } 440 } 441 setLiveWallpaper(final View v)442 public void setLiveWallpaper(final View v) { 443 if (mWallpaperManager.getWallpaperInfo() != null 444 && mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0) { 445 // The lock screen does not have a distinct wallpaper and the current wallpaper is a 446 // live wallpaper, so since we cannot preserve any static imagery on the lock screen, 447 // set the live wallpaper directly without giving the user a destination option. 448 try { 449 setLiveWallpaper(v.getRootView().getWindowToken()); 450 setResult(RESULT_OK); 451 } catch (RuntimeException e) { 452 Log.w(LOG_TAG, "Failure setting wallpaper", e); 453 } 454 finish(); 455 } else { 456 // Otherwise, prompt to either set on home or both home and lock screen. 457 final Context themedContext = new ContextThemeWrapper(this /* base */, 458 android.R.style.Theme_DeviceDefault_Settings); 459 new AlertDialog.Builder(themedContext) 460 .setTitle(R.string.set_live_wallpaper) 461 .setAdapter(new WallpaperTargetAdapter(themedContext), 462 new DialogInterface.OnClickListener() { 463 @Override 464 public void onClick(DialogInterface dialog, int which) { 465 try { 466 setLiveWallpaper(v.getRootView().getWindowToken()); 467 if (which == 1) { 468 // "Home screen and lock screen"; clear the lock 469 // screen so it 470 // shows through to the live wallpaper on home. 471 mWallpaperManager.clear(WallpaperManager.FLAG_LOCK); 472 } 473 setResult(RESULT_OK); 474 } catch (RuntimeException | IOException e) { 475 Log.w(LOG_TAG, "Failure setting wallpaper", e); 476 } 477 finish(); 478 } 479 }) 480 .show(); 481 } 482 } 483 setLiveWallpaper(IBinder windowToken)484 private void setLiveWallpaper(IBinder windowToken) { 485 mWallpaperManager.setWallpaperComponent(mWallpaperIntent.getComponent()); 486 mWallpaperManager.setWallpaperOffsetSteps(0.5f /* xStep */, 0.0f /* yStep */); 487 mWallpaperManager.setWallpaperOffsets(windowToken, 0.5f /* xOffset */, 0.0f /* yOffset */); 488 } 489 490 @VisibleForTesting deleteLiveWallpaper()491 void deleteLiveWallpaper() { 492 if (mDeleteIntent != null) { 493 startService(mDeleteIntent); 494 finish(); 495 } 496 } 497 showDeleteConfirmDialog()498 private void showDeleteConfirmDialog() { 499 final AlertDialog alertDialog = new AlertDialog.Builder(this /* context */, 500 R.style.AlertDialogStyle) 501 .setMessage(R.string.delete_wallpaper_confirmation) 502 .setPositiveButton(R.string.delete_live_wallpaper, 503 (dialog, which) -> deleteLiveWallpaper()) 504 .setNegativeButton(android.R.string.cancel, null /* listener */) 505 .create(); 506 alertDialog.show(); 507 } 508 509 @Override onOptionsItemSelected(MenuItem item)510 public boolean onOptionsItemSelected(MenuItem item) { 511 int id = item.getItemId(); 512 if (id == R.id.configure) { 513 startActivity(mSettingsIntent); 514 return true; 515 } else if (id == R.id.delete_wallpaper) { 516 showDeleteConfirmDialog(); 517 return true; 518 } else if (id == android.R.id.home) { 519 onBackPressed(); 520 return true; 521 } 522 return super.onOptionsItemSelected(item); 523 } 524 525 @Override onResume()526 public void onResume() { 527 super.onResume(); 528 if (mWallpaperConnection != null) { 529 mWallpaperConnection.setVisibility(true); 530 } 531 } 532 533 @Override onPause()534 public void onPause() { 535 super.onPause(); 536 if (mWallpaperConnection != null) { 537 mWallpaperConnection.setVisibility(false); 538 } 539 } 540 541 @Override onDestroy()542 protected void onDestroy () { 543 if (mLiveDataSettings != null && mLiveDataSettings.hasObservers()) { 544 mLiveDataSettings.removeObserver(mSliceViewSettings); 545 mLiveDataSettings = null; 546 } 547 if (mWallpaperConnection != null) { 548 mWallpaperConnection.disconnect(); 549 } 550 mWallpaperConnection = null; 551 super.onDestroy(); 552 } 553 554 @Override dispatchTouchEvent(MotionEvent ev)555 public boolean dispatchTouchEvent(MotionEvent ev) { 556 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 557 onUserInteraction(); 558 } 559 boolean handled = getWindow().superDispatchTouchEvent(ev); 560 if (!handled) { 561 handled = onTouchEvent(ev); 562 } 563 564 if (!handled && mWallpaperConnection != null && mWallpaperConnection.mEngine != null) { 565 MotionEvent dup = MotionEvent.obtainNoHistory(ev); 566 try { 567 mWallpaperConnection.mEngine.dispatchPointer(dup); 568 } catch (RemoteException e) { 569 } 570 571 int action = ev.getActionMasked(); 572 try { 573 if (action == MotionEvent.ACTION_UP) { 574 mWallpaperConnection.mEngine.dispatchWallpaperCommand( 575 WallpaperManager.COMMAND_TAP, 576 (int) ev.getX(), (int) ev.getY(), 0, null); 577 } else if (action == MotionEvent.ACTION_POINTER_UP) { 578 int pointerIndex = ev.getActionIndex(); 579 mWallpaperConnection.mEngine.dispatchWallpaperCommand( 580 WallpaperManager.COMMAND_SECONDARY_TAP, 581 (int) ev.getX(pointerIndex), (int) ev.getY(pointerIndex), 0, null); 582 } 583 } catch (RemoteException e) { 584 } 585 } 586 return handled; 587 } 588 589 class WallpaperConnection extends IWallpaperConnection.Stub implements ServiceConnection { 590 final Intent mIntent; 591 IWallpaperService mService; 592 IWallpaperEngine mEngine; 593 boolean mConnected; 594 boolean mIsVisible; 595 boolean mIsEngineVisible; 596 WallpaperInfo mInfo; 597 WallpaperConnection(Intent intent, WallpaperInfo info)598 WallpaperConnection(Intent intent, WallpaperInfo info) { 599 mIntent = intent; 600 mInfo = info; 601 } 602 connect()603 public boolean connect() { 604 synchronized (this) { 605 if (!bindService(mIntent, this, Context.BIND_AUTO_CREATE)) { 606 return false; 607 } 608 609 mConnected = true; 610 return true; 611 } 612 } 613 disconnect()614 public void disconnect() { 615 synchronized (this) { 616 mConnected = false; 617 if (mEngine != null) { 618 try { 619 mEngine.destroy(); 620 } catch (RemoteException e) { 621 // Ignore 622 } 623 mEngine = null; 624 } 625 try { 626 unbindService(this); 627 } catch (IllegalArgumentException e) { 628 Log.w(LOG_TAG, "Can't unbind wallpaper service. " 629 + "It might have crashed, just ignoring.", e); 630 } 631 mService = null; 632 } 633 } 634 635 @Override onLocalWallpaperColorsChanged(RectF area, WallpaperColors colors, int displayId)636 public void onLocalWallpaperColorsChanged(RectF area, 637 WallpaperColors colors, int displayId) { 638 639 } 640 onServiceConnected(ComponentName name, IBinder service)641 public void onServiceConnected(ComponentName name, IBinder service) { 642 if (mWallpaperConnection == this) { 643 mService = IWallpaperService.Stub.asInterface(service); 644 try { 645 final int displayId = getWindow().getDecorView().getDisplay().getDisplayId(); 646 final View root = getWindow().getDecorView(); 647 mService.attach(this, root.getWindowToken(), 648 LayoutParams.TYPE_APPLICATION_MEDIA, true, root.getWidth(), 649 root.getHeight(), new Rect(0, 0, 0, 0), displayId, 650 WallpaperManager.FLAG_SYSTEM, mInfo); 651 } catch (RemoteException e) { 652 Log.w(LOG_TAG, "Failed attaching wallpaper; clearing", e); 653 } 654 } 655 } 656 onServiceDisconnected(ComponentName name)657 public void onServiceDisconnected(ComponentName name) { 658 mService = null; 659 mEngine = null; 660 if (mWallpaperConnection == this) { 661 Log.w(LOG_TAG, "Wallpaper service gone: " + name); 662 } 663 } 664 attachEngine(IWallpaperEngine engine, int displayId)665 public void attachEngine(IWallpaperEngine engine, int displayId) { 666 synchronized (this) { 667 if (mConnected) { 668 mEngine = engine; 669 if (mIsVisible) { 670 setEngineVisibility(true); 671 } 672 } else { 673 try { 674 engine.destroy(); 675 } catch (RemoteException e) { 676 // Ignore 677 } 678 } 679 } 680 } 681 setWallpaper(String name)682 public ParcelFileDescriptor setWallpaper(String name) { 683 return null; 684 } 685 686 @Override onWallpaperColorsChanged(WallpaperColors colors, int displayId)687 public void onWallpaperColorsChanged(WallpaperColors colors, int displayId) 688 throws RemoteException { 689 690 } 691 692 @Override engineShown(IWallpaperEngine engine)693 public void engineShown(IWallpaperEngine engine) throws RemoteException { 694 mLoading.post(() -> { 695 mLoading.animate() 696 .alpha(0f) 697 .setDuration(220) 698 .setStartDelay(300) 699 .setInterpolator(AnimationUtils.loadInterpolator(LiveWallpaperPreview.this, 700 android.R.interpolator.fast_out_linear_in)) 701 .withEndAction(() -> mLoading.setVisibility(View.INVISIBLE)); 702 }); 703 } 704 setVisibility(boolean visible)705 public void setVisibility(boolean visible) { 706 mIsVisible = visible; 707 setEngineVisibility(visible); 708 } 709 setEngineVisibility(boolean visible)710 private void setEngineVisibility(boolean visible) { 711 if (mEngine != null && visible != mIsEngineVisible) { 712 try { 713 mEngine.setVisibility(visible); 714 mIsEngineVisible = visible; 715 } catch (RemoteException e) { 716 Log.w(LOG_TAG, "Failure setting wallpaper visibility ", e); 717 } 718 } 719 } 720 } 721 722 private static class WallpaperTargetAdapter extends ArrayAdapter<CharSequence> { 723 WallpaperTargetAdapter(Context context)724 public WallpaperTargetAdapter(Context context) { 725 super(context, R.layout.wallpaper_target_dialog_item, 726 context.getResources().getTextArray(R.array.which_wallpaper_options)); 727 } 728 729 @Override hasStableIds()730 public boolean hasStableIds() { 731 return true; 732 } 733 734 @Override getItemId(int position)735 public long getItemId(int position) { 736 return position; 737 } 738 739 @Override getView(int position, View convertView, ViewGroup parent)740 public View getView(int position, View convertView, ViewGroup parent) { 741 TextView tv = (TextView) super.getView(position, convertView, parent); 742 tv.setCompoundDrawablesRelativeWithIntrinsicBounds( 743 position == 0 ? R.drawable.ic_home : R.drawable.ic_device, 0, 0, 0); 744 return tv; 745 } 746 } 747 } 748