1/*
2 * Copyright (C) 2012 Google Inc.
3 * Licensed to The Android Open Source Project.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.mail.browse;
19
20import android.app.FragmentManager;
21import android.app.LoaderManager;
22import android.content.Context;
23import android.content.Loader;
24import android.database.Cursor;
25import android.net.Uri;
26import android.os.Bundle;
27import android.util.AttributeSet;
28import android.view.LayoutInflater;
29import android.view.View;
30import android.widget.LinearLayout;
31import android.widget.TextView;
32
33import com.android.mail.R;
34import com.android.mail.browse.AttachmentLoader.AttachmentCursor;
35import com.android.mail.browse.ConversationContainer.DetachListener;
36import com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem;
37import com.android.mail.providers.Attachment;
38import com.android.mail.providers.Message;
39import com.android.mail.ui.AttachmentTile;
40import com.android.mail.ui.AttachmentTileGrid;
41import com.android.mail.utils.LogTag;
42import com.android.mail.utils.LogUtils;
43
44import com.google.common.base.Objects;
45import com.google.common.collect.Lists;
46
47import java.util.ArrayList;
48import java.util.List;
49
50public class MessageFooterView extends LinearLayout implements DetachListener,
51        LoaderManager.LoaderCallbacks<Cursor> {
52
53    private MessageHeaderItem mMessageHeaderItem;
54    private LoaderManager mLoaderManager;
55    private FragmentManager mFragmentManager;
56    private AttachmentCursor mAttachmentsCursor;
57    private TextView mTitleText;
58    private AttachmentTileGrid mAttachmentGrid;
59    private LinearLayout mAttachmentBarList;
60
61    private final LayoutInflater mInflater;
62
63    private static final String LOG_TAG = LogTag.getLogTag();
64
65    private Uri mAccountUri;
66
67    public MessageFooterView(Context context) {
68        this(context, null);
69    }
70
71    public MessageFooterView(Context context, AttributeSet attrs) {
72        super(context, attrs);
73
74        mInflater = LayoutInflater.from(context);
75    }
76
77    @Override
78    protected void onFinishInflate() {
79        super.onFinishInflate();
80
81        mTitleText = (TextView) findViewById(R.id.attachments_header_text);
82        mAttachmentGrid = (AttachmentTileGrid) findViewById(R.id.attachment_tile_grid);
83        mAttachmentBarList = (LinearLayout) findViewById(R.id.attachment_bar_list);
84    }
85
86    public void initialize(LoaderManager loaderManager, FragmentManager fragmentManager) {
87        mLoaderManager = loaderManager;
88        mFragmentManager = fragmentManager;
89    }
90
91    public void bind(MessageHeaderItem headerItem, Uri accountUri, boolean measureOnly) {
92        mAccountUri = accountUri;
93
94        // Resets the footer view. This step is only done if the
95        // attachmentsListUri changes so that we don't
96        // repeat the work of layout and measure when
97        // we're only updating the attachments.
98        if (mMessageHeaderItem != null &&
99                mMessageHeaderItem.getMessage() != null &&
100                mMessageHeaderItem.getMessage().attachmentListUri != null &&
101                !mMessageHeaderItem.getMessage().attachmentListUri.equals(
102                headerItem.getMessage().attachmentListUri)) {
103            mAttachmentGrid.removeAllViewsInLayout();
104            mAttachmentBarList.removeAllViewsInLayout();
105            mTitleText.setVisibility(View.GONE);
106            mAttachmentGrid.setVisibility(View.GONE);
107            mAttachmentBarList.setVisibility(View.GONE);
108        }
109
110        // If this MessageFooterView is being bound to a new attachment, we need to unbind with the
111        // old loader
112        final Integer oldAttachmentLoaderId = getAttachmentLoaderId();
113
114        mMessageHeaderItem = headerItem;
115
116        final Integer attachmentLoaderId = getAttachmentLoaderId();
117        // Destroy the loader if we are attempting to load a different attachment
118        if (oldAttachmentLoaderId != null &&
119                !Objects.equal(oldAttachmentLoaderId, attachmentLoaderId)) {
120            mLoaderManager.destroyLoader(oldAttachmentLoaderId);
121        }
122
123        // kick off load of Attachment objects in background thread
124        // but don't do any Loader work if we're only measuring
125        if (!measureOnly && attachmentLoaderId != null) {
126            LogUtils.i(LOG_TAG, "binding footer view, calling initLoader for message %d",
127                    attachmentLoaderId);
128            mLoaderManager.initLoader(attachmentLoaderId, Bundle.EMPTY, this);
129        }
130
131        // Do an initial render if initLoader didn't already do one
132        if (mAttachmentGrid.getChildCount() == 0 &&
133                mAttachmentBarList.getChildCount() == 0) {
134            renderAttachments(false);
135        }
136        setVisibility(mMessageHeaderItem.isExpanded() ? VISIBLE : GONE);
137    }
138
139    private void renderAttachments(boolean loaderResult) {
140        final List<Attachment> attachments;
141        if (mAttachmentsCursor != null && !mAttachmentsCursor.isClosed()) {
142            int i = -1;
143            attachments = Lists.newArrayList();
144            while (mAttachmentsCursor.moveToPosition(++i)) {
145                attachments.add(mAttachmentsCursor.get());
146            }
147        } else {
148            // before the attachment loader results are in, we can still render immediately using
149            // the basic info in the message's attachmentsJSON
150            attachments = mMessageHeaderItem.getMessage().getAttachments();
151        }
152        renderAttachments(attachments, loaderResult);
153    }
154
155    private void renderAttachments(List<Attachment> attachments, boolean loaderResult) {
156        if (attachments == null || attachments.isEmpty()) {
157            return;
158        }
159
160        // filter the attachments into tiled and non-tiled
161        final int maxSize = attachments.size();
162        final List<Attachment> tiledAttachments = new ArrayList<Attachment>(maxSize);
163        final List<Attachment> barAttachments = new ArrayList<Attachment>(maxSize);
164
165        for (Attachment attachment : attachments) {
166            if (AttachmentTile.isTiledAttachment(attachment)) {
167                tiledAttachments.add(attachment);
168            } else {
169                barAttachments.add(attachment);
170            }
171        }
172        mMessageHeaderItem.getMessage().attachmentsJson = Attachment.toJSONArray(attachments);
173
174        mTitleText.setVisibility(View.VISIBLE);
175
176        renderTiledAttachments(tiledAttachments, loaderResult);
177        renderBarAttachments(barAttachments, loaderResult);
178    }
179
180    private void renderTiledAttachments(List<Attachment> tiledAttachments, boolean loaderResult) {
181        mAttachmentGrid.setVisibility(View.VISIBLE);
182
183        // Setup the tiles.
184        mAttachmentGrid.configureGrid(mFragmentManager,
185                mMessageHeaderItem.getMessage().attachmentListUri, tiledAttachments, loaderResult);
186    }
187
188    private void renderBarAttachments(List<Attachment> barAttachments, boolean loaderResult) {
189        mAttachmentBarList.setVisibility(View.VISIBLE);
190
191        for (Attachment attachment : barAttachments) {
192            final Uri id = attachment.getIdentifierUri();
193            MessageAttachmentBar barAttachmentView =
194                    (MessageAttachmentBar) mAttachmentBarList.findViewWithTag(id);
195
196            if (barAttachmentView == null) {
197                barAttachmentView = MessageAttachmentBar.inflate(mInflater, this);
198                barAttachmentView.setTag(id);
199                barAttachmentView.initialize(mFragmentManager);
200                mAttachmentBarList.addView(barAttachmentView);
201            }
202
203            barAttachmentView.render(attachment, mAccountUri, loaderResult);
204        }
205    }
206
207    private Integer getAttachmentLoaderId() {
208        Integer id = null;
209        final Message msg = mMessageHeaderItem == null ? null : mMessageHeaderItem.getMessage();
210        if (msg != null && msg.hasAttachments && msg.attachmentListUri != null) {
211            id = msg.attachmentListUri.hashCode();
212        }
213        return id;
214    }
215
216    @Override
217    public void onDetachedFromParent() {
218        // Do nothing
219    }
220
221    @Override
222    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
223        return new AttachmentLoader(getContext(),
224                mMessageHeaderItem.getMessage().attachmentListUri);
225    }
226
227    @Override
228    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
229        mAttachmentsCursor = (AttachmentCursor) data;
230
231        if (mAttachmentsCursor == null || mAttachmentsCursor.isClosed()) {
232            return;
233        }
234
235        renderAttachments(true);
236    }
237
238    @Override
239    public void onLoaderReset(Loader<Cursor> loader) {
240        mAttachmentsCursor = null;
241    }
242}
243