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