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