diff options
Diffstat (limited to 'src/com/android/messaging/ui/mediapicker/AudioListView.java')
-rw-r--r-- | src/com/android/messaging/ui/mediapicker/AudioListView.java | 311 |
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]; + } + }; + } +} |