@Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (mActivity == null) { return false; } final Uri uri = Uri.parse(url); if (Utils.divertMailtoUri(mActivity, uri, mAccount)) { return true; } final Intent intent; if (mAccount != null && !Utils.isEmpty(mAccount.viewIntentProxyUri)) { intent = generateProxyIntent(uri); } else { intent = new Intent(Intent.ACTION_VIEW, uri); intent.putExtra(Browser.EXTRA_APPLICATION_ID, mActivity.getPackageName()); intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true); } boolean result = false; try { intent.setFlags( Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | Intent.FLAG_ACTIVITY_NO_ANIMATION); mActivity.startActivity(intent); result = true; } catch (ActivityNotFoundException ex) { // If no application can handle the URL, assume that the // caller can handle it. } return result; }
/** * @param view The view to be dismissed * @param velocity The desired pixels/second speed at which the view should move */ private void dismissChild(final SwipeableItemView view, float velocity) { final View animView = mCurrView.getSwipeableView().getView(); final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view); float newPos = determinePos(animView, velocity); int duration = determineDuration(animView, newPos, velocity); Utils.enableHardwareLayer(animView); ObjectAnimator anim = createDismissAnimation(animView, newPos, duration); anim.addListener( new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mCallback.onChildDismissed(mCurrView); animView.setLayerType(View.LAYER_TYPE_NONE, null); } }); anim.addUpdateListener( new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) { animView.setAlpha(getAlphaForOffset(animView)); } invalidateGlobalRegion(animView); } }); anim.start(); }
@Override public boolean onOptionsItemSelected(MenuItem item) { if (!isUserVisible()) { // Unclear how this is happening. Current theory is that this fragment was scheduled // to be removed, but the remove transaction failed. When the Activity is later // restored, the FragmentManager restores this fragment, but Fragment.mMenuVisible is // stuck at its initial value (true), which makes this zombie fragment eligible for // menu item clicks. // // Work around this by relying on the (properly restored) extra user visible hint. LogUtils.e( LOG_TAG, "ACVF ignoring onOptionsItemSelected b/c userVisibleHint is false. f=%s", this); if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) { LogUtils.e(LOG_TAG, Utils.dumpFragment(this)); // the dump has '%' chars in it... } return false; } boolean handled = false; final int itemId = item.getItemId(); if (itemId == R.id.inside_conversation_unread) { markUnread(); handled = true; } else if (itemId == R.id.show_original) { showUntransformedConversation(); handled = true; } return handled; }
/** * Get the desired dimensions and scale for the bitmap to be placed in the location corresponding * to id. Caller must allocate the Dimensions object. * * @param key * @param outDim a {@link ImageCanvas.Dimensions} object to write results into */ @Override public void getDesiredDimensions(Object key, Dimensions outDim) { Utils.traceBeginSection("get desired dimensions"); int w = 0, h = 0; float scale = 0; final Integer pos = mDivisionMap.get(transformKeyToDivisionId(key)); if (pos != null && pos >= 0) { final int size = mDivisionMap.size(); switch (size) { case 0: break; case 1: w = mWidth; h = mHeight; scale = Dimensions.SCALE_ONE; break; case 2: w = mWidth / 2; h = mHeight; scale = Dimensions.SCALE_HALF; break; case 3: switch (pos) { case 0: w = mWidth / 2; h = mHeight; scale = Dimensions.SCALE_HALF; break; default: w = mWidth / 2; h = mHeight / 2; scale = Dimensions.SCALE_QUARTER; } break; case 4: w = mWidth / 2; h = mHeight / 2; scale = Dimensions.SCALE_QUARTER; break; } } outDim.width = w; outDim.height = h; outDim.scale = scale; Utils.traceEndSection(); }
public String toString() { if ((this.mName != null) && (!this.mName.equals(this.mAddress))) { if (this.mName.matches(".*[\\(\\)<>@,;:\\\\\".\\[\\]].*")) return Utils.ensureQuotedString(this.mName) + " <" + this.mAddress + ">"; return this.mName + " <" + this.mAddress + ">"; } return this.mAddress; }
public void setQuotedText(int action, Message refMessage, boolean allow) { setVisibility(View.VISIBLE); String htmlText = getHtmlText(refMessage); StringBuilder quotedText = new StringBuilder(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT); Date date = new Date(refMessage.dateReceivedMs); Resources resources = getContext().getResources(); if (action == ComposeActivity.REPLY || action == ComposeActivity.REPLY_ALL) { quotedText.append(sQuoteBegin); quotedText.append( String.format( resources.getString(R.string.reply_attribution), dateFormat.format(date), Utils.cleanUpString(refMessage.getFrom(), true))); quotedText.append(HEADER_SEPARATOR); quotedText.append(BLOCKQUOTE_BEGIN); quotedText.append(htmlText); quotedText.append(BLOCKQUOTE_END); quotedText.append(QUOTE_END); } else if (action == ComposeActivity.FORWARD) { quotedText.append(sQuoteBegin); quotedText.append( String.format( resources.getString(R.string.forward_attribution), Utils.cleanUpString(refMessage.getFrom(), true /* remove empty quotes */), dateFormat.format(date), Utils.cleanUpString(refMessage.subject, false /* don't remove empty quotes */), Utils.cleanUpString(refMessage.getTo(), true))); String ccAddresses = refMessage.getCc(); quotedText.append( String.format( resources.getString(R.string.cc_attribution), Utils.cleanUpString(ccAddresses, true /* remove empty quotes */))); quotedText.append(HEADER_SEPARATOR); quotedText.append(BLOCKQUOTE_BEGIN); quotedText.append(htmlText); quotedText.append(BLOCKQUOTE_END); quotedText.append(QUOTE_END); } setQuotedText(quotedText); allowQuotedText(allow); // If there is quoted text, we always allow respond inline, since this // may be a forward. allowRespondInline(true); }
/** M: Don't show feedback menu if no feedback Uri is set */ @Override public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); MenuItem feedBackMenuItem = menu.findItem(R.id.feedback_menu_item); if (feedBackMenuItem != null) { Uri feedBackUri = Utils.getValidUri(getString(R.string.email_feedback_uri)); // We only want to enable the feedback menu item, if there is a valid feedback uri feedBackMenuItem.setVisible(!Uri.EMPTY.equals(feedBackUri)); } }
/** * Set the width and height of the canvas. * * @param width * @param height */ public void setDimensions(int width, int height) { Utils.traceBeginSection("set dimensions"); if (mWidth == width && mHeight == height) { Utils.traceEndSection(); return; } mWidth = width; mHeight = height; mDividedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mDividedBitmap); for (int i = 0; i < getDivisionCount(); i++) { mDivisionImages.set(i, null); } mBitmapValid = false; Utils.traceEndSection(); }
@Override public void onPrepareOptionsMenu(Menu menu) { // Only show option if we support message transforms and message has been transformed. Utils.setMenuItemVisibility( menu, R.id.show_original, supportsMessageTransforms() && mHasConversationBeenTransformed && !mHasConversationTransformBeenReverted); }
private void respondInline() { // Copy the text in the quoted message to the body of the // message after stripping the html. final String plainText = Utils.convertHtmlToPlainText(getQuotedText().toString()); if (mRespondInlineListener != null) { mRespondInlineListener.onRespondInline("\n" + plainText); } // Set quoted text to unchecked and not visible. updateCheckedState(false); mRespondInlineButton.setVisibility(View.GONE); // Hide everything to do with quoted text. View quotedTextView = findViewById(R.id.quoted_text_area); if (quotedTextView != null) { quotedTextView.setVisibility(View.GONE); } }
/** * Uses the current state to update the current folder {@link #mFolder} and the current account * {@link #mAccount} shown in the actionbar. Also updates the actionbar subtitle to momentarily * display the unread count if it has changed. * * @param folderChanged true if folder changed in terms of URI */ private void setFolderAndAccount(final boolean folderChanged) { // Very little can be done if the actionbar or activity is null. if (mActionBar == null || mActivity == null) { return; } if (ViewMode.isWaitingForSync(mMode)) { // Account is not synced: clear title and update the subtitle. setTitle(""); removeUnreadCount(true); return; } // Check if we should be changing the actionbar at all, and back off if not. final boolean isShowingFolder = mIsOnTablet || ViewMode.isListMode(mMode); if (!isShowingFolder) { // It isn't necessary to set the title in this case, as the title view will // be hidden return; } if (mFolder == null) { // Clear the action bar title. We don't want the app name to be shown while // waiting for the folder query to finish setTitle(""); return; } setTitle(mFolder.name); final int folderUnreadCount = mFolder.isUnreadCountHidden() ? 0 : mFolder.unreadCount; // The user shouldn't see "999+ unread messages", and then a short while later: "999+ // unread messages". So we set our unread count just past the limit. This way we can // change the subtitle the first time around but not for subsequent changes as far as the // unread count remains over the limit. final int toDisplay = (folderUnreadCount > UNREAD_LIMIT) ? (UNREAD_LIMIT + 1) : folderUnreadCount; if ((mUnreadCount != toDisplay || folderChanged) && toDisplay != 0) { setSubtitle(Utils.getUnreadMessageString(mActivity.getApplicationContext(), toDisplay)); } // Schedule a removal of unread count for the future, if there isn't one already. If the // unread count dropped to zero, remove it and show the account name right away. removeUnreadCount(toDisplay == 0); // Remember the new value for the next run mUnreadCount = toDisplay; }
public void viewAttachment() { if (this.mAttachment.contentUri == null) { LogUtils.e(LOG_TAG, "viewAttachment with null content uri", new Object[0]); return; } Intent localIntent = new Intent("android.intent.action.VIEW"); localIntent.setFlags(524289); Utils.setIntentDataAndTypeAndNormalize( localIntent, this.mAttachment.contentUri, this.mAttachment.contentType); try { getContext().startActivity(localIntent); return; } catch (ActivityNotFoundException localActivityNotFoundException) { LogUtils.e( LOG_TAG, localActivityNotFoundException, "Couldn't find Activity for intent", new Object[0]); } }
public QuotedTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs); LayoutInflater factory = LayoutInflater.from(context); factory.inflate(R.layout.quoted_text, this); mQuotedTextWebView = (WebView) findViewById(R.id.quoted_text_web_view); Utils.restrictWebView(mQuotedTextWebView); WebSettings settings = mQuotedTextWebView.getSettings(); settings.setBlockNetworkLoads(true); mShowHideCheckBox = (CheckBox) findViewById(R.id.hide_quoted_text); mShowHideCheckBox.setChecked(true); mShowHideCheckBox.setOnClickListener(this); sQuoteBegin = context.getResources().getString(R.string.quote_begin); findViewById(R.id.hide_quoted_text_label).setOnClickListener(this); mRespondInlineButton = (Button) findViewById(R.id.respond_inline_button); if (mRespondInlineButton != null) { mRespondInlineButton.setEnabled(false); } }
public MailActionBarView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); final Resources r = getResources(); mIsOnTablet = Utils.useTabletUI(r); UNREAD_LIMIT = r.getInteger(R.integer.maxUnreadCount); }
private void addOrClearDivisionImage(Bitmap b, Object key) { Utils.traceBeginSection("add or clear division image"); final Integer pos = mDivisionMap.get(transformKeyToDivisionId(key)); if (pos != null && pos >= 0) { mDivisionImages.set(pos, b); boolean complete = false; final int width = mWidth; final int height = mHeight; // Different layouts depending on count. final int size = mDivisionMap.size(); switch (size) { case 0: // Do nothing. break; case 1: // Draw the bitmap filling the entire canvas. draw(mDivisionImages.get(0), 0, 0, width, height); complete = true; break; case 2: // Draw 2 bitmaps split vertically down the middle switch (pos) { case 0: draw(mDivisionImages.get(0), 0, 0, width / 2, height); break; case 1: draw(mDivisionImages.get(1), width / 2, 0, width, height); break; } complete = mDivisionImages.get(0) != null && mDivisionImages.get(1) != null || isPartialBitmapComplete(); if (complete) { // Draw dividers drawVerticalDivider(width, height); } break; case 3: // Draw 3 bitmaps: the first takes up all vertical // space, the 2nd and 3rd are stacked in the second vertical // position. switch (pos) { case 0: draw(mDivisionImages.get(0), 0, 0, width / 2, height); break; case 1: draw(mDivisionImages.get(1), width / 2, 0, width, height / 2); break; case 2: draw(mDivisionImages.get(2), width / 2, height / 2, width, height); break; } complete = mDivisionImages.get(0) != null && mDivisionImages.get(1) != null && mDivisionImages.get(2) != null || isPartialBitmapComplete(); if (complete) { // Draw dividers drawVerticalDivider(width, height); drawHorizontalDivider(width / 2, height / 2, width, height / 2); } break; default: // Draw all 4 bitmaps in a grid switch (pos) { case 0: draw(mDivisionImages.get(0), 0, 0, width / 2, height / 2); break; case 1: draw(mDivisionImages.get(1), width / 2, 0, width, height / 2); break; case 2: draw(mDivisionImages.get(2), 0, height / 2, width / 2, height); break; case 3: draw(mDivisionImages.get(3), width / 2, height / 2, width, height); break; } complete = mDivisionImages.get(0) != null && mDivisionImages.get(1) != null && mDivisionImages.get(2) != null && mDivisionImages.get(3) != null || isPartialBitmapComplete(); if (complete) { // Draw dividers drawVerticalDivider(width, height); drawHorizontalDivider(0, height / 2, width, height / 2); } break; } // Create the new image bitmap. if (complete) { mBitmapValid = true; mCallback.invalidate(); } } Utils.traceEndSection(); }
public ConversationListFooterView(Context context, AttributeSet attrs) { super(context, attrs); mTabletDevice = Utils.useTabletUI(context.getResources()); }
/** Update the view to reflect the new folder status. */ public boolean updateStatus(final ConversationCursor cursor) { if (cursor == null) { updateLoadingStatus(true); return true; } boolean showFooter = true; final Bundle extras = cursor.getExtras(); final int cursorStatus = extras.getInt(UIProvider.CursorExtraKeys.EXTRA_STATUS); mErrorStatus = extras.containsKey(UIProvider.CursorExtraKeys.EXTRA_ERROR) ? extras.getInt(UIProvider.CursorExtraKeys.EXTRA_ERROR) : UIProvider.LastSyncResult.SUCCESS; final int totalCount = extras.getInt(UIProvider.CursorExtraKeys.EXTRA_TOTAL_COUNT); /// M: Get the value from extra final boolean allMessagesLoadFinish = extras.getBoolean(UIProvider.CursorExtraKeys.EXTRA_MESSAGES_LOAD_FINISH, false); if (UIProvider.CursorStatus.isWaitingForResults(cursorStatus)) { updateLoadingStatus(true); } else if (mErrorStatus != UIProvider.LastSyncResult.SUCCESS) { mNetworkError.setVisibility(View.VISIBLE); mErrorText.setText(Utils.getSyncStatusText(getContext(), mErrorStatus)); mLoading.setVisibility(View.GONE); mLoadMore.setVisibility(View.GONE); // Only show the "Retry" button for I/O errors; it won't help for // internal errors. mErrorActionButton.setVisibility( mErrorStatus != UIProvider.LastSyncResult.SECURITY_ERROR ? View.VISIBLE : View.GONE); final int actionTextResourceId; switch (mErrorStatus) { case UIProvider.LastSyncResult.CONNECTION_ERROR: actionTextResourceId = R.string.retry; break; case UIProvider.LastSyncResult.AUTH_ERROR: actionTextResourceId = R.string.signin; break; case UIProvider.LastSyncResult.SECURITY_ERROR: actionTextResourceId = R.string.retry; mNetworkError.setVisibility(View.GONE); break; // Currently we do nothing for security errors. case UIProvider.LastSyncResult.STORAGE_ERROR: actionTextResourceId = R.string.info; break; case UIProvider.LastSyncResult.INTERNAL_ERROR: actionTextResourceId = R.string.report; break; default: actionTextResourceId = R.string.retry; mNetworkError.setVisibility(View.GONE); break; } mErrorActionButton.setText(actionTextResourceId); } else if (mLoadMoreUri != null && cursor.getCount() < totalCount /** * M: filter some dirty update callback, default when cursor count little than total count. * load more view will show, in fact in the initial step of remote search the loader * complete result is 0 before run search on server, which will cause loading status * stopped. It make user confused, 'It should loading instead of load more' @} */ && cursor.getCount() > 0) { /** @} */ LogUtils.d( LogTag.getLogTag(), " LoadMore finished folder [%s], totalCount %d, cursor count %d", mLoadMoreUri, totalCount, cursor.getCount()); if (allMessagesLoadFinish) { /// M: When all messages load finish, load more do not need to display. showFooter = false; } else { updateLoadingStatus(false); } } else { showFooter = false; } return showFooter; }
// We need to do this here instead of in the fragment public void setConversationModeOptions(Menu menu) { if (mCurrentConversation == null) { return; } final boolean showMarkImportant = !mCurrentConversation.isImportant(); Utils.setMenuItemVisibility( menu, R.id.mark_important, showMarkImportant && mAccount.supportsCapability(UIProvider.AccountCapabilities.MARK_IMPORTANT)); Utils.setMenuItemVisibility( menu, R.id.mark_not_important, !showMarkImportant && mAccount.supportsCapability(UIProvider.AccountCapabilities.MARK_IMPORTANT)); final boolean showDelete = mFolder != null && mFolder.supportsCapability(UIProvider.FolderCapabilities.DELETE); Utils.setMenuItemVisibility(menu, R.id.delete, showDelete); // We only want to show the discard drafts menu item if we are not showing the delete menu // item, and the current folder is a draft folder and the account supports discarding // drafts for a conversation final boolean showDiscardDrafts = !showDelete && mFolder != null && mFolder.isDraft() && mAccount.supportsCapability(AccountCapabilities.DISCARD_CONVERSATION_DRAFTS); Utils.setMenuItemVisibility(menu, R.id.discard_drafts, showDiscardDrafts); final boolean archiveVisible = mAccount.supportsCapability(AccountCapabilities.ARCHIVE) && mFolder != null && mFolder.supportsCapability(FolderCapabilities.ARCHIVE) && !mFolder.isTrash(); Utils.setMenuItemVisibility(menu, R.id.archive, archiveVisible); Utils.setMenuItemVisibility( menu, R.id.remove_folder, !archiveVisible && mFolder != null && mFolder.supportsCapability(FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES) && !mFolder.isProviderFolder() && mAccount.supportsCapability(AccountCapabilities.ARCHIVE)); Utils.setMenuItemVisibility( menu, R.id.move_to, mFolder != null && mFolder.supportsCapability(FolderCapabilities.ALLOWS_REMOVE_CONVERSATION)); Utils.setMenuItemVisibility( menu, R.id.move_to_inbox, mFolder != null && mFolder.supportsCapability(FolderCapabilities.ALLOWS_MOVE_TO_INBOX)); final MenuItem removeFolder = menu.findItem(R.id.remove_folder); if (mFolder != null && removeFolder != null) { removeFolder.setTitle( mActivity.getApplicationContext().getString(R.string.remove_folder, mFolder.name)); } Utils.setMenuItemVisibility( menu, R.id.report_spam, mAccount.supportsCapability(AccountCapabilities.REPORT_SPAM) && mFolder != null && mFolder.supportsCapability(FolderCapabilities.REPORT_SPAM) && !mCurrentConversation.spam); Utils.setMenuItemVisibility( menu, R.id.mark_not_spam, mAccount.supportsCapability(AccountCapabilities.REPORT_SPAM) && mFolder != null && mFolder.supportsCapability(FolderCapabilities.MARK_NOT_SPAM) && mCurrentConversation.spam); Utils.setMenuItemVisibility( menu, R.id.report_phishing, mAccount.supportsCapability(AccountCapabilities.REPORT_PHISHING) && mFolder != null && mFolder.supportsCapability(FolderCapabilities.REPORT_PHISHING) && !mCurrentConversation.phishing); Utils.setMenuItemVisibility( menu, R.id.mute, mAccount.supportsCapability(AccountCapabilities.MUTE) && mFolder != null && mFolder.supportsCapability(FolderCapabilities.DESTRUCTIVE_MUTE) && !mCurrentConversation.muted); }
public boolean onPrepareOptionsMenu(Menu menu) { // We start out with every option enabled. Based on the current view, we disable actions // that are possible. LogUtils.d(LOG_TAG, "ActionBarView.onPrepareOptionsMenu()."); if (mHelpItem != null) { mHelpItem.setVisible( mAccount != null && mAccount.supportsCapability(AccountCapabilities.HELP_CONTENT)); } if (mSendFeedbackItem != null) { mSendFeedbackItem.setVisible( mAccount != null && mAccount.supportsCapability(AccountCapabilities.SEND_FEEDBACK)); } if (mController.shouldHideMenuItems()) { // Shortcut: hide all remaining menu items if the drawer is shown final int size = menu.size(); for (int i = 0; i < size; i++) { final MenuItem item = menu.getItem(i); final int id = item.getItemId(); if (id != R.id.settings && id != R.id.feedback_menu_item && id != R.id.help_info_menu_item) { item.setVisible(false); } } return false; } if (mRefreshItem != null) { // See b/11158759 // Disable refresh on drafts folders. mRefreshItem.setVisible( mFolder != null && !mFolder.isDraft() && !mFolder.supportsCapability(FolderCapabilities.IS_VIRTUAL)); } if (mFolderSettingsItem != null) { mFolderSettingsItem.setVisible( mFolder != null && mFolder.supportsCapability(FolderCapabilities.SUPPORTS_SETTINGS)); } if (mEmptyTrashItem != null) { mEmptyTrashItem.setVisible( mAccount != null && mFolder != null && mAccount.supportsCapability(AccountCapabilities.EMPTY_TRASH) && mFolder.isTrash() && mFolder.totalCount > 0); } if (mEmptySpamItem != null) { mEmptySpamItem.setVisible( mAccount != null && mFolder != null && mAccount.supportsCapability(AccountCapabilities.EMPTY_SPAM) && mFolder.isType(FolderType.SPAM) && mFolder.totalCount > 0); } switch (mMode) { case ViewMode.CONVERSATION: case ViewMode.SEARCH_RESULTS_CONVERSATION: // We update the ActionBar options when we are entering conversation view because // waiting for the AbstractConversationViewFragment to do it causes duplicate icons // to show up during the time between the conversation is selected and the fragment // is added. setConversationModeOptions(menu); // We want to use the user's preferred menu items here final Resources resources = getResources(); final int maxItems = resources.getInteger(R.integer.actionbar_max_items); final int hiddenItems = resources.getInteger(R.integer.actionbar_hidden_non_cab_items_no_physical_button); final int totalItems = maxItems - (ViewConfiguration.get(getContext()).hasPermanentMenuKey() ? 0 : hiddenItems); reorderMenu(getContext(), mAccount, menu, totalItems); break; case ViewMode.CONVERSATION_LIST: // Show compose and search based on the account // The only option that needs to be disabled is search Utils.setMenuItemVisibility( menu, R.id.search, mAccount.supportsCapability(AccountCapabilities.FOLDER_SERVER_SEARCH)); break; case ViewMode.SEARCH_RESULTS_LIST: // Hide compose and search Utils.setMenuItemVisibility(menu, R.id.compose, false); Utils.setMenuItemVisibility(menu, R.id.search, false); break; } return false; }