Messaging: Use ActivityResultContracts for media and contacts
Instead of handling showing and picking the media ourselves, use existing AcitivityResultContracts to pick images, videos or contacts Change-Id: I20079ad94882cc6bbfeae8d57ca0e4aa30612c01
This commit is contained in:
@@ -165,20 +165,6 @@ public abstract class UIIntents {
|
||||
*/
|
||||
public abstract void launchAddContactActivity(final Context context, final String destination);
|
||||
|
||||
/**
|
||||
* Launch an activity to show the document picker to pick an image/video/audio.
|
||||
*
|
||||
* @param fragment the requesting fragment
|
||||
*/
|
||||
public abstract void launchDocumentImagePicker(final Fragment fragment);
|
||||
|
||||
/**
|
||||
* Launch an activity to show the contacts list to pick one.
|
||||
*
|
||||
* @param fragment the requesting fragment
|
||||
*/
|
||||
public abstract void launchContactCardPicker(final Fragment fragment);
|
||||
|
||||
/**
|
||||
* Launch an activity to show people & options for a given conversation.
|
||||
*/
|
||||
@@ -223,12 +209,6 @@ public abstract class UIIntents {
|
||||
*/
|
||||
public abstract void launchSaveVCardToContactsActivity(Context context, Uri vcardUri);
|
||||
|
||||
/**
|
||||
* Launch an activity to let the user select & unselect the list of attachments to send.
|
||||
*/
|
||||
public abstract void launchAttachmentChooserActivity(final Activity activity,
|
||||
final String conversationId, final int requestCode);
|
||||
|
||||
/**
|
||||
* Launch full screen video viewer.
|
||||
*/
|
||||
|
||||
@@ -35,7 +35,6 @@ import android.provider.MediaStore;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.TaskStackBuilder;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
@@ -52,7 +51,6 @@ import com.android.messaging.sms.MmsSmsUtils;
|
||||
import com.android.messaging.ui.appsettings.ApplicationSettingsActivity;
|
||||
import com.android.messaging.ui.appsettings.PerSubscriptionSettingsActivity;
|
||||
import com.android.messaging.ui.appsettings.SettingsActivity;
|
||||
import com.android.messaging.ui.attachmentchooser.AttachmentChooserActivity;
|
||||
import com.android.messaging.ui.conversation.ConversationActivity;
|
||||
import com.android.messaging.ui.conversation.LaunchConversationActivity;
|
||||
import com.android.messaging.ui.conversationlist.ArchivedConversationListActivity;
|
||||
@@ -216,29 +214,6 @@ public class UIIntentsImpl extends UIIntents {
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchDocumentImagePicker(final Fragment fragment) {
|
||||
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, MessagePartData.ACCEPTABLE_GALLERY_MEDIA_TYPES);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType(ContentType.ANY_TYPE);
|
||||
|
||||
fragment.startActivityForResult(intent, REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchContactCardPicker(final Fragment fragment) {
|
||||
final Intent intent = new Intent(Intent.ACTION_PICK);
|
||||
intent.setType(Contacts.CONTENT_TYPE);
|
||||
|
||||
try {
|
||||
fragment.startActivityForResult(intent, REQUEST_PICK_CONTACT_CARD);
|
||||
} catch (final ActivityNotFoundException ex) {
|
||||
LogUtil.w(LogUtil.BUGLE_TAG, "Couldn't find activity:", ex);
|
||||
UiUtils.showToastAtBottom(R.string.activity_not_found_message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchPeopleAndOptionsActivity(final Activity activity,
|
||||
final String conversationId) {
|
||||
@@ -290,14 +265,6 @@ public class UIIntentsImpl extends UIIntents {
|
||||
startExternalActivity(context, intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchAttachmentChooserActivity(final Activity activity,
|
||||
final String conversationId, final int requestCode) {
|
||||
final Intent intent = new Intent(activity, AttachmentChooserActivity.class);
|
||||
intent.putExtra(UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchFullScreenVideoViewer(final Context context, final Uri videoUri) {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
|
||||
@@ -240,7 +240,7 @@ public class ConversationActivity extends BugleActionBarActivity
|
||||
ContactPickerFragment.FRAGMENT_TAG);
|
||||
}
|
||||
|
||||
private ConversationFragment getConversationFragment() {
|
||||
public ConversationFragment getConversationFragment() {
|
||||
return (ConversationFragment) getSupportFragmentManager().findFragmentByTag(
|
||||
ConversationFragment.FRAGMENT_TAG);
|
||||
}
|
||||
@@ -366,22 +366,4 @@ public class ConversationActivity extends BugleActionBarActivity
|
||||
public boolean shouldResumeComposeMessage() {
|
||||
return mUiState.shouldResumeComposeMessage();
|
||||
}
|
||||
|
||||
@SuppressWarnings("MissingSuperCall") // TODO: fix me
|
||||
@Override
|
||||
protected void onActivityResult(final int requestCode, final int resultCode,
|
||||
final Intent data) {
|
||||
if (requestCode == ConversationFragment.REQUEST_CHOOSE_ATTACHMENTS &&
|
||||
resultCode == RESULT_OK) {
|
||||
final ConversationFragment conversationFragment = getConversationFragment();
|
||||
if (conversationFragment != null) {
|
||||
conversationFragment.onAttachmentChoosen();
|
||||
} else {
|
||||
LogUtil.e(LogUtil.BUGLE_TAG, "ConversationFragment is missing after launching " +
|
||||
"AttachmentChooserActivity!");
|
||||
}
|
||||
} else if (resultCode == FINISH_RESULT_CODE) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,10 @@ import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.activity.result.ActivityResultCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
@@ -87,6 +91,7 @@ import com.android.messaging.ui.ConversationDrawables;
|
||||
import com.android.messaging.ui.SnackBar;
|
||||
import com.android.messaging.ui.UIIntents;
|
||||
import com.android.messaging.ui.animation.PopupTransitionAnimation;
|
||||
import com.android.messaging.ui.attachmentchooser.AttachmentChooserActivity;
|
||||
import com.android.messaging.ui.contact.AddContactsConfirmationDialog;
|
||||
import com.android.messaging.ui.conversation.ComposeMessageView.IComposeMessageViewHost;
|
||||
import com.android.messaging.ui.conversation.ConversationInputManager.ConversationInputHost;
|
||||
@@ -399,6 +404,25 @@ public class ConversationFragment extends Fragment implements ConversationDataLi
|
||||
}
|
||||
};
|
||||
|
||||
private final ActivityResultLauncher<Intent> mLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(), result -> {
|
||||
if (result.getResultCode() == Activity.RESULT_OK) {
|
||||
final ConversationFragment conversationFragment = getConversationFragment();
|
||||
if (conversationFragment != null) {
|
||||
conversationFragment.onAttachmentChoosen();
|
||||
} else {
|
||||
LogUtil.e(LogUtil.BUGLE_TAG,
|
||||
"ConversationFragment is missing after launching " +
|
||||
"AttachmentChooserActivity!");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
public ConversationFragment getConversationFragment() {
|
||||
return (ConversationFragment) getParentFragmentManager().findFragmentByTag(
|
||||
ConversationFragment.FRAGMENT_TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} from Fragment
|
||||
*/
|
||||
@@ -429,8 +453,8 @@ public class ConversationFragment extends Fragment implements ConversationDataLi
|
||||
* loading when onActivityCreated() is called, which is guaranteed to happen after both.
|
||||
*/
|
||||
@Override
|
||||
public void onActivityCreated(final Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
public void onViewCreated(@NonNull View view, final Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
// Delay showing the message list until the participant list is loaded.
|
||||
mRecyclerView.setVisibility(View.INVISIBLE);
|
||||
mBinding.ensureBound();
|
||||
@@ -1448,7 +1472,7 @@ public class ConversationFragment extends Fragment implements ConversationDataLi
|
||||
getActivity(), tooManyVideos);
|
||||
}
|
||||
|
||||
public static void warnOfExceedingMessageLimit(final boolean sending,
|
||||
public void warnOfExceedingMessageLimit(final boolean sending,
|
||||
final ComposeMessageView composeMessageView, final String conversationId,
|
||||
final Activity activity, final boolean tooManyVideos) {
|
||||
final AlertDialog.Builder builder =
|
||||
@@ -1478,10 +1502,11 @@ public class ConversationFragment extends Fragment implements ConversationDataLi
|
||||
showAttachmentChooser(mConversationId, getActivity());
|
||||
}
|
||||
|
||||
public static void showAttachmentChooser(final String conversationId,
|
||||
public void showAttachmentChooser(final String conversationId,
|
||||
final Activity activity) {
|
||||
UIIntents.get().launchAttachmentChooserActivity(activity,
|
||||
conversationId, REQUEST_CHOOSE_ATTACHMENTS);
|
||||
final Intent intent = new Intent(activity, AttachmentChooserActivity.class);
|
||||
intent.putExtra(UIIntents.UI_INTENT_EXTRA_CONVERSATION_ID, conversationId);
|
||||
mLauncher.launch(intent);
|
||||
}
|
||||
|
||||
private void updateActionAndStatusBarColor(final ActionBar actionBar) {
|
||||
|
||||
@@ -90,11 +90,6 @@ public abstract class AbstractConversationListActivity extends BugleActionBarAct
|
||||
return isInConversationListSelectMode();
|
||||
}
|
||||
|
||||
@SuppressWarnings("MissingSuperCall") // TODO: fix me
|
||||
@Override
|
||||
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionBarDelete(final Collection<SelectedConversation> conversations) {
|
||||
if (!PhoneUtils.getDefault().isDefaultSmsApp()) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.messaging.ui.mediapicker;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
@@ -28,12 +29,16 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.datamodel.data.PendingAttachmentData;
|
||||
import com.android.messaging.ui.UIIntents;
|
||||
import com.android.messaging.util.ContactUtil;
|
||||
import com.android.messaging.util.ContentType;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
import com.android.messaging.util.SafeAsyncTask;
|
||||
import com.android.messaging.util.UiUtils;
|
||||
|
||||
/**
|
||||
* Chooser which allows the user to select an existing contact from contacts apps on this device.
|
||||
@@ -43,9 +48,42 @@ import com.android.messaging.util.SafeAsyncTask;
|
||||
class ContactMediaChooser extends MediaChooser {
|
||||
private View mEnabledView;
|
||||
private View mMissingPermissionView;
|
||||
private final ActivityResultLauncher<Intent> mPickerLauncher;
|
||||
|
||||
ContactMediaChooser(final MediaPicker mediaPicker) {
|
||||
super(mediaPicker);
|
||||
mPickerLauncher = mediaPicker.registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(), result -> {
|
||||
if (result.getResultCode() != Activity.RESULT_OK ||
|
||||
result.getData() == null) {
|
||||
return;
|
||||
}
|
||||
Uri contactUri = result.getData().getData();
|
||||
if (contactUri != null) {
|
||||
String lookupKey = null;
|
||||
try (final Cursor c = getContext().getContentResolver().query(
|
||||
contactUri,
|
||||
new String[]{Contacts.LOOKUP_KEY},
|
||||
null,
|
||||
null,
|
||||
null)) {
|
||||
if (c != null) {
|
||||
c.moveToFirst();
|
||||
lookupKey = c.getString(0);
|
||||
}
|
||||
}
|
||||
final Uri vCardUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI,
|
||||
lookupKey);
|
||||
if (vCardUri != null) {
|
||||
SafeAsyncTask.executeOnThreadPool(() -> {
|
||||
final PendingAttachmentData pendingItem =
|
||||
PendingAttachmentData.createPendingAttachmentData(
|
||||
ContentType.TEXT_X_VCARD.toLowerCase(), vCardUri);
|
||||
mMediaPicker.dispatchPendingItemAdded(pendingItem);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,7 +118,14 @@ class ContactMediaChooser extends MediaChooser {
|
||||
mMissingPermissionView = view.findViewById(R.id.missing_permission_view);
|
||||
mEnabledView.setOnClickListener(v -> {
|
||||
// Launch an external picker to pick a contact as attachment.
|
||||
UIIntents.get().launchContactCardPicker(mMediaPicker);
|
||||
final Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
|
||||
|
||||
try {
|
||||
mPickerLauncher.launch(intent);
|
||||
} catch (final ActivityNotFoundException ex) {
|
||||
LogUtil.w(LogUtil.BUGLE_TAG, "Couldn't find activity:", ex);
|
||||
UiUtils.showToastAtBottom(R.string.activity_not_found_message);
|
||||
}
|
||||
});
|
||||
return view;
|
||||
}
|
||||
@@ -104,35 +149,4 @@ class ContactMediaChooser extends MediaChooser {
|
||||
mMissingPermissionView.setVisibility(permissionGranted ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == UIIntents.REQUEST_PICK_CONTACT_CARD
|
||||
&& resultCode == Activity.RESULT_OK) {
|
||||
Uri contactUri = data.getData();
|
||||
if (contactUri != null) {
|
||||
String lookupKey = null;
|
||||
try (final Cursor c = getContext().getContentResolver().query(
|
||||
contactUri,
|
||||
new String[] {Contacts.LOOKUP_KEY},
|
||||
null,
|
||||
null,
|
||||
null)) {
|
||||
if (c != null) {
|
||||
c.moveToFirst();
|
||||
lookupKey = c.getString(0);
|
||||
}
|
||||
}
|
||||
final Uri vCardUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
|
||||
if (vCardUri != null) {
|
||||
SafeAsyncTask.executeOnThreadPool(() -> {
|
||||
final PendingAttachmentData pendingItem =
|
||||
PendingAttachmentData.createPendingAttachmentData(
|
||||
ContentType.TEXT_X_VCARD.toLowerCase(), vCardUri);
|
||||
mMediaPicker.dispatchPendingItemAdded(pendingItem);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,16 +20,23 @@ import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.PickVisualMediaRequest;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.android.messaging.Factory;
|
||||
import com.android.messaging.datamodel.data.PendingAttachmentData;
|
||||
import com.android.messaging.ui.UIIntents;
|
||||
import com.android.messaging.util.BugleGservicesKeys;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
import com.android.messaging.util.FileUtil;
|
||||
import com.android.messaging.util.ImageUtils;
|
||||
import com.android.messaging.util.SafeAsyncTask;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Wraps around the functionalities to allow the user to pick an image/video/audio from the document
|
||||
* picker. Instances of this class must be tied to a Fragment which is able to delegate activity
|
||||
@@ -58,6 +65,8 @@ public class DocumentImagePicker {
|
||||
|
||||
private static final String EXTRA_PHOTO_URL = "photo_url";
|
||||
|
||||
private final ActivityResultLauncher<PickVisualMediaRequest> mPickMultipleMedia;
|
||||
|
||||
/**
|
||||
* Creates a new instance of DocumentImagePicker.
|
||||
* @param activity The activity that owns the picker, or the activity that hosts the owning
|
||||
@@ -67,39 +76,29 @@ public class DocumentImagePicker {
|
||||
final SelectionListener listener) {
|
||||
mFragment = fragment;
|
||||
mListener = listener;
|
||||
|
||||
mPickMultipleMedia = mFragment.registerForActivityResult(
|
||||
new ActivityResultContracts.PickMultipleVisualMedia(
|
||||
BugleGservicesKeys.MMS_ATTACHMENT_LIMIT_DEFAULT), uris -> {
|
||||
// Callback is invoked after the user selects media items or closes the
|
||||
// photo picker.
|
||||
if (!uris.isEmpty()) {
|
||||
onDocumentsPicked(uris);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Intent out to open an image/video from document picker.
|
||||
*/
|
||||
public void launchPicker() {
|
||||
UIIntents.get().launchDocumentImagePicker(mFragment);
|
||||
mPickMultipleMedia.launch(new PickVisualMediaRequest.Builder()
|
||||
.setMediaType(ActivityResultContracts.PickVisualMedia.ImageAndVideo.INSTANCE)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called from the fragment/activity's onActivityResult().
|
||||
*/
|
||||
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
|
||||
// Sometimes called after media item has been picked from the document picker.
|
||||
String url = data.getStringExtra(EXTRA_PHOTO_URL);
|
||||
if (url == null) {
|
||||
// we're using the builtin photo picker which supplies the return
|
||||
// url as it's "data"
|
||||
url = data.getDataString();
|
||||
if (url == null) {
|
||||
final Bundle extras = data.getExtras();
|
||||
if (extras != null) {
|
||||
final Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM);
|
||||
if (uri != null) {
|
||||
url = uri.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Guard against null uri cases for when the activity returns a null/invalid intent.
|
||||
if (url != null) {
|
||||
final Uri uri = Uri.parse(url);
|
||||
public void onDocumentsPicked(List<Uri> uris) {
|
||||
for (Uri uri: uris) {
|
||||
prepareDocumentForAttachment(uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.messaging.ui.mediapicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CursorAdapter;
|
||||
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.ui.mediapicker.GalleryGridItemView.HostInterface;
|
||||
import com.android.messaging.util.Assert;
|
||||
|
||||
/**
|
||||
* Bridges between the image cursor loaded by GalleryBoundCursorLoader and the GalleryGridView.
|
||||
*/
|
||||
public class GalleryGridAdapter extends CursorAdapter {
|
||||
private GalleryGridItemView.HostInterface mGgivHostInterface;
|
||||
|
||||
public GalleryGridAdapter(final Context context, final Cursor cursor) {
|
||||
super(context, cursor, 0);
|
||||
}
|
||||
|
||||
public void setHostInterface(final HostInterface ggivHostInterface) {
|
||||
mGgivHostInterface = ggivHostInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void bindView(final View view, final Context context, final Cursor cursor) {
|
||||
Assert.isTrue(view instanceof GalleryGridItemView);
|
||||
final GalleryGridItemView galleryImageView = (GalleryGridItemView) view;
|
||||
galleryImageView.bind(cursor, mGgivHostInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public View newView(final Context context, final Cursor cursor, final ViewGroup parent) {
|
||||
final LayoutInflater layoutInflater = LayoutInflater.from(context);
|
||||
return layoutInflater.inflate(R.layout.gallery_grid_item_view, parent, false);
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* Copyright (C) 2024 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.messaging.ui.mediapicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.TouchDelegate;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.datamodel.DataModel;
|
||||
import com.android.messaging.datamodel.data.GalleryGridItemData;
|
||||
import com.android.messaging.ui.AsyncImageView;
|
||||
import com.android.messaging.ui.ConversationDrawables;
|
||||
import com.android.messaging.util.ContentType;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Shows an item in the gallery picker grid view. Hosts an FileImageView with a checkbox.
|
||||
*/
|
||||
public class GalleryGridItemView extends FrameLayout {
|
||||
/**
|
||||
* Implemented by the owner of this GalleryGridItemView instance to communicate on media
|
||||
* picking and selection events.
|
||||
*/
|
||||
public interface HostInterface {
|
||||
void onItemClicked(View view, GalleryGridItemData data, boolean longClick);
|
||||
boolean isItemSelected(GalleryGridItemData data);
|
||||
boolean isMultiSelectEnabled();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
final
|
||||
GalleryGridItemData mData;
|
||||
private AsyncImageView mImageView;
|
||||
private CheckBox mCheckBox;
|
||||
private RelativeLayout mAdditionalInfo;
|
||||
private ImageView mIcon;
|
||||
private LinearLayout mFileInfo;
|
||||
private TextView mFileName;
|
||||
private TextView mFileType;
|
||||
private HostInterface mHostInterface;
|
||||
private final OnClickListener mOnClickListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
mHostInterface.onItemClicked(GalleryGridItemView.this, mData, false /*longClick*/);
|
||||
}
|
||||
};
|
||||
|
||||
public GalleryGridItemView(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mData = DataModel.get().createGalleryGridItemData();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mImageView = (AsyncImageView) findViewById(R.id.thumbnail);
|
||||
mCheckBox = (CheckBox) findViewById(R.id.checkbox);
|
||||
mCheckBox.setOnClickListener(mOnClickListener);
|
||||
mAdditionalInfo = (RelativeLayout) findViewById(R.id.additional_info);
|
||||
mIcon = (ImageView) findViewById(R.id.icon);
|
||||
mFileInfo = (LinearLayout) findViewById(R.id.file_info);
|
||||
mFileName = (TextView) findViewById(R.id.file_name);
|
||||
mFileType = (TextView) findViewById(R.id.file_type);
|
||||
setOnClickListener(mOnClickListener);
|
||||
final OnLongClickListener longClickListener = v -> {
|
||||
mHostInterface.onItemClicked(v, mData, true /* longClick */);
|
||||
return true;
|
||||
};
|
||||
setOnLongClickListener(longClickListener);
|
||||
mCheckBox.setOnLongClickListener(longClickListener);
|
||||
addOnLayoutChangeListener(new OnLayoutChangeListener() {
|
||||
@Override
|
||||
public void onLayoutChange(View v, int left, int top, int right, int bottom,
|
||||
int oldLeft, int oldTop, int oldRight, int oldBottom) {
|
||||
// Enlarge the clickable region for the checkbox to fill the entire view.
|
||||
final Rect region = new Rect(0, 0, getWidth(), getHeight());
|
||||
setTouchDelegate(new TouchDelegate(region, mCheckBox) {
|
||||
@Override
|
||||
public boolean onTouchEvent(@NonNull MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
setPressed(true);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
setPressed(false);
|
||||
break;
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
|
||||
// The grid view auto-fit the columns, so we want to let the height match the width
|
||||
// to make the image square.
|
||||
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
|
||||
}
|
||||
|
||||
public void bind(final Cursor cursor, final HostInterface hostInterface) {
|
||||
final int desiredSize = getResources()
|
||||
.getDimensionPixelSize(R.dimen.gallery_image_cell_size);
|
||||
mData.bind(cursor, desiredSize, desiredSize);
|
||||
mHostInterface = hostInterface;
|
||||
updateViewState();
|
||||
}
|
||||
|
||||
private void updateViewState() {
|
||||
updateImageView();
|
||||
if (mHostInterface.isMultiSelectEnabled() && !mData.isDocumentPickerItem()) {
|
||||
mCheckBox.setVisibility(VISIBLE);
|
||||
mCheckBox.setClickable(true);
|
||||
mCheckBox.setChecked(mHostInterface.isItemSelected(mData));
|
||||
} else {
|
||||
mCheckBox.setVisibility(GONE);
|
||||
mCheckBox.setClickable(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateImageView() {
|
||||
if (mData.isDocumentPickerItem()) {
|
||||
setBackgroundColor(ConversationDrawables.get().getConversationThemeColor());
|
||||
mIcon.setImageResource(R.drawable.ic_photo_library_light);
|
||||
mIcon.clearColorFilter();
|
||||
mImageView.setVisibility(GONE);
|
||||
mIcon.setVisibility(VISIBLE);
|
||||
mFileInfo.setVisibility(GONE);
|
||||
mAdditionalInfo.setVisibility(VISIBLE);
|
||||
} else {
|
||||
final String contentType = mData.getContentType();
|
||||
Resources.Theme theme = getContext().getTheme();
|
||||
if (ContentType.isAudioType(contentType)) {
|
||||
setBackgroundColor(
|
||||
getResources().getColor(R.color.gallery_image_default_background, theme));
|
||||
mIcon.setImageResource(R.drawable.ic_music);
|
||||
mIcon.setColorFilter(
|
||||
ConversationDrawables.get().getConversationThemeColor(),
|
||||
PorterDuff.Mode.SRC_IN);
|
||||
mFileName.setText(mData.getFileName());
|
||||
String[] type = contentType.split("/");
|
||||
mFileType.setText(type[1].toUpperCase() + " " + type[0]);
|
||||
mImageView.setVisibility(GONE);
|
||||
mIcon.setVisibility(VISIBLE);
|
||||
mFileInfo.setVisibility(VISIBLE);
|
||||
mAdditionalInfo.setVisibility(VISIBLE);
|
||||
} else { // For image and video types
|
||||
mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
setBackgroundColor(
|
||||
getResources().getColor(R.color.gallery_image_default_background, theme));
|
||||
mImageView.setImageResourceId(mData.getImageRequestDescriptor());
|
||||
mImageView.setVisibility(VISIBLE);
|
||||
if (ContentType.isVideoType(mData.getContentType())) {
|
||||
mIcon.setImageResource(R.drawable.ic_play_light);
|
||||
mIcon.clearColorFilter();
|
||||
mIcon.setVisibility(VISIBLE);
|
||||
} else {
|
||||
mIcon.setVisibility(GONE);
|
||||
}
|
||||
mFileInfo.setVisibility(GONE);
|
||||
mAdditionalInfo.setVisibility(VISIBLE);
|
||||
final long dateSeconds = mData.getDateSeconds();
|
||||
final boolean isValidDate = (dateSeconds > 0);
|
||||
final int templateId = isValidDate ?
|
||||
R.string.mediapicker_gallery_image_item_description :
|
||||
R.string.mediapicker_gallery_image_item_description_no_date;
|
||||
String contentDescription = String.format(getResources().getString(templateId),
|
||||
dateSeconds * TimeUnit.SECONDS.toMillis(1));
|
||||
mImageView.setContentDescription(contentDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* Copyright (C) 2024 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.messaging.ui.mediapicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import androidx.collection.ArrayMap;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.datamodel.binding.BindingBase;
|
||||
import com.android.messaging.datamodel.binding.ImmutableBindingRef;
|
||||
import com.android.messaging.datamodel.data.DraftMessageData;
|
||||
import com.android.messaging.datamodel.data.GalleryGridItemData;
|
||||
import com.android.messaging.datamodel.data.MessagePartData;
|
||||
import com.android.messaging.datamodel.data.DraftMessageData.DraftMessageDataListener;
|
||||
import com.android.messaging.ui.PersistentInstanceState;
|
||||
import com.android.messaging.util.Assert;
|
||||
import com.android.messaging.util.ContentType;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Shows a list of galley mediae from external storage in a GridView with multi-select capabilities,
|
||||
* and with the option to intent out to a standalone media picker.
|
||||
*/
|
||||
public class GalleryGridView extends MediaPickerGridView implements
|
||||
GalleryGridItemView.HostInterface,
|
||||
PersistentInstanceState,
|
||||
DraftMessageDataListener {
|
||||
/**
|
||||
* Implemented by the owner of this GalleryGridView instance to communicate on media picking and
|
||||
* multi-media selection events.
|
||||
*/
|
||||
public interface GalleryGridViewListener {
|
||||
void onDocumentPickerItemClicked();
|
||||
void onItemSelected(MessagePartData item);
|
||||
void onItemUnselected(MessagePartData item);
|
||||
void onConfirmSelection();
|
||||
void onUpdate();
|
||||
}
|
||||
|
||||
private GalleryGridViewListener mListener;
|
||||
|
||||
// TODO: Consider putting this into the data model object if we add more states.
|
||||
private final ArrayMap<Uri, MessagePartData> mSelectedImages;
|
||||
private boolean mIsMultiSelectMode = false;
|
||||
private ImmutableBindingRef<DraftMessageData> mDraftMessageDataModel;
|
||||
|
||||
public GalleryGridView(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mSelectedImages = new ArrayMap<>();
|
||||
}
|
||||
|
||||
public void setHostInterface(final GalleryGridViewListener hostInterface) {
|
||||
mListener = hostInterface;
|
||||
}
|
||||
|
||||
public void setDraftMessageDataModel(final BindingBase<DraftMessageData> dataModel) {
|
||||
mDraftMessageDataModel = BindingBase.createBindingReference(dataModel);
|
||||
mDraftMessageDataModel.getData().addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClicked(final View view, final GalleryGridItemData data,
|
||||
final boolean longClick) {
|
||||
if (data.isDocumentPickerItem()) {
|
||||
mListener.onDocumentPickerItemClicked();
|
||||
} else if (ContentType.isMediaType(data.getContentType())) {
|
||||
if (longClick) {
|
||||
// Turn on multi-select mode when an item is long-pressed.
|
||||
setMultiSelectEnabled(true);
|
||||
}
|
||||
|
||||
final Rect startRect = new Rect();
|
||||
view.getGlobalVisibleRect(startRect);
|
||||
if (isMultiSelectEnabled()) {
|
||||
toggleItemSelection(startRect, data);
|
||||
} else {
|
||||
mListener.onItemSelected(data.constructMessagePartData(startRect));
|
||||
}
|
||||
} else {
|
||||
LogUtil.w(LogUtil.BUGLE_TAG,
|
||||
"Selected item has invalid contentType " + data.getContentType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemSelected(final GalleryGridItemData data) {
|
||||
return mSelectedImages.containsKey(data.getImageUri());
|
||||
}
|
||||
|
||||
int getSelectionCount() {
|
||||
return mSelectedImages.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMultiSelectEnabled() {
|
||||
return mIsMultiSelectMode;
|
||||
}
|
||||
|
||||
private void toggleItemSelection(final Rect startRect, final GalleryGridItemData data) {
|
||||
Assert.isTrue(isMultiSelectEnabled());
|
||||
if (isItemSelected(data)) {
|
||||
final MessagePartData item = mSelectedImages.remove(data.getImageUri());
|
||||
mListener.onItemUnselected(item);
|
||||
if (mSelectedImages.size() == 0) {
|
||||
// No media is selected any more, turn off multi-select mode.
|
||||
setMultiSelectEnabled(false);
|
||||
}
|
||||
} else {
|
||||
final MessagePartData item = data.constructMessagePartData(startRect);
|
||||
mSelectedImages.put(data.getImageUri(), item);
|
||||
mListener.onItemSelected(item);
|
||||
}
|
||||
invalidateViews();
|
||||
}
|
||||
|
||||
private void toggleMultiSelect() {
|
||||
mIsMultiSelectMode = !mIsMultiSelectMode;
|
||||
invalidateViews();
|
||||
}
|
||||
|
||||
private void setMultiSelectEnabled(final boolean enabled) {
|
||||
if (mIsMultiSelectMode != enabled) {
|
||||
toggleMultiSelect();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canToggleMultiSelect() {
|
||||
// We allow the user to toggle multi-select mode only when nothing has selected. If
|
||||
// something has been selected, we show a confirm button instead.
|
||||
return mSelectedImages.size() == 0;
|
||||
}
|
||||
|
||||
public void onCreateOptionsMenu(final MenuInflater inflater, final Menu menu) {
|
||||
inflater.inflate(R.menu.gallery_picker_menu, menu);
|
||||
final MenuItem toggleMultiSelect = menu.findItem(R.id.action_multiselect);
|
||||
final MenuItem confirmMultiSelect = menu.findItem(R.id.action_confirm_multiselect);
|
||||
final boolean canToggleMultiSelect = canToggleMultiSelect();
|
||||
toggleMultiSelect.setVisible(canToggleMultiSelect);
|
||||
confirmMultiSelect.setVisible(!canToggleMultiSelect);
|
||||
}
|
||||
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_multiselect:
|
||||
Assert.isTrue(canToggleMultiSelect());
|
||||
toggleMultiSelect();
|
||||
return true;
|
||||
|
||||
case R.id.action_confirm_multiselect:
|
||||
Assert.isTrue(!canToggleMultiSelect());
|
||||
mListener.onConfirmSelection();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDraftChanged(final DraftMessageData data, final int changeFlags) {
|
||||
mDraftMessageDataModel.ensureBound(data);
|
||||
// Whenever attachment changed, refresh selection state to remove those that are not
|
||||
// selected.
|
||||
if ((changeFlags & DraftMessageData.ATTACHMENTS_CHANGED) ==
|
||||
DraftMessageData.ATTACHMENTS_CHANGED) {
|
||||
refreshImageSelectionStateOnAttachmentChange();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraftAttachmentLimitReached(final DraftMessageData data) {
|
||||
mDraftMessageDataModel.ensureBound(data);
|
||||
// Whenever draft attachment limit is reach, refresh selection state to remove those
|
||||
// not actually added to draft.
|
||||
refreshImageSelectionStateOnAttachmentChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraftAttachmentLoadFailed() {
|
||||
// Nothing to do since the failed attachment gets removed automatically.
|
||||
}
|
||||
|
||||
private void refreshImageSelectionStateOnAttachmentChange() {
|
||||
boolean changed = false;
|
||||
final Iterator<Map.Entry<Uri, MessagePartData>> iterator =
|
||||
mSelectedImages.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<Uri, MessagePartData> entry = iterator.next();
|
||||
if (!mDraftMessageDataModel.getData().containsAttachment(entry.getKey())) {
|
||||
iterator.remove();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
mListener.onUpdate();
|
||||
invalidateViews();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // PersistentInstanceState
|
||||
public Parcelable saveState() {
|
||||
return onSaveInstanceState();
|
||||
}
|
||||
|
||||
@Override // PersistentInstanceState
|
||||
public void restoreState(final Parcelable restoredState) {
|
||||
onRestoreInstanceState(restoredState);
|
||||
invalidateViews();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
final Parcelable superState = super.onSaveInstanceState();
|
||||
final SavedState savedState = new SavedState(superState);
|
||||
savedState.isMultiSelectMode = mIsMultiSelectMode;
|
||||
savedState.selectedImages = mSelectedImages.values()
|
||||
.toArray(new MessagePartData[mSelectedImages.size()]);
|
||||
return savedState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(final Parcelable state) {
|
||||
if (!(state instanceof SavedState)) {
|
||||
super.onRestoreInstanceState(state);
|
||||
return;
|
||||
}
|
||||
|
||||
final SavedState savedState = (SavedState) state;
|
||||
super.onRestoreInstanceState(savedState.getSuperState());
|
||||
mIsMultiSelectMode = savedState.isMultiSelectMode;
|
||||
mSelectedImages.clear();
|
||||
for (int i = 0; i < savedState.selectedImages.length; i++) {
|
||||
final MessagePartData selectedImage = savedState.selectedImages[i];
|
||||
mSelectedImages.put(selectedImage.getContentUri(), selectedImage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // PersistentInstanceState
|
||||
public void resetState() {
|
||||
mSelectedImages.clear();
|
||||
mIsMultiSelectMode = false;
|
||||
invalidateViews();
|
||||
}
|
||||
|
||||
public static class SavedState extends BaseSavedState {
|
||||
boolean isMultiSelectMode;
|
||||
MessagePartData[] selectedImages;
|
||||
|
||||
SavedState(final Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
private SavedState(final Parcel in) {
|
||||
super(in);
|
||||
isMultiSelectMode = in.readInt() == 1 ? true : false;
|
||||
|
||||
// Read parts
|
||||
final int partCount = in.readInt();
|
||||
selectedImages = new MessagePartData[partCount];
|
||||
for (int i = 0; i < partCount; i++) {
|
||||
selectedImages[i] = ((MessagePartData) in.readParcelable(
|
||||
MessagePartData.class.getClassLoader()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel out, final int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeInt(isMultiSelectMode ? 1 : 0);
|
||||
|
||||
// Write parts
|
||||
out.writeInt(selectedImages.length);
|
||||
for (final MessagePartData image : selectedImages) {
|
||||
out.writeParcelable(image, flags);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR =
|
||||
new Parcelable.Creator<>() {
|
||||
@Override
|
||||
public SavedState createFromParcel(final Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedState[] newArray(final int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -17,46 +17,21 @@
|
||||
|
||||
package com.android.messaging.ui.mediapicker;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MergeCursor;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.messaging.Factory;
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.datamodel.data.GalleryGridItemData;
|
||||
import com.android.messaging.datamodel.data.MediaPickerData;
|
||||
import com.android.messaging.datamodel.data.MessagePartData;
|
||||
import com.android.messaging.datamodel.data.MediaPickerData.MediaPickerDataListener;
|
||||
import com.android.messaging.ui.UIIntents;
|
||||
import com.android.messaging.util.Assert;
|
||||
import com.android.messaging.util.OsUtil;
|
||||
|
||||
/**
|
||||
* Chooser which allows the user to select one or more existing images or videos or audios.
|
||||
*/
|
||||
class GalleryMediaChooser extends MediaChooser implements
|
||||
GalleryGridView.GalleryGridViewListener, MediaPickerDataListener {
|
||||
private final GalleryGridAdapter mAdapter;
|
||||
private GalleryGridView mGalleryGridView;
|
||||
private View mMissingPermissionView;
|
||||
|
||||
class GalleryMediaChooser extends MediaChooser {
|
||||
/** Handles picking a media from the document picker. */
|
||||
private final DocumentImagePicker mDocumentImagePicker;
|
||||
|
||||
GalleryMediaChooser(final MediaPicker mediaPicker) {
|
||||
super(mediaPicker);
|
||||
mAdapter = new GalleryGridAdapter(Factory.get().getApplicationContext(), null);
|
||||
mDocumentImagePicker = new DocumentImagePicker(mMediaPicker, data -> {
|
||||
if (mBindingRef.isBound()) {
|
||||
mMediaPicker.dispatchPendingItemAdded(data);
|
||||
@@ -71,17 +46,6 @@ class GalleryMediaChooser extends MediaChooser implements
|
||||
| MediaPicker.MEDIA_TYPE_AUDIO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View destroyView() {
|
||||
mGalleryGridView.setAdapter(null);
|
||||
mAdapter.setHostInterface(null);
|
||||
// The loader is started only if startMediaPickerDataLoader() is called
|
||||
if (OsUtil.hasStoragePermission()) {
|
||||
mBindingRef.getData().destroyLoader(MediaPickerData.GALLERY_MEDIA_LOADER);
|
||||
}
|
||||
return super.destroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconResource() {
|
||||
return R.drawable.ic_image_light;
|
||||
@@ -92,45 +56,6 @@ class GalleryMediaChooser extends MediaChooser implements
|
||||
return R.string.mediapicker_galleryChooserDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSwipeDown() {
|
||||
return mGalleryGridView.canSwipeDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemSelected(final MessagePartData item) {
|
||||
mMediaPicker.dispatchItemsSelected(item, !mGalleryGridView.isMultiSelectEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemUnselected(final MessagePartData item) {
|
||||
mMediaPicker.dispatchItemUnselected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfirmSelection() {
|
||||
// The user may only confirm if multiselect is enabled.
|
||||
Assert.isTrue(mGalleryGridView.isMultiSelectEnabled());
|
||||
mMediaPicker.dispatchConfirmItemSelection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
mMediaPicker.invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(final MenuInflater inflater, final Menu menu) {
|
||||
if (mView != null) {
|
||||
mGalleryGridView.onCreateOptionsMenu(inflater, menu);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
return (mView != null) ? mGalleryGridView.onOptionsItemSelected(item) : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View createView(final ViewGroup container) {
|
||||
final LayoutInflater inflater = getLayoutInflater();
|
||||
@@ -139,17 +64,11 @@ class GalleryMediaChooser extends MediaChooser implements
|
||||
container /* root */,
|
||||
false /* attachToRoot */);
|
||||
|
||||
mGalleryGridView = (GalleryGridView) view.findViewById(R.id.gallery_grid_view);
|
||||
mAdapter.setHostInterface(mGalleryGridView);
|
||||
mGalleryGridView.setAdapter(mAdapter);
|
||||
mGalleryGridView.setHostInterface(this);
|
||||
mGalleryGridView.setDraftMessageDataModel(mMediaPicker.getDraftMessageDataModel());
|
||||
if (OsUtil.hasStoragePermission()) {
|
||||
startMediaPickerDataLoader();
|
||||
}
|
||||
|
||||
mMissingPermissionView = view.findViewById(R.id.missing_permission_view);
|
||||
updateForPermissionState(OsUtil.hasStoragePermission());
|
||||
final View enabledView = view.findViewById(R.id.mediapicker_enabled);
|
||||
enabledView.setOnClickListener(v -> {
|
||||
// Launch an external picker to pick item from document picker as attachment.
|
||||
mDocumentImagePicker.launchPicker();
|
||||
});
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -157,99 +76,4 @@ class GalleryMediaChooser extends MediaChooser implements
|
||||
int getActionBarTitleResId() {
|
||||
return R.string.mediapicker_gallery_title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDocumentPickerItemClicked() {
|
||||
// Launch an external picker to pick item from document picker as attachment.
|
||||
mDocumentImagePicker.launchPicker();
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateActionBar(final ActionBar actionBar) {
|
||||
super.updateActionBar(actionBar);
|
||||
if (mGalleryGridView == null) {
|
||||
return;
|
||||
}
|
||||
final int selectionCount = mGalleryGridView.getSelectionCount();
|
||||
if (selectionCount > 0 && mGalleryGridView.isMultiSelectEnabled()) {
|
||||
actionBar.setTitle(getContext().getResources().getString(
|
||||
R.string.mediapicker_gallery_title_selection,
|
||||
selectionCount));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaPickerDataUpdated(final MediaPickerData mediaPickerData, final Object data,
|
||||
final int loaderId) {
|
||||
mBindingRef.ensureBound(mediaPickerData);
|
||||
Assert.equals(MediaPickerData.GALLERY_MEDIA_LOADER, loaderId);
|
||||
Cursor rawCursor = null;
|
||||
if (data instanceof Cursor) {
|
||||
rawCursor = (Cursor) data;
|
||||
}
|
||||
// Before delivering the cursor, wrap around the local gallery cursor
|
||||
// with an extra item for document picker integration in the front.
|
||||
final MatrixCursor specialItemsCursor =
|
||||
new MatrixCursor(GalleryGridItemData.SPECIAL_ITEM_COLUMNS);
|
||||
specialItemsCursor.addRow(new Object[] { GalleryGridItemData.ID_DOCUMENT_PICKER_ITEM });
|
||||
final MergeCursor cursor =
|
||||
new MergeCursor(new Cursor[] { specialItemsCursor, rawCursor });
|
||||
mAdapter.swapCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
if (OsUtil.hasStoragePermission()) {
|
||||
// Work around a bug in MediaStore where cursors querying the Files provider don't get
|
||||
// updated for changes to Images.Media or Video.Media.
|
||||
startMediaPickerDataLoader();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setSelected(final boolean selected) {
|
||||
super.setSelected(selected);
|
||||
if (selected && !OsUtil.hasStoragePermission()) {
|
||||
mMediaPicker.requestPermissions(
|
||||
new String[] { Manifest.permission.READ_EXTERNAL_STORAGE },
|
||||
MediaPicker.GALLERY_PERMISSION_REQUEST_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
private void startMediaPickerDataLoader() {
|
||||
mBindingRef
|
||||
.getData()
|
||||
.startLoader(MediaPickerData.GALLERY_MEDIA_LOADER, mBindingRef, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRequestPermissionsResult(
|
||||
final int requestCode, final String[] permissions, final int[] grantResults) {
|
||||
if (requestCode == MediaPicker.GALLERY_PERMISSION_REQUEST_CODE) {
|
||||
final boolean permissionGranted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||
if (permissionGranted) {
|
||||
startMediaPickerDataLoader();
|
||||
}
|
||||
updateForPermissionState(permissionGranted);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateForPermissionState(final boolean granted) {
|
||||
// onRequestPermissionsResult can sometimes get called before createView().
|
||||
if (mGalleryGridView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mGalleryGridView.setVisibility(granted ? View.VISIBLE : View.GONE);
|
||||
mMissingPermissionView.setVisibility(granted ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(
|
||||
final int requestCode, final int resultCode, final Intent data) {
|
||||
if (requestCode == UIIntents.REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER
|
||||
&& resultCode == Activity.RESULT_OK) {
|
||||
mDocumentImagePicker.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package com.android.messaging.ui.mediapicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@@ -197,9 +196,6 @@ abstract class MediaChooser extends BasePagerViewHolder
|
||||
public void stopTouchHandling() {
|
||||
}
|
||||
|
||||
protected void onActivityResult(
|
||||
final int requestCode, final int resultCode, final Intent data) {}
|
||||
|
||||
@Override
|
||||
public int getConversationSelfSubId() {
|
||||
return mMediaPicker.getConversationSelfSubId();
|
||||
|
||||
@@ -19,7 +19,6 @@ package com.android.messaging.ui.mediapicker;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -34,7 +33,6 @@ import android.widget.LinearLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
@@ -45,10 +43,10 @@ import com.android.messaging.datamodel.binding.Binding;
|
||||
import com.android.messaging.datamodel.binding.BindingBase;
|
||||
import com.android.messaging.datamodel.binding.ImmutableBindingRef;
|
||||
import com.android.messaging.datamodel.data.DraftMessageData;
|
||||
import com.android.messaging.datamodel.data.DraftMessageData.DraftMessageSubscriptionDataProvider;
|
||||
import com.android.messaging.datamodel.data.MediaPickerData;
|
||||
import com.android.messaging.datamodel.data.MessagePartData;
|
||||
import com.android.messaging.datamodel.data.PendingAttachmentData;
|
||||
import com.android.messaging.datamodel.data.DraftMessageData.DraftMessageSubscriptionDataProvider;
|
||||
import com.android.messaging.ui.BugleActionBarActivity;
|
||||
import com.android.messaging.ui.FixedViewPagerAdapter;
|
||||
import com.android.messaging.util.AccessibilityUtil;
|
||||
@@ -202,7 +200,6 @@ public class MediaPicker extends Fragment implements DraftMessageSubscriptionDat
|
||||
@Override
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mBinding.getData().init(LoaderManager.getInstance(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -283,12 +280,6 @@ public class MediaPicker extends Fragment implements DraftMessageSubscriptionDat
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
mSelectedChooser.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
@@ -442,7 +433,6 @@ public class MediaPicker extends Fragment implements DraftMessageSubscriptionDat
|
||||
if (mBinding.isBound() && getActivity() != null) {
|
||||
mBinding.unbind();
|
||||
mBinding.bind(DataModel.get().createMediaPickerData(getActivity()));
|
||||
mBinding.getData().init(LoaderManager.getInstance(this));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user