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 androidx.viewpager.widget.PagerAdapter; 27 import androidx.viewpager.widget.ViewPager; 28 import androidx.appcompat.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 */ onOpened()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 */ onFullScreenChanged(boolean fullScreen)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 */ onItemsSelected(Collection<MessagePartData> items, boolean dismissMediaPicker)80 void onItemsSelected(Collection<MessagePartData> items, boolean dismissMediaPicker); 81 82 /** 83 * Called when the user unselects one item. 84 */ onItemUnselected(MessagePartData item)85 void onItemUnselected(MessagePartData item); 86 87 /** 88 * Called when the media picker is closed. Always called immediately after onItemsSelected 89 */ onDismissed()90 void onDismissed(); 91 92 /** 93 * Called when media item selection is confirmed in a multi-select action. 94 */ onConfirmItemSelection()95 void onConfirmItemSelection(); 96 97 /** 98 * Called when a pending attachment is added. 99 * @param pendingItem the pending attachment data being loaded. 100 */ onPendingItemAdded(PendingAttachmentData pendingItem)101 void onPendingItemAdded(PendingAttachmentData pendingItem); 102 103 /** 104 * Called when a new media chooser is selected. 105 */ onChooserSelected(final int chooserIndex)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 a media 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 MediaPicker()171 public MediaPicker() { 172 this(Factory.get().getApplicationContext()); 173 } 174 MediaPicker(final Context context)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 onAttach(final Activity activity)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 onCreate(final Bundle savedInstanceState)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 onCreateView( final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState)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 onPause()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 onResume()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 onActivityResult(final int requestCode, final int resultCode, final Intent data)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 onDestroy()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 */ setConversationThemeColor(final int themeColor)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 */ getConversationThemeColor()325 public int getConversationThemeColor() { 326 return mThemeColor; 327 } 328 setDraftMessageDataModel(final BindingBase<DraftMessageData> draftBinding)329 public void setDraftMessageDataModel(final BindingBase<DraftMessageData> draftBinding) { 330 mDraftMessageDataModel = Binding.createBindingReference(draftBinding); 331 } 332 getDraftMessageDataModel()333 public ImmutableBindingRef<DraftMessageData> getDraftMessageDataModel() { 334 return mDraftMessageDataModel; 335 } 336 setSubscriptionDataProvider(final DraftMessageSubscriptionDataProvider provider)337 public void setSubscriptionDataProvider(final DraftMessageSubscriptionDataProvider provider) { 338 mSubscriptionDataProvider = provider; 339 } 340 341 @Override getConversationSelfSubId()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 */ open(final int startingMediaType, final boolean animate)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 doOpen(int startingMediaType, final boolean animate)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 */ isOpen()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 */ setSupportedMediaTypes(final int mediaTypes)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 getViewPager()458 ViewPager getViewPager() { 459 return mViewPager; 460 } 461 462 /** Hides the media picker, and frees up any resources it’s using */ dismiss(final boolean animate)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 */ setListener(final MediaPickerListener listener)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 */ isFullScreen()482 public boolean isFullScreen() { 483 return mMediaPickerPanel != null && mMediaPickerPanel.isFullScreen(); 484 } 485 setFullScreen(final boolean fullScreen)486 public void setFullScreen(final boolean fullScreen) { 487 mMediaPickerPanel.setFullScreenView(fullScreen, true); 488 } 489 updateActionBar(final ActionBar actionBar)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 */ selectChooser(final MediaChooser newSelectedChooser)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 canShowIme()536 public boolean canShowIme() { 537 if (mSelectedChooser != null) { 538 return mSelectedChooser.canShowIme(); 539 } 540 return false; 541 } 542 onBackPressed()543 public boolean onBackPressed() { 544 return mSelectedChooser != null && mSelectedChooser.onBackPressed(); 545 } 546 invalidateOptionsMenu()547 void invalidateOptionsMenu() { 548 ((BugleActionBarActivity) getActivity()).supportInvalidateOptionsMenu(); 549 } 550 dispatchOpened()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 dispatchDismissed()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 dispatchFullScreen(final boolean fullScreen)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 dispatchItemsSelected(final MessagePartData item, final boolean dismissMediaPicker)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 dispatchItemsSelected(final Collection<MessagePartData> items, final boolean dismissMediaPicker)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 dispatchItemUnselected(final MessagePartData item)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 dispatchConfirmItemSelection()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 dispatchPendingItemAdded(final PendingAttachmentData pendingItem)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 dispatchChooserSelected(final int chooserIndex)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 canSwipeDownChooser()674 public boolean canSwipeDownChooser() { 675 return mSelectedChooser == null ? false : mSelectedChooser.canSwipeDown(); 676 } 677 isChooserHandlingTouch()678 public boolean isChooserHandlingTouch() { 679 return mSelectedChooser == null ? false : mSelectedChooser.isHandlingTouch(); 680 } 681 stopChooserTouchHandling()682 public void stopChooserTouchHandling() { 683 if (mSelectedChooser != null) { 684 mSelectedChooser.stopTouchHandling(); 685 } 686 } 687 getChooserShowsActionBarInFullScreen()688 boolean getChooserShowsActionBarInFullScreen() { 689 return mSelectedChooser == null ? false : mSelectedChooser.getActionBarTitleResId() != 0; 690 } 691 692 @Override onCreateOptionsMenu(final Menu menu, final MenuInflater inflater)693 public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { 694 if (mSelectedChooser != null) { 695 mSelectedChooser.onCreateOptionsMenu(inflater, menu); 696 } 697 } 698 699 @Override onOptionsItemSelected(final MenuItem item)700 public boolean onOptionsItemSelected(final MenuItem item) { 701 return (mSelectedChooser != null && mSelectedChooser.onOptionsItemSelected(item)) || 702 super.onOptionsItemSelected(item); 703 } 704 getPagerAdapter()705 PagerAdapter getPagerAdapter() { 706 return mPagerAdapter; 707 } 708 resetViewHolderState()709 public void resetViewHolderState() { 710 mPagerAdapter.resetState(); 711 } 712 713 /** 714 * Launch an external picker to pick item from document picker as attachment. 715 */ launchDocumentPicker()716 public void launchDocumentPicker() { 717 mDocumentImagePicker.launchPicker(); 718 } 719 getMediaPickerDataBinding()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 onRequestPermissionsResult( final int requestCode, final String permissions[], final int[] grantResults)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