Home | History | Annotate | Download | only in livepicker
      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.WallpaperInfo;
     22 import android.app.WallpaperManager;
     23 import android.content.ActivityNotFoundException;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.DialogInterface;
     27 import android.content.Intent;
     28 import android.content.ServiceConnection;
     29 import android.content.pm.PackageManager;
     30 import android.content.res.Resources.NotFoundException;
     31 import android.graphics.Rect;
     32 import android.net.Uri;
     33 import android.os.Bundle;
     34 import android.os.IBinder;
     35 import android.os.ParcelFileDescriptor;
     36 import android.os.RemoteException;
     37 import android.service.wallpaper.IWallpaperConnection;
     38 import android.service.wallpaper.IWallpaperEngine;
     39 import android.service.wallpaper.IWallpaperService;
     40 import android.service.wallpaper.WallpaperService;
     41 import android.service.wallpaper.WallpaperSettingsActivity;
     42 import android.support.design.widget.BottomSheetBehavior;
     43 import android.text.TextUtils;
     44 import android.util.Log;
     45 import android.view.ContextThemeWrapper;
     46 import android.view.Menu;
     47 import android.view.MenuItem;
     48 import android.view.MotionEvent;
     49 import android.view.View;
     50 import android.view.View.OnClickListener;
     51 import android.view.ViewGroup;
     52 import android.view.WindowManager.LayoutParams;
     53 import android.view.animation.AnimationUtils;
     54 import android.widget.ArrayAdapter;
     55 import android.widget.Button;
     56 import android.widget.ImageButton;
     57 import android.widget.TextView;
     58 import android.widget.Toolbar;
     59 
     60 import java.io.IOException;
     61 
     62 public class LiveWallpaperPreview extends Activity {
     63     static final String EXTRA_LIVE_WALLPAPER_INFO = "android.live_wallpaper.info";
     64 
     65     private static final String LOG_TAG = "LiveWallpaperPreview";
     66 
     67     private static final boolean SHOW_DUMMY_DATA = false;
     68 
     69     private WallpaperManager mWallpaperManager;
     70     private WallpaperConnection mWallpaperConnection;
     71 
     72     private String mSettings;
     73     private String mPackageName;
     74     private Intent mWallpaperIntent;
     75 
     76     private TextView mAttributionTitle;
     77     private TextView mAttributionSubtitle1;
     78     private TextView mAttributionSubtitle2;
     79     private Button mAttributionExploreButton;
     80     private ImageButton mPreviewPaneArrow;
     81     private View mBottomSheet;
     82     private View mSpacer;
     83     private View mLoading;
     84 
     85     @Override
     86     protected void onCreate(Bundle savedInstanceState) {
     87         super.onCreate(savedInstanceState);
     88         init();
     89     }
     90 
     91     protected void init() {
     92         Bundle extras = getIntent().getExtras();
     93         WallpaperInfo info = extras.getParcelable(EXTRA_LIVE_WALLPAPER_INFO);
     94         if (info == null) {
     95             setResult(RESULT_CANCELED);
     96             finish();
     97         }
     98         initUI(info);
     99     }
    100 
    101     protected void initUI(WallpaperInfo info) {
    102         getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    103                 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    104                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    105         setContentView(R.layout.live_wallpaper_preview);
    106         mAttributionTitle = (TextView) findViewById(R.id.preview_attribution_pane_title);
    107         mAttributionSubtitle1 = (TextView) findViewById(R.id.preview_attribution_pane_subtitle1);
    108         mAttributionSubtitle2 = (TextView) findViewById(R.id.preview_attribution_pane_subtitle2);
    109         mAttributionExploreButton = (Button) findViewById(
    110                 R.id.preview_attribution_pane_explore_button);
    111         mPreviewPaneArrow = (ImageButton) findViewById(R.id.preview_attribution_pane_arrow);
    112         mBottomSheet = findViewById(R.id.bottom_sheet);
    113         mSpacer = findViewById(R.id.spacer);
    114         mLoading = findViewById(R.id.loading);
    115 
    116         mSettings = info.getSettingsActivity();
    117         mPackageName = info.getPackageName();
    118         mWallpaperIntent = new Intent(WallpaperService.SERVICE_INTERFACE)
    119                 .setClassName(info.getPackageName(), info.getServiceName());
    120 
    121         setActionBar((Toolbar) findViewById(R.id.toolbar));
    122         getActionBar().setDisplayHomeAsUpEnabled(true);
    123         getActionBar().setDisplayShowTitleEnabled(false);
    124 
    125         mWallpaperManager = WallpaperManager.getInstance(this);
    126         mWallpaperConnection = new WallpaperConnection(mWallpaperIntent);
    127 
    128         populateAttributionPane(info);
    129     }
    130 
    131     private void populateAttributionPane(WallpaperInfo info) {
    132         if (!info.getShowMetadataInPreview() && !SHOW_DUMMY_DATA) {
    133             mBottomSheet.setVisibility(View.GONE);
    134             return;
    135         }
    136         final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
    137 
    138         OnClickListener onClickListener = new OnClickListener() {
    139             @Override
    140             public void onClick(View view) {
    141                 if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) {
    142                     bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
    143                 } else if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
    144                     bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
    145                 }
    146             }
    147         };
    148         mAttributionTitle.setOnClickListener(onClickListener);
    149         mPreviewPaneArrow.setOnClickListener(onClickListener);
    150 
    151         bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
    152             @Override
    153             public void onStateChanged(View bottomSheet, int newState) {
    154                 if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
    155                     mPreviewPaneArrow.setImageResource(R.drawable.ic_keyboard_arrow_up_white_24dp);
    156                     mPreviewPaneArrow.setContentDescription(
    157                             getResources().getString(R.string.expand_attribution_panel));
    158                 } else if (newState == BottomSheetBehavior.STATE_EXPANDED) {
    159                     mPreviewPaneArrow.setImageResource(
    160                             R.drawable.ic_keyboard_arrow_down_white_24dp);
    161                     mPreviewPaneArrow.setContentDescription(
    162                             getResources().getString(R.string.collapse_attribution_panel));
    163                 }
    164             }
    165 
    166             @Override
    167             public void onSlide(View bottomSheet, float slideOffset) {
    168                 float alpha;
    169                 if (slideOffset >= 0) {
    170                     alpha = slideOffset;
    171                 } else {
    172                     alpha = 1f - slideOffset;
    173                 }
    174                 mAttributionTitle.setAlpha(slideOffset);
    175                 mAttributionSubtitle1.setAlpha(slideOffset);
    176                 mAttributionSubtitle2.setAlpha(slideOffset);
    177                 mAttributionExploreButton.setAlpha(slideOffset);
    178             }
    179         });
    180 
    181         bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
    182         mPreviewPaneArrow.setImageResource(R.drawable.ic_keyboard_arrow_down_white_24dp);
    183 
    184         if (SHOW_DUMMY_DATA) {
    185             mAttributionTitle.setText("Diorama, Yosemite");
    186             mAttributionSubtitle1.setText("Live Earth Collection - Android Earth");
    187             mAttributionSubtitle2.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit."
    188                     + " Sed imperdiet et mauris molestie laoreet. Proin volutpat elit nec magna"
    189                     + " tempus, ac aliquet lectus volutpat.");
    190             mAttributionExploreButton.setText("Explore");
    191         } else {
    192             PackageManager pm = getPackageManager();
    193 
    194             CharSequence title = info.loadLabel(pm);
    195             if (!TextUtils.isEmpty(title)) {
    196                 mAttributionTitle.setText(title);
    197             } else {
    198                 mAttributionTitle.setVisibility(View.GONE);
    199             }
    200 
    201             try {
    202                 CharSequence author = info.loadAuthor(pm);
    203                 if (TextUtils.isEmpty(author)) {
    204                     throw new NotFoundException();
    205                 }
    206                 mAttributionSubtitle1.setText(author);
    207             } catch (NotFoundException e) {
    208                 mAttributionSubtitle1.setVisibility(View.GONE);
    209             }
    210 
    211             try {
    212                 CharSequence description = info.loadDescription(pm);
    213                 if (TextUtils.isEmpty(description)) {
    214                     throw new NotFoundException();
    215                 }
    216                 mAttributionSubtitle2.setText(description);
    217             } catch (NotFoundException e) {
    218                 mAttributionSubtitle2.setVisibility(View.GONE);
    219             }
    220 
    221             try {
    222                 Uri contextUri = info.loadContextUri(pm);
    223                 CharSequence contextDescription = info.loadContextDescription(pm);
    224                 if (contextUri == null) {
    225                     throw new NotFoundException();
    226                 }
    227                 mAttributionExploreButton.setText(contextDescription);
    228                 mAttributionExploreButton.setOnClickListener(v -> {
    229                     Intent intent = new Intent(Intent.ACTION_VIEW, contextUri);
    230                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    231                     try {
    232                         startActivity(intent);
    233                     } catch (ActivityNotFoundException e) {
    234                         Log.e(LOG_TAG, "Couldn't find activity for context link.", e);
    235                     }
    236                 });
    237             } catch (NotFoundException e) {
    238                 mAttributionExploreButton.setVisibility(View.GONE);
    239                 mSpacer.setVisibility(View.VISIBLE);
    240             }
    241         }
    242 
    243 
    244     }
    245 
    246     @Override
    247     public boolean onCreateOptionsMenu(Menu menu) {
    248         getMenuInflater().inflate(R.menu.menu_preview, menu);
    249         menu.findItem(R.id.configure).setVisible(mSettings != null);
    250         menu.findItem(R.id.set_wallpaper).getActionView().setOnClickListener(
    251                 this::setLiveWallpaper);
    252         return super.onCreateOptionsMenu(menu);
    253     }
    254 
    255     public void setLiveWallpaper(final View v) {
    256         if (mWallpaperManager.getWallpaperInfo() != null
    257             && mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0) {
    258             // The lock screen does not have a distinct wallpaper and the current wallpaper is a
    259             // live wallpaper, so since we cannot preserve any static imagery on the lock screen,
    260             // set the live wallpaper directly without giving the user a destination option.
    261             try {
    262                 setLiveWallpaper(v.getRootView().getWindowToken());
    263                 setResult(RESULT_OK);
    264             } catch (RuntimeException e) {
    265                 Log.w(LOG_TAG, "Failure setting wallpaper", e);
    266             }
    267             finish();
    268         } else {
    269             // Otherwise, prompt to either set on home or both home and lock screen.
    270             Context themedContext = new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault_Settings);
    271             new AlertDialog.Builder(themedContext)
    272                     .setTitle(R.string.set_live_wallpaper)
    273                     .setAdapter(new WallpaperTargetAdapter(themedContext), new DialogInterface.OnClickListener() {
    274                         @Override
    275                         public void onClick(DialogInterface dialog, int which) {
    276                             try {
    277                                 setLiveWallpaper(v.getRootView().getWindowToken());
    278                                 if (which == 1) {
    279                                     // "Home screen and lock screen"; clear the lock screen so it
    280                                     // shows through to the live wallpaper on home.
    281                                     mWallpaperManager.clear(WallpaperManager.FLAG_LOCK);
    282                                 }
    283                                 setResult(RESULT_OK);
    284                             } catch (RuntimeException|IOException e) {
    285                                 Log.w(LOG_TAG, "Failure setting wallpaper", e);
    286                             }
    287                             finish();
    288                         }
    289                     })
    290                     .show();
    291         }
    292     }
    293 
    294     private void setLiveWallpaper(IBinder windowToken) {
    295         mWallpaperManager.setWallpaperComponent(mWallpaperIntent.getComponent());
    296         mWallpaperManager.setWallpaperOffsetSteps(0.5f, 0.0f);
    297         mWallpaperManager.setWallpaperOffsets(windowToken, 0.5f, 0.0f);
    298     }
    299 
    300     @Override
    301     public boolean onOptionsItemSelected(MenuItem item) {
    302         int id = item.getItemId();
    303         if (id == R.id.configure) {
    304             Intent intent = new Intent();
    305             intent.setComponent(new ComponentName(mPackageName, mSettings));
    306             intent.putExtra(WallpaperSettingsActivity.EXTRA_PREVIEW_MODE, true);
    307             startActivity(intent);
    308             return true;
    309         } else if (id == R.id.set_wallpaper) {
    310             setLiveWallpaper(getWindow().getDecorView());
    311             return true;
    312         } else if (id == android.R.id.home) {
    313             onBackPressed();
    314             return true;
    315         }
    316         return super.onOptionsItemSelected(item);
    317     }
    318 
    319     @Override
    320     public void onResume() {
    321         super.onResume();
    322         if (mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
    323             try {
    324                 mWallpaperConnection.mEngine.setVisibility(true);
    325             } catch (RemoteException e) {
    326                 // Ignore
    327             }
    328         }
    329     }
    330 
    331     @Override
    332     public void onPause() {
    333         super.onPause();
    334         if (mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
    335             try {
    336                 mWallpaperConnection.mEngine.setVisibility(false);
    337             } catch (RemoteException e) {
    338                 // Ignore
    339             }
    340         }
    341     }
    342 
    343     @Override
    344     public void onAttachedToWindow() {
    345         super.onAttachedToWindow();
    346 
    347         getWindow().getDecorView().post(new Runnable() {
    348             public void run() {
    349                 if (!mWallpaperConnection.connect()) {
    350                     mWallpaperConnection = null;
    351                 }
    352             }
    353         });
    354     }
    355 
    356     @Override
    357     public void onDetachedFromWindow() {
    358         super.onDetachedFromWindow();
    359 
    360         if (mWallpaperConnection != null) {
    361             mWallpaperConnection.disconnect();
    362         }
    363         mWallpaperConnection = null;
    364     }
    365 
    366     @Override
    367     public boolean dispatchTouchEvent(MotionEvent ev) {
    368         if (mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
    369             MotionEvent dup = MotionEvent.obtainNoHistory(ev);
    370             try {
    371                 mWallpaperConnection.mEngine.dispatchPointer(dup);
    372             } catch (RemoteException e) {
    373             }
    374         }
    375 
    376         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    377             onUserInteraction();
    378         }
    379         boolean handled = getWindow().superDispatchTouchEvent(ev);
    380         if (!handled) {
    381             handled = onTouchEvent(ev);
    382         }
    383 
    384         if (!handled && mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
    385             int action = ev.getActionMasked();
    386             try {
    387                 if (action == MotionEvent.ACTION_UP) {
    388                     mWallpaperConnection.mEngine.dispatchWallpaperCommand(
    389                             WallpaperManager.COMMAND_TAP,
    390                             (int) ev.getX(), (int) ev.getY(), 0, null);
    391                 } else if (action == MotionEvent.ACTION_POINTER_UP) {
    392                     int pointerIndex = ev.getActionIndex();
    393                     mWallpaperConnection.mEngine.dispatchWallpaperCommand(
    394                             WallpaperManager.COMMAND_SECONDARY_TAP,
    395                             (int) ev.getX(pointerIndex), (int) ev.getY(pointerIndex), 0, null);
    396                 }
    397             } catch (RemoteException e) {
    398             }
    399         }
    400         return handled;
    401     }
    402 
    403     class WallpaperConnection extends IWallpaperConnection.Stub implements ServiceConnection {
    404         final Intent mIntent;
    405         IWallpaperService mService;
    406         IWallpaperEngine mEngine;
    407         boolean mConnected;
    408 
    409         WallpaperConnection(Intent intent) {
    410             mIntent = intent;
    411         }
    412 
    413         public boolean connect() {
    414             synchronized (this) {
    415                 if (!bindService(mIntent, this, Context.BIND_AUTO_CREATE)) {
    416                     return false;
    417                 }
    418 
    419                 mConnected = true;
    420                 return true;
    421             }
    422         }
    423 
    424         public void disconnect() {
    425             synchronized (this) {
    426                 mConnected = false;
    427                 if (mEngine != null) {
    428                     try {
    429                         mEngine.destroy();
    430                     } catch (RemoteException e) {
    431                         // Ignore
    432                     }
    433                     mEngine = null;
    434                 }
    435                 unbindService(this);
    436                 mService = null;
    437             }
    438         }
    439 
    440         public void onServiceConnected(ComponentName name, IBinder service) {
    441             if (mWallpaperConnection == this) {
    442                 mService = IWallpaperService.Stub.asInterface(service);
    443                 try {
    444                     final View root = getWindow().getDecorView();
    445                     mService.attach(this, root.getWindowToken(),
    446                             LayoutParams.TYPE_APPLICATION_MEDIA,
    447                             true, root.getWidth(), root.getHeight(),
    448                             new Rect(0, 0, 0, 0));
    449                 } catch (RemoteException e) {
    450                     Log.w(LOG_TAG, "Failed attaching wallpaper; clearing", e);
    451                 }
    452             }
    453         }
    454 
    455         public void onServiceDisconnected(ComponentName name) {
    456             mService = null;
    457             mEngine = null;
    458             if (mWallpaperConnection == this) {
    459                 Log.w(LOG_TAG, "Wallpaper service gone: " + name);
    460             }
    461         }
    462 
    463         public void attachEngine(IWallpaperEngine engine) {
    464             synchronized (this) {
    465                 if (mConnected) {
    466                     mEngine = engine;
    467                     try {
    468                         engine.setVisibility(true);
    469                     } catch (RemoteException e) {
    470                         // Ignore
    471                     }
    472                 } else {
    473                     try {
    474                         engine.destroy();
    475                     } catch (RemoteException e) {
    476                         // Ignore
    477                     }
    478                 }
    479             }
    480         }
    481 
    482         public ParcelFileDescriptor setWallpaper(String name) {
    483             return null;
    484         }
    485 
    486         @Override
    487         public void engineShown(IWallpaperEngine engine) throws RemoteException {
    488             mLoading.post(() -> {
    489                 mLoading.animate()
    490                         .alpha(0f)
    491                         .setDuration(220)
    492                         .setInterpolator(AnimationUtils.loadInterpolator(LiveWallpaperPreview.this,
    493                                 android.R.interpolator.fast_out_linear_in))
    494                         .withEndAction(() -> mLoading.setVisibility(View.INVISIBLE));
    495             });
    496         }
    497     }
    498 
    499     private static class WallpaperTargetAdapter extends ArrayAdapter<CharSequence> {
    500 
    501         public WallpaperTargetAdapter(Context context) {
    502             super(context, R.layout.wallpaper_target_dialog_item,
    503                     context.getResources().getTextArray(R.array.which_wallpaper_options));
    504         }
    505 
    506         @Override
    507         public boolean hasStableIds() {
    508             return true;
    509         }
    510 
    511         @Override
    512         public long getItemId(int position) {
    513             return position;
    514         }
    515 
    516         @Override
    517         public View getView(int position, View convertView, ViewGroup parent) {
    518             TextView tv = (TextView) super.getView(position, convertView, parent);
    519             tv.setCompoundDrawablesRelativeWithIntrinsicBounds(
    520                     position == 0 ? R.drawable.ic_home : R.drawable.ic_device, 0, 0, 0);
    521             return tv;
    522         }
    523     }
    524 }
    525