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