1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/* 2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project 3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License"); 5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License. 6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at 7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * http://www.apache.org/licenses/LICENSE-2.0 9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software 11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS, 12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and 14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License. 15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.ui.attachmentchooser; 17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context; 19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.Rect; 20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.Uri; 21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Parcel; 22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Parcelable; 23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.util.AttributeSet; 24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.widget.GridView; 25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.MessagePartData; 27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.attachmentchooser.AttachmentChooserFragment.AttachmentGridAdapter; 28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert; 29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.UiUtils; 30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Collections; 32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.HashSet; 33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Set; 34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/** 36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Displays a grid of attachment previews for the user to choose which to select/unselect 37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class AttachmentGridView extends GridView implements 39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd AttachmentGridItemView.HostInterface { 40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public interface AttachmentGridHost { 41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void displayPhoto(final Rect viewRect, final Uri photoUri); 42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void updateSelectionCount(final int count); 43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // By default everything is selected so only need to keep track of the unselected set. 46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final Set<MessagePartData> mUnselectedSet; 47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private AttachmentGridHost mHost; 48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public AttachmentGridView(final Context context, final AttributeSet attrs) { 50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super(context, attrs); 51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mUnselectedSet = new HashSet<>(); 52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void setHost(final AttachmentGridHost host) { 55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost = host; 56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean isItemSelected(final MessagePartData attachment) { 60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return !mUnselectedSet.contains(attachment); 61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onItemClicked(final AttachmentGridItemView view, final MessagePartData attachment) { 65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If the item is an image, show the photo viewer. All the other types (video, audio, 66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // vcard) have internal click handling for showing previews so we don't need to handle them 67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (attachment.isImage()) { 68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.displayPhoto(UiUtils.getMeasuredBoundsOnScreen(view), attachment.getContentUri()); 69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onItemCheckedChanged(AttachmentGridItemView view, MessagePartData attachment) { 74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Toggle selection. 75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (isItemSelected(attachment)) { 76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mUnselectedSet.add(attachment); 77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mUnselectedSet.remove(attachment); 79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd view.updateSelectedState(); 81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateSelectionCount(); 82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public Set<MessagePartData> getUnselectedAttachments() { 85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return Collections.unmodifiableSet(mUnselectedSet); 86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void updateSelectionCount() { 89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int count = getAdapter().getCount() - mUnselectedSet.size(); 90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(count >= 0); 91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.updateSelectionCount(count); 92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void refreshViews() { 95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (getAdapter() instanceof AttachmentGridAdapter) { 96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ((AttachmentGridAdapter) getAdapter()).notifyDataSetChanged(); 97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public Parcelable onSaveInstanceState() { 102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Parcelable superState = super.onSaveInstanceState(); 103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SavedState savedState = new SavedState(superState); 104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd savedState.unselectedParts = mUnselectedSet 105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .toArray(new MessagePartData[mUnselectedSet.size()]); 106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return savedState; 107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onRestoreInstanceState(final Parcelable state) { 111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!(state instanceof SavedState)) { 112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onRestoreInstanceState(state); 113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SavedState savedState = (SavedState) state; 117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onRestoreInstanceState(savedState.getSuperState()); 118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mUnselectedSet.clear(); 119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < savedState.unselectedParts.length; i++) { 120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData unselectedPart = savedState.unselectedParts[i]; 121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mUnselectedSet.add(unselectedPart); 122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd refreshViews(); 124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Persists the item selection state to saved instance state so we can restore on activity 128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * recreation 129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static class SavedState extends BaseSavedState { 131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagePartData[] unselectedParts; 132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd SavedState(final Parcelable superState) { 134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super(superState); 135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private SavedState(final Parcel in) { 138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super(in); 139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Read parts 141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int partCount = in.readInt(); 142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd unselectedParts = new MessagePartData[partCount]; 143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < partCount; i++) { 144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd unselectedParts[i] = ((MessagePartData) in.readParcelable( 145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagePartData.class.getClassLoader())); 146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void writeToParcel(final Parcel out, final int flags) { 151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.writeToParcel(out, flags); 152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Write parts 154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd out.writeInt(unselectedParts.length); 155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final MessagePartData image : unselectedParts) { 156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd out.writeParcelable(image, flags); 157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final Parcelable.Creator<SavedState> CREATOR = 161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new Parcelable.Creator<SavedState>() { 162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SavedState createFromParcel(final Parcel in) { 164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new SavedState(in); 165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SavedState[] newArray(final int size) { 168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new SavedState[size]; 169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd} 173