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