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.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0) {
    257             // The lock screen does not have a wallpaper, so no need to prompt; can only set both.
    258             try {
    259                 setLiveWallpaper(v.getRootView().getWindowToken());
    260                 setResult(RESULT_OK);
    261             } catch (RuntimeException e) {
    262                 Log.w(LOG_TAG, "Failure setting wallpaper", e);
    263             }
    264             finish();
    265         } else {
    266             // Otherwise, prompt to either set on home or both home and lock screen.
    267             Context themedContext = new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault_Settings);
    268             new AlertDialog.Builder(themedContext)
    269                     .setTitle(R.string.set_live_wallpaper)
    270                     .setAdapter(new WallpaperTargetAdapter(themedContext), new DialogInterface.OnClickListener() {
    271                         @Override
    272                         public void onClick(DialogInterface dialog, int which) {
    273                             try {
    274                                 setLiveWallpaper(v.getRootView().getWindowToken());
    275                                 if (which == 1) {
    276                                     // "Home screen and lock screen"; clear the lock screen so it
    277                                     // shows through to the live wallpaper on home.
    278                                     mWallpaperManager.clear(WallpaperManager.FLAG_LOCK);
    279                                 }
    280                                 setResult(RESULT_OK);
    281                             } catch (RuntimeException|IOException e) {
    282                                 Log.w(LOG_TAG, "Failure setting wallpaper", e);
    283                             }
    284                             finish();
    285                         }
    286                     })
    287                     .show();
    288         }
    289     }
    290 
    291     private void setLiveWallpaper(IBinder windowToken) {
    292         mWallpaperManager.setWallpaperComponent(mWallpaperIntent.getComponent());
    293         mWallpaperManager.setWallpaperOffsetSteps(0.5f, 0.0f);
    294         mWallpaperManager.setWallpaperOffsets(windowToken, 0.5f, 0.0f);
    295     }
    296 
    297     @Override
    298     public boolean onOptionsItemSelected(MenuItem item) {
    299         int id = item.getItemId();
    300         if (id == R.id.configure) {
    301             Intent intent = new Intent();
    302             intent.setComponent(new ComponentName(mPackageName, mSettings));
    303             intent.putExtra(WallpaperSettingsActivity.EXTRA_PREVIEW_MODE, true);
    304             startActivity(intent);
    305             return true;
    306         } else if (id == R.id.set_wallpaper) {
    307             setLiveWallpaper(getWindow().getDecorView());
    308             return true;
    309         } else if (id == android.R.id.home) {
    310             onBackPressed();
    311             return true;
    312         }
    313         return super.onOptionsItemSelected(item);
    314     }
    315 
    316     @Override
    317     public void onResume() {
    318         super.onResume();
    319         if (mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
    320             try {
    321                 mWallpaperConnection.mEngine.setVisibility(true);
    322             } catch (RemoteException e) {
    323                 // Ignore
    324             }
    325         }
    326     }
    327 
    328     @Override
    329     public void onPause() {
    330         super.onPause();
    331         if (mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
    332             try {
    333                 mWallpaperConnection.mEngine.setVisibility(false);
    334             } catch (RemoteException e) {
    335                 // Ignore
    336             }
    337         }
    338     }
    339 
    340     @Override
    341     public void onAttachedToWindow() {
    342         super.onAttachedToWindow();
    343 
    344         getWindow().getDecorView().post(new Runnable() {
    345             public void run() {
    346                 if (!mWallpaperConnection.connect()) {
    347                     mWallpaperConnection = null;
    348                 }
    349             }
    350         });
    351     }
    352 
    353     @Override
    354     public void onDetachedFromWindow() {
    355         super.onDetachedFromWindow();
    356 
    357         if (mWallpaperConnection != null) {
    358             mWallpaperConnection.disconnect();
    359         }
    360         mWallpaperConnection = null;
    361     }
    362 
    363     @Override
    364     public boolean dispatchTouchEvent(MotionEvent ev) {
    365         if (mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
    366             MotionEvent dup = MotionEvent.obtainNoHistory(ev);
    367             try {
    368                 mWallpaperConnection.mEngine.dispatchPointer(dup);
    369             } catch (RemoteException e) {
    370             }
    371         }
    372 
    373         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    374             onUserInteraction();
    375         }
    376         boolean handled = getWindow().superDispatchTouchEvent(ev);
    377         if (!handled) {
    378             handled = onTouchEvent(ev);
    379         }
    380 
    381         if (!handled && mWallpaperConnection != null && mWallpaperConnection.mEngine != null) {
    382             int action = ev.getActionMasked();
    383             try {
    384                 if (action == MotionEvent.ACTION_UP) {
    385                     mWallpaperConnection.mEngine.dispatchWallpaperCommand(
    386                             WallpaperManager.COMMAND_TAP,
    387                             (int) ev.getX(), (int) ev.getY(), 0, null);
    388                 } else if (action == MotionEvent.ACTION_POINTER_UP) {
    389                     int pointerIndex = ev.getActionIndex();
    390                     mWallpaperConnection.mEngine.dispatchWallpaperCommand(
    391                             WallpaperManager.COMMAND_SECONDARY_TAP,
    392                             (int) ev.getX(pointerIndex), (int) ev.getY(pointerIndex), 0, null);
    393                 }
    394             } catch (RemoteException e) {
    395             }
    396         }
    397         return handled;
    398     }
    399 
    400     class WallpaperConnection extends IWallpaperConnection.Stub implements ServiceConnection {
    401         final Intent mIntent;
    402         IWallpaperService mService;
    403         IWallpaperEngine mEngine;
    404         boolean mConnected;
    405 
    406         WallpaperConnection(Intent intent) {
    407             mIntent = intent;
    408         }
    409 
    410         public boolean connect() {
    411             synchronized (this) {
    412                 if (!bindService(mIntent, this, Context.BIND_AUTO_CREATE)) {
    413                     return false;
    414                 }
    415 
    416                 mConnected = true;
    417                 return true;
    418             }
    419         }
    420 
    421         public void disconnect() {
    422             synchronized (this) {
    423                 mConnected = false;
    424                 if (mEngine != null) {
    425                     try {
    426                         mEngine.destroy();
    427                     } catch (RemoteException e) {
    428                         // Ignore
    429                     }
    430                     mEngine = null;
    431                 }
    432                 unbindService(this);
    433                 mService = null;
    434             }
    435         }
    436 
    437         public void onServiceConnected(ComponentName name, IBinder service) {
    438             if (mWallpaperConnection == this) {
    439                 mService = IWallpaperService.Stub.asInterface(service);
    440                 try {
    441                     final View root = getWindow().getDecorView();
    442                     mService.attach(this, root.getWindowToken(),
    443                             LayoutParams.TYPE_APPLICATION_MEDIA,
    444                             true, root.getWidth(), root.getHeight(),
    445                             new Rect(0, 0, 0, 0));
    446                 } catch (RemoteException e) {
    447                     Log.w(LOG_TAG, "Failed attaching wallpaper; clearing", e);
    448                 }
    449             }
    450         }
    451 
    452         public void onServiceDisconnected(ComponentName name) {
    453             mService = null;
    454             mEngine = null;
    455             if (mWallpaperConnection == this) {
    456                 Log.w(LOG_TAG, "Wallpaper service gone: " + name);
    457             }
    458         }
    459 
    460         public void attachEngine(IWallpaperEngine engine) {
    461             synchronized (this) {
    462                 if (mConnected) {
    463                     mEngine = engine;
    464                     try {
    465                         engine.setVisibility(true);
    466                     } catch (RemoteException e) {
    467                         // Ignore
    468                     }
    469                 } else {
    470                     try {
    471                         engine.destroy();
    472                     } catch (RemoteException e) {
    473                         // Ignore
    474                     }
    475                 }
    476             }
    477         }
    478 
    479         public ParcelFileDescriptor setWallpaper(String name) {
    480             return null;
    481         }
    482 
    483         @Override
    484         public void engineShown(IWallpaperEngine engine) throws RemoteException {
    485             mLoading.post(() -> {
    486                 mLoading.animate()
    487                         .alpha(0f)
    488                         .setDuration(220)
    489                         .setInterpolator(AnimationUtils.loadInterpolator(LiveWallpaperPreview.this,
    490                                 android.R.interpolator.fast_out_linear_in))
    491                         .withEndAction(() -> mLoading.setVisibility(View.INVISIBLE));
    492             });
    493         }
    494     }
    495 
    496     private static class WallpaperTargetAdapter extends ArrayAdapter<CharSequence> {
    497 
    498         public WallpaperTargetAdapter(Context context) {
    499             super(context, R.layout.wallpaper_target_dialog_item,
    500                     context.getResources().getTextArray(R.array.which_wallpaper_options));
    501         }
    502 
    503         @Override
    504         public boolean hasStableIds() {
    505             return true;
    506         }
    507 
    508         @Override
    509         public long getItemId(int position) {
    510             return position;
    511         }
    512 
    513         @Override
    514         public View getView(int position, View convertView, ViewGroup parent) {
    515             TextView tv = (TextView) super.getView(position, convertView, parent);
    516             tv.setCompoundDrawablesRelativeWithIntrinsicBounds(
    517                     position == 0 ? R.drawable.ic_home : R.drawable.ic_device, 0, 0, 0);
    518             return tv;
    519         }
    520     }
    521 }
    522