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