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