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