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 
18 package com.android.mail.browse;
19 
20 import android.app.Activity;
21 import android.app.AlertDialog;
22 import android.app.Dialog;
23 import android.app.DialogFragment;
24 import android.content.ClipData;
25 import android.content.ClipboardManager;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.os.Bundle;
29 import android.util.AttributeSet;
30 import android.view.View;
31 import android.view.View.OnClickListener;
32 import android.view.ViewGroup;
33 import android.widget.LinearLayout;
34 
35 import com.android.mail.R;
36 import com.android.mail.browse.ConversationViewAdapter.ConversationHeaderItem;
37 import com.android.mail.providers.Conversation;
38 import com.android.mail.providers.UIProvider;
39 import com.android.mail.ui.ConversationUpdater;
40 import com.android.mail.utils.LogTag;
41 import com.android.mail.utils.LogUtils;
42 import com.android.mail.utils.Utils;
43 
44 /**
45  * A view for the subject and folders in the conversation view. This container
46  * makes an attempt to combine subject and folders on the same horizontal line if
47  * there is enough room to fit both without wrapping. If they overlap, it
48  * adjusts the layout to position the folders below the subject.
49  */
50 public class ConversationViewHeader extends LinearLayout implements OnClickListener,
51         View.OnLongClickListener {
52 
53     public interface ConversationViewHeaderCallbacks {
54         /**
55          * Called in response to a click on the folders region.
56          */
onFoldersClicked()57         void onFoldersClicked();
58 
59         /**
60          * Called when the height of the {@link ConversationViewHeader} changes.
61          *
62          * @param newHeight the new height in px
63          */
onConversationViewHeaderHeightChange(int newHeight)64         void onConversationViewHeaderHeightChange(int newHeight);
65 
getActivity()66         Activity getActivity();
67     }
68 
69     private static final String LOG_TAG = LogTag.getLogTag();
70     private SubjectAndFolderView mSubjectAndFolderView;
71     private StarView mStarView;
72     private ConversationViewHeaderCallbacks mCallbacks;
73     private ConversationAccountController mAccountController;
74     private ConversationUpdater mConversationUpdater;
75     private ConversationHeaderItem mHeaderItem;
76     private Conversation mConversation;
77 
78     /**
79      * Instantiated from this layout: conversation_view_header.xml
80      * @param context
81      */
ConversationViewHeader(Context context)82     public ConversationViewHeader(Context context) {
83         this(context, null);
84     }
85 
ConversationViewHeader(Context context, AttributeSet attrs)86     public ConversationViewHeader(Context context, AttributeSet attrs) {
87         super(context, attrs);
88     }
89 
90     @Override
onFinishInflate()91     protected void onFinishInflate() {
92         super.onFinishInflate();
93 
94         mSubjectAndFolderView =
95                 (SubjectAndFolderView) findViewById(R.id.subject_and_folder_view);
96         mSubjectAndFolderView.setOnLongClickListener(this);
97         mStarView = (StarView) findViewById(R.id.conversation_header_star);
98         mStarView.setOnClickListener(this);
99     }
100 
setCallbacks(ConversationViewHeaderCallbacks callbacks, ConversationAccountController accountController, ConversationUpdater conversationUpdater)101     public void setCallbacks(ConversationViewHeaderCallbacks callbacks,
102             ConversationAccountController accountController,
103             ConversationUpdater conversationUpdater) {
104         mCallbacks = callbacks;
105         mAccountController = accountController;
106         mConversationUpdater = conversationUpdater;
107     }
108 
setSubject(final String subject)109     public void setSubject(final String subject) {
110         mSubjectAndFolderView.setSubject(subject);
111     }
112 
setFolders(Conversation conv)113     public void setFolders(Conversation conv) {
114         mSubjectAndFolderView.setFolders(mCallbacks, mAccountController.getAccount(), conv);
115     }
116 
setStarred(boolean isStarred)117     public void setStarred(boolean isStarred) {
118         mStarView.setStarred(isStarred);
119         boolean showStar = mConversation != null && !mConversation.isInTrash();
120         mStarView.setStarred(isStarred);
121         mStarView.setVisibility(showStar ? View.VISIBLE : View.INVISIBLE);
122     }
123 
bind(ConversationHeaderItem headerItem)124     public void bind(ConversationHeaderItem headerItem) {
125         mHeaderItem = headerItem;
126         mConversation = mHeaderItem.mConversation;
127         if (mSubjectAndFolderView != null) {
128             mSubjectAndFolderView.bind(headerItem);
129         }
130         setStarred(mConversation.starred);
131     }
132 
measureHeight()133     private int measureHeight() {
134         ViewGroup parent = (ViewGroup) getParent();
135         if (parent == null) {
136             LogUtils.e(LOG_TAG, "Unable to measure height of conversation header");
137             return getHeight();
138         }
139         final int h = Utils.measureViewHeight(this, parent);
140         return h;
141     }
142 
143     /**
144      * Update the conversation view header to reflect the updated conversation.
145      */
onConversationUpdated(Conversation conv)146     public void onConversationUpdated(Conversation conv) {
147         // The only things we have to worry about when the conversation changes
148         // in the conversation header are the folders, priority indicators, and starred state.
149         // Updating these will resize the space for the header.
150         mConversation = conv;
151         setSubject(conv.subject);
152         setFolders(conv);
153         setStarred(conv.starred);
154         if (mHeaderItem != null) {
155             final int h = measureHeight();
156             if (mHeaderItem.setHeight(h)) {
157                 mCallbacks.onConversationViewHeaderHeightChange(h);
158             }
159         }
160     }
161 
162     @Override
onClick(View v)163     public void onClick(View v) {
164         final int id = v.getId();
165         if (mConversation != null && id == R.id.conversation_header_star) {
166             mConversation.starred = !mConversation.starred;
167             setStarred(mConversation.starred);
168             mConversationUpdater.updateConversation(Conversation.listOf(mConversation),
169                     UIProvider.ConversationColumns.STARRED, mConversation.starred);
170         }
171     }
172 
173     @Override
onLongClick(View v)174     public boolean onLongClick(View v) {
175         final DialogFragment frag =
176                 CopySubjectDialog.newInstance(mSubjectAndFolderView.getSubject());
177         frag.show(mCallbacks.getActivity().getFragmentManager(), CopySubjectDialog.TAG);
178         return true;
179     }
180 
181     public static class CopySubjectDialog extends DialogFragment
182             implements DialogInterface.OnClickListener {
183 
184         public static final String TAG = "copy-subject-dialog";
185 
186         private static final String ARG_SUBJECT = "subject";
187 
188         private String mSubject;
189 
newInstance(String subject)190         public static CopySubjectDialog newInstance(String subject) {
191             final CopySubjectDialog frag = new CopySubjectDialog();
192             final Bundle args = new Bundle(1);
193             args.putString(ARG_SUBJECT, subject);
194             frag.setArguments(args);
195             return frag;
196         }
197 
CopySubjectDialog()198         public CopySubjectDialog() {}
199 
200         @Override
onCreateDialog(Bundle savedInstanceState)201         public Dialog onCreateDialog(Bundle savedInstanceState) {
202             mSubject = getArguments().getString(ARG_SUBJECT);
203             return new AlertDialog.Builder(getActivity())
204                     .setMessage(mSubject)
205                     .setPositiveButton(R.string.contextmenu_copy, this)
206                     .setNegativeButton(R.string.cancel, this)
207                     .create();
208         }
209 
210         @Override
onClick(DialogInterface dialog, int which)211         public void onClick(DialogInterface dialog, int which) {
212             if (which == DialogInterface.BUTTON_POSITIVE) {
213                 final ClipboardManager clipboard = (ClipboardManager)
214                         getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
215                 clipboard.setPrimaryClip(ClipData.newPlainText(null, mSubject));
216             }
217         }
218     }
219 }
220