package com.fsck.k9.ui.messageview; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.Dialog; import android.app.DialogFragment; import android.app.Fragment; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.os.Bundle; import android.support.annotation.AttrRes; import android.support.annotation.ColorInt; import android.support.annotation.DrawableRes; import android.support.annotation.StringRes; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnLayoutChangeListener; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.ImageView; import android.widget.TextView; import com.fsck.k9.R; import com.fsck.k9.view.MessageCryptoDisplayStatus; import com.fsck.k9.view.ThemeUtils; public class CryptoInfoDialog extends DialogFragment { public static final String ARG_DISPLAY_STATUS = "display_status"; public static final String ARG_HAS_SECURITY_WARNING = "has_security_warning"; public static final int ICON_ANIM_DELAY = 400; public static final int ICON_ANIM_DURATION = 350; private View dialogView; private View topIconFrame; private ImageView topIcon_1; private ImageView topIcon_2; private ImageView topIcon_3; private TextView topText; private View bottomIconFrame; private ImageView bottomIcon_1; private ImageView bottomIcon_2; private TextView bottomText; public static CryptoInfoDialog newInstance(MessageCryptoDisplayStatus displayStatus, boolean hasSecurityWarning) { CryptoInfoDialog frag = new CryptoInfoDialog(); Bundle args = new Bundle(); args.putString(ARG_DISPLAY_STATUS, displayStatus.toString()); args.putBoolean(ARG_HAS_SECURITY_WARNING, hasSecurityWarning); frag.setArguments(args); return frag; } @SuppressLint("InflateParams") // inflating without root element is fine for creating a dialog @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Builder b = new AlertDialog.Builder(getActivity()); dialogView = LayoutInflater.from(getActivity()).inflate(R.layout.message_crypto_info_dialog, null); topIconFrame = dialogView.findViewById(R.id.crypto_info_top_frame); topIcon_1 = (ImageView) topIconFrame.findViewById(R.id.crypto_info_top_icon_1); topIcon_2 = (ImageView) topIconFrame.findViewById(R.id.crypto_info_top_icon_2); topIcon_3 = (ImageView) topIconFrame.findViewById(R.id.crypto_info_top_icon_3); topText = (TextView) dialogView.findViewById(R.id.crypto_info_top_text); bottomIconFrame = dialogView.findViewById(R.id.crypto_info_bottom_frame); bottomIcon_1 = (ImageView) bottomIconFrame.findViewById(R.id.crypto_info_bottom_icon_1); bottomIcon_2 = (ImageView) bottomIconFrame.findViewById(R.id.crypto_info_bottom_icon_2); bottomText = (TextView) dialogView.findViewById(R.id.crypto_info_bottom_text); MessageCryptoDisplayStatus displayStatus = MessageCryptoDisplayStatus.valueOf(getArguments().getString(ARG_DISPLAY_STATUS)); setMessageForDisplayStatus(displayStatus); b.setView(dialogView); b.setPositiveButton(R.string.crypto_info_ok, new OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dismiss(); } }); boolean hasSecurityWarning = getArguments().getBoolean(ARG_HAS_SECURITY_WARNING); if (hasSecurityWarning) { b.setNeutralButton(R.string.crypto_info_view_security_warning, new OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { Fragment frag = getTargetFragment(); if (! (frag instanceof OnClickShowCryptoKeyListener)) { throw new AssertionError("Displaying activity must implement OnClickShowCryptoKeyListener!"); } ((OnClickShowCryptoKeyListener) frag).onClickShowSecurityWarning(); } }); } else if (displayStatus.hasAssociatedKey()) { b.setNeutralButton(R.string.crypto_info_view_key, new OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { Fragment frag = getTargetFragment(); if (! (frag instanceof OnClickShowCryptoKeyListener)) { throw new AssertionError("Displaying activity must implement OnClickShowCryptoKeyListener!"); } ((OnClickShowCryptoKeyListener) frag).onClickShowCryptoKey(); } }); } return b.create(); } private void setMessageForDisplayStatus(MessageCryptoDisplayStatus displayStatus) { if (displayStatus.textResTop == null) { throw new AssertionError("Crypto info dialog can only be displayed for items with text!"); } if (displayStatus.textResBottom == null) { setMessageSingleLine(displayStatus.colorAttr, displayStatus.textResTop, displayStatus.statusIconRes, displayStatus.statusDotsRes); } else { if (displayStatus.statusDotsRes == null) { throw new AssertionError("second icon must be non-null if second text is non-null!"); } setMessageWithAnimation(displayStatus.colorAttr, displayStatus.textResTop, displayStatus.statusIconRes, displayStatus.textResBottom, displayStatus.statusDotsRes); } } private void setMessageSingleLine(@AttrRes int colorAttr, @StringRes int topTextRes, @DrawableRes int statusIconRes, @DrawableRes Integer statusDotsRes) { @ColorInt int color = ThemeUtils.getStyledColor(getActivity(), colorAttr); topIcon_1.setImageResource(statusIconRes); topIcon_1.setColorFilter(color); topText.setText(topTextRes); if (statusDotsRes != null) { topIcon_3.setImageResource(statusDotsRes); topIcon_3.setColorFilter(color); topIcon_3.setVisibility(View.VISIBLE); } else { topIcon_3.setVisibility(View.GONE); } bottomText.setVisibility(View.GONE); bottomIconFrame.setVisibility(View.GONE); } private void setMessageWithAnimation(@AttrRes int colorAttr, @StringRes int topTextRes, @DrawableRes int statusIconRes, @StringRes int bottomTextRes, @DrawableRes int statusDotsRes) { topIcon_1.setImageResource(statusIconRes); topIcon_2.setImageResource(statusDotsRes); topIcon_3.setVisibility(View.GONE); topText.setText(topTextRes); bottomIcon_1.setImageResource(statusIconRes); bottomIcon_2.setImageResource(statusDotsRes); bottomText.setText(bottomTextRes); topIcon_1.setColorFilter(ThemeUtils.getStyledColor(getActivity(), colorAttr)); bottomIcon_2.setColorFilter(ThemeUtils.getStyledColor(getActivity(), colorAttr)); prepareIconAnimation(); } private void prepareIconAnimation() { topText.setAlpha(0.0f); bottomText.setAlpha(0.0f); dialogView.addOnLayoutChangeListener(new OnLayoutChangeListener() { @Override public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) { float halfVerticalPixelDifference = (bottomIconFrame.getY() - topIconFrame.getY()) / 2.0f; topIconFrame.setTranslationY(halfVerticalPixelDifference); bottomIconFrame.setTranslationY(-halfVerticalPixelDifference); topIconFrame.animate().translationY(0) .setStartDelay(ICON_ANIM_DELAY) .setDuration(ICON_ANIM_DURATION) .setInterpolator(new AccelerateDecelerateInterpolator()) .start(); bottomIconFrame.animate().translationY(0) .setStartDelay(ICON_ANIM_DELAY) .setDuration(ICON_ANIM_DURATION) .setInterpolator(new AccelerateDecelerateInterpolator()) .start(); topText.animate().alpha(1.0f).setStartDelay(ICON_ANIM_DELAY + ICON_ANIM_DURATION).start(); bottomText.animate().alpha(1.0f).setStartDelay(ICON_ANIM_DELAY + ICON_ANIM_DURATION).start(); view.removeOnLayoutChangeListener(this); } }); } public interface OnClickShowCryptoKeyListener { void onClickShowCryptoKey(); void onClickShowSecurityWarning(); } }