Home | History | Annotate | Download | only in dialog
      1 /*
      2  * Copyright (C) 2014 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.tv.settings.dialog;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorInflater;
     21 import android.animation.AnimatorListenerAdapter;
     22 import android.animation.AnimatorSet;
     23 import android.animation.ObjectAnimator;
     24 import android.animation.TimeInterpolator;
     25 import android.animation.ValueAnimator;
     26 import android.app.Activity;
     27 import android.app.Fragment;
     28 import android.app.FragmentManager;
     29 import android.app.FragmentTransaction;
     30 import android.content.Context;
     31 import android.content.Intent;
     32 import android.content.pm.PackageManager;
     33 import android.content.res.Resources;
     34 import android.graphics.Bitmap;
     35 import android.graphics.Color;
     36 import android.graphics.drawable.BitmapDrawable;
     37 import android.graphics.drawable.ColorDrawable;
     38 import android.graphics.drawable.Drawable;
     39 import android.net.Uri;
     40 import android.os.Bundle;
     41 import android.os.Parcel;
     42 import android.os.Parcelable;
     43 import android.support.v17.leanback.R;
     44 import android.support.v17.leanback.widget.VerticalGridView;
     45 import android.support.v4.view.ViewCompat;
     46 import android.support.v7.widget.RecyclerView;
     47 import android.util.Log;
     48 import android.view.LayoutInflater;
     49 import android.view.View;
     50 import android.view.ViewGroup;
     51 import android.view.ViewPropertyAnimator;
     52 import android.view.ViewTreeObserver;
     53 import android.view.ViewGroup.LayoutParams;
     54 import android.view.animation.DecelerateInterpolator;
     55 import android.view.animation.Interpolator;
     56 import android.widget.ImageView;
     57 import android.widget.RelativeLayout;
     58 import android.widget.TextView;
     59 
     60 import com.android.tv.settings.util.AccessibilityHelper;
     61 import com.android.tv.settings.widget.BitmapWorkerOptions;
     62 import com.android.tv.settings.widget.DrawableDownloader;
     63 import com.android.tv.settings.widget.DrawableDownloader.BitmapCallback;
     64 
     65 import java.util.ArrayList;
     66 import java.util.List;
     67 
     68 /**
     69  * Displays content on the left and actions on the right.
     70  */
     71 public class DialogFragment extends Fragment {
     72 
     73     private static final String TAG_LEAN_BACK_DIALOG_FRAGMENT = "leanBackDialogFragment";
     74     private static final String EXTRA_CONTENT_TITLE = "title";
     75     private static final String EXTRA_CONTENT_BREADCRUMB = "breadcrumb";
     76     private static final String EXTRA_CONTENT_DESCRIPTION = "description";
     77     private static final String EXTRA_CONTENT_ICON_RESOURCE_ID = "iconResourceId";
     78     private static final String EXTRA_CONTENT_ICON_URI = "iconUri";
     79     private static final String EXTRA_CONTENT_ICON_BITMAP = "iconBitmap";
     80     private static final String EXTRA_CONTENT_ICON_BACKGROUND = "iconBackground";
     81     private static final String EXTRA_ACTION_NAME = "name";
     82     private static final String EXTRA_ACTION_ACTIONS = "actions";
     83     private static final String EXTRA_ACTION_SELECTED_INDEX = "selectedIndex";
     84     private static final String EXTRA_ENTRY_TRANSITION_PERFORMED = "entryTransitionPerformed";
     85     private static final int ANIMATE_IN_DURATION = 250;
     86     private static final int ANIMATE_DELAY = 550;
     87     private static final int SECONDARY_ANIMATE_DELAY = 120;
     88     private static final int SLIDE_IN_STAGGER = 100;
     89     private static final int SLIDE_IN_DISTANCE = 120;
     90     private static final int ANIMATION_FRAGMENT_ENTER = 1;
     91     private static final int ANIMATION_FRAGMENT_EXIT = 2;
     92     private static final int ANIMATION_FRAGMENT_ENTER_POP = 3;
     93     private static final int ANIMATION_FRAGMENT_EXIT_POP = 4;
     94 
     95     /**
     96      * Builds a LeanBackDialogFragment object.
     97      */
     98     public static class Builder {
     99 
    100         private String mContentTitle;
    101         private String mContentBreadcrumb;
    102         private String mContentDescription;
    103         private int mIconResourceId;
    104         private Uri mIconUri;
    105         private Bitmap mIconBitmap;
    106         private int mIconBackgroundColor = Color.TRANSPARENT;
    107         private ArrayList<Action> mActions;
    108         private String mName;
    109         private int mSelectedIndex;
    110 
    111         public DialogFragment build() {
    112             DialogFragment fragment = new DialogFragment();
    113             Bundle args = new Bundle();
    114             args.putString(EXTRA_CONTENT_TITLE, mContentTitle);
    115             args.putString(EXTRA_CONTENT_BREADCRUMB, mContentBreadcrumb);
    116             args.putString(EXTRA_CONTENT_DESCRIPTION, mContentDescription);
    117             args.putInt(EXTRA_CONTENT_ICON_RESOURCE_ID, mIconResourceId);
    118             args.putParcelable(EXTRA_CONTENT_ICON_URI, mIconUri);
    119             args.putParcelable(EXTRA_CONTENT_ICON_BITMAP, mIconBitmap);
    120             args.putInt(EXTRA_CONTENT_ICON_BACKGROUND, mIconBackgroundColor);
    121             args.putParcelableArrayList(EXTRA_ACTION_ACTIONS, mActions);
    122             args.putString(EXTRA_ACTION_NAME, mName);
    123             args.putInt(EXTRA_ACTION_SELECTED_INDEX, mSelectedIndex);
    124             fragment.setArguments(args);
    125             return fragment;
    126         }
    127 
    128         public Builder title(String title) {
    129             mContentTitle = title;
    130             return this;
    131         }
    132 
    133         public Builder breadcrumb(String breadcrumb) {
    134             mContentBreadcrumb = breadcrumb;
    135             return this;
    136         }
    137 
    138         public Builder description(String description) {
    139             mContentDescription = description;
    140             return this;
    141         }
    142 
    143         public Builder iconResourceId(int iconResourceId) {
    144             mIconResourceId = iconResourceId;
    145             return this;
    146         }
    147 
    148         public Builder iconUri(Uri iconUri) {
    149             mIconUri = iconUri;
    150             return this;
    151         }
    152 
    153         public Builder iconBitmap(Bitmap iconBitmap) {
    154             mIconBitmap = iconBitmap;
    155             return this;
    156         }
    157 
    158         public Builder iconBackgroundColor(int iconBackgroundColor) {
    159             mIconBackgroundColor = iconBackgroundColor;
    160             return this;
    161         }
    162 
    163         public Builder actions(ArrayList<Action> actions) {
    164             mActions = actions;
    165             return this;
    166         }
    167 
    168         public Builder name(String name) {
    169             mName = name;
    170             return this;
    171         }
    172 
    173         public Builder selectedIndex(int selectedIndex) {
    174             mSelectedIndex = selectedIndex;
    175             return this;
    176         }
    177     }
    178 
    179     public static void add(FragmentManager fm, DialogFragment f) {
    180         boolean hasDialog = fm.findFragmentByTag(TAG_LEAN_BACK_DIALOG_FRAGMENT) != null;
    181         FragmentTransaction ft = fm.beginTransaction();
    182 
    183         if (hasDialog) {
    184             ft.setCustomAnimations(ANIMATION_FRAGMENT_ENTER,
    185                     ANIMATION_FRAGMENT_EXIT, ANIMATION_FRAGMENT_ENTER_POP,
    186                     ANIMATION_FRAGMENT_EXIT_POP);
    187             ft.addToBackStack(null);
    188         }
    189         ft.replace(android.R.id.content, f, TAG_LEAN_BACK_DIALOG_FRAGMENT).commit();
    190     }
    191 
    192     private DialogActionAdapter mAdapter;
    193     private SelectorAnimator mSelectorAnimator;
    194     private VerticalGridView mListView;
    195     private Action.Listener mListener;
    196     private String mTitle;
    197     private String mBreadcrumb;
    198     private String mDescription;
    199     private int mIconResourceId;
    200     private Uri mIconUri;
    201     private Bitmap mIconBitmap;
    202     private int mIconBackgroundColor = Color.TRANSPARENT;
    203     private ArrayList<Action> mActions;
    204     private String mName;
    205     private int mSelectedIndex = -1;
    206     private boolean mEntryTransitionPerformed;
    207     private boolean mIntroAnimationInProgress;
    208     private BitmapCallback mBitmapCallBack;
    209 
    210     @Override
    211     public void onCreate(Bundle savedInstanceState) {
    212         android.util.Log.v("DialogFragment", "onCreate");
    213         super.onCreate(savedInstanceState);
    214         Bundle state = (savedInstanceState != null) ? savedInstanceState : getArguments();
    215         if (mTitle == null) {
    216             mTitle = state.getString(EXTRA_CONTENT_TITLE);
    217         }
    218         if (mBreadcrumb == null) {
    219             mBreadcrumb = state.getString(EXTRA_CONTENT_BREADCRUMB);
    220         }
    221         if (mDescription == null) {
    222             mDescription = state.getString(EXTRA_CONTENT_DESCRIPTION);
    223         }
    224         if (mIconResourceId == 0) {
    225             mIconResourceId = state.getInt(EXTRA_CONTENT_ICON_RESOURCE_ID, 0);
    226         }
    227         if (mIconUri == null) {
    228             mIconUri = state.getParcelable(EXTRA_CONTENT_ICON_URI);
    229         }
    230         if (mIconBitmap == null) {
    231             mIconBitmap = state.getParcelable(EXTRA_CONTENT_ICON_BITMAP);
    232         }
    233         if (mIconBackgroundColor == Color.TRANSPARENT) {
    234             mIconBackgroundColor = state.getInt(EXTRA_CONTENT_ICON_BACKGROUND, Color.TRANSPARENT);
    235         }
    236         if (mActions == null) {
    237             mActions = state.getParcelableArrayList(EXTRA_ACTION_ACTIONS);
    238         }
    239         if (mName == null) {
    240             mName = state.getString(EXTRA_ACTION_NAME);
    241         }
    242         if (mSelectedIndex == -1) {
    243             mSelectedIndex = state.getInt(EXTRA_ACTION_SELECTED_INDEX, -1);
    244         }
    245         mEntryTransitionPerformed = state.getBoolean(EXTRA_ENTRY_TRANSITION_PERFORMED, false);
    246     }
    247 
    248     @Override
    249     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    250             Bundle savedInstanceState) {
    251 
    252         View v = inflater.inflate(R.layout.lb_dialog_fragment, container, false);
    253 
    254         View contentContainer = v.findViewById(R.id.content_fragment);
    255         View content = inflater.inflate(R.layout.lb_dialog_content, container, false);
    256         ((ViewGroup) contentContainer).addView(content);
    257         setContentView(content);
    258         v.setTag(R.id.content_fragment, content);
    259 
    260         View actionContainer = v.findViewById(R.id.action_fragment);
    261         View action = inflater.inflate(R.layout.lb_dialog_action_list, container, false);
    262         ((ViewGroup) actionContainer).addView(action);
    263         setActionView(action);
    264         v.setTag(R.id.action_fragment, action);
    265 
    266         return v;
    267     }
    268 
    269     @Override
    270     public void onSaveInstanceState(Bundle outState) {
    271         super.onSaveInstanceState(outState);
    272         outState.putString(EXTRA_CONTENT_TITLE, mTitle);
    273         outState.putString(EXTRA_CONTENT_BREADCRUMB, mBreadcrumb);
    274         outState.putString(EXTRA_CONTENT_DESCRIPTION, mDescription);
    275         outState.putInt(EXTRA_CONTENT_ICON_RESOURCE_ID, mIconResourceId);
    276         outState.putParcelable(EXTRA_CONTENT_ICON_URI, mIconUri);
    277         outState.putParcelable(EXTRA_CONTENT_ICON_BITMAP, mIconBitmap);
    278         outState.putInt(EXTRA_CONTENT_ICON_BACKGROUND, mIconBackgroundColor);
    279         outState.putParcelableArrayList(EXTRA_ACTION_ACTIONS, mActions);
    280         outState.putInt(EXTRA_ACTION_SELECTED_INDEX,
    281                 (mListView != null) ? getSelectedItemPosition() : mSelectedIndex);
    282         outState.putString(EXTRA_ACTION_NAME, mName);
    283         outState.putBoolean(EXTRA_ENTRY_TRANSITION_PERFORMED, mEntryTransitionPerformed);
    284     }
    285 
    286     @Override
    287     public void onStart() {
    288         super.onStart();
    289         if (!mEntryTransitionPerformed) {
    290             mEntryTransitionPerformed = true;
    291             performEntryTransition();
    292         } else {
    293             performSelectorTransition();
    294         }
    295     }
    296 
    297     @Override
    298     public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
    299         View dialogView = getView();
    300         View contentView = (View) dialogView.getTag(R.id.content_fragment);
    301         View actionView = (View) dialogView.getTag(R.id.action_fragment);
    302         View actionContainerView = dialogView.findViewById(R.id.action_fragment);
    303         View titleView = (View) contentView.getTag(R.id.title);
    304         View breadcrumbView = (View) contentView.getTag(R.id.breadcrumb);
    305         View descriptionView = (View) contentView.getTag(R.id.description);
    306         View iconView = (View) contentView.getTag(R.id.icon);
    307         View listView = (View) actionView.getTag(R.id.list);
    308         View selectorView = (View) actionView.getTag(R.id.selector);
    309 
    310         ArrayList<Animator> animators = new ArrayList<Animator>();
    311 
    312         switch (nextAnim) {
    313             case ANIMATION_FRAGMENT_ENTER:
    314                 animators.add(createSlideInFromEndAnimator(titleView));
    315                 animators.add(createSlideInFromEndAnimator(breadcrumbView));
    316                 animators.add(createSlideInFromEndAnimator(descriptionView));
    317                 animators.add(createSlideInFromEndAnimator(iconView));
    318                 animators.add(createSlideInFromEndAnimator(listView));
    319                 animators.add(createSlideInFromEndAnimator(selectorView));
    320                 break;
    321             case ANIMATION_FRAGMENT_EXIT:
    322                 animators.add(createSlideOutToStartAnimator(titleView));
    323                 animators.add(createSlideOutToStartAnimator(breadcrumbView));
    324                 animators.add(createSlideOutToStartAnimator(descriptionView));
    325                 animators.add(createSlideOutToStartAnimator(iconView));
    326                 animators.add(createSlideOutToStartAnimator(listView));
    327                 animators.add(createSlideOutToStartAnimator(selectorView));
    328                 animators.add(createFadeOutAnimator(actionContainerView));
    329                 break;
    330             case ANIMATION_FRAGMENT_ENTER_POP:
    331                 animators.add(createSlideInFromStartAnimator(titleView));
    332                 animators.add(createSlideInFromStartAnimator(breadcrumbView));
    333                 animators.add(createSlideInFromStartAnimator(descriptionView));
    334                 animators.add(createSlideInFromStartAnimator(iconView));
    335                 animators.add(createSlideInFromStartAnimator(listView));
    336                 animators.add(createSlideInFromStartAnimator(selectorView));
    337                 break;
    338             case ANIMATION_FRAGMENT_EXIT_POP:
    339                 animators.add(createSlideOutToEndAnimator(titleView));
    340                 animators.add(createSlideOutToEndAnimator(breadcrumbView));
    341                 animators.add(createSlideOutToEndAnimator(descriptionView));
    342                 animators.add(createSlideOutToEndAnimator(iconView));
    343                 animators.add(createSlideOutToEndAnimator(listView));
    344                 animators.add(createSlideOutToEndAnimator(selectorView));
    345                 animators.add(createFadeOutAnimator(actionContainerView));
    346                 break;
    347             default:
    348                 return super.onCreateAnimator(transit, enter, nextAnim);
    349         }
    350 
    351         mEntryTransitionPerformed = true;
    352         return createDummyAnimator(dialogView, animators);
    353     }
    354 
    355     public void setIcon(int iconResourceId) {
    356         mIconResourceId = iconResourceId;
    357         View v = getView();
    358         if (v != null) {
    359             final ImageView iconImageView = (ImageView) v.findViewById(R.id.icon);
    360             if (iconImageView != null) {
    361                 if (iconResourceId != 0) {
    362                     iconImageView.setImageResource(iconResourceId);
    363                     iconImageView.setVisibility(View.VISIBLE);
    364                     updateViewSize(iconImageView);
    365                 }
    366             }
    367         }
    368     }
    369 
    370     /**
    371      * Fragments need to call this method in its {@link #onResume()} to set the
    372      * custom listener. <br/>
    373      * Activities do not need to call this method
    374      *
    375      * @param listener
    376      */
    377     public void setListener(Action.Listener listener) {
    378         mListener = listener;
    379     }
    380 
    381     public boolean hasListener() {
    382         return mListener != null;
    383     }
    384 
    385     public ArrayList<Action> getActions() {
    386         return mActions;
    387     }
    388 
    389     public void setActions(ArrayList<Action> actions) {
    390         mActions = actions;
    391         if (mAdapter != null) {
    392             mAdapter.setActions(mActions);
    393         }
    394     }
    395 
    396     public View getItemView(int position) {
    397         return mListView.getChildAt(position);
    398     }
    399 
    400     public void setSelectedPosition(int position) {
    401         mListView.setSelectedPosition(position);
    402     }
    403 
    404     public int getSelectedItemPosition() {
    405         return mListView.indexOfChild(mListView.getFocusedChild());
    406     }
    407 
    408     /**
    409      * Called when intro animation is finished.
    410      * <p>
    411      * If a subclass is going to alter the view, should wait until this is
    412      * called.
    413      */
    414     public void onIntroAnimationFinished() {
    415         mIntroAnimationInProgress = false;
    416     }
    417 
    418     public boolean isIntroAnimationInProgress() {
    419         return mIntroAnimationInProgress;
    420     }
    421 
    422     private void setContentView(View content) {
    423         TextView titleView = (TextView) content.findViewById(R.id.title);
    424         TextView breadcrumbView = (TextView) content.findViewById(R.id.breadcrumb);
    425         TextView descriptionView = (TextView) content.findViewById(R.id.description);
    426         titleView.setText(mTitle);
    427         breadcrumbView.setText(mBreadcrumb);
    428         descriptionView.setText(mDescription);
    429         final ImageView iconImageView = (ImageView) content.findViewById(R.id.icon);
    430         if (mIconBackgroundColor != Color.TRANSPARENT) {
    431             iconImageView.setBackgroundColor(mIconBackgroundColor);
    432         }
    433 
    434         if (AccessibilityHelper.forceFocusableViews(getActivity())) {
    435             titleView.setFocusable(true);
    436             titleView.setFocusableInTouchMode(true);
    437             descriptionView.setFocusable(true);
    438             descriptionView.setFocusableInTouchMode(true);
    439             breadcrumbView.setFocusable(true);
    440             breadcrumbView.setFocusableInTouchMode(true);
    441         }
    442 
    443         if (mIconResourceId != 0) {
    444             iconImageView.setImageResource(mIconResourceId);
    445             updateViewSize(iconImageView);
    446         } else {
    447             if (mIconBitmap != null) {
    448                 iconImageView.setImageBitmap(mIconBitmap);
    449                 updateViewSize(iconImageView);
    450             } else {
    451                 if (mIconUri != null) {
    452                     iconImageView.setVisibility(View.INVISIBLE);
    453 
    454                     DrawableDownloader bitmapDownloader = DrawableDownloader.getInstance(
    455                             content.getContext());
    456                     mBitmapCallBack = new BitmapCallback() {
    457                         @Override
    458                         public void onBitmapRetrieved(Drawable bitmap) {
    459                             if (bitmap != null) {
    460                                 mIconBitmap = (bitmap instanceof BitmapDrawable) ? ((BitmapDrawable) bitmap)
    461                                         .getBitmap()
    462                                         : null;
    463                                 iconImageView.setVisibility(View.VISIBLE);
    464                                 iconImageView.setImageDrawable(bitmap);
    465                                 updateViewSize(iconImageView);
    466                             }
    467                         }
    468                     };
    469 
    470                     bitmapDownloader.getBitmap(new BitmapWorkerOptions.Builder(
    471                             content.getContext()).resource(mIconUri)
    472                             .width(iconImageView.getLayoutParams().width).build(),
    473                             mBitmapCallBack);
    474                 } else {
    475                     iconImageView.setVisibility(View.GONE);
    476                 }
    477             }
    478         }
    479 
    480         content.setTag(R.id.title, titleView);
    481         content.setTag(R.id.breadcrumb, breadcrumbView);
    482         content.setTag(R.id.description, descriptionView);
    483         content.setTag(R.id.icon, iconImageView);
    484     }
    485 
    486     private void setActionView(View action) {
    487         mAdapter = new DialogActionAdapter(new Action.Listener() {
    488             @Override
    489             public void onActionClicked(Action action) {
    490                 // eat events if action is disabled or only displays info
    491                 if (!action.isEnabled() || action.infoOnly()) {
    492                     return;
    493                 }
    494 
    495                 /**
    496                  * If the custom lister has been set using
    497                  * {@link #setListener(DialogActionAdapter.Listener)}, use it.
    498                  * If not, use the activity's default listener.
    499                  */
    500                 if (mListener != null) {
    501                     mListener.onActionClicked(action);
    502                 } else if (getActivity() instanceof Action.Listener) {
    503                     Action.Listener listener = (Action.Listener) getActivity();
    504                     listener.onActionClicked(action);
    505                 }
    506             }
    507         }, new Action.OnFocusListener() {
    508             @Override
    509             public void onActionFocused(Action action) {
    510                 if (getActivity() instanceof Action.OnFocusListener) {
    511                     Action.OnFocusListener listener = (Action.OnFocusListener) getActivity();
    512                     listener.onActionFocused(action);
    513                 }
    514             }
    515         }, mActions);
    516 
    517         if (action instanceof VerticalGridView) {
    518             mListView = (VerticalGridView) action;
    519         } else {
    520             mListView = (VerticalGridView) action.findViewById(R.id.list);
    521             if (mListView == null) {
    522                 throw new IllegalStateException("No ListView exists.");
    523             }
    524 //            mListView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
    525 //            mListView.setWindowAlignmentOffsetPercent(0.5f);
    526 //            mListView.setItemAlignmentOffset(0);
    527 //            mListView.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
    528             mListView.setWindowAlignmentOffset(0);
    529             mListView.setWindowAlignmentOffsetPercent(50f);
    530             mListView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
    531             View selectorView = action.findViewById(R.id.selector);
    532             if (selectorView != null) {
    533                 mSelectorAnimator = new SelectorAnimator(selectorView, mListView);
    534                 mListView.setOnScrollListener(mSelectorAnimator);
    535             }
    536         }
    537 
    538         mListView.requestFocusFromTouch();
    539         mListView.setAdapter(mAdapter);
    540         mListView.setSelectedPosition(
    541                 (mSelectedIndex >= 0 && mSelectedIndex < mActions.size()) ? mSelectedIndex
    542                 : getFirstCheckedAction());
    543 
    544         action.setTag(R.id.list, mListView);
    545         action.setTag(R.id.selector, action.findViewById(R.id.selector));
    546     }
    547 
    548     private int getFirstCheckedAction() {
    549         for (int i = 0, size = mActions.size(); i < size; i++) {
    550             if (mActions.get(i).isChecked()) {
    551                 return i;
    552             }
    553         }
    554         return 0;
    555     }
    556 
    557     private void updateViewSize(ImageView iconView) {
    558         int intrinsicWidth = iconView.getDrawable().getIntrinsicWidth();
    559         LayoutParams lp = iconView.getLayoutParams();
    560         if (intrinsicWidth > 0) {
    561             lp.height = lp.width * iconView.getDrawable().getIntrinsicHeight()
    562                     / intrinsicWidth;
    563         } else {
    564             // If no intrinsic width, then just mke this a square.
    565             lp.height = lp.width;
    566         }
    567     }
    568 
    569     private void fadeIn(View v) {
    570         v.setAlpha(0f);
    571         ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(v, "alpha", 1f);
    572         alphaAnimator.setDuration(v.getContext().getResources().getInteger(
    573                 android.R.integer.config_mediumAnimTime));
    574         alphaAnimator.start();
    575     }
    576 
    577     private void runDelayedAnim(final Runnable runnable) {
    578         final View dialogView = getView();
    579         final View contentView = (View) dialogView.getTag(R.id.content_fragment);
    580 
    581         contentView.getViewTreeObserver().addOnGlobalLayoutListener(
    582                 new ViewTreeObserver.OnGlobalLayoutListener() {
    583                 @Override
    584                     public void onGlobalLayout() {
    585                         contentView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    586                         // if we buildLayer() at this time, the texture is
    587                         // actually not created delay a little so we can make
    588                         // sure all hardware layer is created before animation,
    589                         // in that way we can avoid the jittering of start
    590                         // animation
    591                         contentView.postOnAnimationDelayed(runnable, ANIMATE_DELAY);
    592                     }
    593                 });
    594 
    595     }
    596 
    597     private void performSelectorTransition() {
    598         runDelayedAnim(new Runnable() {
    599             @Override
    600             public void run() {
    601                 // Fade in the selector.
    602                 if (mSelectorAnimator != null) {
    603                     mSelectorAnimator.fadeIn();
    604                 }
    605             }
    606         });
    607     }
    608 
    609     private void performEntryTransition() {
    610         final View dialogView = getView();
    611         final View contentView = (View) dialogView.getTag(R.id.content_fragment);
    612         final View actionContainerView = dialogView.findViewById(R.id.action_fragment);
    613 
    614         mIntroAnimationInProgress = true;
    615 
    616         // Fade out the old activity.
    617         getActivity().overridePendingTransition(0, R.anim.lb_dialog_fade_out);
    618 
    619         int bgColor = contentView.getContext().getResources()
    620                 .getColor(R.color.lb_dialog_activity_background);
    621         final ColorDrawable bgDrawable = new ColorDrawable();
    622         bgDrawable.setColor(bgColor);
    623         bgDrawable.setAlpha(0);
    624         dialogView.setBackground(bgDrawable);
    625         dialogView.setVisibility(View.INVISIBLE);
    626 
    627         runDelayedAnim(new Runnable() {
    628             @Override
    629             public void run() {
    630                 if (!isAdded()) {
    631                     // We have been detached before this could run,
    632                     // so just bail
    633                     return;
    634                 }
    635 
    636                 dialogView.setVisibility(View.VISIBLE);
    637 
    638                 // Fade in the activity background protection
    639                 ObjectAnimator oa = ObjectAnimator.ofInt(bgDrawable, "alpha", 255);
    640                 oa.setDuration(ANIMATE_IN_DURATION);
    641                 oa.setStartDelay(SECONDARY_ANIMATE_DELAY);
    642                 oa.setInterpolator(new DecelerateInterpolator(1.0f));
    643                 oa.start();
    644 
    645                 boolean isRtl = ViewCompat.getLayoutDirection(contentView) ==
    646                         View.LAYOUT_DIRECTION_RTL;
    647                 int startDist = isRtl ? SLIDE_IN_DISTANCE : -SLIDE_IN_DISTANCE;
    648                 int endDist = isRtl ? -actionContainerView.getMeasuredWidth() :
    649                         actionContainerView.getMeasuredWidth();
    650 
    651                 // Fade in and slide in the ContentFragment
    652                 // TextViews from the start.
    653                 prepareAndAnimateView((View) contentView.getTag(R.id.title),
    654                         startDist, false);
    655                 prepareAndAnimateView((View) contentView.getTag(R.id.breadcrumb),
    656                         startDist, false);
    657                 prepareAndAnimateView((View) contentView.getTag(R.id.description),
    658                         startDist, false);
    659 
    660                 // Fade in and slide in the ActionFragment from the
    661                 // end.
    662                 prepareAndAnimateView(actionContainerView,
    663                         endDist, false);
    664                 prepareAndAnimateView((View) contentView.getTag(R.id.icon),
    665                         startDist, true);
    666 
    667                 // Fade in the selector.
    668                 if (mSelectorAnimator != null) {
    669                     mSelectorAnimator.fadeIn();
    670                 }
    671             }
    672         });
    673     }
    674 
    675     private void prepareAndAnimateView(final View v, float initTransX,
    676             final boolean notifyAnimationFinished) {
    677         v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    678         v.buildLayer();
    679         v.setAlpha(0);
    680         v.setTranslationX(initTransX);
    681         v.animate().alpha(1f).translationX(0).setDuration(ANIMATE_IN_DURATION)
    682                 .setStartDelay(SECONDARY_ANIMATE_DELAY);
    683         v.animate().setInterpolator(new DecelerateInterpolator(1.0f));
    684         v.animate().setListener(new AnimatorListenerAdapter() {
    685                 @Override
    686             public void onAnimationEnd(Animator animation) {
    687                 v.setLayerType(View.LAYER_TYPE_NONE, null);
    688                 if (notifyAnimationFinished) {
    689                     onIntroAnimationFinished();
    690                 }
    691             }
    692         });
    693         v.animate().start();
    694     }
    695 
    696     private Animator createDummyAnimator(final View v, ArrayList<Animator> animators) {
    697         final AnimatorSet animatorSet = new AnimatorSet();
    698         animatorSet.playTogether(animators);
    699         return new UntargetableAnimatorSet(animatorSet);
    700     }
    701 
    702     private Animator createAnimator(View v, int resourceId) {
    703         Animator animator = AnimatorInflater.loadAnimator(v.getContext(), resourceId);
    704         animator.setTarget(v);
    705         return animator;
    706     }
    707 
    708     private Animator createSlideOutToStartAnimator(View v) {
    709         boolean isRtl = ViewCompat.getLayoutDirection(v) == View.LAYOUT_DIRECTION_RTL;
    710         return createTranslateAlphaAnimator(v, 0, isRtl ? 200f : -200f, 1f, 0);
    711     }
    712 
    713     private Animator createSlideInFromEndAnimator(View v) {
    714         boolean isRtl = ViewCompat.getLayoutDirection(v) == View.LAYOUT_DIRECTION_RTL;
    715         return createTranslateAlphaAnimator(v, isRtl ? -200f : 200f, 0, 0, 1f);
    716     }
    717 
    718     private Animator createSlideInFromStartAnimator(View v) {
    719         boolean isRtl = ViewCompat.getLayoutDirection(v) == View.LAYOUT_DIRECTION_RTL;
    720         return createTranslateAlphaAnimator(v, isRtl ? 200f : -200f, 0, 0, 1f);
    721     }
    722 
    723     private Animator createSlideOutToEndAnimator(View v) {
    724         boolean isRtl = ViewCompat.getLayoutDirection(v) == View.LAYOUT_DIRECTION_RTL;
    725         return createTranslateAlphaAnimator(v, 0, isRtl ? -200f : 200f, 1f, 0);
    726     }
    727 
    728     private Animator createFadeOutAnimator(View v) {
    729         return createAlphaAnimator(v, 1f, 0);
    730     }
    731 
    732     private Animator createTranslateAlphaAnimator(View v, float fromTranslateX, float toTranslateX,
    733             float fromAlpha, float toAlpha) {
    734         ObjectAnimator translateAnimator = ObjectAnimator.ofFloat(v, "translationX", fromTranslateX,
    735                 toTranslateX);
    736         translateAnimator.setDuration(
    737                 getResources().getInteger(android.R.integer.config_longAnimTime));
    738         Animator alphaAnimator = createAlphaAnimator(v, fromAlpha, toAlpha);
    739         AnimatorSet animatorSet = new AnimatorSet();
    740         animatorSet.play(translateAnimator).with(alphaAnimator);
    741         return animatorSet;
    742     }
    743 
    744     private Animator createAlphaAnimator(View v, float fromAlpha, float toAlpha) {
    745         ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(v, "alpha", fromAlpha, toAlpha);
    746         alphaAnimator.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
    747         return alphaAnimator;
    748     }
    749 
    750     private static class SelectorAnimator extends RecyclerView.OnScrollListener {
    751 
    752         private final View mSelectorView;
    753         private final ViewGroup mParentView;
    754         private final int mAnimationDuration;
    755         private volatile boolean mFadedOut = true;
    756 
    757         SelectorAnimator(View selectorView, ViewGroup parentView) {
    758             mSelectorView = selectorView;
    759             mParentView = parentView;
    760             mAnimationDuration = selectorView.getContext()
    761                     .getResources().getInteger(R.integer.lb_dialog_animation_duration);
    762         }
    763 
    764         // We want to fade in the selector if we've stopped scrolling on it. If
    765         // we're scrolling, we want to ensure to dim the selector if we haven't
    766         // already. We dim the last highlighted view so that while a user is
    767         // scrolling, nothing is highlighted.
    768         @Override
    769         public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
    770             if (newState == RecyclerView.SCROLL_STATE_IDLE) {
    771                 fadeIn();
    772             } else {
    773                 fadeOut();
    774             }
    775         }
    776 
    777         public void fadeIn() {
    778             // The selector starts with a height of 0. In order to scale up
    779             // from
    780             // 0 we first need the set the height to 1 and scale form there.
    781             int selectorHeight = mSelectorView.getHeight();
    782             if (selectorHeight == 0) {
    783                 LayoutParams lp = mSelectorView.getLayoutParams();
    784                 lp.height = selectorHeight = mSelectorView.getContext().getResources()
    785                         .getDimensionPixelSize(R.dimen.lb_action_fragment_selector_min_height);
    786                 mSelectorView.setLayoutParams(lp);
    787             }
    788             View focusedChild = mParentView.getFocusedChild();
    789             if (focusedChild != null) {
    790                 float scaleY = (float) focusedChild.getHeight() / selectorHeight;
    791                 ViewPropertyAnimator animation = mSelectorView.animate()
    792                         .alpha(1f)
    793                         .setListener(new Listener(false))
    794                         .setDuration(mAnimationDuration)
    795                         .setInterpolator(new DecelerateInterpolator(2f));
    796                 if (mFadedOut) {
    797                     // selector is completely faded out, so we can just
    798                     // scale
    799                     // before fading in.
    800                     mSelectorView.setScaleY(scaleY);
    801                 } else {
    802                     // selector is not faded out, so we must animate the
    803                     // scale
    804                     // as we fade in.
    805                     animation.scaleY(scaleY);
    806                 }
    807                 animation.start();
    808             }
    809         }
    810 
    811         public void fadeOut() {
    812             mSelectorView.animate()
    813             .alpha(0f)
    814             .setDuration(mAnimationDuration)
    815             .setInterpolator(new DecelerateInterpolator(2f))
    816             .setListener(new Listener(true))
    817             .start();
    818         }
    819 
    820         /**
    821          * Sets {@link BaseScrollAdapterFragment#mFadedOut}
    822          * {@link BaseScrollAdapterFragment#mFadedOut} is true, iff
    823          * {@link BaseScrollAdapterFragment#mSelectorView} has an alpha of 0
    824          * (faded out). If false the view either has an alpha of 1 (visible) or
    825          * is in the process of animating.
    826          */
    827         private class Listener implements Animator.AnimatorListener {
    828             private boolean mFadingOut;
    829             private boolean mCanceled;
    830 
    831             public Listener(boolean fadingOut) {
    832                 mFadingOut = fadingOut;
    833             }
    834 
    835             @Override
    836             public void onAnimationStart(Animator animation) {
    837                 if (!mFadingOut) {
    838                     mFadedOut = false;
    839                 }
    840             }
    841 
    842             @Override
    843             public void onAnimationEnd(Animator animation) {
    844                 if (!mCanceled && mFadingOut) {
    845                     mFadedOut = true;
    846                 }
    847             }
    848 
    849             @Override
    850             public void onAnimationCancel(Animator animation) {
    851                 mCanceled = true;
    852             }
    853 
    854             @Override
    855             public void onAnimationRepeat(Animator animation) {
    856             }
    857         }
    858     }
    859 
    860     private static class UntargetableAnimatorSet extends Animator {
    861 
    862         private final AnimatorSet mAnimatorSet;
    863 
    864         UntargetableAnimatorSet(AnimatorSet animatorSet) {
    865             mAnimatorSet = animatorSet;
    866         }
    867 
    868         @Override
    869         public void addListener(Animator.AnimatorListener listener) {
    870             mAnimatorSet.addListener(listener);
    871         }
    872 
    873         @Override
    874         public void cancel() {
    875             mAnimatorSet.cancel();
    876         }
    877 
    878         @Override
    879         public Animator clone() {
    880             return mAnimatorSet.clone();
    881         }
    882 
    883         @Override
    884         public void end() {
    885             mAnimatorSet.end();
    886         }
    887 
    888         @Override
    889         public long getDuration() {
    890             return mAnimatorSet.getDuration();
    891         }
    892 
    893         @Override
    894         public ArrayList<Animator.AnimatorListener> getListeners() {
    895             return mAnimatorSet.getListeners();
    896         }
    897 
    898         @Override
    899         public long getStartDelay() {
    900             return mAnimatorSet.getStartDelay();
    901         }
    902 
    903         @Override
    904         public boolean isRunning() {
    905             return mAnimatorSet.isRunning();
    906         }
    907 
    908         @Override
    909         public boolean isStarted() {
    910             return mAnimatorSet.isStarted();
    911         }
    912 
    913         @Override
    914         public void removeAllListeners() {
    915             mAnimatorSet.removeAllListeners();
    916         }
    917 
    918         @Override
    919         public void removeListener(Animator.AnimatorListener listener) {
    920             mAnimatorSet.removeListener(listener);
    921         }
    922 
    923         @Override
    924         public Animator setDuration(long duration) {
    925             return mAnimatorSet.setDuration(duration);
    926         }
    927 
    928         @Override
    929         public void setInterpolator(TimeInterpolator value) {
    930             mAnimatorSet.setInterpolator(value);
    931         }
    932 
    933         @Override
    934         public void setStartDelay(long startDelay) {
    935             mAnimatorSet.setStartDelay(startDelay);
    936         }
    937 
    938         @Override
    939         public void setTarget(Object target) {
    940             // ignore
    941         }
    942 
    943         @Override
    944         public void setupEndValues() {
    945             mAnimatorSet.setupEndValues();
    946         }
    947 
    948         @Override
    949         public void setupStartValues() {
    950             mAnimatorSet.setupStartValues();
    951         }
    952 
    953         @Override
    954         public void start() {
    955             mAnimatorSet.start();
    956         }
    957     }
    958 
    959     /**
    960      * An data class which represents an action within an
    961      * {@link DialogFragment}. A list of Actions represent a list of choices the
    962      * user can make, a radio-button list of configuration options, or just a
    963      * list of information.
    964      */
    965     public static class Action implements Parcelable {
    966 
    967         private static final String TAG = "Action";
    968 
    969         public static final int NO_DRAWABLE = 0;
    970         public static final int NO_CHECK_SET = 0;
    971         public static final int DEFAULT_CHECK_SET_ID = 1;
    972 
    973         /**
    974          * Object listening for adapter events.
    975          */
    976         public interface Listener {
    977 
    978             /**
    979              * Called when the user clicks on an action.
    980              */
    981             public void onActionClicked(Action action);
    982         }
    983 
    984         public interface OnFocusListener {
    985 
    986             /**
    987              * Called when the user focuses on an action.
    988              */
    989             public void onActionFocused(Action action);
    990         }
    991 
    992         /**
    993          * Builds a Action object.
    994          */
    995         public static class Builder {
    996             private String mKey;
    997             private String mTitle;
    998             private String mDescription;
    999             private Intent mIntent;
   1000             private String mResourcePackageName;
   1001             private int mDrawableResource = NO_DRAWABLE;
   1002             private Uri mIconUri;
   1003             private boolean mChecked;
   1004             private boolean mMultilineDescription;
   1005             private boolean mHasNext;
   1006             private boolean mInfoOnly;
   1007             private int mCheckSetId = NO_CHECK_SET;
   1008             private boolean mEnabled = true;
   1009 
   1010             public Action build() {
   1011                 Action action = new Action();
   1012                 action.mKey = mKey;
   1013                 action.mTitle = mTitle;
   1014                 action.mDescription = mDescription;
   1015                 action.mIntent = mIntent;
   1016                 action.mResourcePackageName = mResourcePackageName;
   1017                 action.mDrawableResource = mDrawableResource;
   1018                 action.mIconUri = mIconUri;
   1019                 action.mChecked = mChecked;
   1020                 action.mMultilineDescription = mMultilineDescription;
   1021                 action.mHasNext = mHasNext;
   1022                 action.mInfoOnly = mInfoOnly;
   1023                 action.mCheckSetId = mCheckSetId;
   1024                 action.mEnabled = mEnabled;
   1025                 return action;
   1026             }
   1027 
   1028             public Builder key(String key) {
   1029                 mKey = key;
   1030                 return this;
   1031             }
   1032 
   1033             public Builder title(String title) {
   1034                 mTitle = title;
   1035                 return this;
   1036             }
   1037 
   1038             public Builder description(String description) {
   1039                 mDescription = description;
   1040                 return this;
   1041             }
   1042 
   1043             public Builder intent(Intent intent) {
   1044                 mIntent = intent;
   1045                 return this;
   1046             }
   1047 
   1048             public Builder resourcePackageName(String resourcePackageName) {
   1049                 mResourcePackageName = resourcePackageName;
   1050                 return this;
   1051             }
   1052 
   1053             public Builder drawableResource(int drawableResource) {
   1054                 mDrawableResource = drawableResource;
   1055                 return this;
   1056             }
   1057 
   1058             public Builder iconUri(Uri iconUri) {
   1059                 mIconUri = iconUri;
   1060                 return this;
   1061             }
   1062 
   1063             public Builder checked(boolean checked) {
   1064                 mChecked = checked;
   1065                 return this;
   1066             }
   1067 
   1068             public Builder multilineDescription(boolean multilineDescription) {
   1069                 mMultilineDescription = multilineDescription;
   1070                 return this;
   1071             }
   1072 
   1073             public Builder hasNext(boolean hasNext) {
   1074                 mHasNext = hasNext;
   1075                 return this;
   1076             }
   1077 
   1078             public Builder infoOnly(boolean infoOnly) {
   1079                 mInfoOnly = infoOnly;
   1080                 return this;
   1081             }
   1082 
   1083             public Builder checkSetId(int checkSetId) {
   1084                 mCheckSetId = checkSetId;
   1085                 return this;
   1086             }
   1087 
   1088             public Builder enabled(boolean enabled) {
   1089                 mEnabled = enabled;
   1090                 return this;
   1091             }
   1092         }
   1093 
   1094         private String mKey;
   1095         private String mTitle;
   1096         private String mDescription;
   1097         private Intent mIntent;
   1098 
   1099         /**
   1100          * If not {@code null}, the package name to use to retrieve
   1101          * {@link #mDrawableResource}.
   1102          */
   1103         private String mResourcePackageName;
   1104 
   1105         private int mDrawableResource;
   1106         private Uri mIconUri;
   1107         private boolean mChecked;
   1108         private boolean mMultilineDescription;
   1109         private boolean mHasNext;
   1110         private boolean mInfoOnly;
   1111         private int mCheckSetId;
   1112         private boolean mEnabled;
   1113 
   1114         private Action() {
   1115         }
   1116 
   1117         public String getKey() {
   1118             return mKey;
   1119         }
   1120 
   1121         public String getTitle() {
   1122             return mTitle;
   1123         }
   1124 
   1125         public String getDescription() {
   1126             return mDescription;
   1127         }
   1128 
   1129         public void setDescription(String description) {
   1130             mDescription = description;
   1131         }
   1132 
   1133         public Intent getIntent() {
   1134             return mIntent;
   1135         }
   1136 
   1137         public boolean isChecked() {
   1138             return mChecked;
   1139         }
   1140 
   1141         public int getDrawableResource() {
   1142             return mDrawableResource;
   1143         }
   1144 
   1145         public Uri getIconUri() {
   1146             return mIconUri;
   1147         }
   1148 
   1149         public String getResourcePackageName() {
   1150             return mResourcePackageName;
   1151         }
   1152 
   1153         /**
   1154          * Returns the check set id this action is a part of. All actions in the
   1155          * same list with the same check set id are considered linked. When one
   1156          * of the actions within that set is selected that action becomes
   1157          * checked while all the other actions become unchecked.
   1158          *
   1159          * @return an integer representing the check set this action is a part
   1160          *         of or {@link NO_CHECK_SET} if this action isn't a
   1161          *         part of a check set.
   1162          */
   1163         public int getCheckSetId() {
   1164             return mCheckSetId;
   1165         }
   1166 
   1167         public boolean hasMultilineDescription() {
   1168             return mMultilineDescription;
   1169         }
   1170 
   1171         public boolean isEnabled() {
   1172             return mEnabled;
   1173         }
   1174 
   1175         public void setChecked(boolean checked) {
   1176             mChecked = checked;
   1177         }
   1178 
   1179         public void setEnabled(boolean enabled) {
   1180             mEnabled = enabled;
   1181         }
   1182 
   1183         /**
   1184          * @return true if the action will request further user input when
   1185          *         selected (such as showing another dialog or launching a new
   1186          *         activity). False, otherwise.
   1187          */
   1188         public boolean hasNext() {
   1189             return mHasNext;
   1190         }
   1191 
   1192         /**
   1193          * @return true if the action will only display information and is thus
   1194          *         unactionable. If both this and {@link #hasNext()} are true,
   1195          *         infoOnly takes precedence. (default is false) e.g. the amount
   1196          *         of storage a document uses or cost of an app.
   1197          */
   1198         public boolean infoOnly() {
   1199             return mInfoOnly;
   1200         }
   1201 
   1202         /**
   1203          * Returns an indicator to be drawn. If null is returned, no space for
   1204          * the indicator will be made.
   1205          *
   1206          * @param context the context of the Activity this Action belongs to
   1207          * @return an indicator to draw or null if no indicator space should
   1208          *         exist.
   1209          */
   1210         public Drawable getIndicator(Context context) {
   1211             if (mDrawableResource == NO_DRAWABLE) {
   1212                 return null;
   1213             }
   1214             if (mResourcePackageName == null) {
   1215                 return context.getResources().getDrawable(mDrawableResource);
   1216             }
   1217             // If we get to here, need to load the resources.
   1218             Drawable icon = null;
   1219             try {
   1220                 Context packageContext = context.createPackageContext(mResourcePackageName, 0);
   1221                 icon = packageContext.getResources().getDrawable(mDrawableResource);
   1222             } catch (PackageManager.NameNotFoundException e) {
   1223                 if (Log.isLoggable(TAG, Log.WARN)) {
   1224                     Log.w(TAG, "No icon for this action.");
   1225                 }
   1226             } catch (Resources.NotFoundException e) {
   1227                 if (Log.isLoggable(TAG, Log.WARN)) {
   1228                     Log.w(TAG, "No icon for this action.");
   1229                 }
   1230             }
   1231             return icon;
   1232         }
   1233 
   1234         public static Parcelable.Creator<Action> CREATOR = new Parcelable.Creator<Action>() {
   1235 
   1236                 @Override
   1237             public Action createFromParcel(Parcel source) {
   1238 
   1239                 return new Action.Builder()
   1240                         .key(source.readString())
   1241                         .title(source.readString())
   1242                         .description(source.readString())
   1243                         .intent((Intent) source.readParcelable(Intent.class.getClassLoader()))
   1244                         .resourcePackageName(source.readString())
   1245                         .drawableResource(source.readInt())
   1246                         .iconUri((Uri) source.readParcelable(Uri.class.getClassLoader()))
   1247                         .checked(source.readInt() != 0)
   1248                         .multilineDescription(source.readInt() != 0)
   1249                         .checkSetId(source.readInt())
   1250                         .build();
   1251             }
   1252 
   1253                 @Override
   1254             public Action[] newArray(int size) {
   1255                 return new Action[size];
   1256             }
   1257         };
   1258 
   1259         @Override
   1260         public int describeContents() {
   1261             return 0;
   1262         }
   1263 
   1264         @Override
   1265         public void writeToParcel(Parcel dest, int flags) {
   1266             dest.writeString(mKey);
   1267             dest.writeString(mTitle);
   1268             dest.writeString(mDescription);
   1269             dest.writeParcelable(mIntent, flags);
   1270             dest.writeString(mResourcePackageName);
   1271             dest.writeInt(mDrawableResource);
   1272             dest.writeParcelable(mIconUri, flags);
   1273             dest.writeInt(mChecked ? 1 : 0);
   1274             dest.writeInt(mMultilineDescription ? 1 : 0);
   1275             dest.writeInt(mCheckSetId);
   1276         }
   1277     }
   1278 }
   1279