Java tutorial
/* * 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.support.v4.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]; } }; } }