Home | History | Annotate | Download | only in mediapicker
      1 /*
      2  * Copyright (C) 2015 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.messaging.ui.mediapicker;
     18 
     19 
     20 import android.app.Activity;
     21 import android.app.Fragment;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.os.Bundle;
     25 import android.os.Handler;
     26 import android.support.v4.view.PagerAdapter;
     27 import android.support.v4.view.ViewPager;
     28 import android.support.v7.app.ActionBar;
     29 import android.view.LayoutInflater;
     30 import android.view.Menu;
     31 import android.view.MenuInflater;
     32 import android.view.MenuItem;
     33 import android.view.View;
     34 import android.view.ViewGroup;
     35 import android.widget.ImageButton;
     36 import android.widget.LinearLayout;
     37 
     38 import com.android.messaging.Factory;
     39 import com.android.messaging.R;
     40 import com.android.messaging.datamodel.DataModel;
     41 import com.android.messaging.datamodel.binding.Binding;
     42 import com.android.messaging.datamodel.binding.BindingBase;
     43 import com.android.messaging.datamodel.binding.ImmutableBindingRef;
     44 import com.android.messaging.datamodel.data.DraftMessageData;
     45 import com.android.messaging.datamodel.data.MediaPickerData;
     46 import com.android.messaging.datamodel.data.MessagePartData;
     47 import com.android.messaging.datamodel.data.PendingAttachmentData;
     48 import com.android.messaging.datamodel.data.DraftMessageData.DraftMessageSubscriptionDataProvider;
     49 import com.android.messaging.ui.BugleActionBarActivity;
     50 import com.android.messaging.ui.FixedViewPagerAdapter;
     51 import com.android.messaging.ui.mediapicker.DocumentImagePicker.SelectionListener;
     52 import com.android.messaging.util.AccessibilityUtil;
     53 import com.android.messaging.util.Assert;
     54 import com.android.messaging.util.UiUtils;
     55 import com.google.common.annotations.VisibleForTesting;
     56 
     57 import java.util.ArrayList;
     58 import java.util.Collection;
     59 import java.util.List;
     60 
     61 /**
     62  * Fragment used to select or capture media to be added to the message
     63  */
     64 public class MediaPicker extends Fragment implements DraftMessageSubscriptionDataProvider {
     65     /** The listener interface for events from the media picker */
     66     public interface MediaPickerListener {
     67         /** Called when the media picker is opened so the host can accommodate the UI */
     68         void onOpened();
     69 
     70         /**
     71          * Called when the media picker goes into or leaves full screen mode so the host can
     72          * accommodate the fullscreen UI
     73          */
     74         void onFullScreenChanged(boolean fullScreen);
     75 
     76         /**
     77          * Called when the user selects one or more items
     78          * @param items The list of items which were selected
     79          */
     80         void onItemsSelected(Collection<MessagePartData> items, boolean dismissMediaPicker);
     81 
     82         /**
     83          * Called when the user unselects one item.
     84          */
     85         void onItemUnselected(MessagePartData item);
     86 
     87         /**
     88          * Called when the media picker is closed.  Always called immediately after onItemsSelected
     89          */
     90         void onDismissed();
     91 
     92         /**
     93          * Called when media item selection is confirmed in a multi-select action.
     94          */
     95         void onConfirmItemSelection();
     96 
     97         /**
     98          * Called when a pending attachment is added.
     99          * @param pendingItem the pending attachment data being loaded.
    100          */
    101         void onPendingItemAdded(PendingAttachmentData pendingItem);
    102 
    103         /**
    104          * Called when a new media chooser is selected.
    105          */
    106         void onChooserSelected(final int chooserIndex);
    107     }
    108 
    109     /** The tag used when registering and finding this fragment */
    110     public static final String FRAGMENT_TAG = "mediapicker";
    111 
    112     // Media type constants that the media picker supports
    113     public static final int MEDIA_TYPE_DEFAULT     = 0x0000;
    114     public static final int MEDIA_TYPE_NONE        = 0x0000;
    115     public static final int MEDIA_TYPE_IMAGE       = 0x0001;
    116     public static final int MEDIA_TYPE_VIDEO       = 0x0002;
    117     public static final int MEDIA_TYPE_AUDIO       = 0x0004;
    118     public static final int MEDIA_TYPE_VCARD       = 0x0008;
    119     public static final int MEDIA_TYPE_LOCATION    = 0x0010;
    120     private static final int MEDA_TYPE_INVALID     = 0x0020;
    121     public static final int MEDIA_TYPE_ALL         = 0xFFFF;
    122 
    123     /** The listener to call when events occur */
    124     private MediaPickerListener mListener;
    125 
    126     /** The handler used to dispatch calls to the listener */
    127     private Handler mListenerHandler;
    128 
    129     /** The bit flags of media types supported */
    130     private int mSupportedMediaTypes;
    131 
    132     /** The list of choosers which could be within the media picker */
    133     private final MediaChooser[] mChoosers;
    134 
    135     /** The list of currently enabled choosers */
    136     private final ArrayList<MediaChooser> mEnabledChoosers;
    137 
    138     /** The currently selected chooser */
    139     private MediaChooser mSelectedChooser;
    140 
    141     /** The main panel that controls the custom layout */
    142     private MediaPickerPanel mMediaPickerPanel;
    143 
    144     /** The linear layout that holds the icons to select individual chooser tabs */
    145     private LinearLayout mTabStrip;
    146 
    147     /** The view pager to swap between choosers */
    148     private ViewPager mViewPager;
    149 
    150     /** The current pager adapter for the view pager */
    151     private FixedViewPagerAdapter<MediaChooser> mPagerAdapter;
    152 
    153     /** True if the media picker is visible */
    154     private boolean mOpen;
    155 
    156     /** The theme color to use to make the media picker match the rest of the UI */
    157     private int mThemeColor;
    158 
    159     @VisibleForTesting
    160     final Binding<MediaPickerData> mBinding = BindingBase.createBinding(this);
    161 
    162     /** Handles picking image from the document picker */
    163     private DocumentImagePicker mDocumentImagePicker;
    164 
    165     /** Provides subscription-related data to access per-subscription configurations. */
    166     private DraftMessageSubscriptionDataProvider mSubscriptionDataProvider;
    167 
    168     /** Provides access to DraftMessageData associated with the current conversation */
    169     private ImmutableBindingRef<DraftMessageData> mDraftMessageDataModel;
    170 
    171     public MediaPicker() {
    172         this(Factory.get().getApplicationContext());
    173     }
    174 
    175     public MediaPicker(final Context context) {
    176         mBinding.bind(DataModel.get().createMediaPickerData(context));
    177         mEnabledChoosers = new ArrayList<MediaChooser>();
    178         mChoosers = new MediaChooser[] {
    179             new CameraMediaChooser(this),
    180             new GalleryMediaChooser(this),
    181             new AudioMediaChooser(this),
    182         };
    183 
    184         mOpen = false;
    185         setSupportedMediaTypes(MEDIA_TYPE_ALL);
    186     }
    187 
    188     private boolean mIsAttached;
    189     private int mStartingMediaTypeOnAttach = MEDA_TYPE_INVALID;
    190     private boolean mAnimateOnAttach;
    191 
    192     @Override
    193     public void onAttach (final Activity activity) {
    194         super.onAttach(activity);
    195         mIsAttached = true;
    196         if (mStartingMediaTypeOnAttach != MEDA_TYPE_INVALID) {
    197             // open() was previously called. Do the pending open now.
    198             doOpen(mStartingMediaTypeOnAttach, mAnimateOnAttach);
    199         }
    200     }
    201 
    202     @Override
    203     public void onCreate(final Bundle savedInstanceState) {
    204         super.onCreate(savedInstanceState);
    205         mBinding.getData().init(getLoaderManager());
    206         mDocumentImagePicker = new DocumentImagePicker(this,
    207                 new SelectionListener() {
    208             @Override
    209             public void onDocumentSelected(final PendingAttachmentData data) {
    210                 if (mBinding.isBound()) {
    211                     dispatchPendingItemAdded(data);
    212                 }
    213             }
    214         });
    215     }
    216 
    217     @Override
    218     public View onCreateView(
    219             final LayoutInflater inflater,
    220             final ViewGroup container,
    221             final Bundle savedInstanceState) {
    222         mMediaPickerPanel = (MediaPickerPanel) inflater.inflate(
    223                 R.layout.mediapicker_fragment,
    224                 container,
    225                 false);
    226         mMediaPickerPanel.setMediaPicker(this);
    227         mTabStrip = (LinearLayout) mMediaPickerPanel.findViewById(R.id.mediapicker_tabstrip);
    228         mTabStrip.setBackgroundColor(mThemeColor);
    229         for (final MediaChooser chooser : mChoosers) {
    230             chooser.onCreateTabButton(inflater, mTabStrip);
    231             final boolean enabled = (chooser.getSupportedMediaTypes() & mSupportedMediaTypes) !=
    232                     MEDIA_TYPE_NONE;
    233             final ImageButton tabButton = chooser.getTabButton();
    234             if (tabButton != null) {
    235                 tabButton.setVisibility(enabled ? View.VISIBLE : View.GONE);
    236                 mTabStrip.addView(tabButton);
    237             }
    238         }
    239 
    240         mViewPager = (ViewPager) mMediaPickerPanel.findViewById(R.id.mediapicker_view_pager);
    241         mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    242             @Override
    243             public void onPageScrolled(
    244                     final int position,
    245                     final float positionOffset,
    246                     final int positionOffsetPixels) {
    247             }
    248 
    249             @Override
    250             public void onPageSelected(int position) {
    251                 // The position returned is relative to if we are in RtL mode. This class never
    252                 // switches the indices of the elements if we are in RtL mode so we need to
    253                 // translate the index back. For example, if the user clicked the item most to the
    254                 // right in RtL mode we would want the index to appear as 0 here, however the
    255                 // position returned would the last possible index.
    256                 if (UiUtils.isRtlMode()) {
    257                     position = mEnabledChoosers.size() - 1 - position;
    258                 }
    259                 selectChooser(mEnabledChoosers.get(position));
    260             }
    261 
    262             @Override
    263             public void onPageScrollStateChanged(final int state) {
    264             }
    265         });
    266         // Camera initialization is expensive, so don't realize offscreen pages if not needed.
    267         mViewPager.setOffscreenPageLimit(0);
    268         mViewPager.setAdapter(mPagerAdapter);
    269         final boolean isTouchExplorationEnabled = AccessibilityUtil.isTouchExplorationEnabled(
    270                 getActivity());
    271         mMediaPickerPanel.setFullScreenOnly(isTouchExplorationEnabled);
    272         mMediaPickerPanel.setExpanded(mOpen, true, mEnabledChoosers.indexOf(mSelectedChooser));
    273         return mMediaPickerPanel;
    274     }
    275 
    276     @Override
    277     public void onPause() {
    278         super.onPause();
    279         CameraManager.get().onPause();
    280         for (final MediaChooser chooser : mEnabledChoosers) {
    281             chooser.onPause();
    282         }
    283     }
    284 
    285     @Override
    286     public void onResume() {
    287         super.onResume();
    288         CameraManager.get().onResume();
    289 
    290         for (final MediaChooser chooser : mEnabledChoosers) {
    291             chooser.onResume();
    292         }
    293     }
    294 
    295     @Override
    296     public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
    297         super.onActivityResult(requestCode, resultCode, data);
    298         mDocumentImagePicker.onActivityResult(requestCode, resultCode, data);
    299     }
    300 
    301     @Override
    302     public void onDestroy() {
    303         super.onDestroy();
    304         mBinding.unbind();
    305     }
    306 
    307     /**
    308      * Sets the theme color to make the media picker match the surrounding UI
    309      * @param themeColor The new theme color
    310      */
    311     public void setConversationThemeColor(final int themeColor) {
    312         mThemeColor = themeColor;
    313         if (mTabStrip != null) {
    314             mTabStrip.setBackgroundColor(mThemeColor);
    315         }
    316 
    317         for (final MediaChooser chooser : mEnabledChoosers) {
    318             chooser.setThemeColor(mThemeColor);
    319         }
    320     }
    321 
    322     /**
    323      * Gets the current conversation theme color.
    324      */
    325     public int getConversationThemeColor() {
    326         return mThemeColor;
    327     }
    328 
    329     public void setDraftMessageDataModel(final BindingBase<DraftMessageData> draftBinding) {
    330         mDraftMessageDataModel = Binding.createBindingReference(draftBinding);
    331     }
    332 
    333     public ImmutableBindingRef<DraftMessageData> getDraftMessageDataModel() {
    334         return mDraftMessageDataModel;
    335     }
    336 
    337     public void setSubscriptionDataProvider(final DraftMessageSubscriptionDataProvider provider) {
    338         mSubscriptionDataProvider = provider;
    339     }
    340 
    341     @Override
    342     public int getConversationSelfSubId() {
    343         return mSubscriptionDataProvider.getConversationSelfSubId();
    344     }
    345 
    346     /**
    347      * Opens the media picker and optionally shows the chooser for the supplied media type
    348      * @param startingMediaType The media type of the chooser to open if {@link #MEDIA_TYPE_DEFAULT}
    349      *                          is used, then the default chooser from saved shared prefs is opened
    350      */
    351     public void open(final int startingMediaType, final boolean animate) {
    352         mOpen = true;
    353         if (mIsAttached) {
    354             doOpen(startingMediaType, animate);
    355         } else {
    356             // open() can get called immediately after the MediaPicker is created. In that case,
    357             // we defer doing work as it may require an attached fragment (eg. calling
    358             // Fragment#requestPermission)
    359             mStartingMediaTypeOnAttach = startingMediaType;
    360             mAnimateOnAttach = animate;
    361         }
    362     }
    363 
    364     private void doOpen(int startingMediaType, final boolean animate) {
    365         final boolean isTouchExplorationEnabled = AccessibilityUtil.isTouchExplorationEnabled(
    366                 // getActivity() will be null at this point
    367                 Factory.get().getApplicationContext());
    368 
    369         // If no specific starting type is specified (i.e. MEDIA_TYPE_DEFAULT), try to get the
    370         // last opened chooser index from shared prefs.
    371         if (startingMediaType == MEDIA_TYPE_DEFAULT) {
    372             final int selectedChooserIndex = mBinding.getData().getSelectedChooserIndex();
    373             if (selectedChooserIndex >= 0 && selectedChooserIndex < mEnabledChoosers.size()) {
    374                 selectChooser(mEnabledChoosers.get(selectedChooserIndex));
    375             } else {
    376                 // This is the first time the picker is being used
    377                 if (isTouchExplorationEnabled) {
    378                     // Accessibility defaults to audio attachment mode.
    379                     startingMediaType = MEDIA_TYPE_AUDIO;
    380                 }
    381             }
    382         }
    383 
    384         if (mSelectedChooser == null) {
    385             for (final MediaChooser chooser : mEnabledChoosers) {
    386                 if (startingMediaType == MEDIA_TYPE_DEFAULT ||
    387                         (startingMediaType & chooser.getSupportedMediaTypes()) != MEDIA_TYPE_NONE) {
    388                     selectChooser(chooser);
    389                     break;
    390                 }
    391             }
    392         }
    393 
    394         if (mSelectedChooser == null) {
    395             // Fall back to the first chooser.
    396             selectChooser(mEnabledChoosers.get(0));
    397         }
    398 
    399         if (mMediaPickerPanel != null) {
    400             mMediaPickerPanel.setFullScreenOnly(isTouchExplorationEnabled);
    401             mMediaPickerPanel.setExpanded(true, animate,
    402                     mEnabledChoosers.indexOf(mSelectedChooser));
    403         }
    404     }
    405 
    406     /** @return True if the media picker is open */
    407     public boolean isOpen() {
    408         return mOpen;
    409     }
    410 
    411     /**
    412      * Sets the list of media types to allow the user to select
    413      * @param mediaTypes The bit flags of media types to allow.  Can be any combination of the
    414      *                   MEDIA_TYPE_* values
    415      */
    416     void setSupportedMediaTypes(final int mediaTypes) {
    417         mSupportedMediaTypes = mediaTypes;
    418         mEnabledChoosers.clear();
    419         boolean selectNextChooser = false;
    420         for (final MediaChooser chooser : mChoosers) {
    421             final boolean enabled = (chooser.getSupportedMediaTypes() & mSupportedMediaTypes) !=
    422                     MEDIA_TYPE_NONE;
    423             if (enabled) {
    424                 // TODO Add a way to inform the chooser which media types are supported
    425                 mEnabledChoosers.add(chooser);
    426                 if (selectNextChooser) {
    427                     selectChooser(chooser);
    428                     selectNextChooser = false;
    429                 }
    430             } else if (mSelectedChooser == chooser) {
    431                 selectNextChooser = true;
    432             }
    433             final ImageButton tabButton = chooser.getTabButton();
    434             if (tabButton != null) {
    435                 tabButton.setVisibility(enabled ? View.VISIBLE : View.GONE);
    436             }
    437         }
    438 
    439         if (selectNextChooser && mEnabledChoosers.size() > 0) {
    440             selectChooser(mEnabledChoosers.get(0));
    441         }
    442         final MediaChooser[] enabledChoosers = new MediaChooser[mEnabledChoosers.size()];
    443         mEnabledChoosers.toArray(enabledChoosers);
    444         mPagerAdapter = new FixedViewPagerAdapter<MediaChooser>(enabledChoosers);
    445         if (mViewPager != null) {
    446             mViewPager.setAdapter(mPagerAdapter);
    447         }
    448 
    449         // Only rebind data if we are currently bound. Otherwise, we must have not
    450         // bound to any data yet and should wait until onCreate() to bind data.
    451         if (mBinding.isBound() && getActivity() != null) {
    452             mBinding.unbind();
    453             mBinding.bind(DataModel.get().createMediaPickerData(getActivity()));
    454             mBinding.getData().init(getLoaderManager());
    455         }
    456     }
    457 
    458     ViewPager getViewPager() {
    459         return mViewPager;
    460     }
    461 
    462     /** Hides the media picker, and frees up any resources its using */
    463     public void dismiss(final boolean animate) {
    464         mOpen = false;
    465         if (mMediaPickerPanel != null) {
    466             mMediaPickerPanel.setExpanded(false, animate, MediaPickerPanel.PAGE_NOT_SET);
    467         }
    468         mSelectedChooser = null;
    469     }
    470 
    471     /**
    472      * Sets the listener for the media picker events
    473      * @param listener The listener which will receive events
    474      */
    475     public void setListener(final MediaPickerListener listener) {
    476         Assert.isMainThread();
    477         mListener = listener;
    478         mListenerHandler = listener != null ? new Handler() : null;
    479     }
    480 
    481     /** @return True if the media picker is in full-screen mode */
    482     public boolean isFullScreen() {
    483         return mMediaPickerPanel != null && mMediaPickerPanel.isFullScreen();
    484     }
    485 
    486     public void setFullScreen(final boolean fullScreen) {
    487         mMediaPickerPanel.setFullScreenView(fullScreen, true);
    488     }
    489 
    490     public void updateActionBar(final ActionBar actionBar) {
    491         if (getActivity() == null) {
    492             return;
    493         }
    494         if (isFullScreen() && mSelectedChooser != null) {
    495             mSelectedChooser.updateActionBar(actionBar);
    496         } else {
    497             actionBar.hide();
    498         }
    499     }
    500 
    501     /**
    502      * Selects a new chooser
    503      * @param newSelectedChooser The newly selected chooser
    504      */
    505     void selectChooser(final MediaChooser newSelectedChooser) {
    506         if (mSelectedChooser == newSelectedChooser) {
    507             return;
    508         }
    509 
    510         if (mSelectedChooser != null) {
    511             mSelectedChooser.setSelected(false);
    512         }
    513         mSelectedChooser = newSelectedChooser;
    514         if (mSelectedChooser != null) {
    515             mSelectedChooser.setSelected(true);
    516         }
    517 
    518         final int chooserIndex = mEnabledChoosers.indexOf(mSelectedChooser);
    519         if (mViewPager != null) {
    520             mViewPager.setCurrentItem(chooserIndex, true /* smoothScroll */);
    521         }
    522 
    523         if (isFullScreen()) {
    524             invalidateOptionsMenu();
    525         }
    526 
    527         // Save the newly selected chooser's index so we may directly switch to it the
    528         // next time user opens the media picker.
    529         mBinding.getData().saveSelectedChooserIndex(chooserIndex);
    530         if (mMediaPickerPanel != null) {
    531             mMediaPickerPanel.onChooserChanged();
    532         }
    533         dispatchChooserSelected(chooserIndex);
    534     }
    535 
    536     public boolean canShowIme() {
    537         if (mSelectedChooser != null) {
    538             return mSelectedChooser.canShowIme();
    539         }
    540         return false;
    541     }
    542 
    543     public boolean onBackPressed() {
    544         return mSelectedChooser != null && mSelectedChooser.onBackPressed();
    545     }
    546 
    547     void invalidateOptionsMenu() {
    548         ((BugleActionBarActivity) getActivity()).supportInvalidateOptionsMenu();
    549     }
    550 
    551     void dispatchOpened() {
    552         setHasOptionsMenu(false);
    553         mOpen = true;
    554         mPagerAdapter.notifyDataSetChanged();
    555         if (mListener != null) {
    556             mListenerHandler.post(new Runnable() {
    557                 @Override
    558                 public void run() {
    559                     mListener.onOpened();
    560                 }
    561             });
    562         }
    563         if (mSelectedChooser != null) {
    564             mSelectedChooser.onFullScreenChanged(false);
    565             mSelectedChooser.onOpenedChanged(true);
    566         }
    567     }
    568 
    569     void dispatchDismissed() {
    570         setHasOptionsMenu(false);
    571         mOpen = false;
    572         if (mListener != null) {
    573             mListenerHandler.post(new Runnable() {
    574                 @Override
    575                 public void run() {
    576                     mListener.onDismissed();
    577                 }
    578             });
    579         }
    580         if (mSelectedChooser != null) {
    581             mSelectedChooser.onOpenedChanged(false);
    582         }
    583     }
    584 
    585     void dispatchFullScreen(final boolean fullScreen) {
    586         setHasOptionsMenu(fullScreen);
    587         if (mListener != null) {
    588             mListenerHandler.post(new Runnable() {
    589                 @Override
    590                 public void run() {
    591                     mListener.onFullScreenChanged(fullScreen);
    592                 }
    593             });
    594         }
    595         if (mSelectedChooser != null) {
    596             mSelectedChooser.onFullScreenChanged(fullScreen);
    597         }
    598     }
    599 
    600     void dispatchItemsSelected(final MessagePartData item, final boolean dismissMediaPicker) {
    601         final List<MessagePartData> items = new ArrayList<MessagePartData>(1);
    602         items.add(item);
    603         dispatchItemsSelected(items, dismissMediaPicker);
    604     }
    605 
    606     void dispatchItemsSelected(final Collection<MessagePartData> items,
    607             final boolean dismissMediaPicker) {
    608         if (mListener != null) {
    609             mListenerHandler.post(new Runnable() {
    610                 @Override
    611                 public void run() {
    612                     mListener.onItemsSelected(items, dismissMediaPicker);
    613                 }
    614             });
    615         }
    616 
    617         if (isFullScreen() && !dismissMediaPicker) {
    618             invalidateOptionsMenu();
    619         }
    620     }
    621 
    622     void dispatchItemUnselected(final MessagePartData item) {
    623         if (mListener != null) {
    624             mListenerHandler.post(new Runnable() {
    625                 @Override
    626                 public void run() {
    627                     mListener.onItemUnselected(item);
    628                 }
    629             });
    630         }
    631 
    632         if (isFullScreen()) {
    633             invalidateOptionsMenu();
    634         }
    635     }
    636 
    637     void dispatchConfirmItemSelection() {
    638         if (mListener != null) {
    639             mListenerHandler.post(new Runnable() {
    640                 @Override
    641                 public void run() {
    642                     mListener.onConfirmItemSelection();
    643                 }
    644             });
    645         }
    646     }
    647 
    648     void dispatchPendingItemAdded(final PendingAttachmentData pendingItem) {
    649         if (mListener != null) {
    650             mListenerHandler.post(new Runnable() {
    651                 @Override
    652                 public void run() {
    653                     mListener.onPendingItemAdded(pendingItem);
    654                 }
    655             });
    656         }
    657 
    658         if (isFullScreen()) {
    659             invalidateOptionsMenu();
    660         }
    661     }
    662 
    663     void dispatchChooserSelected(final int chooserIndex) {
    664         if (mListener != null) {
    665             mListenerHandler.post(new Runnable() {
    666                 @Override
    667                 public void run() {
    668                     mListener.onChooserSelected(chooserIndex);
    669                 }
    670             });
    671         }
    672     }
    673 
    674     public boolean canSwipeDownChooser() {
    675         return mSelectedChooser == null ? false : mSelectedChooser.canSwipeDown();
    676     }
    677 
    678     public boolean isChooserHandlingTouch() {
    679         return mSelectedChooser == null ? false : mSelectedChooser.isHandlingTouch();
    680     }
    681 
    682     public void stopChooserTouchHandling() {
    683         if (mSelectedChooser != null) {
    684             mSelectedChooser.stopTouchHandling();
    685         }
    686     }
    687 
    688     boolean getChooserShowsActionBarInFullScreen() {
    689         return mSelectedChooser == null ? false : mSelectedChooser.getActionBarTitleResId() != 0;
    690     }
    691 
    692     @Override
    693     public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
    694         if (mSelectedChooser != null) {
    695             mSelectedChooser.onCreateOptionsMenu(inflater, menu);
    696         }
    697     }
    698 
    699     @Override
    700     public boolean onOptionsItemSelected(final MenuItem item) {
    701         return (mSelectedChooser != null && mSelectedChooser.onOptionsItemSelected(item)) ||
    702                 super.onOptionsItemSelected(item);
    703     }
    704 
    705     PagerAdapter getPagerAdapter() {
    706         return mPagerAdapter;
    707     }
    708 
    709     public void resetViewHolderState() {
    710         mPagerAdapter.resetState();
    711     }
    712 
    713     /**
    714      * Launch an external picker to pick item from document picker as attachment.
    715      */
    716     public void launchDocumentPicker() {
    717         mDocumentImagePicker.launchPicker();
    718     }
    719 
    720     public ImmutableBindingRef<MediaPickerData> getMediaPickerDataBinding() {
    721         return BindingBase.createBindingReference(mBinding);
    722     }
    723 
    724     protected static final int CAMERA_PERMISSION_REQUEST_CODE = 1;
    725     protected static final int LOCATION_PERMISSION_REQUEST_CODE = 2;
    726     protected static final int RECORD_AUDIO_PERMISSION_REQUEST_CODE = 3;
    727     protected static final int GALLERY_PERMISSION_REQUEST_CODE = 4;
    728 
    729     @Override
    730     public void onRequestPermissionsResult(
    731             final int requestCode, final String permissions[], final int[] grantResults) {
    732         if (mSelectedChooser != null) {
    733             mSelectedChooser.onRequestPermissionsResult(requestCode, permissions, grantResults);
    734         }
    735     }
    736 }
    737