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