summaryrefslogtreecommitdiffstats
path: root/src/com/android/messaging/ui/mediapicker/AudioListView.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/messaging/ui/mediapicker/AudioListView.java')
-rw-r--r--src/com/android/messaging/ui/mediapicker/AudioListView.java311
1 files changed, 311 insertions, 0 deletions
diff --git a/src/com/android/messaging/ui/mediapicker/AudioListView.java b/src/com/android/messaging/ui/mediapicker/AudioListView.java
new file mode 100644
index 0000000..7fc7d2c
--- /dev/null
+++ b/src/com/android/messaging/ui/mediapicker/AudioListView.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2016 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.graphics.Rect;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.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.AudioListItemData;
+import com.android.messaging.datamodel.data.DraftMessageData;
+import com.android.messaging.datamodel.data.DraftMessageData.DraftMessageDataListener;
+import com.android.messaging.datamodel.data.MessagePartData;
+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 audio filenames from external storage in a ListView with multi-select
+ * capabilities
+ */
+public class AudioListView extends MediaPickerListView implements
+ AudioListItemView.HostInterface,
+ PersistentInstanceState,
+ DraftMessageDataListener {
+ /**
+ * Implemented by the owner of this GalleryGridView instance to communicate on image
+ * picking and multi-image selection events.
+ */
+ public interface AudioListViewListener {
+ void onItemSelected(MessagePartData item);
+ void onItemUnselected(MessagePartData item);
+ void onConfirmSelection();
+ void onUpdate();
+ }
+
+ private AudioListViewListener mListener;
+
+ // TODO: Consider putting this into the data model object if we add more states.
+ private final ArrayMap<Uri, MessagePartData> mSelectedAudios;
+ private boolean mIsMultiSelectMode = false;
+ private ImmutableBindingRef<DraftMessageData> mDraftMessageDataModel;
+
+ public AudioListView(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ mSelectedAudios = new ArrayMap<Uri, MessagePartData>();
+ }
+
+ public void setHostInterface(final AudioListViewListener 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 AudioListItemData data,
+ final boolean longClick) {
+ 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 AudioListItemData data) {
+ return mSelectedAudios.containsKey(data.getAudioUri());
+ }
+
+ int getSelectionCount() {
+ return mSelectedAudios.size();
+ }
+
+ @Override
+ public boolean isMultiSelectEnabled() {
+ return mIsMultiSelectMode;
+ }
+
+ private void toggleItemSelection(final Rect startRect, final AudioListItemData data) {
+ Assert.isTrue(isMultiSelectEnabled());
+ if (isItemSelected(data)) {
+ final MessagePartData item = mSelectedAudios.remove(data.getAudioUri());
+ mListener.onItemUnselected(item);
+ if (mSelectedAudios.size() == 0) {
+ // No image is selected any more, turn off multi-select mode.
+ setMultiSelectEnabled(false);
+ }
+ } else {
+ final MessagePartData item = data.constructMessagePartData(startRect);
+ mSelectedAudios.put(data.getAudioUri(), 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 mSelectedAudios.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 =
+ mSelectedAudios.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.selectedAudios = mSelectedAudios.values()
+ .toArray(new MessagePartData[mSelectedAudios.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;
+ mSelectedAudios.clear();
+ for (int i = 0; i < savedState.selectedAudios.length; i++) {
+ final MessagePartData selectedAudio = savedState.selectedAudios[i];
+ mSelectedAudios.put(selectedAudio.getContentUri(), selectedAudio);
+ }
+ }
+
+ @Override // PersistentInstanceState
+ public void resetState() {
+ mSelectedAudios.clear();
+ mIsMultiSelectMode = false;
+ invalidateViews();
+ }
+
+ public static class SavedState extends BaseSavedState {
+ boolean isMultiSelectMode;
+ MessagePartData[] selectedAudios;
+
+ 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();
+ selectedAudios = new MessagePartData[partCount];
+ for (int i = 0; i < partCount; i++) {
+ selectedAudios[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(selectedAudios.length);
+ for (final MessagePartData image : selectedAudios) {
+ out.writeParcelable(image, flags);
+ }
+ }
+
+ public static final Creator<SavedState> CREATOR =
+ new Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(final Parcel in) {
+ return new SavedState(in);
+ }
+ @Override
+ public SavedState[] newArray(final int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+}