diff --git a/res/menu/message_view_option.xml b/res/menu/message_view_option.xml
index 87253641e..4c1512aa6 100644
--- a/res/menu/message_view_option.xml
+++ b/res/menu/message_view_option.xml
@@ -12,69 +12,4 @@
android:icon="?attr/iconActionPreviousMessage"
android:showAsAction="always"
/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/com/fsck/k9/activity/K9Activity.java b/src/com/fsck/k9/activity/K9Activity.java
index afd9c5bf8..c1b35ce79 100644
--- a/src/com/fsck/k9/activity/K9Activity.java
+++ b/src/com/fsck/k9/activity/K9Activity.java
@@ -1,230 +1,37 @@
package com.fsck.k9.activity;
-
-import java.util.Locale;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
import android.os.Bundle;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.Animation;
-import android.view.animation.TranslateAnimation;
import com.actionbarsherlock.app.SherlockActivity;
-import com.fsck.k9.K9;
+import com.fsck.k9.activity.K9ActivityCommon.K9ActivityMagic;
+import com.fsck.k9.activity.misc.SwipeGestureDetector.OnSwipeGestureListener;
-public class K9Activity extends SherlockActivity {
- protected static final int BEZEL_SWIPE_THRESHOLD = 20;
+public class K9Activity extends SherlockActivity implements K9ActivityMagic {
+
+ private K9ActivityCommon mBase;
- protected GestureDetector mGestureDetector;
@Override
- public void onCreate(Bundle icicle) {
- setLanguage(this, K9.getK9Language());
- setTheme(K9.getK9ThemeResourceId());
- super.onCreate(icicle);
- setupFormats();
- }
-
- public static void setLanguage(Context context, String language) {
- Locale locale;
- if (language == null || language.equals("")) {
- locale = Locale.getDefault();
- } else if (language.length() == 5 && language.charAt(2) == '_') {
- // language is in the form: en_US
- locale = new Locale(language.substring(0, 2), language.substring(3));
- } else {
- locale = new Locale(language);
- }
- Configuration config = new Configuration();
- config.locale = locale;
- context.getResources().updateConfiguration(config,
- context.getResources().getDisplayMetrics());
+ public void onCreate(Bundle savedInstanceState) {
+ mBase = K9ActivityCommon.newInstance(this);
+ super.onCreate(savedInstanceState);
}
@Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (mGestureDetector != null) {
- mGestureDetector.onTouchEvent(ev);
- }
- return super.dispatchTouchEvent(ev);
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ mBase.preDispatchTouchEvent(event);
+ return super.dispatchTouchEvent(event);
}
@Override
- public void onResume() {
- super.onResume();
- setupFormats();
- }
-
- private java.text.DateFormat mTimeFormat;
-
- private void setupFormats() {
- mTimeFormat = android.text.format.DateFormat.getTimeFormat(this); // 12/24 date format
- }
-
- public java.text.DateFormat getTimeFormat() {
- return mTimeFormat;
- }
-
- /**
- * Called when a swipe from right to left is handled by {@link MyGestureDetector}. See
- * {@link android.view.GestureDetector.OnGestureListener#onFling(android.view.MotionEvent, android.view.MotionEvent, float, float)}
- * for more information on the {@link MotionEvent}s being passed.
- * @param e1 First down motion event that started the fling.
- * @param e2 The move motion event that triggered the current onFling.
- */
- protected void onSwipeRightToLeft(final MotionEvent e1, final MotionEvent e2) {
- }
-
- /**
- * Called when a swipe from left to right is handled by {@link MyGestureDetector}. See
- * {@link android.view.GestureDetector.OnGestureListener#onFling(android.view.MotionEvent, android.view.MotionEvent, float, float)}
- * for more information on the {@link MotionEvent}s being passed.
- * @param e1 First down motion event that started the fling.
- * @param e2 The move motion event that triggered the current onFling.
- */
- protected void onSwipeLeftToRight(final MotionEvent e1, final MotionEvent e2) {
- }
-
- protected Animation inFromRightAnimation() {
- return slideAnimation(0.0f, +1.0f);
- }
-
- protected Animation outToLeftAnimation() {
- return slideAnimation(0.0f, -1.0f);
- }
-
- private Animation slideAnimation(float right, float left) {
-
- Animation slide = new TranslateAnimation(
- Animation.RELATIVE_TO_PARENT, right, Animation.RELATIVE_TO_PARENT, left,
- Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f
- );
- slide.setDuration(125);
- slide.setFillBefore(true);
- slide.setInterpolator(new AccelerateInterpolator());
- return slide;
- }
-
- class MyGestureDetector extends SimpleOnGestureListener {
- private boolean gesturesEnabled = false;
-
- /**
- * Creates a new {@link android.view.GestureDetector.OnGestureListener}. Enabled/disabled based upon
- * {@link com.fsck.k9.K9#gesturesEnabled()}}.
- */
- public MyGestureDetector() {
- super();
- }
-
- /**
- * Create a new {@link android.view.GestureDetector.OnGestureListener}.
- * @param gesturesEnabled Setting to true will enable gesture detection,
- * regardless of the system-wide gesture setting.
- */
- public MyGestureDetector(final boolean gesturesEnabled) {
- super();
- this.gesturesEnabled = gesturesEnabled;
- }
-
- private static final float SWIPE_MAX_OFF_PATH_DIP = 250f;
- private static final float SWIPE_THRESHOLD_VELOCITY_DIP = 325f;
-
-
- protected MotionEvent mLastOnDownEvent = null;
-
- @Override
- public boolean onDown(MotionEvent e) {
- mLastOnDownEvent = e;
- return super.onDown(e);
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- // Do fling-detection if gestures are force-enabled or we have system-wide gestures enabled.
- if (gesturesEnabled || K9.gesturesEnabled()) {
-
- // Apparently sometimes e1 is null
- // Found a workaround here: http://stackoverflow.com/questions/4151385/
- if (e1 == null) {
- e1 = mLastOnDownEvent;
- }
-
- // Make sure we avoid NullPointerExceptions
- if (e1 == null || e2 == null) {
- return false;
- }
-
- // Calculate the minimum distance required for this to count as a swipe.
- // Convert the constant dips to pixels.
- final float mGestureScale = getResources().getDisplayMetrics().density;
- final int minVelocity = (int)(SWIPE_THRESHOLD_VELOCITY_DIP * mGestureScale + 0.5f);
- final int maxOffPath = (int)(SWIPE_MAX_OFF_PATH_DIP * mGestureScale + 0.5f);
-
- // Calculate how much was actually swiped.
- final float deltaX = e2.getX() - e1.getX();
- final float deltaY = e2.getY() - e1.getY();
-
- // Calculate the minimum distance required for this to be considered a swipe.
- final int minDistance = (int)Math.abs(deltaY * 4);
-
- if(K9.DEBUG) {
- final boolean movedAcross = (Math.abs(deltaX) > Math.abs(deltaY * 4));
- final boolean steadyHand = (Math.abs(deltaX / deltaY) > 2);
- Log.d(K9.LOG_TAG, String.format("Old swipe algorithm: movedAcross=%s steadyHand=%s result=%s", movedAcross, steadyHand, movedAcross && steadyHand));
- Log.d(K9.LOG_TAG, String.format("New swipe algorithm: deltaX=%.2f deltaY=%.2f minDistance=%d velocity=%.2f (min=%d)", deltaX, deltaY, minDistance, velocityX, minVelocity));
- }
-
- try {
- if (Math.abs(deltaY) > maxOffPath) {
- if(K9.DEBUG)
- Log.d(K9.LOG_TAG, "New swipe algorithm: Swipe too far off horizontal path.");
- return false;
- }
- if(Math.abs(velocityX) < minVelocity) {
- if(K9.DEBUG)
- Log.d(K9.LOG_TAG, "New swipe algorithm: Swipe too slow.");
- return false;
- }
- // right to left swipe
- if (deltaX < (minDistance * -1)) {
- onSwipeRightToLeft(e1, e2);
- if(K9.DEBUG)
- Log.d(K9.LOG_TAG, "New swipe algorithm: Right to Left swipe OK.");
- } else if (deltaX > minDistance) {
- onSwipeLeftToRight(e1, e2);
- if(K9.DEBUG)
- Log.d(K9.LOG_TAG, "New swipe algorithm: Left to Right swipe OK.");
- } else {
- if(K9.DEBUG)
- Log.d(K9.LOG_TAG, "New swipe algorithm: Swipe did not meet minimum distance requirements.");
- return false;
- }
-
- // successful fling, cancel the 2nd event to prevent any other action from happening
- // see http://code.google.com/p/android/issues/detail?id=8497
- e2.setAction(MotionEvent.ACTION_CANCEL);
- } catch (Exception e) {
- // nothing
- }
- }
- return false;
- }
- }
-
public int getThemeBackgroundColor() {
- TypedArray array = getTheme().obtainStyledAttributes(new int[] {
- android.R.attr.colorBackground,
- });
- int backgroundColor = array.getColor(0, 0xFF00FF);
- array.recycle();
- return backgroundColor;
+ return mBase.getThemeBackgroundColor();
}
+ @Override
+ public void setupGestureDetector(OnSwipeGestureListener listener) {
+ mBase.setupGestureDetector(listener);
+ }
}
diff --git a/src/com/fsck/k9/activity/K9ActivityCommon.java b/src/com/fsck/k9/activity/K9ActivityCommon.java
new file mode 100644
index 000000000..2dd65e9b7
--- /dev/null
+++ b/src/com/fsck/k9/activity/K9ActivityCommon.java
@@ -0,0 +1,115 @@
+package com.fsck.k9.activity;
+
+import java.util.Locale;
+
+import com.fsck.k9.K9;
+import com.fsck.k9.activity.misc.SwipeGestureDetector;
+import com.fsck.k9.activity.misc.SwipeGestureDetector.OnSwipeGestureListener;
+import com.fsck.k9.helper.StringUtils;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+
+/**
+ * This class implements functionality common to most activities used in K-9 Mail.
+ *
+ * @see K9Activity
+ * @see K9ListActivity
+ * @see K9FragmentActivity
+ */
+public class K9ActivityCommon {
+ /**
+ * Creates a new instance of {@link K9ActivityCommon} bound to the specified activity.
+ *
+ * @param activity
+ * The {@link Activity} the returned {@code K9ActivityCommon} instance will be bound to.
+ *
+ * @return The {@link K9ActivityCommon} instance that will provide the base functionality of the
+ * "K9" activities.
+ */
+ public static K9ActivityCommon newInstance(Activity activity) {
+ return new K9ActivityCommon(activity);
+ }
+
+ public static void setLanguage(Activity activity, String language) {
+ Locale locale;
+ if (StringUtils.isNullOrEmpty(language)) {
+ locale = Locale.getDefault();
+ } else if (language.length() == 5 && language.charAt(2) == '_') {
+ // language is in the form: en_US
+ locale = new Locale(language.substring(0, 2), language.substring(3));
+ } else {
+ locale = new Locale(language);
+ }
+
+ Configuration config = new Configuration();
+ config.locale = locale;
+ Resources resources = activity.getResources();
+ resources.updateConfiguration(config, resources.getDisplayMetrics());
+ }
+
+
+ /**
+ * Base activities need to implement this interface.
+ *
+ *
The implementing class simply has to call through to the implementation of these methods
+ * in {@link K9ActivityCommon}.
+ */
+ public interface K9ActivityMagic {
+ int getThemeBackgroundColor();
+ void setupGestureDetector(OnSwipeGestureListener listener);
+ }
+
+
+ private Activity mActivity;
+ private GestureDetector mGestureDetector;
+
+
+ private K9ActivityCommon(Activity activity) {
+ mActivity = activity;
+ setLanguage(mActivity, K9.getK9Language());
+ mActivity.setTheme(K9.getK9ThemeResourceId());
+ }
+
+ /**
+ * Call this before calling {@code super.dispatchTouchEvent(MotionEvent)}.
+ */
+ public void preDispatchTouchEvent(MotionEvent event) {
+ if (mGestureDetector != null) {
+ mGestureDetector.onTouchEvent(event);
+ }
+ }
+
+ /**
+ * Get the background color of the theme used for this activity.
+ *
+ * @return The background color of the current theme.
+ */
+ public int getThemeBackgroundColor() {
+ TypedArray array = mActivity.getTheme().obtainStyledAttributes(
+ new int[] { android.R.attr.colorBackground });
+
+ int backgroundColor = array.getColor(0, 0xFF00FF);
+
+ array.recycle();
+
+ return backgroundColor;
+ }
+
+ /**
+ * Call this if you wish to use the swipe gesture detector.
+ *
+ * @param listener
+ * A listener that will be notified if a left to right or right to left swipe has been
+ * detected.
+ */
+ public void setupGestureDetector(OnSwipeGestureListener listener) {
+ mGestureDetector = new GestureDetector(mActivity,
+ new SwipeGestureDetector(mActivity, listener));
+ }
+}
diff --git a/src/com/fsck/k9/activity/K9FragmentActivity.java b/src/com/fsck/k9/activity/K9FragmentActivity.java
new file mode 100644
index 000000000..289765b0c
--- /dev/null
+++ b/src/com/fsck/k9/activity/K9FragmentActivity.java
@@ -0,0 +1,37 @@
+package com.fsck.k9.activity;
+
+import android.os.Bundle;
+import android.view.MotionEvent;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.fsck.k9.activity.K9ActivityCommon.K9ActivityMagic;
+import com.fsck.k9.activity.misc.SwipeGestureDetector.OnSwipeGestureListener;
+
+
+public class K9FragmentActivity extends SherlockFragmentActivity implements K9ActivityMagic {
+
+ private K9ActivityCommon mBase;
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mBase = K9ActivityCommon.newInstance(this);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ mBase.preDispatchTouchEvent(event);
+ return super.dispatchTouchEvent(event);
+ }
+
+ @Override
+ public int getThemeBackgroundColor() {
+ return mBase.getThemeBackgroundColor();
+ }
+
+ @Override
+ public void setupGestureDetector(OnSwipeGestureListener listener) {
+ mBase.setupGestureDetector(listener);
+ }
+}
diff --git a/src/com/fsck/k9/activity/K9ListActivity.java b/src/com/fsck/k9/activity/K9ListActivity.java
index 91427e505..9c37a58e6 100644
--- a/src/com/fsck/k9/activity/K9ListActivity.java
+++ b/src/com/fsck/k9/activity/K9ListActivity.java
@@ -1,7 +1,7 @@
package com.fsck.k9.activity;
-import android.util.Log;
-import android.view.GestureDetector;
+import java.text.DateFormat;
+
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.AdapterView;
@@ -10,94 +10,96 @@ import android.os.Bundle;
import com.actionbarsherlock.app.SherlockListActivity;
import com.fsck.k9.K9;
+import com.fsck.k9.activity.K9ActivityCommon.K9ActivityMagic;
+import com.fsck.k9.activity.misc.SwipeGestureDetector.OnSwipeGestureListener;
import com.fsck.k9.helper.DateFormatter;
-public class K9ListActivity extends SherlockListActivity {
- protected GestureDetector mGestureDetector;
+
+public class K9ListActivity extends SherlockListActivity implements K9ActivityMagic {
+
+ private K9ActivityCommon mBase;
+ private DateFormat mDateFormat;
+ private DateFormat mTimeFormat;
+
@Override
- public void onCreate(Bundle icicle) {
- K9Activity.setLanguage(this, K9.getK9Language());
- setTheme(K9.getK9ThemeResourceId());
- super.onCreate(icicle);
+ public void onCreate(Bundle savedInstanceState) {
+ mBase = K9ActivityCommon.newInstance(this);
+ super.onCreate(savedInstanceState);
setupFormats();
}
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ mBase.preDispatchTouchEvent(event);
+ return super.dispatchTouchEvent(event);
+ }
+
@Override
public void onResume() {
super.onResume();
setupFormats();
}
- private java.text.DateFormat mDateFormat;
- private java.text.DateFormat mTimeFormat;
-
- private void setupFormats() {
- mDateFormat = DateFormatter.getDateFormat(this);
- mTimeFormat = android.text.format.DateFormat.getTimeFormat(this); // 12/24 date format
+ public DateFormat getDateFormat() {
+ return mDateFormat;
}
- public java.text.DateFormat getTimeFormat() {
+ public DateFormat getTimeFormat() {
return mTimeFormat;
}
- public java.text.DateFormat getDateFormat() {
- return mDateFormat;
+ private void setupFormats() {
+ mTimeFormat = android.text.format.DateFormat.getTimeFormat(this);
+ mDateFormat = DateFormatter.getDateFormat(this);
+ }
+
+ @Override
+ public int getThemeBackgroundColor() {
+ return mBase.getThemeBackgroundColor();
+ }
+
+ @Override
+ public void setupGestureDetector(OnSwipeGestureListener listener) {
+ mBase.setupGestureDetector(listener);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Shortcuts that work no matter what is selected
- switch (keyCode) {
- case KeyEvent.KEYCODE_VOLUME_UP: {
- final ListView listView = getListView();
- if (K9.useVolumeKeysForListNavigationEnabled()) {
- int currentPosition = listView.getSelectedItemPosition();
- if (currentPosition == AdapterView.INVALID_POSITION || listView.isInTouchMode()) {
- currentPosition = listView.getFirstVisiblePosition();
- }
- if (currentPosition > 0) {
- listView.setSelection(currentPosition - 1);
- }
- return true;
- }
- }
- case KeyEvent.KEYCODE_VOLUME_DOWN: {
- final ListView listView = getListView();
- if (K9.useVolumeKeysForListNavigationEnabled()) {
- int currentPosition = listView.getSelectedItemPosition();
- if (currentPosition == AdapterView.INVALID_POSITION || listView.isInTouchMode()) {
- currentPosition = listView.getFirstVisiblePosition();
- }
+ if (K9.useVolumeKeysForListNavigationEnabled() &&
+ (keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
+ keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
- if (currentPosition < listView.getCount()) {
- listView.setSelection(currentPosition + 1);
- }
- return true;
+ final ListView listView = getListView();
+
+ int currentPosition = listView.getSelectedItemPosition();
+ if (currentPosition == AdapterView.INVALID_POSITION || listView.isInTouchMode()) {
+ currentPosition = listView.getFirstVisiblePosition();
}
+
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && currentPosition > 0) {
+ listView.setSelection(currentPosition - 1);
+ } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN &&
+ currentPosition < listView.getCount()) {
+ listView.setSelection(currentPosition + 1);
+ }
+
+ return true;
}
- }
+
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// Swallow these events too to avoid the audible notification of a volume change
- if (K9.useVolumeKeysForListNavigationEnabled()) {
- if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
- if (K9.DEBUG)
- Log.v(K9.LOG_TAG, "Swallowed key up.");
- return true;
- }
+ if (K9.useVolumeKeysForListNavigationEnabled() &&
+ (keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
+ keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
+ return true;
}
+
return super.onKeyUp(keyCode, event);
}
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (mGestureDetector != null) {
- mGestureDetector.onTouchEvent(ev);
- }
- return super.dispatchTouchEvent(ev);
- }
}
diff --git a/src/com/fsck/k9/activity/K9PreferenceActivity.java b/src/com/fsck/k9/activity/K9PreferenceActivity.java
index 1b83b68f4..d30cf9d63 100644
--- a/src/com/fsck/k9/activity/K9PreferenceActivity.java
+++ b/src/com/fsck/k9/activity/K9PreferenceActivity.java
@@ -12,7 +12,7 @@ import android.preference.Preference;
public class K9PreferenceActivity extends SherlockPreferenceActivity {
@Override
public void onCreate(Bundle icicle) {
- K9Activity.setLanguage(this, K9.getK9Language());
+ K9ActivityCommon.setLanguage(this, K9.getK9Language());
if (Build.VERSION.SDK_INT >= 6 && Build.VERSION.SDK_INT < 14) {
// There's a display bug in all supported Android versions before 4.0 (SDK 14) which
@@ -88,5 +88,4 @@ public class K9PreferenceActivity extends SherlockPreferenceActivity {
return false;
}
}
-
}
diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java
index d7862a833..3b5e0589f 100644
--- a/src/com/fsck/k9/activity/MessageCompose.java
+++ b/src/com/fsck/k9/activity/MessageCompose.java
@@ -440,12 +440,11 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
* Save will attempt to replace the message in the given folder with the updated version.
* Discard will delete the message from the given folder.
* @param context
- * @param account
* @param message
*/
- public static void actionEditDraft(Context context, Account account, Message message) {
+ public static void actionEditDraft(Context context, MessageReference messageReference) {
Intent i = new Intent(context, MessageCompose.class);
- i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference());
+ i.putExtra(EXTRA_MESSAGE_REFERENCE, messageReference);
i.setAction(ACTION_EDIT_DRAFT);
context.startActivity(i);
}
diff --git a/src/com/fsck/k9/activity/MessageList.java b/src/com/fsck/k9/activity/MessageList.java
index ca5d3f261..8862314e9 100644
--- a/src/com/fsck/k9/activity/MessageList.java
+++ b/src/com/fsck/k9/activity/MessageList.java
@@ -1,84 +1,40 @@
package com.fsck.k9.activity;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Future;
-import android.app.AlertDialog;
-import android.app.Dialog;
import android.app.SearchManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences.Editor;
-import android.graphics.Color;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.os.Handler;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.style.AbsoluteSizeSpan;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.StyleSpan;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentManager.OnBackStackChangedListener;
+import android.support.v4.app.FragmentTransaction;
import android.util.Log;
-import android.util.TypedValue;
-import android.view.*;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.OnClickListener;
-import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemLongClickListener;
-import android.widget.BaseAdapter;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.ImageButton;
-import android.widget.ListView;
-import android.widget.ProgressBar;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.internal.view.menu.MenuBuilder;
-import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
-import com.actionbarsherlock.view.Window;
import com.fsck.k9.Account;
import com.fsck.k9.Account.SortType;
-import com.fsck.k9.AccountStats;
-import com.fsck.k9.FontSizes;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.SearchSpecification;
-import com.fsck.k9.activity.misc.SwipeGestureDetector;
import com.fsck.k9.activity.misc.SwipeGestureDetector.OnSwipeGestureListener;
import com.fsck.k9.activity.setup.AccountSettings;
import com.fsck.k9.activity.setup.FolderSettings;
import com.fsck.k9.activity.setup.Prefs;
-import com.fsck.k9.controller.MessagingController;
-import com.fsck.k9.controller.MessagingListener;
-import com.fsck.k9.helper.MenuPopup;
-import com.fsck.k9.helper.MessageHelper;
+import com.fsck.k9.fragment.MessageListFragment;
+import com.fsck.k9.fragment.MessageListFragment.MessageListFragmentListener;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Flag;
-import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Message;
-import com.fsck.k9.mail.Store;
-import com.fsck.k9.mail.store.ImapStore;
-import com.fsck.k9.mail.store.LocalStore;
-import com.fsck.k9.mail.store.LocalStore.LocalFolder;
import com.fsck.k9.mail.store.StorageManager;
-import com.handmark.pulltorefresh.library.PullToRefreshBase;
-import com.handmark.pulltorefresh.library.PullToRefreshListView;
/**
@@ -86,158 +42,10 @@ import com.handmark.pulltorefresh.library.PullToRefreshListView;
* shows a list of messages.
* From this Activity the user can perform all standard message operations.
*/
-public class MessageList extends K9ListActivity implements OnItemClickListener,
- OnSwipeGestureListener {
-
- /**
- * Reverses the result of a {@link Comparator}.
- *
- * @param
- */
- public static class ReverseComparator implements Comparator {
- private Comparator mDelegate;
-
- /**
- * @param delegate
- * Never null.
- */
- public ReverseComparator(final Comparator delegate) {
- mDelegate = delegate;
- }
-
- @Override
- public int compare(final T object1, final T object2) {
- // arg1 & 2 are mixed up, this is done on purpose
- return mDelegate.compare(object2, object1);
- }
-
- }
-
- /**
- * Chains comparator to find a non-0 result.
- *
- * @param
- */
- public static class ComparatorChain implements Comparator {
-
- private List> mChain;
-
- /**
- * @param chain
- * Comparator chain. Never null.
- */
- public ComparatorChain(final List> chain) {
- mChain = chain;
- }
-
- @Override
- public int compare(T object1, T object2) {
- int result = 0;
- for (final Comparator comparator : mChain) {
- result = comparator.compare(object1, object2);
- if (result != 0) {
- break;
- }
- }
- return result;
- }
-
- }
-
- public static class AttachmentComparator implements Comparator {
-
- @Override
- public int compare(MessageInfoHolder object1, MessageInfoHolder object2) {
- return (object1.message.hasAttachments() ? 0 : 1) - (object2.message.hasAttachments() ? 0 : 1);
- }
-
- }
-
- public static class FlaggedComparator implements Comparator {
-
- @Override
- public int compare(MessageInfoHolder object1, MessageInfoHolder object2) {
- return (object1.flagged ? 0 : 1) - (object2.flagged ? 0 : 1);
- }
-
- }
-
- public static class UnreadComparator implements Comparator {
-
- @Override
- public int compare(MessageInfoHolder object1, MessageInfoHolder object2) {
- return (object1.read ? 1 : 0) - (object2.read ? 1 : 0);
- }
-
- }
-
- public static class SenderComparator implements Comparator {
-
- @Override
- public int compare(MessageInfoHolder object1, MessageInfoHolder object2) {
- if (object1.compareCounterparty == null) {
- return (object2.compareCounterparty == null ? 0 : 1);
- } else if (object2.compareCounterparty == null) {
- return -1;
- } else {
- return object1.compareCounterparty.toLowerCase().compareTo(object2.compareCounterparty.toLowerCase());
- }
- }
-
- }
-
- public static class DateComparator implements Comparator {
-
- @Override
- public int compare(MessageInfoHolder object1, MessageInfoHolder object2) {
- if (object1.compareDate == null) {
- return (object2.compareDate == null ? 0 : 1);
- } else if (object2.compareDate == null) {
- return -1;
- } else {
- return object1.compareDate.compareTo(object2.compareDate);
- }
- }
-
- }
-
- public static class ArrivalComparator implements Comparator {
-
- @Override
- public int compare(MessageInfoHolder object1, MessageInfoHolder object2) {
- return object1.compareArrival.compareTo(object2.compareArrival);
- }
-
- }
-
- public static class SubjectComparator implements Comparator {
-
- @Override
- public int compare(MessageInfoHolder arg0, MessageInfoHolder arg1) {
- // XXX doesn't respect the Comparator contract since it alters the compared object
- if (arg0.compareSubject == null) {
- arg0.compareSubject = Utility.stripSubject(arg0.message.getSubject());
- }
- if (arg1.compareSubject == null) {
- arg1.compareSubject = Utility.stripSubject(arg1.message.getSubject());
- }
- return arg0.compareSubject.compareToIgnoreCase(arg1.compareSubject);
- }
-
- }
-
- /**
- * Immutable empty {@link Message} array
- */
- private static final Message[] EMPTY_MESSAGE_ARRAY = new Message[0];
-
-
- private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
- private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;
-
+public class MessageList extends K9FragmentActivity implements MessageListFragmentListener,
+ OnBackStackChangedListener, OnSwipeGestureListener {
private static final String EXTRA_ACCOUNT = "account";
private static final String EXTRA_FOLDER = "folder";
- private static final String EXTRA_REMOTE_SEARCH = "com.fsck.k9.remote_search";
private static final String EXTRA_SEARCH_ACCOUNT = "com.fsck.k9.search_account";
private static final String EXTRA_SEARCH_FOLDER = "com.fsck.k9.search_folder";
private static final String EXTRA_QUERY_FLAGS = "queryFlags";
@@ -246,357 +54,7 @@ public class MessageList extends K9ListActivity implements OnItemClickListener,
private static final String EXTRA_ACCOUNT_UUIDS = "accountUuids";
private static final String EXTRA_FOLDER_NAMES = "folderNames";
private static final String EXTRA_TITLE = "title";
- private static final String EXTRA_LIST_POSITION = "listPosition";
- /**
- * Maps a {@link SortType} to a {@link Comparator} implementation.
- */
- private static final Map> SORT_COMPARATORS;
-
- static {
- // fill the mapping at class time loading
-
- final Map> map = new EnumMap>(SortType.class);
- map.put(SortType.SORT_ATTACHMENT, new AttachmentComparator());
- map.put(SortType.SORT_DATE, new DateComparator());
- map.put(SortType.SORT_ARRIVAL, new ArrivalComparator());
- map.put(SortType.SORT_FLAGGED, new FlaggedComparator());
- map.put(SortType.SORT_SENDER, new SenderComparator());
- map.put(SortType.SORT_SUBJECT, new SubjectComparator());
- map.put(SortType.SORT_UNREAD, new UnreadComparator());
-
- // make it immutable to prevent accidental alteration (content is immutable already)
- SORT_COMPARATORS = Collections.unmodifiableMap(map);
- }
-
- private ListView mListView;
- private PullToRefreshListView mPullToRefreshView;
-
- private int mPreviewLines = 0;
-
-
- private MessageListAdapter mAdapter;
- private View mFooterView;
-
- private FolderInfoHolder mCurrentFolder;
-
- private LayoutInflater mInflater;
-
- private MessagingController mController;
-
- private Account mAccount;
- private int mUnreadMessageCount = 0;
-
- /**
- * Stores the name of the folder that we want to open as soon as possible
- * after load.
- */
- private String mFolderName;
-
- /**
- * If we're doing a search, this contains the query string.
- */
- private String mQueryString;
- private Flag[] mQueryFlags = null;
- private Flag[] mForbiddenFlags = null;
- private boolean mRemoteSearch = false;
- private String mSearchAccount = null;
- private String mSearchFolder = null;
- private Future mRemoteSearchFuture = null;
- private boolean mIntegrate = false;
- private String[] mAccountUuids = null;
- private String[] mFolderNames = null;
- private String mTitle;
-
- private MessageListHandler mHandler = new MessageListHandler();
-
- private SortType mSortType = SortType.SORT_DATE;
- private boolean mSortAscending = true;
- private boolean mSortDateAscending = false;
- private boolean mSenderAboveSubject = false;
-
- private int mSelectedCount = 0;
-
- private FontSizes mFontSizes = K9.getFontSizes();
-
- private MenuItem mRefreshMenuItem;
- private ActionBar mActionBar;
- private ActionMode mActionMode;
- private View mActionBarProgressView;
- private Bundle mState = null;
-
- private Boolean mHasConnectivity;
-
- /**
- * Relevant messages for the current context when we have to remember the
- * chosen messages between user interactions (eg. Selecting a folder for
- * move operation)
- */
- private List mActiveMessages;
-
- private Context context;
-
- /* package visibility for faster inner class access */
- MessageHelper mMessageHelper = MessageHelper.getInstance(this);
-
- private StorageManager.StorageListener mStorageListener = new StorageListenerImplementation();
-
- private TextView mActionBarTitle;
- private TextView mActionBarSubTitle;
- private TextView mActionBarUnread;
-
- private ActionModeCallback mActionModeCallback = new ActionModeCallback();
-
- private final class StorageListenerImplementation implements StorageManager.StorageListener {
- @Override
- public void onUnmount(String providerId) {
- if (mAccount != null && providerId.equals(mAccount.getLocalStorageProviderId())) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- onAccountUnavailable();
- }
- });
- }
- }
-
- @Override
- public void onMount(String providerId) {
- // no-op
- }
- }
-
- /**
- * This class is used to run operations that modify UI elements in the UI thread.
- *
- *
We are using convenience methods that add a {@link android.os.Message} instance or a
- * {@link Runnable} to the message queue.
- *
- *
Note: If you add a method to this class make sure you don't accidentally
- * perform the operation in the calling thread.
- */
- class MessageListHandler extends Handler {
- private static final int ACTION_REMOVE_MESSAGE = 1;
- private static final int ACTION_RESET_UNREAD_COUNT = 2;
- private static final int ACTION_SORT_MESSAGES = 3;
- private static final int ACTION_FOLDER_LOADING = 4;
- private static final int ACTION_REFRESH_TITLE = 5;
- private static final int ACTION_PROGRESS = 6;
-
- public void removeMessage(MessageReference messageReference) {
- android.os.Message msg = android.os.Message.obtain(this, ACTION_REMOVE_MESSAGE,
- messageReference);
- sendMessage(msg);
- }
-
- public void sortMessages() {
- android.os.Message msg = android.os.Message.obtain(this, ACTION_SORT_MESSAGES);
- sendMessage(msg);
- }
-
- public void folderLoading(String folder, boolean loading) {
- android.os.Message msg = android.os.Message.obtain(this, ACTION_FOLDER_LOADING,
- (loading) ? 1 : 0, 0, folder);
- sendMessage(msg);
- }
-
- public void refreshTitle() {
- android.os.Message msg = android.os.Message.obtain(this, ACTION_REFRESH_TITLE);
- sendMessage(msg);
- }
-
- public void progress(final boolean progress) {
- android.os.Message msg = android.os.Message.obtain(this, ACTION_PROGRESS,
- (progress) ? 1 : 0, 0);
- sendMessage(msg);
- }
-
- public void updateFooter(final String message, final boolean showProgress) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- MessageList.this.updateFooter(message, showProgress);
- }
- });
- }
-
- public void changeMessageUid(final MessageReference ref, final String newUid) {
- // Instead of explicitly creating a container to be able to pass both arguments in a
- // Message we post a Runnable to the message queue.
- post(new Runnable() {
- @Override
- public void run() {
- mAdapter.changeMessageUid(ref, newUid);
- }
- });
- }
-
- public void addOrUpdateMessages(final Account account, final String folderName,
- final List providedMessages, final boolean verifyAgainstSearch) {
- // We copy the message list because it's later modified by MessagingController
- final List messages = new ArrayList(providedMessages);
-
- post(new Runnable() {
- @Override
- public void run() {
- mAdapter.addOrUpdateMessages(account, folderName, messages,
- verifyAgainstSearch);
- }
- });
- }
-
- @Override
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case ACTION_REMOVE_MESSAGE: {
- MessageReference messageReference = (MessageReference) msg.obj;
- mAdapter.removeMessage(messageReference);
- break;
- }
- case ACTION_RESET_UNREAD_COUNT: {
- mAdapter.resetUnreadCount();
- break;
- }
- case ACTION_SORT_MESSAGES: {
- mAdapter.sortMessages();
- break;
- }
- case ACTION_FOLDER_LOADING: {
- String folder = (String) msg.obj;
- boolean loading = (msg.arg1 == 1);
- MessageList.this.folderLoading(folder, loading);
- break;
- }
- case ACTION_REFRESH_TITLE: {
- MessageList.this.refreshTitle();
- break;
- }
- case ACTION_PROGRESS: {
- boolean progress = (msg.arg1 == 1);
- MessageList.this.progress(progress);
- break;
- }
- }
- }
- }
-
- /**
- * @return The comparator to use to display messages in an ordered
- * fashion. Never null.
- */
- protected Comparator getComparator() {
- final List> chain = new ArrayList>(2 /* we add 2 comparators at most */);
-
- {
- // add the specified comparator
- final Comparator comparator = SORT_COMPARATORS.get(mSortType);
- if (mSortAscending) {
- chain.add(comparator);
- } else {
- chain.add(new ReverseComparator(comparator));
- }
- }
-
- {
- // add the date comparator if not already specified
- if (mSortType != SortType.SORT_DATE && mSortType != SortType.SORT_ARRIVAL) {
- final Comparator comparator = SORT_COMPARATORS.get(SortType.SORT_DATE);
- if (mSortDateAscending) {
- chain.add(comparator);
- } else {
- chain.add(new ReverseComparator(comparator));
- }
- }
- }
-
- return new ComparatorChain(chain);
- }
-
- private void folderLoading(String folder, boolean loading) {
- if (mCurrentFolder != null && mCurrentFolder.name.equals(folder)) {
- mCurrentFolder.loading = loading;
- }
- updateFooterView();
- }
-
- private void refreshTitle() {
- setWindowTitle();
- if (!mRemoteSearch) {
- setWindowProgress();
- }
- }
-
- private void setWindowProgress() {
- int level = Window.PROGRESS_END;
-
- if (mCurrentFolder != null && mCurrentFolder.loading && mAdapter.mListener.getFolderTotal() > 0) {
- int divisor = mAdapter.mListener.getFolderTotal();
- if (divisor != 0) {
- level = (Window.PROGRESS_END / divisor) * (mAdapter.mListener.getFolderCompleted()) ;
- if (level > Window.PROGRESS_END) {
- level = Window.PROGRESS_END;
- }
- }
- }
-
- setSupportProgress(level);
- }
-
- private void setWindowTitle() {
- // regular folder content display
- if (mFolderName != null) {
- String displayName = FolderInfoHolder.getDisplayName(MessageList.this, mAccount,
- mFolderName);
-
- mActionBarTitle.setText(displayName);
-
- String operation = mAdapter.mListener.getOperation(MessageList.this, getTimeFormat()).trim();
- if (operation.length() < 1) {
- mActionBarSubTitle.setText(mAccount.getEmail());
- } else {
- mActionBarSubTitle.setText(operation);
- }
- } else if (mQueryString != null) {
- // query result display. This may be for a search folder as opposed to a user-initiated search.
- if (mTitle != null) {
- // This was a search folder; the search folder has overridden our title.
- mActionBarTitle.setText(mTitle);
- } else {
- // This is a search result; set it to the default search result line.
- mActionBarTitle.setText(getString(R.string.search_results));
- }
- }
-
- // set unread count
- if (mUnreadMessageCount == 0) {
- mActionBarUnread.setVisibility(View.GONE);
- } else {
- if (mQueryString != null && mTitle == null) {
- // This is a search result. The unread message count is easily confused
- // with total number of messages in the search result, so let's hide it.
- mActionBarUnread.setVisibility(View.GONE);
- } else {
- mActionBarUnread.setText(Integer.toString(mUnreadMessageCount));
- mActionBarUnread.setVisibility(View.VISIBLE);
- }
- }
- }
-
- private void progress(final boolean progress) {
- // Make sure we don't try this before the menu is initialized
- // this could happen while the activity is initialized.
- if (mRefreshMenuItem != null) {
- if (progress) {
- mRefreshMenuItem.setActionView(mActionBarProgressView);
- } else {
- mRefreshMenuItem.setActionView(null);
- }
- }
-
- if (mPullToRefreshView != null && !progress) {
- mPullToRefreshView.onRefreshComplete();
- }
- }
public static void actionHandleFolder(Context context, Account account, String folder) {
Intent intent = new Intent(context, MessageList.class);
@@ -666,84 +124,70 @@ public class MessageList extends K9ListActivity implements OnItemClickListener,
context.startActivity(intent);
}
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- if (view == mFooterView) {
- if (mCurrentFolder != null && !mRemoteSearch) {
- mController.loadMoreMessages(mAccount, mFolderName, mAdapter.mListener);
- } else if (mRemoteSearch && mAdapter.mExtraSearchResults != null && mAdapter.mExtraSearchResults.size() > 0 && mSearchAccount != null) {
- int numResults = mAdapter.mExtraSearchResults.size();
- Account account = Preferences.getPreferences(this).getAccount(mSearchAccount);
- if (account == null) {
- mHandler.updateFooter("", false);
- return;
- }
- int limit = account.getRemoteSearchNumResults();
- List toProcess = mAdapter.mExtraSearchResults;
- if (limit > 0 && numResults > limit) {
- toProcess = toProcess.subList(0, limit);
- mAdapter.mExtraSearchResults = mAdapter.mExtraSearchResults.subList(limit, mAdapter.mExtraSearchResults.size());
- } else {
- mAdapter.mExtraSearchResults = null;
- mHandler.updateFooter("", false);
- }
- mController.loadSearchResults(account, mSearchFolder, toProcess, mAdapter.mListener);
- }
- return;
- }
- final MessageInfoHolder message = (MessageInfoHolder) parent.getItemAtPosition(position);
- if (mSelectedCount > 0) {
- toggleMessageSelect(message);
- } else {
- onOpenMessage(message);
- }
- }
+ private StorageManager.StorageListener mStorageListener = new StorageListenerImplementation();
+
+ private ActionBar mActionBar;
+ private TextView mActionBarTitle;
+ private TextView mActionBarSubTitle;
+ private TextView mActionBarUnread;
+ private String mTitle;
+ private Menu mMenu;
+
+ private MessageListFragment mMessageListFragment;
+
+ private Account mAccount;
+ private String mQueryString;
+ private String mFolderName;
+ private Flag[] mQueryFlags;
+ private Flag[] mForbiddenFlags;
+ private String mSearchAccount = null;
+ private String mSearchFolder = null;
+ private boolean mIntegrate;
+ private String[] mAccountUuids;
+ private String[] mFolderNames;
+
@Override
public void onCreate(Bundle savedInstanceState) {
- context = this;
super.onCreate(savedInstanceState);
setContentView(R.layout.message_list);
- mActionBarProgressView = getLayoutInflater().inflate(R.layout.actionbar_indeterminate_progress_actionview, null);
-
// need this for actionbar initialization
mQueryString = getIntent().getStringExtra(SearchManager.QUERY);
- mPullToRefreshView = (PullToRefreshListView) findViewById(R.id.message_list);
-
- mInflater = getLayoutInflater();
mActionBar = getSupportActionBar();
initializeActionBar();
- initializeLayout();
-
- mPreviewLines = K9.messageListPreviewLines();
-
- initializeMessageList(getIntent(), true);
- mListView.setVerticalFadingEdgeEnabled(false);
// Enable gesture detection for MessageLists
- mGestureDetector = new GestureDetector(new SwipeGestureDetector(this, this));
+ setupGestureDetector(this);
- // Correcting for screen rotation when in ActionMode
- mSelectedCount = getSelectionFromCheckboxes().size();
- if (mSelectedCount > 0) {
- mActionMode = MessageList.this.startActionMode(mActionModeCallback);
- updateActionModeTitle();
+ decodeExtras(getIntent());
+
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ fragmentManager.addOnBackStackChangedListener(this);
+
+ mMessageListFragment = (MessageListFragment) fragmentManager.findFragmentById(R.id.message_list_container);
+
+ if (mMessageListFragment == null) {
+ FragmentTransaction ft = fragmentManager.beginTransaction();
+ if (mQueryString == null) {
+ mMessageListFragment = MessageListFragment.newInstance(mAccount, mFolderName);
+ } else if (mSearchAccount != null) {
+ mMessageListFragment = MessageListFragment.newInstance(mSearchAccount,
+ mSearchFolder, mQueryString, false);
+ } else {
+ mMessageListFragment = MessageListFragment.newInstance(mTitle, mAccountUuids,
+ mFolderNames, mQueryString, mQueryFlags, mForbiddenFlags, mIntegrate);
+ }
+ ft.add(R.id.message_list_container, mMessageListFragment);
+ ft.commit();
}
}
- @Override
- public void onNewIntent(Intent intent) {
- setIntent(intent); // onNewIntent doesn't autoset our "internal" intent
- initializeMessageList(intent, false);
- }
-
- private void initializeMessageList(Intent intent, boolean create) {
+ private void decodeExtras(Intent intent) {
mQueryString = intent.getStringExtra(SearchManager.QUERY);
mFolderName = null;
- mRemoteSearch = false;
mSearchAccount = null;
mSearchFolder = null;
if (mQueryString != null) {
@@ -753,12 +197,10 @@ public class MessageList extends K9ListActivity implements OnItemClickListener,
if (appData != null) {
mSearchAccount = appData.getString(EXTRA_SEARCH_ACCOUNT);
mSearchFolder = appData.getString(EXTRA_SEARCH_FOLDER);
- mRemoteSearch = appData.getBoolean(EXTRA_REMOTE_SEARCH);
}
} else {
mSearchAccount = intent.getStringExtra(EXTRA_SEARCH_ACCOUNT);
mSearchFolder = intent.getStringExtra(EXTRA_SEARCH_FOLDER);
-
}
}
@@ -773,9 +215,6 @@ public class MessageList extends K9ListActivity implements OnItemClickListener,
return;
}
-
-
-
String queryFlags = intent.getStringExtra(EXTRA_QUERY_FLAGS);
if (queryFlags != null) {
String[] flagStrings = queryFlags.split(",");
@@ -802,198 +241,30 @@ public class MessageList extends K9ListActivity implements OnItemClickListener,
if (mFolderName == null && mQueryString == null) {
mFolderName = mAccount.getAutoExpandFolderName();
}
-
- mAdapter = new MessageListAdapter();
- restorePreviousData();
-
- if (mFolderName != null) {
- mCurrentFolder = mAdapter.getFolder(mFolderName, mAccount);
- }
-
- // Hide "Load up to x more" footer for local search views
- mFooterView.setVisibility((mQueryString != null && !mRemoteSearch) ? View.GONE : View.VISIBLE);
-
- mController = MessagingController.getInstance(getApplication());
- mListView.setAdapter(mAdapter);
- }
-
- private void restorePreviousData() {
- final ActivityState previousData = getLastNonConfigurationInstance();
-
- if (previousData != null) {
- mAdapter.restoreMessages(previousData.messages);
- mActiveMessages = previousData.activeMessages;
- }
}
@Override
public void onPause() {
super.onPause();
- mController.removeListener(mAdapter.mListener);
- saveListState();
StorageManager.getInstance(getApplication()).removeListener(mStorageListener);
}
- public void saveListState() {
- mState = new Bundle();
- mState.putInt(EXTRA_LIST_POSITION, mListView.getSelectedItemPosition());
- }
-
- public void restoreListState() {
- if (mState == null) {
- return;
- }
-
- int pos = mState.getInt(EXTRA_LIST_POSITION, ListView.INVALID_POSITION);
-
- if (pos >= mListView.getCount()) {
- pos = mListView.getCount() - 1;
- }
-
- if (pos == ListView.INVALID_POSITION) {
- mListView.setSelected(false);
- } else {
- mListView.setSelection(pos);
- }
- }
-
- /**
- * On resume we refresh messages for the folder that is currently open.
- * This guarantees that things like unread message count and read status
- * are updated.
- */
@Override
public void onResume() {
super.onResume();
- if (mAccount != null && !mAccount.isAvailable(this)) {
- onAccountUnavailable();
- return;
- }
-
-
if (!(this instanceof Search)) {
//necessary b/c no guarantee Search.onStop will be called before MessageList.onResume
//when returning from search results
Search.setActive(false);
}
+ if (mAccount != null && !mAccount.isAvailable(this)) {
+ onAccountUnavailable();
+ return;
+ }
StorageManager.getInstance(getApplication()).addListener(mStorageListener);
- mSenderAboveSubject = K9.messageListSenderAboveSubject();
-
- final Preferences prefs = Preferences.getPreferences(getApplicationContext());
-
- boolean allowRemoteSearch = false;
- if (mSearchAccount != null) {
- final Account searchAccount = prefs.getAccount(mSearchAccount);
- if (searchAccount != null) {
- allowRemoteSearch = searchAccount.allowRemoteSearch();
- }
- }
-
- // Check if we have connectivity. Cache the value.
- if (mHasConnectivity == null) {
- mHasConnectivity = Utility.hasConnectivity(getApplication());
- }
-
- if (mQueryString == null) {
- mPullToRefreshView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener() {
- @Override
- public void onRefresh(PullToRefreshBase refreshView) {
- checkMail(mAccount, mFolderName);
- }
- });
- } else if (allowRemoteSearch && !mRemoteSearch && !mIntegrate && mHasConnectivity) {
- // mQueryString != null is implied if we get this far.
- mPullToRefreshView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener() {
- @Override
- public void onRefresh(PullToRefreshBase refreshView) {
- mPullToRefreshView.onRefreshComplete();
- onRemoteSearchRequested(true);
- }
- });
- mPullToRefreshView.setPullLabel(getString(R.string.pull_to_refresh_remote_search_from_local_search_pull));
- mPullToRefreshView.setReleaseLabel(getString(R.string.pull_to_refresh_remote_search_from_local_search_release));
- } else {
- mPullToRefreshView.setMode(PullToRefreshBase.Mode.DISABLED);
- }
-
- mController.addListener(mAdapter.mListener);
-
- //Cancel pending new mail notifications when we open an account
- Account[] accountsWithNotification;
-
- Account account = getCurrentAccount(prefs);
-
- if (account != null) {
- accountsWithNotification = new Account[] { account };
- mSortType = account.getSortType();
- mSortAscending = account.isSortAscending(mSortType);
- mSortDateAscending = account.isSortAscending(SortType.SORT_DATE);
- } else {
- accountsWithNotification = prefs.getAccounts();
- mSortType = K9.getSortType();
- mSortAscending = K9.isSortAscending(mSortType);
- mSortDateAscending = K9.isSortAscending(SortType.SORT_DATE);
- }
-
- for (Account accountWithNotification : accountsWithNotification) {
- mController.notifyAccountCancel(this, accountWithNotification);
- }
-
-
- if (mAdapter.isEmpty()) {
- if (mRemoteSearch) {
- //TODO: Support flag based search
- mRemoteSearchFuture = mController.searchRemoteMessages(mSearchAccount, mSearchFolder, mQueryString, null, null, mAdapter.mListener);
- } else if (mFolderName != null) {
- mController.listLocalMessages(mAccount, mFolderName, mAdapter.mListener);
- // Hide the archive button if we don't have an archive folder.
- if (!mAccount.hasArchiveFolder()) {
-// mBatchArchiveButton.setVisibility(View.GONE);
- }
- } else if (mQueryString != null) {
- mController.searchLocalMessages(mAccountUuids, mFolderNames, null, mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, mAdapter.mListener);
- // Don't show the archive button if this is a search.
-// mBatchArchiveButton.setVisibility(View.GONE);
- }
- } else {
- // reread the selected date format preference in case it has changed
- mMessageHelper.refresh();
-
- mAdapter.markAllMessagesAsDirty();
-
- new Thread() {
- @Override
- public void run() {
- if (!mRemoteSearch) {
- if (mFolderName != null) {
- mController.listLocalMessagesSynchronous(mAccount, mFolderName, mAdapter.mListener);
- } else if (mQueryString != null) {
- mController.searchLocalMessagesSynchronous(mAccountUuids, mFolderNames, null, mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, mAdapter.mListener);
- }
-
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mAdapter.pruneDirtyMessages();
- mAdapter.notifyDataSetChanged();
- restoreListState();
- }
- });
- }
- }
-
- }
- .start();
- }
-
- if (mAccount != null && mFolderName != null && !mRemoteSearch) {
- mController.getFolderUnreadMessageCount(mAccount, mFolderName, mAdapter.mListener);
- }
-
- refreshTitle();
}
private void initializeActionBar() {
@@ -1012,162 +283,78 @@ public class MessageList extends K9ListActivity implements OnItemClickListener,
mActionBar.setDisplayHomeAsUpEnabled(true);
}
- private void initializeLayout() {
- mListView = mPullToRefreshView.getRefreshableView();
- mListView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
- mListView.setLongClickable(true);
- mListView.setFastScrollEnabled(true);
- mListView.setScrollingCacheEnabled(false);
- mListView.setOnItemClickListener(this);
- mListView.addFooterView(getFooterView(mListView));
-
- registerForContextMenu(mListView);
- }
-
-
- /**
- * Container for values to be kept while the device configuration is
- * modified at runtime (keyboard, orientation, etc.) and Android restarts
- * this activity.
- *
- * @see MessageList#onRetainNonConfigurationInstance()
- * @see MessageList#getLastNonConfigurationInstance()
- */
- static class ActivityState {
- public List messages;
- public List activeMessages;
- }
-
- /* (non-Javadoc)
- *
- * Method overridden for proper typing within this class (the return type is
- * more specific than the super implementation)
- *
- * @see android.app.Activity#onRetainNonConfigurationInstance()
- */
- @Override
- public ActivityState onRetainNonConfigurationInstance() {
- final ActivityState state = new ActivityState();
- state.messages = mAdapter.getMessages();
- state.activeMessages = mActiveMessages;
- return state;
- }
-
- /*
- * (non-Javadoc)
- *
- * Method overridden for proper typing within this class (the return type is
- * more specific than the super implementation)
- *
- * @see android.app.Activity#getLastNonConfigurationInstance()
- */
- @Override
- public ActivityState getLastNonConfigurationInstance() {
- return (ActivityState) super.getLastNonConfigurationInstance();
- }
-
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Shortcuts that work no matter what is selected
switch (keyCode) {
-
- // messagelist is actually a K9Activity, not a K9ListActivity
- // This saddens me greatly, but to support volume key navigation
- // in MessageView, we implement this bit of wrapper code
- case KeyEvent.KEYCODE_VOLUME_UP: {
- if (K9.useVolumeKeysForListNavigationEnabled()) {
- int currentPosition = mListView.getSelectedItemPosition();
- if (currentPosition == AdapterView.INVALID_POSITION || mListView.isInTouchMode()) {
- currentPosition = mListView.getFirstVisiblePosition();
+ case KeyEvent.KEYCODE_VOLUME_UP: {
+ if (K9.useVolumeKeysForListNavigationEnabled()) {
+ mMessageListFragment.onMoveUp();
+ return true;
}
- if (currentPosition > 0) {
- mListView.setSelection(currentPosition - 1);
+ return false;
+ }
+ case KeyEvent.KEYCODE_VOLUME_DOWN: {
+ if (K9.useVolumeKeysForListNavigationEnabled()) {
+ mMessageListFragment.onMoveDown();
+ return true;
}
+ return false;
+ }
+ case KeyEvent.KEYCODE_C: {
+ mMessageListFragment.onCompose();
return true;
}
- return false;
- }
- case KeyEvent.KEYCODE_VOLUME_DOWN: {
- if (K9.useVolumeKeysForListNavigationEnabled()) {
- int currentPosition = mListView.getSelectedItemPosition();
- if (currentPosition == AdapterView.INVALID_POSITION || mListView.isInTouchMode()) {
- currentPosition = mListView.getFirstVisiblePosition();
- }
-
- if (currentPosition < mListView.getCount()) {
- mListView.setSelection(currentPosition + 1);
- }
+ case KeyEvent.KEYCODE_Q: {
+ onShowFolderList();
+ return true;
+ }
+ case KeyEvent.KEYCODE_O: {
+ mMessageListFragment.onCycleSort();
+ return true;
+ }
+ case KeyEvent.KEYCODE_I: {
+ mMessageListFragment.onReverseSort();
+ return true;
+ }
+ case KeyEvent.KEYCODE_H: {
+ Toast toast = Toast.makeText(this, R.string.message_list_help_key, Toast.LENGTH_LONG);
+ toast.show();
return true;
}
- return false;
- }
- case KeyEvent.KEYCODE_C: {
- onCompose();
- return true;
- }
- case KeyEvent.KEYCODE_Q: {
- onShowFolderList();
- return true;
- }
- case KeyEvent.KEYCODE_O: {
- onCycleSort();
- return true;
- }
- case KeyEvent.KEYCODE_I: {
- changeSort(mSortType);
- return true;
- }
- case KeyEvent.KEYCODE_H: {
- Toast toast = Toast.makeText(this, R.string.message_list_help_key, Toast.LENGTH_LONG);
- toast.show();
- return true;
- }
}
- //Shortcuts that only work when a message is selected
boolean retval = true;
- int position = mListView.getSelectedItemPosition();
try {
- if (position >= 0) {
- MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(position);
-
- final List selection = getSelectionFromMessage(message);
-
- if (message != null) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_DEL: {
- onDelete(selection);
- return true;
- }
- case KeyEvent.KEYCODE_S: {
- toggleMessageSelect(message);
- return true;
- }
- case KeyEvent.KEYCODE_D: {
- onDelete(selection);
- return true;
- }
- case KeyEvent.KEYCODE_G: {
- setFlag(selection, Flag.FLAGGED, !message.flagged);
- return true;
- }
- case KeyEvent.KEYCODE_M: {
- onMove(selection);
- return true;
- }
- case KeyEvent.KEYCODE_V: {
- onArchive(selection);
- return true;
- }
- case KeyEvent.KEYCODE_Y: {
- onCopy(selection);
- return true;
- }
- case KeyEvent.KEYCODE_Z: {
- setFlag(selection, Flag.SEEN, !message.read);
- return true;
- }
- }
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DEL:
+ case KeyEvent.KEYCODE_D: {
+ mMessageListFragment.onDelete();
+ return true;
+ }
+ case KeyEvent.KEYCODE_S: {
+ mMessageListFragment.toggleMessageSelect();
+ return true;
+ }
+ case KeyEvent.KEYCODE_G: {
+ mMessageListFragment.onToggleFlag();
+ return true;
+ }
+ case KeyEvent.KEYCODE_M: {
+ mMessageListFragment.onMove();
+ return true;
+ }
+ case KeyEvent.KEYCODE_V: {
+ mMessageListFragment.onArchive();
+ return true;
+ }
+ case KeyEvent.KEYCODE_Y: {
+ mMessageListFragment.onCopy();
+ return true;
+ }
+ case KeyEvent.KEYCODE_Z: {
+ mMessageListFragment.onToggleRead();
+ return true;
}
}
} finally {
@@ -1189,36 +376,6 @@ public class MessageList extends K9ListActivity implements OnItemClickListener,
return super.onKeyUp(keyCode, event);
}
- private void onOpenMessage(MessageInfoHolder message) {
- if (message.folder.name.equals(message.message.getFolder().getAccount().getDraftsFolderName())) {
- MessageCompose.actionEditDraft(this, message.message.getFolder().getAccount(), message.message);
- } else {
- // Need to get the list before the sort starts
- ArrayList messageRefs = new ArrayList();
-
- for (MessageInfoHolder holder : mAdapter.getMessages()) {
- MessageReference ref = holder.message.makeMessageReference();
- messageRefs.add(ref);
- }
-
- MessageReference ref = message.message.makeMessageReference();
- Log.i(K9.LOG_TAG, "MessageList sending message " + ref);
-
- MessageView.actionView(this, ref, messageRefs, getIntent().getExtras());
- }
-
- /*
- * We set read=true here for UI performance reasons. The actual value
- * will get picked up on the refresh when the Activity is resumed but
- * that may take a second or so and we don't want this to show and
- * then go away. I've gone back and forth on this, and this gives a
- * better UI experience, so I am putting it back in.
- */
- if (!message.read) {
- message.read = true;
- }
- }
-
private void onAccounts() {
Accounts.listAccounts(this);
finish();
@@ -1229,34 +386,6 @@ public class MessageList extends K9ListActivity implements OnItemClickListener,
finish();
}
- private void onCompose() {
- if (mQueryString != null) {
- /*
- * If we have a query string, we don't have an account to let
- * compose start the default action.
- */
- MessageCompose.actionCompose(this, null);
- } else {
- MessageCompose.actionCompose(this, mAccount);
- }
- }
-
- private void onReply(MessageInfoHolder holder) {
- MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, false, null);
- }
-
- private void onReplyAll(MessageInfoHolder holder) {
- MessageCompose.actionReply(this, holder.message.getFolder().getAccount(), holder.message, true, null);
- }
-
- private void onForward(MessageInfoHolder holder) {
- MessageCompose.actionForward(this, holder.message.getFolder().getAccount(), holder.message, null);
- }
-
- private void onResendMessage(MessageInfoHolder message) {
- MessageCompose.actionEditDraft(this, message.message.getFolder().getAccount(), message.message);
- }
-
private void onEditPrefs() {
Prefs.actionPrefs(this);
}
@@ -1265,365 +394,80 @@ public class MessageList extends K9ListActivity implements OnItemClickListener,
AccountSettings.actionSettings(this, mAccount);
}
- /**
- * User has requested a remote search. Setup the bundle and start the intent.
- * @param fromLocalSearch true if this is being called from a local search result screen. This affects
- * where we pull the account and folder info used for the next search.
- */
- public void onRemoteSearchRequested(final boolean fromLocalSearch) {
- final Bundle appData = new Bundle();
- if (fromLocalSearch) {
- appData.putString(EXTRA_SEARCH_ACCOUNT, mSearchAccount);
- appData.putString(EXTRA_SEARCH_FOLDER, mSearchFolder);
- } else {
- appData.putString(EXTRA_SEARCH_ACCOUNT, mAccount.getUuid());
- appData.putString(EXTRA_SEARCH_FOLDER, mCurrentFolder.name);
- }
- appData.putBoolean(EXTRA_REMOTE_SEARCH, true);
-
- final Intent intent = new Intent(Intent.ACTION_SEARCH);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(SearchManager.QUERY, mQueryString);
- intent.putExtra(SearchManager.APP_DATA, appData);
- intent.setComponent(new ComponentName(context.getApplicationContext(), MessageList.class));
- context.startActivity(intent);
- }
-
-
@Override
public boolean onSearchRequested() {
- // If this search was started from a MessageList of a single folder, pass along that folder info
- // so that we can enable remote search.
- if (mAccount != null && mCurrentFolder != null) {
- final Bundle appData = new Bundle();
- appData.putString(EXTRA_SEARCH_ACCOUNT, mAccount.getUuid());
- appData.putString(EXTRA_SEARCH_FOLDER, mCurrentFolder.name);
- startSearch(null, false, appData, false);
- } else {
- // TODO Handle the case where we're searching from within a search result.
- startSearch(null, false, null, false);
- }
- return true;
- }
-
- private void changeSort(SortType sortType) {
- Boolean sortAscending = (mSortType == sortType) ? !mSortAscending : null;
- changeSort(sortType, sortAscending);
- }
-
- /**
- * Change the sort type and sort order used for the message list.
- *
- * @param sortType
- * Specifies which field to use for sorting the message list.
- * @param sortAscending
- * Specifies the sort order. If this argument is {@code null} the default search order
- * for the sort type is used.
- */
- // FIXME: Don't save the changes in the UI thread
- private void changeSort(SortType sortType, Boolean sortAscending) {
- mSortType = sortType;
-
- Preferences prefs = Preferences.getPreferences(getApplicationContext());
- Account account = getCurrentAccount(prefs);
-
- if (account != null) {
- account.setSortType(mSortType);
-
- if (sortAscending == null) {
- mSortAscending = account.isSortAscending(mSortType);
- } else {
- mSortAscending = sortAscending;
- }
- account.setSortAscending(mSortType, mSortAscending);
- mSortDateAscending = account.isSortAscending(SortType.SORT_DATE);
-
- account.save(prefs);
- } else {
- K9.setSortType(mSortType);
-
- if (sortAscending == null) {
- mSortAscending = K9.isSortAscending(mSortType);
- } else {
- mSortAscending = sortAscending;
- }
- K9.setSortAscending(mSortType, mSortAscending);
- mSortDateAscending = K9.isSortAscending(SortType.SORT_DATE);
-
- Editor editor = prefs.getPreferences().edit();
- K9.save(editor);
- editor.commit();
- }
-
- reSort();
- }
-
- private void reSort() {
- int toastString = mSortType.getToast(mSortAscending);
-
- Toast toast = Toast.makeText(this, toastString, Toast.LENGTH_SHORT);
- toast.show();
-
- mAdapter.sortMessages();
- }
-
- private void onCycleSort() {
- SortType[] sorts = SortType.values();
- int curIndex = 0;
-
- for (int i = 0; i < sorts.length; i++) {
- if (sorts[i] == mSortType) {
- curIndex = i;
- break;
- }
- }
-
- curIndex++;
-
- if (curIndex == sorts.length) {
- curIndex = 0;
- }
-
- changeSort(sorts[curIndex]);
- }
-
- /**
- * @param holders
- * Never {@code null}.
- */
- private void onDelete(final List holders) {
- final List messagesToRemove = new ArrayList();
- for (MessageInfoHolder holder : holders) {
- messagesToRemove.add(holder.message);
- }
- mAdapter.removeMessages(holders);
- mController.deleteMessages(messagesToRemove.toArray(EMPTY_MESSAGE_ARRAY), null);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode != RESULT_OK) {
- return;
- }
-
- switch (requestCode) {
- case ACTIVITY_CHOOSE_FOLDER_MOVE:
- case ACTIVITY_CHOOSE_FOLDER_COPY: {
- if (data == null) {
- return;
- }
-
- final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER);
- final List holders = mActiveMessages;
-
- if (destFolderName != null) {
-
- mActiveMessages = null; // don't need it any more
-
- final Account account = holders.get(0).message.getFolder().getAccount();
- account.setLastSelectedFolderName(destFolderName);
-
- switch (requestCode) {
- case ACTIVITY_CHOOSE_FOLDER_MOVE:
- move(holders, destFolderName);
- break;
-
- case ACTIVITY_CHOOSE_FOLDER_COPY:
- copy(holders, destFolderName);
- break;
- }
- }
- break;
- }
- }
- }
-
-
- private void onExpunge(final Account account, String folderName) {
- mController.expunge(account, folderName, null);
- }
-
- @Override
- public Dialog onCreateDialog(int id) {
- switch (id) {
- case R.id.dialog_confirm_spam:
- return ConfirmationDialog.create(this, id,
- R.string.dialog_confirm_spam_title,
- "" /* text is refreshed by #onPrepareDialog(int, Dialog) below */,
- R.string.dialog_confirm_spam_confirm_button,
- R.string.dialog_confirm_spam_cancel_button,
- new Runnable() {
- @Override
- public void run() {
- onSpamConfirmed(mActiveMessages);
- // No further need for this reference
- mActiveMessages = null;
- }
- }, new Runnable() {
- @Override
- public void run() {
- // event for cancel, we don't need this reference any more
- mActiveMessages = null;
- }
- });
- }
-
- return super.onCreateDialog(id);
- }
-
- /*
- * (non-Javadoc)
- *
- * Android happens to invoke this method even if the given dialog is not
- * shown (eg. a dismissed dialog) as part of the automatic activity
- * reloading following a configuration change (orientation, keyboard,
- * locale, etc.).
- */
- @Override
- public void onPrepareDialog(final int id, final Dialog dialog) {
- switch (id) {
- case R.id.dialog_confirm_spam: {
- // mActiveMessages can be null if Android restarts the activity
- // while this dialog is not actually shown (but was displayed at
- // least once)
- if (mActiveMessages != null) {
- final int selectionSize = mActiveMessages.size();
- final String message;
- message = getResources().getQuantityString(R.plurals.dialog_confirm_spam_message, selectionSize,
- Integer.valueOf(selectionSize));
- ((AlertDialog) dialog).setMessage(message);
- }
- break;
- }
- default: {
- super.onPrepareDialog(id, dialog);
- }
- }
- }
-
- private void onToggleFlag(MessageInfoHolder messageInfo) {
- Message message = messageInfo.message;
- Folder folder = message.getFolder();
- Account account = folder.getAccount();
- String folderName = folder.getName();
-
- mController.setFlag(account, folderName, new Message[] { message }, Flag.FLAGGED,
- !messageInfo.flagged);
-
- messageInfo.flagged = !messageInfo.flagged;
- mAdapter.sortMessages();
-
- computeBatchDirection();
- }
-
- private void checkMail(Account account, String folderName) {
- mController.synchronizeMailbox(account, folderName, mAdapter.mListener, null);
- mController.sendPendingMessages(account, mAdapter.mListener);
- }
-
- /**
- * We need to do some special clean up when leaving a remote search result screen. If no remote search is
- * in progress, this method does nothing special.
- */
- @Override
- public void onBackPressed() {
- // If we represent a remote search, then kill that before going back.
- if (mSearchAccount != null && mSearchFolder != null && mRemoteSearchFuture != null) {
- try {
- Log.i(K9.LOG_TAG, "Remote search in progress, attempting to abort...");
- // Canceling the future stops any message fetches in progress.
- final boolean cancelSuccess = mRemoteSearchFuture.cancel(true); // mayInterruptIfRunning = true
- if (!cancelSuccess) {
- Log.e(K9.LOG_TAG, "Could not cancel remote search future.");
- }
- // Closing the folder will kill off the connection if we're mid-search.
- final Account searchAccount = Preferences.getPreferences(this).getAccount(mSearchAccount);
- final Store remoteStore = searchAccount.getRemoteStore();
- final Folder remoteFolder = remoteStore.getFolder(mSearchFolder);
- remoteFolder.close();
- // Send a remoteSearchFinished() message for good measure.
- mAdapter.mListener.remoteSearchFinished(searchAccount, mSearchFolder, 0, null);
- } catch (Exception e) {
- // Since the user is going back, log and squash any exceptions.
- Log.e(K9.LOG_TAG, "Could not abort remote search before going back", e);
- }
- }
- super.onBackPressed();
+ return mMessageListFragment.onSearchRequested();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
switch (itemId) {
- case android.R.id.home: {
- if (mIntegrate) {
- // If we were in one of the integrated mailboxes (think All Mail or Integrated Inbox), then
- // go to accounts.
- onAccounts();
- } else if (mQueryString != null) {
- // We did a search of some sort. Go back to wherever the user searched from.
- onBackPressed();
- } else {
- // In a standard message list of a folder. Go to folder list.
- onShowFolderList();
+ case android.R.id.home: {
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ if (fragmentManager.getBackStackEntryCount() > 0) {
+ fragmentManager.popBackStack();
+ } else if (mIntegrate) {
+ // If we were in one of the integrated mailboxes (think All Mail or Integrated Inbox), then
+ // go to accounts.
+ onAccounts();
+ } else if (mQueryString != null) {
+ // We did a search of some sort. Go back to wherever the user searched from.
+ onBackPressed();
+ } else {
+ // In a standard message list of a folder. Go to folder list.
+ onShowFolderList();
+ }
+ return true;
}
- return true;
- }
- case R.id.compose: {
- onCompose();
- return true;
- }
- case R.id.check_mail: {
- checkMail(mAccount, mFolderName);
- return true;
- }
- case R.id.set_sort_date: {
- changeSort(SortType.SORT_DATE);
- return true;
- }
- case R.id.set_sort_arrival: {
- changeSort(SortType.SORT_ARRIVAL);
- return true;
- }
- case R.id.set_sort_subject: {
- changeSort(SortType.SORT_SUBJECT);
- return true;
- }
- case R.id.set_sort_sender: {
- changeSort(SortType.SORT_SENDER);
- return true;
- }
- case R.id.set_sort_flag: {
- changeSort(SortType.SORT_FLAGGED);
- return true;
- }
- case R.id.set_sort_unread: {
- changeSort(SortType.SORT_UNREAD);
- return true;
- }
- case R.id.set_sort_attach: {
- changeSort(SortType.SORT_ATTACHMENT);
- return true;
- }
- case R.id.select_all: {
- setSelectionState(true);
- return true;
- }
- case R.id.app_settings: {
- onEditPrefs();
- return true;
- }
- case R.id.search_remote: {
- // Remote search is useless without the network.
- if (mHasConnectivity) {
- onRemoteSearchRequested(true);
- } else {
- final Toast unavailableToast =
- Toast.makeText(context, getText(R.string.remote_search_unavailable_no_network), Toast.LENGTH_SHORT);
- unavailableToast.show();
+ case R.id.compose: {
+ mMessageListFragment.onCompose();
+ return true;
+ }
+ case R.id.check_mail: {
+ mMessageListFragment.checkMail();
+ return true;
+ }
+ case R.id.set_sort_date: {
+ mMessageListFragment.changeSort(SortType.SORT_DATE);
+ return true;
+ }
+ case R.id.set_sort_arrival: {
+ mMessageListFragment.changeSort(SortType.SORT_ARRIVAL);
+ return true;
+ }
+ case R.id.set_sort_subject: {
+ mMessageListFragment.changeSort(SortType.SORT_SUBJECT);
+ return true;
+ }
+ case R.id.set_sort_sender: {
+ mMessageListFragment.changeSort(SortType.SORT_SENDER);
+ return true;
+ }
+ case R.id.set_sort_flag: {
+ mMessageListFragment.changeSort(SortType.SORT_FLAGGED);
+ return true;
+ }
+ case R.id.set_sort_unread: {
+ mMessageListFragment.changeSort(SortType.SORT_UNREAD);
+ return true;
+ }
+ case R.id.set_sort_attach: {
+ mMessageListFragment.changeSort(SortType.SORT_ATTACHMENT);
+ return true;
+ }
+ case R.id.select_all: {
+ mMessageListFragment.selectAll();
+ return true;
+ }
+ case R.id.app_settings: {
+ onEditPrefs();
+ return true;
+ }
+ case R.id.search_remote: {
+ mMessageListFragment.onRemoteSearch();
+ return true;
}
- return true;
- }
}
if (mQueryString != null) {
@@ -1633,1425 +477,94 @@ public class MessageList extends K9ListActivity implements OnItemClickListener,
}
switch (itemId) {
- case R.id.send_messages: {
- mController.sendPendingMessages(mAccount, mAdapter.mListener);
- return true;
- }
- case R.id.folder_settings: {
- if (mFolderName != null) {
- FolderSettings.actionSettings(this, mAccount, mFolderName);
+ case R.id.send_messages: {
+ mMessageListFragment.onSendPendingMessages();
+ return true;
}
- return true;
- }
- case R.id.account_settings: {
- onEditAccount();
- return true;
- }
- case R.id.expunge: {
- if (mCurrentFolder != null) {
- onExpunge(mAccount, mCurrentFolder.name);
+ case R.id.folder_settings: {
+ if (mFolderName != null) {
+ FolderSettings.actionSettings(this, mAccount, mFolderName);
+ }
+ return true;
+ }
+ case R.id.account_settings: {
+ onEditAccount();
+ return true;
+ }
+ case R.id.expunge: {
+ mMessageListFragment.onExpunge();
+ return true;
+ }
+ default: {
+ return super.onOptionsItemSelected(item);
}
- return true;
- }
- default: {
- return super.onOptionsItemSelected(item);
- }
}
}
- @Override
- public boolean onContextItemSelected(android.view.MenuItem item) {
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
- final MessageInfoHolder message = (MessageInfoHolder) mListView.getItemAtPosition(info.position);
-
-
- final List selection = getSelectionFromMessage(message);
- switch (item.getItemId()) {
- case R.id.reply: {
- onReply(message);
- break;
- }
- case R.id.reply_all: {
- onReplyAll(message);
- break;
- }
- case R.id.forward: {
- onForward(message);
- break;
- }
- case R.id.send_again: {
- onResendMessage(message);
- mSelectedCount = 0;
- break;
- }
- case R.id.same_sender: {
- MessageList.actionHandle(MessageList.this, "From " + message.sender,
- message.senderAddress, false, null, null);
- break;
- }
- case R.id.delete: {
- onDelete(selection);
- break;
- }
- case R.id.mark_as_read: {
- setFlag(selection, Flag.SEEN, true);
- break;
- }
- case R.id.mark_as_unread: {
- setFlag(selection, Flag.SEEN, false);
- break;
- }
- case R.id.flag: {
- setFlag(selection, Flag.FLAGGED, true);
- break;
- }
- case R.id.unflag: {
- setFlag(selection, Flag.FLAGGED, false);
- break;
- }
-
- // only if the account supports this
- case R.id.archive: {
- onArchive(selection);
- break;
- }
- case R.id.spam: {
- onSpam(selection);
- break;
- }
- case R.id.move: {
- onMove(selection);
- break;
- }
- case R.id.copy: {
- onCopy(selection);
- break;
- }
- }
-
- return true;
- }
-
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
-
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
- MessageInfoHolder message = (MessageInfoHolder) mListView.getItemAtPosition(info.position);
-
- if (message == null) {
- return;
- }
-
- getMenuInflater().inflate(R.menu.message_list_item_context, menu);
-
- menu.setHeaderTitle(message.message.getSubject());
-
- if (message.read) {
- menu.findItem(R.id.mark_as_read).setVisible(false);
- } else {
- menu.findItem(R.id.mark_as_unread).setVisible(false);
- }
-
- if (message.flagged) {
- menu.findItem(R.id.flag).setVisible(false);
- } else {
- menu.findItem(R.id.unflag).setVisible(false);
- }
-
- Account account = message.message.getFolder().getAccount();
- if (!mController.isCopyCapable(account)) {
- menu.findItem(R.id.copy).setVisible(false);
- }
-
- if (!mController.isMoveCapable(account)) {
- menu.findItem(R.id.move).setVisible(false);
- menu.findItem(R.id.archive).setVisible(false);
- menu.findItem(R.id.spam).setVisible(false);
- }
-
- if (!account.hasArchiveFolder()) {
- menu.findItem(R.id.archive).setVisible(false);
- }
-
- if (!account.hasSpamFolder()) {
- menu.findItem(R.id.spam).setVisible(false);
- }
-
- }
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
getSupportMenuInflater().inflate(R.menu.message_list_option, menu);
- mRefreshMenuItem = menu.findItem(R.id.check_mail);
+ mMenu = menu;
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
+ configureMenu(menu);
+ return true;
+ }
- if (mQueryString != null || mIntegrate) {
- menu.findItem(R.id.expunge).setVisible(false);
+ private void configureMenu(Menu menu) {
+ if (menu == null) {
+ return;
+ }
+
+ menu.findItem(R.id.search_remote).setVisible(false);
+
+ if (mMessageListFragment == null) {
+ // Hide everything (except "compose") if no MessageListFragment instance is available
menu.findItem(R.id.check_mail).setVisible(false);
+ menu.findItem(R.id.set_sort).setVisible(false);
+ menu.findItem(R.id.select_all).setVisible(false);
menu.findItem(R.id.send_messages).setVisible(false);
- menu.findItem(R.id.folder_settings).setVisible(false);
- menu.findItem(R.id.account_settings).setVisible(false);
- // If this is an explicit local search, show the option to search the cloud.
-
- final Preferences prefs = Preferences.getPreferences(getApplicationContext());
-
- boolean allowRemoteSearch = false;
- if (mSearchAccount != null) {
- final Account searchAccount = prefs.getAccount(mSearchAccount);
- if (searchAccount != null) {
- allowRemoteSearch = searchAccount.allowRemoteSearch();
- }
- }
-
- if (allowRemoteSearch && mQueryString != null && !mRemoteSearch && !mIntegrate && mSearchFolder != null) {
- menu.findItem(R.id.search_remote).setVisible(true);
- }
+ menu.findItem(R.id.expunge).setVisible(false);
+ menu.findItem(R.id.settings).setVisible(false);
} else {
- if (mCurrentFolder != null && mCurrentFolder.name.equals(mAccount.getOutboxFolderName())) {
+ menu.findItem(R.id.set_sort).setVisible(true);
+ menu.findItem(R.id.select_all).setVisible(true);
+ menu.findItem(R.id.settings).setVisible(true);
+
+ if (mMessageListFragment.isSearchQuery()) {
+ menu.findItem(R.id.expunge).setVisible(false);
menu.findItem(R.id.check_mail).setVisible(false);
- } else {
menu.findItem(R.id.send_messages).setVisible(false);
- }
+ menu.findItem(R.id.folder_settings).setVisible(false);
+ menu.findItem(R.id.account_settings).setVisible(false);
- if (mCurrentFolder != null && K9.ERROR_FOLDER_NAME.equals(mCurrentFolder.name)) {
- menu.findItem(R.id.expunge).setVisible(false);
- }
+ // If this is an explicit local search, show the option to search the cloud.
+ if (!mMessageListFragment.isRemoteSearch() &&
+ mMessageListFragment.isRemoteSearchAllowed()) {
+ menu.findItem(R.id.search_remote).setVisible(true);
+ }
- if (!mController.isMoveCapable(mAccount)) {
- // FIXME: Really we want to do this for all local-only folders
- if (mCurrentFolder != null &&
- !mAccount.getInboxFolderName().equals(mCurrentFolder.name)) {
+ } else {
+ menu.findItem(R.id.folder_settings).setVisible(true);
+ menu.findItem(R.id.account_settings).setVisible(true);
+
+ if (mMessageListFragment.isOutbox()) {
+ menu.findItem(R.id.send_messages).setVisible(true);
+ } else {
+ menu.findItem(R.id.send_messages).setVisible(false);
+ }
+
+ if (mMessageListFragment.isRemoteFolder()) {
+ menu.findItem(R.id.check_mail).setVisible(true);
+ menu.findItem(R.id.expunge).setVisible(mMessageListFragment.isAccountExpungeCapable());
+ } else {
menu.findItem(R.id.check_mail).setVisible(false);
- }
- menu.findItem(R.id.expunge).setVisible(false);
- }
- }
-
- return true;
- }
-
- @Override
- public void onSwipeRightToLeft(final MotionEvent e1, final MotionEvent e2) {
- // Handle right-to-left as an un-select
- handleSwipe(e1, false);
- }
-
- @Override
- public void onSwipeLeftToRight(final MotionEvent e1, final MotionEvent e2) {
- // Handle left-to-right as a select.
- handleSwipe(e1, true);
- }
-
- /**
- * Handle a select or unselect swipe event
- * @param downMotion Event that started the swipe
- * @param selected true if this was an attempt to select (i.e. left to right).
- */
- private void handleSwipe(final MotionEvent downMotion, final boolean selected) {
- int[] listPosition = new int[2];
- mListView.getLocationOnScreen(listPosition);
- int position = mListView.pointToPosition((int) downMotion.getRawX() - listPosition[0], (int) downMotion.getRawY() - listPosition[1]);
- if (position != AdapterView.INVALID_POSITION) {
- final MessageInfoHolder message = (MessageInfoHolder) mListView.getItemAtPosition(position);
- toggleMessageSelect(message);
- }
- }
-
- class MessageListAdapter extends BaseAdapter {
- private final List mMessages =
- Collections.synchronizedList(new ArrayList());
-
- public List mExtraSearchResults;
-
- private final ActivityListener mListener = new ActivityListener() {
-
- @Override
- public void remoteSearchAddMessage(Account account, String folderName, Message message, final int numDone, final int numTotal) {
-
- if (numTotal > 0 && numDone < numTotal) {
- setSupportProgress(Window.PROGRESS_END / numTotal * numDone);
- } else {
- setSupportProgress(Window.PROGRESS_END);
- }
-
- mHandler.addOrUpdateMessages(account, folderName, Collections.singletonList(message), false);
- }
-
- @Override
- public void remoteSearchFailed(Account acct, String folder, final String err) {
- //TODO: Better error handling
- runOnUiThread(new Runnable() {
- public void run() {
- Toast.makeText(getApplication(), err, Toast.LENGTH_LONG).show();
- }
- });
- }
-
- @Override
- public void remoteSearchStarted(Account acct, String folder) {
- mHandler.progress(true);
- mHandler.updateFooter(getString(R.string.remote_search_sending_query), true);
- }
-
-
- @Override
- public void remoteSearchFinished(Account acct, String folder, int numResults, List extraResults) {
- mHandler.progress(false);
- if (extraResults != null && extraResults.size() > 0) {
- mExtraSearchResults = extraResults;
- mHandler.updateFooter(String.format(getString(R.string.load_more_messages_fmt), acct.getRemoteSearchNumResults()), false);
- } else {
- mHandler.updateFooter("", false);
- }
- setSupportProgress(Window.PROGRESS_END);
-
- }
-
- @Override
- public void remoteSearchServerQueryComplete(Account account, String folderName, int numResults) {
- mHandler.progress(true);
- if (account != null && account.getRemoteSearchNumResults() != 0 && numResults > account.getRemoteSearchNumResults()) {
- mHandler.updateFooter(getString(R.string.remote_search_downloading_limited, account.getRemoteSearchNumResults(), numResults), true);
- } else {
- mHandler.updateFooter(getString(R.string.remote_search_downloading, numResults), true);
- }
- setSupportProgress(Window.PROGRESS_START);
- }
-
- @Override
- public void informUserOfStatus() {
- mHandler.refreshTitle();
- }
-
- @Override
- public void synchronizeMailboxStarted(Account account, String folder) {
- if (updateForMe(account, folder)) {
- mHandler.progress(true);
- mHandler.folderLoading(folder, true);
- }
- super.synchronizeMailboxStarted(account, folder);
- }
-
- @Override
- public void synchronizeMailboxFinished(Account account, String folder,
- int totalMessagesInMailbox, int numNewMessages) {
-
- if (updateForMe(account, folder)) {
- mHandler.progress(false);
- mHandler.folderLoading(folder, false);
- mHandler.sortMessages();
- }
- super.synchronizeMailboxFinished(account, folder, totalMessagesInMailbox, numNewMessages);
- }
-
- @Override
- public void synchronizeMailboxFailed(Account account, String folder, String message) {
-
- if (updateForMe(account, folder)) {
- mHandler.progress(false);
- mHandler.folderLoading(folder, false);
- mHandler.sortMessages();
- }
- super.synchronizeMailboxFailed(account, folder, message);
- }
-
- @Override
- public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder, Message message) {
- mHandler.addOrUpdateMessages(account, folder, Collections.singletonList(message), true);
- }
-
- @Override
- public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) {
- mHandler.removeMessage(message.makeMessageReference());
- }
-
- @Override
- public void listLocalMessagesStarted(Account account, String folder) {
- if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
- mHandler.progress(true);
- if (folder != null) {
- mHandler.folderLoading(folder, true);
- }
+ menu.findItem(R.id.expunge).setVisible(false);
}
}
-
- @Override
- public void listLocalMessagesFailed(Account account, String folder, String message) {
- if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
- mHandler.sortMessages();
- mHandler.progress(false);
- if (folder != null) {
- mHandler.folderLoading(folder, false);
- }
- }
- }
-
- @Override
- public void listLocalMessagesFinished(Account account, String folder) {
- if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
- mHandler.sortMessages();
- mHandler.progress(false);
- if (folder != null) {
- mHandler.folderLoading(folder, false);
- }
- }
- }
-
- @Override
- public void listLocalMessagesRemoveMessage(Account account, String folder, Message message) {
- mHandler.removeMessage(message.makeMessageReference());
- }
-
- @Override
- public void listLocalMessagesAddMessages(Account account, String folder, List messages) {
- mHandler.addOrUpdateMessages(account, folder, messages, false);
- }
-
- @Override
- public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {
- mHandler.addOrUpdateMessages(account, folder, Collections.singletonList(message), false);
- }
-
- @Override
- public void searchStats(AccountStats stats) {
- mUnreadMessageCount = stats.unreadMessageCount;
- super.searchStats(stats);
- }
-
- @Override
- public void folderStatusChanged(Account account, String folder, int unreadMessageCount) {
- if (updateForMe(account, folder)) {
- mUnreadMessageCount = unreadMessageCount;
- }
- super.folderStatusChanged(account, folder, unreadMessageCount);
- }
-
- @Override
- public void messageUidChanged(Account account, String folder, String oldUid, String newUid) {
- MessageReference ref = new MessageReference();
- ref.accountUuid = account.getUuid();
- ref.folderName = folder;
- ref.uid = oldUid;
-
- mHandler.changeMessageUid(ref, newUid);
- }
- };
-
- private boolean updateForMe(Account account, String folder) {
- if ((account.equals(mAccount) && mFolderName != null && folder.equals(mFolderName))) {
- return true;
- } else {
- return false;
- }
- }
-
- public List getMessages() {
- return mMessages;
- }
-
- public void restoreMessages(List messages) {
- mMessages.addAll(messages);
- }
-
- private Drawable mAttachmentIcon;
- private Drawable mForwardedIcon;
- private Drawable mAnsweredIcon;
- private Drawable mForwardedAnsweredIcon;
-
- MessageListAdapter() {
- mAttachmentIcon = getResources().getDrawable(R.drawable.ic_email_attachment_small);
- mAnsweredIcon = getResources().getDrawable(R.drawable.ic_email_answered_small);
- mForwardedIcon = getResources().getDrawable(R.drawable.ic_email_forwarded_small);
- mForwardedAnsweredIcon = getResources().getDrawable(R.drawable.ic_email_forwarded_answered_small);
- }
-
- public void markAllMessagesAsDirty() {
- for (MessageInfoHolder holder : mMessages) {
- holder.dirty = true;
- }
- }
-
- public void pruneDirtyMessages() {
- List messagesToRemove = new ArrayList();
-
- for (MessageInfoHolder holder : mMessages) {
- if (holder.dirty) {
- messagesToRemove.add(holder);
- }
- }
- removeMessages(messagesToRemove);
- }
-
- public void removeMessage(MessageReference messageReference) {
- MessageInfoHolder holder = getMessage(messageReference);
- if (holder == null) {
- Log.w(K9.LOG_TAG, "Got callback to remove non-existent message with UID " +
- messageReference.uid);
- } else {
- removeMessages(Collections.singletonList(holder));
- }
- }
-
- public void removeMessages(final List messages) {
- if (messages.isEmpty()) {
- return;
- }
-
- for (MessageInfoHolder message : messages) {
- if (message != null && (mFolderName == null || (
- message.folder != null &&
- message.folder.name.equals(mFolderName)))) {
- if (message.selected && mSelectedCount > 0) {
- mSelectedCount--;
- }
- mMessages.remove(message);
- }
- }
- resetUnreadCount();
-
- notifyDataSetChanged();
- computeSelectAllVisibility();
- }
-
- /**
- * Set the selection state for all messages at once.
- * @param selected Selection state to set.
- */
- public void setSelectionForAllMesages(final boolean selected) {
- for (MessageInfoHolder message : mMessages) {
- message.selected = selected;
- }
-
- notifyDataSetChanged();
- }
-
- public void addMessages(final List messages) {
- if (messages.isEmpty()) {
- return;
- }
-
- final boolean wasEmpty = mMessages.isEmpty();
-
- for (final MessageInfoHolder message : messages) {
- if (mFolderName == null || (message.folder != null && message.folder.name.equals(mFolderName))) {
- int index = Collections.binarySearch(mMessages, message, getComparator());
-
- if (index < 0) {
- index = (index * -1) - 1;
- }
-
- mMessages.add(index, message);
- }
- }
-
- if (wasEmpty) {
- mListView.setSelection(0);
- }
- resetUnreadCount();
-
- notifyDataSetChanged();
- computeSelectAllVisibility();
- }
-
- public void changeMessageUid(MessageReference ref, String newUid) {
- MessageInfoHolder holder = getMessage(ref);
- if (holder != null) {
- holder.uid = newUid;
- holder.message.setUid(newUid);
- }
- }
-
- public void resetUnreadCount() {
- if (mQueryString != null) {
- int unreadCount = 0;
-
- for (MessageInfoHolder holder : mMessages) {
- unreadCount += holder.read ? 0 : 1;
- }
-
- mUnreadMessageCount = unreadCount;
- refreshTitle();
- }
- }
-
- public void sortMessages() {
- final Comparator chainComparator = getComparator();
-
- Collections.sort(mMessages, chainComparator);
-
- notifyDataSetChanged();
- }
-
- public void addOrUpdateMessages(final Account account, final String folderName,
- final List messages, final boolean verifyAgainstSearch) {
-
- boolean needsSort = false;
- final List messagesToAdd = new ArrayList();
- List messagesToRemove = new ArrayList();
- List messagesToSearch = new ArrayList();
-
- // cache field into local variable for faster access for JVM without JIT
- final MessageHelper messageHelper = mMessageHelper;
-
- for (Message message : messages) {
- MessageInfoHolder m = getMessage(message);
- if (message.isSet(Flag.DELETED)) {
- if (m != null) {
- messagesToRemove.add(m);
- }
- } else {
- final Folder messageFolder = message.getFolder();
- final Account messageAccount = messageFolder.getAccount();
- if (m == null) {
- if (updateForMe(account, folderName)) {
- m = new MessageInfoHolder();
- FolderInfoHolder folderInfoHolder = new FolderInfoHolder(
- MessageList.this, messageFolder, messageAccount);
- messageHelper.populate(m, message, folderInfoHolder, messageAccount);
- messagesToAdd.add(m);
- } else {
- if (mQueryString != null) {
- if (verifyAgainstSearch) {
- messagesToSearch.add(message);
- } else {
- m = new MessageInfoHolder();
- FolderInfoHolder folderInfoHolder = new FolderInfoHolder(
- MessageList.this, messageFolder, messageAccount);
- messageHelper.populate(m, message, folderInfoHolder,
- messageAccount);
- messagesToAdd.add(m);
- }
- }
- }
- } else {
- m.dirty = false; // as we reload the message, unset its dirty flag
- FolderInfoHolder folderInfoHolder = new FolderInfoHolder(MessageList.this,
- messageFolder, account);
- messageHelper.populate(m, message, folderInfoHolder, account);
- needsSort = true;
- }
- }
- }
-
- if (!messagesToSearch.isEmpty()) {
- mController.searchLocalMessages(mAccountUuids, mFolderNames,
- messagesToSearch.toArray(EMPTY_MESSAGE_ARRAY), mQueryString, mIntegrate,
- mQueryFlags, mForbiddenFlags,
- new MessagingListener() {
- @Override
- public void listLocalMessagesAddMessages(Account account, String folder,
- List messages) {
- mHandler.addOrUpdateMessages(account, folder, messages, false);
- }
- });
- }
-
- if (!messagesToRemove.isEmpty()) {
- removeMessages(messagesToRemove);
- }
-
- if (!messagesToAdd.isEmpty()) {
- addMessages(messagesToAdd);
- }
-
- if (needsSort) {
- sortMessages();
- resetUnreadCount();
- }
- }
-
- /**
- * Find a specific message in the message list.
- *
- *
Note:
- * This method was optimized because it is called a lot. Don't change it unless you know
- * what you are doing.
- *
- * @param message
- * A {@link Message} instance describing the message to look for.
- *
- * @return The corresponding {@link MessageInfoHolder} instance if the message was found in
- * the message list. {@code null} otherwise.
- */
- private MessageInfoHolder getMessage(Message message) {
- String uid;
- Folder folder;
- for (MessageInfoHolder holder : mMessages) {
- uid = message.getUid();
- if (uid != null && (holder.uid == uid || uid.equals(holder.uid))) {
- folder = message.getFolder();
- if (holder.folder.name.equals(folder.getName()) &&
- holder.account.equals(folder.getAccount().getUuid())) {
- return holder;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Find a specific message in the message list.
- *
- *
Note:
- * This method was optimized because it is called a lot. Don't change it unless you know
- * what you are doing.
- *
- * @param messageReference
- * A {@link MessageReference} instance describing the message to look for.
- *
- * @return The corresponding {@link MessageInfoHolder} instance if the message was found in
- * the message list. {@code null} otherwise.
- */
- private MessageInfoHolder getMessage(MessageReference messageReference) {
- String uid;
- for (MessageInfoHolder holder : mMessages) {
- uid = messageReference.uid;
- if ((holder.uid == uid || uid.equals(holder.uid)) &&
- holder.folder.name.equals(messageReference.folderName) &&
- holder.account.equals(messageReference.accountUuid)) {
- return holder;
- }
- }
-
- return null;
- }
-
- public FolderInfoHolder getFolder(String folder, Account account) {
- LocalFolder local_folder = null;
- try {
- LocalStore localStore = account.getLocalStore();
- local_folder = localStore.getFolder(folder);
- return new FolderInfoHolder(context, local_folder, account);
- } catch (Exception e) {
- Log.e(K9.LOG_TAG, "getFolder(" + folder + ") goes boom: ", e);
- return null;
- } finally {
- if (local_folder != null) {
- local_folder.close();
- }
- }
- }
-
-
- @Override
- public int getCount() {
- return mMessages.size();
- }
-
- @Override
- public long getItemId(int position) {
- try {
- MessageInfoHolder messageHolder = (MessageInfoHolder) getItem(position);
- if (messageHolder != null) {
- return messageHolder.message.getId();
- }
- } catch (Exception e) {
- Log.i(K9.LOG_TAG, "getItemId(" + position + ") ", e);
- }
- return -1;
- }
-
- @Override
- public Object getItem(int position) {
- try {
- if (position < mMessages.size()) {
- return mMessages.get(position);
- }
- } catch (Exception e) {
- Log.e(K9.LOG_TAG, "getItem(" + position + "), but folder.messages.size() = " + mMessages.size(), e);
- }
- return null;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- MessageInfoHolder message = (MessageInfoHolder) getItem(position);
- View view;
-
- if ((convertView != null) && (convertView.getId() == R.layout.message_list_item)) {
- view = convertView;
- } else {
- view = mInflater.inflate(R.layout.message_list_item, parent, false);
- view.setId(R.layout.message_list_item);
- }
-
- MessageViewHolder holder = (MessageViewHolder) view.getTag();
-
- if (holder == null) {
- holder = new MessageViewHolder();
- holder.date = (TextView) view.findViewById(R.id.date);
- holder.chip = view.findViewById(R.id.chip);
- holder.preview = (TextView) view.findViewById(R.id.preview);
-
- if (mSenderAboveSubject) {
- holder.from = (TextView) view.findViewById(R.id.subject);
- holder.from.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListSender());
- } else {
- holder.subject = (TextView) view.findViewById(R.id.subject);
- holder.subject.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListSubject());
- }
-
- holder.date.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListDate());
-
- holder.preview.setLines(mPreviewLines);
- holder.preview.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getMessageListPreview());
-
- view.setTag(holder);
- }
-
- if (message != null) {
- bindView(position, view, holder, message);
- } else {
- // This branch code is triggered when the local store
- // hands us an invalid message
-
- holder.chip.getBackground().setAlpha(0);
- if (holder.subject != null) {
- holder.subject.setText(getString(R.string.general_no_subject));
- holder.subject.setTypeface(null, Typeface.NORMAL);
- }
-
- String noSender = getString(R.string.general_no_sender);
-
- if (holder.preview != null) {
- holder.preview.setText(noSender, TextView.BufferType.SPANNABLE);
- Spannable str = (Spannable) holder.preview.getText();
-
- str.setSpan(new StyleSpan(Typeface.NORMAL),
- 0,
- noSender.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- str.setSpan(new AbsoluteSizeSpan(mFontSizes.getMessageListSender(), true),
- 0,
- noSender.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- } else {
- holder.from.setText(noSender);
- holder.from.setTypeface(null, Typeface.NORMAL);
- holder.from.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
- }
-
- holder.date.setText(getString(R.string.general_no_date));
-
- //WARNING: Order of the next 2 lines matter
- holder.position = -1;
- }
-
-
- return view;
- }
-
- /**
- * Associate model data to view object.
- *
- * @param position
- * The position of the item within the adapter's data set of
- * the item whose view we want.
- * @param view
- * Main view component to alter. Never null.
- * @param holder
- * Convenience view holder - eases access to view
- * child views. Never null.
- * @param message
- * Never null.
- */
-
-
-
- private void bindView(final int position, final View view, final MessageViewHolder holder,
- final MessageInfoHolder message) {
-
- int maybeBoldTypeface = message.read ? Typeface.NORMAL : Typeface.BOLD;
-
- // So that the mSelectedCount is only incremented/decremented
- // when a user checks the checkbox (vs code)
- holder.position = -1;
-
- if (message.selected) {
-
- holder.chip.setBackgroundDrawable(message.message.getFolder().getAccount().getCheckmarkChip().drawable());
- }
-
- else {
- holder.chip.setBackgroundDrawable(message.message.getFolder().getAccount().generateColorChip(message.read,message.message.toMe(), message.message.ccMe(), message.message.fromMe(), message.flagged).drawable());
-
- }
-
- if (K9.useBackgroundAsUnreadIndicator()) {
- int res = (message.read) ? R.attr.messageListReadItemBackgroundColor :
- R.attr.messageListUnreadItemBackgroundColor;
-
- TypedValue outValue = new TypedValue();
- getTheme().resolveAttribute(res, outValue, true);
- view.setBackgroundColor(outValue.data);
- }
-
- String subject = null;
-
- if ((message.message.getSubject() == null) || message.message.getSubject().equals("")) {
- subject = (String) getText(R.string.general_no_subject);
-
- } else {
- subject = message.message.getSubject();
- }
-
- // We'll get badge support soon --jrv
-// if (holder.badge != null) {
-// String email = message.counterpartyAddress;
-// holder.badge.assignContactFromEmail(email, true);
-// if (email != null) {
-// mContactsPictureLoader.loadContactPicture(email, holder.badge);
-// }
-// }
-
- if (holder.preview != null) {
- /*
- * In the touchable UI, we have previews. Otherwise, we
- * have just a "from" line.
- * Because text views can't wrap around each other(?) we
- * compose a custom view containing the preview and the
- * from.
- */
-
- CharSequence beforePreviewText = null;
- if (mSenderAboveSubject) {
- beforePreviewText = subject;
- } else {
- beforePreviewText = message.sender;
- }
-
- holder.preview.setText(new SpannableStringBuilder(recipientSigil(message))
- .append(beforePreviewText).append(" ").append(message.message.getPreview()),
- TextView.BufferType.SPANNABLE);
- Spannable str = (Spannable)holder.preview.getText();
-
- // Create a span section for the sender, and assign the correct font size and weight.
- str.setSpan(new AbsoluteSizeSpan((mSenderAboveSubject ? mFontSizes.getMessageListSubject(): mFontSizes.getMessageListSender()), true),
- 0, beforePreviewText.length() + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-
- int color = (K9.getK9Theme() == K9.THEME_LIGHT) ?
- Color.rgb(105, 105, 105) :
- Color.rgb(160, 160, 160);
-
- // set span for preview message.
- str.setSpan(new ForegroundColorSpan(color), // How do I can specify the android.R.attr.textColorTertiary
- beforePreviewText.length() + 1,
- str.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
-
-
- if (holder.from != null ) {
- holder.from.setTypeface(null, maybeBoldTypeface);
- if (mSenderAboveSubject) {
- holder.from.setCompoundDrawablesWithIntrinsicBounds(
- message.answered ? mAnsweredIcon : null, // left
- null, // top
- message.message.hasAttachments() ? mAttachmentIcon : null, // right
- null); // bottom
-
- holder.from.setText(message.sender);
- } else {
- holder.from.setText(new SpannableStringBuilder(recipientSigil(message)).append(message.sender));
- }
- }
-
- if (holder.subject != null ) {
- if (!mSenderAboveSubject) {
- holder.subject.setCompoundDrawablesWithIntrinsicBounds(
- message.answered ? mAnsweredIcon : null, // left
- null, // top
- message.message.hasAttachments() ? mAttachmentIcon : null, // right
- null); // bottom
- }
-
- holder.subject.setTypeface(null, maybeBoldTypeface);
- holder.subject.setText(subject);
- }
-
- holder.date.setText(message.getDate(mMessageHelper));
- holder.position = position;
- }
-
-
- private String recipientSigil(MessageInfoHolder message) {
- if (message.message.toMe()) {
- return getString(R.string.messagelist_sent_to_me_sigil);
- } else if (message.message.ccMe()) {
- return getString(R.string.messagelist_sent_cc_me_sigil);
- } else {
- return "";
- }
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
- }
-
- class MessageViewHolder
- implements OnCheckedChangeListener {
- public TextView subject;
- public TextView preview;
- public TextView from;
- public TextView time;
- public TextView date;
- public View chip;
- public int position = -1;
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (position != -1) {
- MessageInfoHolder message = (MessageInfoHolder) mAdapter.getItem(position);
- toggleMessageSelect(message);
-
-
- }
- }
- }
-
-
- private View getFooterView(ViewGroup parent) {
- if (mFooterView == null) {
- mFooterView = mInflater.inflate(R.layout.message_list_item_footer, parent, false);
- mFooterView.setId(R.layout.message_list_item_footer);
- FooterViewHolder holder = new FooterViewHolder();
- holder.progress = (ProgressBar) mFooterView.findViewById(R.id.message_list_progress);
- holder.progress.setIndeterminate(true);
- holder.main = (TextView) mFooterView.findViewById(R.id.main_text);
- mFooterView.setTag(holder);
- }
-
- return mFooterView;
- }
-
- private void updateFooterView() {
- if (mCurrentFolder != null && mAccount != null) {
- if (mCurrentFolder.loading) {
- final boolean showProgress = true;
- updateFooter(getString(R.string.status_loading_more), showProgress);
- } else {
- String message;
- if (!mCurrentFolder.lastCheckFailed) {
- if (mAccount.getDisplayCount() == 0) {
- message = getString(R.string.message_list_load_more_messages_action);
- } else {
- message = String.format(getString(R.string.load_more_messages_fmt), mAccount.getDisplayCount());
- }
- } else {
- message = getString(R.string.status_loading_more_failed);
- }
- final boolean showProgress = false;
- updateFooter(message, showProgress);
- }
- } else {
- final boolean showProgress = false;
- updateFooter(null, showProgress);
- }
- }
-
- public void updateFooter(final String text, final boolean progressVisible) {
- FooterViewHolder holder = (FooterViewHolder) mFooterView.getTag();
-
- holder.progress.setVisibility(progressVisible ? ProgressBar.VISIBLE : ProgressBar.INVISIBLE);
- if (text != null) {
- holder.main.setText(text);
- }
- if (progressVisible || holder.main.getText().length() > 0) {
- holder.main.setVisibility(View.VISIBLE);
- } else {
- holder.main.setVisibility(View.GONE);
- }
- }
-
- static class FooterViewHolder {
- public ProgressBar progress;
- public TextView main;
- }
-
- private void setAllSelected(boolean isSelected) {
- mSelectedCount = 0;
-
- for (MessageInfoHolder holder : mAdapter.getMessages()) {
- holder.selected = isSelected;
- mSelectedCount += (isSelected ? 1 : 0);
- }
-
- computeBatchDirection();
- mAdapter.notifyDataSetChanged();
-
- if (isSelected) {
- updateActionModeTitle();
- computeSelectAllVisibility();
- }
- }
-
- /**
- * Set selection state for all messages.
- *
- * @param selected
- * If {@code true} all messages get selected. Otherwise, all messages get deselected and
- * action mode is finished.
- */
- private void setSelectionState(boolean selected) {
- mAdapter.setSelectionForAllMesages(selected);
-
- if (selected) {
- mSelectedCount = mAdapter.getCount();
- mActionMode = MessageList.this.startActionMode(mActionModeCallback);
- updateActionModeTitle();
- computeSelectAllVisibility();
- computeBatchDirection();
- } else {
- mSelectedCount = 0;
- if (mActionMode != null) {
- mActionMode.finish();
- }
- }
- }
-
- private void toggleMessageSelect(final MessageInfoHolder holder){
- if (mActionMode != null) {
- if (mSelectedCount == 1 && holder.selected) {
- mActionMode.finish();
- return;
- }
- } else {
- mActionMode = startActionMode(mActionModeCallback);
- }
-
- if (holder.selected) {
- holder.selected = false;
- mSelectedCount -= 1;
- } else {
- holder.selected = true;
- mSelectedCount += 1;
- }
- mAdapter.notifyDataSetChanged();
-
- computeBatchDirection();
- updateActionModeTitle();
-
- // make sure the onPrepareActionMode is called
- mActionMode.invalidate();
-
- computeSelectAllVisibility();
- }
-
- private void updateActionModeTitle() {
- mActionMode.setTitle(String.format(getString(R.string.actionbar_selected), mSelectedCount));
- }
-
- private void computeSelectAllVisibility() {
- mActionModeCallback.showSelectAll(mSelectedCount != mAdapter.getCount());
- }
-
- private void computeBatchDirection() {
- boolean isBatchFlag = false;
- boolean isBatchRead = false;
-
- for (MessageInfoHolder holder : mAdapter.getMessages()) {
- if (holder.selected) {
- if (!holder.flagged) {
- isBatchFlag = true;
- }
- if (!holder.read) {
- isBatchRead = true;
- }
-
- if (isBatchFlag && isBatchRead) {
- break;
- }
- }
- }
-
- mActionModeCallback.showMarkAsRead(isBatchRead);
- mActionModeCallback.showFlag(isBatchFlag);
- }
-
- /**
- * @param holders
- * Messages to update. Never {@code null}.
- * @param flag
- * Flag to be updated on the specified messages. Never
- * {@code null}.
- * @param newState
- * State to set for the given flag.
- */
- private void setFlag(final List holders, final Flag flag, final boolean newState) {
- if (holders.isEmpty()) {
- return;
- }
- final Message[] messageList = new Message[holders.size()];
- int i = 0;
- for (final Iterator iterator = holders.iterator(); iterator.hasNext(); i++) {
- final MessageInfoHolder holder = iterator.next();
- messageList[i] = holder.message;
- if (flag == Flag.SEEN) {
- holder.read = newState;
- } else if (flag == Flag.FLAGGED) {
- holder.flagged = newState;
- }
- }
- mController.setFlag(messageList, flag, newState);
- mAdapter.sortMessages();
- }
-
- /**
- * Display the message move activity.
- *
- * @param holders
- * Never {@code null}.
- */
- private void onMove(final List holders) {
- if (!checkCopyOrMovePossible(holders, FolderOperation.MOVE)) {
- return;
- }
-
- final Folder folder = holders.size() == 1 ? holders.get(0).message.getFolder() : mCurrentFolder.folder;
- displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_MOVE, folder, holders);
- }
-
- /**
- * Display the message copy activity.
- *
- * @param holders
- * Never {@code null}.
- */
- private void onCopy(final List holders) {
- if (!checkCopyOrMovePossible(holders, FolderOperation.COPY)) {
- return;
- }
-
- final Folder folder = holders.size() == 1 ? holders.get(0).message.getFolder() : mCurrentFolder.folder;
- displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_COPY, folder, holders);
- }
-
- /**
- * Helper method to manage the invocation of
- * {@link #startActivityForResult(Intent, int)} for a folder operation
- * ({@link ChooseFolder} activity), while saving a list of associated
- * messages.
- *
- * @param requestCode
- * If >= 0, this code will be returned in onActivityResult() when
- * the activity exits.
- * @param folder
- * Never {@code null}.
- * @param holders
- * Messages to be affected by the folder operation. Never
- * {@code null}.
- * @see #startActivityForResult(Intent, int)
- */
- private void displayFolderChoice(final int requestCode, final Folder folder, final List holders) {
- final Intent intent = new Intent(this, ChooseFolder.class);
- intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, folder.getAccount().getUuid());
- intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, folder.getName());
- intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, folder.getAccount().getLastSelectedFolderName());
- // remember the selected messages for #onActivityResult
- mActiveMessages = holders;
- startActivityForResult(intent, requestCode);
- }
-
- /**
- * @param holders
- * Never {@code null}.
- */
- private void onArchive(final List holders) {
- final String folderName = holders.get(0).message.getFolder().getAccount().getArchiveFolderName();
- if (K9.FOLDER_NONE.equalsIgnoreCase(folderName)) {
- return;
- }
- // TODO one should separate messages by account and call move afterwards
- // (because each account might have a specific Archive folder name)
- move(holders, folderName);
- }
-
- /**
- * @param holders
- * Never {@code null}.
- */
- private void onSpam(final List holders) {
- if (K9.confirmSpam()) {
- // remember the message selection for #onCreateDialog(int)
- mActiveMessages = holders;
- showDialog(R.id.dialog_confirm_spam);
- } else {
- onSpamConfirmed(holders);
- }
- }
-
- /**
- * @param holders
- * Never {@code null}.
- */
- private void onSpamConfirmed(final List holders) {
- final String folderName = holders.get(0).message.getFolder().getAccount().getSpamFolderName();
- if (K9.FOLDER_NONE.equalsIgnoreCase(folderName)) {
- return;
- }
- // TODO one should separate messages by account and call move afterwards
- // (because each account might have a specific Spam folder name)
- move(holders, folderName);
- }
-
- private static enum FolderOperation {
- COPY, MOVE
- }
-
- /**
- * Display an Toast message if any message isn't synchronized
- *
- * @param holders
- * Never null.
- * @param operation
- * Never {@code null}.
- *
- * @return true if operation is possible
- */
- private boolean checkCopyOrMovePossible(final List holders, final FolderOperation operation) {
- if (holders.isEmpty()) {
- return false;
- }
- boolean first = true;
- for (final MessageInfoHolder holder : holders) {
- final Message message = holder.message;
- if (first) {
- first = false;
- // account check
- final Account account = message.getFolder().getAccount();
- if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(account)) || (operation == FolderOperation.COPY && !mController.isCopyCapable(account))) {
- return false;
- }
- }
- // message check
- if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(message)) || (operation == FolderOperation.COPY && !mController.isCopyCapable(message))) {
- final Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message,
- Toast.LENGTH_LONG);
- toast.show();
- return false;
- }
- }
- return true;
- }
-
- /**
- * Helper method to get a List of message ready to be processed. This implementation will return a list containing the sole argument.
- *
- * @param holder Never {@code null}.
- * @return Never {@code null}.
- */
- private List getSelectionFromMessage(final MessageInfoHolder holder) {
- final List selection = Collections.singletonList(holder);
- return selection;
- }
-
- /**
- * Helper method to get a List of message ready to be processed. This implementation will iterate over messages and choose the checked ones.
- *
- * @return Never {@code null}.
- */
- private List getSelectionFromCheckboxes() {
- final List selection = new ArrayList();
-
- for (final MessageInfoHolder holder : mAdapter.getMessages()) {
- if (holder.selected) {
- selection.add(holder);
- }
- }
-
- return selection;
- }
-
- /**
- * Copy the specified messages to the specified folder.
- *
- * @param holders Never {@code null}.
- * @param destination Never {@code null}.
- */
- private void copy(final List holders, final String destination) {
- copyOrMove(holders, destination, FolderOperation.COPY);
- }
-
- /**
- * Move the specified messages to the specified folder.
- *
- * @param holders Never {@code null}.
- * @param destination Never {@code null}.
- */
- private void move(final List holders, final String destination) {
- copyOrMove(holders, destination, FolderOperation.MOVE);
- }
-
- /**
- * The underlying implementation for {@link #copy(List, String)} and
- * {@link #move(List, String)}. This method was added mainly because those 2
- * methods share common behavior.
- *
- * Note: Must be called from the UI thread!
- *
- * @param holders
- * Never {@code null}.
- * @param destination
- * Never {@code null}.
- * @param operation
- * Never {@code null}.
- */
- private void copyOrMove(final List holders, final String destination, final FolderOperation operation) {
- if (K9.FOLDER_NONE.equalsIgnoreCase(destination)) {
- return;
- }
-
- boolean first = true;
- Account account = null;
- String folderName = null;
-
- final List messages = new ArrayList(holders.size());
-
- for (final MessageInfoHolder holder : holders) {
- final Message message = holder.message;
- if (first) {
- first = false;
- folderName = message.getFolder().getName();
- account = message.getFolder().getAccount();
- if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(account)) || (operation == FolderOperation.COPY && !mController.isCopyCapable(account))) {
- // account is not copy/move capable
- return;
- }
- } else if (!account.equals(message.getFolder().getAccount())
- || !folderName.equals(message.getFolder().getName())) {
- // make sure all messages come from the same account/folder?
- return;
- }
- if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(message)) || (operation == FolderOperation.COPY && !mController.isCopyCapable(message))) {
- final Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message,
- Toast.LENGTH_LONG);
- toast.show();
-
- // XXX return meaningful error value?
-
- // message isn't synchronized
- return;
- }
- messages.add(message);
- }
-
- if (operation == FolderOperation.MOVE) {
- mController.moveMessages(account, folderName, messages.toArray(new Message[messages.size()]), destination,
- null);
- mAdapter.removeMessages(holders);
- } else {
- mController.copyMessages(account, folderName, messages.toArray(new Message[messages.size()]), destination,
- null);
}
}
@@ -3061,226 +574,178 @@ public class MessageList extends K9ListActivity implements OnItemClickListener,
Accounts.listAccounts(this);
}
-
- /**
- * Return the currently "open" account if available.
- *
- * @param prefs
- * A {@link Preferences} instance that might be used to retrieve the current
- * {@link Account}.
- *
- * @return The {@code Account} all displayed messages belong to.
- */
- private Account getCurrentAccount(Preferences prefs) {
- Account account = null;
- if (mQueryString != null && !mIntegrate && mAccountUuids != null &&
- mAccountUuids.length == 1) {
- String uuid = mAccountUuids[0];
- account = prefs.getAccount(uuid);
- } else if (mAccount != null) {
- account = mAccount;
- }
-
- return account;
+ public void setActionBarTitle(String title) {
+ mActionBarTitle.setText(title);
}
+ public void setActionBarSubTitle(String subTitle) {
+ mActionBarSubTitle.setText(subTitle);
+ }
- class ActionModeCallback implements ActionMode.Callback {
- private MenuItem mSelectAll;
- private MenuItem mMarkAsRead;
- private MenuItem mMarkAsUnread;
- private MenuItem mFlag;
- private MenuItem mUnflag;
+ public void setActionBarUnread(int unread) {
+ if (unread == 0) {
+ mActionBarUnread.setVisibility(View.GONE);
+ } else {
+ mActionBarUnread.setVisibility(View.VISIBLE);
+ mActionBarUnread.setText(Integer.toString(unread));
+ }
+ }
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- mSelectAll = menu.findItem(R.id.select_all);
- mMarkAsRead = menu.findItem(R.id.mark_as_read);
- mMarkAsUnread = menu.findItem(R.id.mark_as_unread);
- mFlag = menu.findItem(R.id.flag);
- mUnflag = menu.findItem(R.id.unflag);
+ @Override
+ public void setMessageListTitle(String title) {
+ setActionBarTitle(title);
+ }
- if (mQueryString != null) {
- // show all
- menu.findItem(R.id.move).setVisible(true);
- menu.findItem(R.id.archive).setVisible(true);
- menu.findItem(R.id.spam).setVisible(true);
- menu.findItem(R.id.copy).setVisible(true);
+ @Override
+ public void setMessageListSubTitle(String subTitle) {
+ setActionBarSubTitle(subTitle);
+ }
- // hide uncapable
- /*
- * TODO think of a better way then looping over all
- * messages.
- */
- final List selection = getSelectionFromCheckboxes();
- Account account;
+ @Override
+ public void setUnreadCount(int unread) {
+ setActionBarUnread(unread);
+ }
- for (MessageInfoHolder holder : selection) {
- account = holder.message.getFolder().getAccount();
- setContextCapabilities(account, menu);
- }
+ @Override
+ public void setMessageListProgress(int progress) {
+ setSupportProgress(progress);
+ }
- }
- return true;
+ @Override
+ public void openMessage(MessageReference messageReference) {
+ Preferences prefs = Preferences.getPreferences(getApplicationContext());
+ Account account = prefs.getAccount(messageReference.accountUuid);
+ String folderName = messageReference.folderName;
+
+ if (folderName.equals(account.getDraftsFolderName())) {
+ MessageCompose.actionEditDraft(this, messageReference);
+ } else {
+ ArrayList messageRefs = mMessageListFragment.getMessageReferences();
+
+ Log.i(K9.LOG_TAG, "MessageList sending message " + messageReference);
+
+ MessageView.actionView(this, messageReference, messageRefs, getIntent().getExtras());
}
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- mActionMode = null;
- mSelectAll = null;
- mMarkAsRead = null;
- mMarkAsUnread = null;
- mFlag = null;
- mUnflag = null;
- setAllSelected(false);
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- MenuInflater inflater = mode.getMenuInflater();
- inflater.inflate(R.menu.message_list_context, menu);
-
- // check capabilities
- if (mQueryString == null) {
- setContextCapabilities(mAccount, menu);
- }
-
- return true;
- }
-
- /**
- * Disables menu options based on if the account supports it or not.
- * It also checks the controller and for now the 'mode' the messagelist
- * is operation in ( query or not ).
- *
- * @param mAccount Account to check capabilities of.
- * @param menu Menu to adapt.
+ /*
+ * We set read=true here for UI performance reasons. The actual value
+ * will get picked up on the refresh when the Activity is resumed but
+ * that may take a second or so and we don't want this to show and
+ * then go away. I've gone back and forth on this, and this gives a
+ * better UI experience, so I am putting it back in.
*/
- private void setContextCapabilities(Account mAccount, Menu menu) {
- /*
- * TODO get rid of this when we finally split the messagelist into
- * a folder content display and a search result display
- */
- if (mQueryString != null) {
- menu.findItem(R.id.move).setVisible(false);
- menu.findItem(R.id.copy).setVisible(false);
+// if (!message.read) {
+// message.read = true;
+// }
+ }
- menu.findItem(R.id.archive).setVisible(false);
- menu.findItem(R.id.spam).setVisible(false);
+ @Override
+ public void onResendMessage(Message message) {
+ MessageCompose.actionEditDraft(this, message.makeMessageReference());
+ }
- return;
- }
+ @Override
+ public void onForward(Message message) {
+ MessageCompose.actionForward(this, message.getFolder().getAccount(), message, null);
+ }
- // hide unsupported
- if (!mController.isCopyCapable(mAccount)) {
- menu.findItem(R.id.copy).setVisible(false);
- }
+ @Override
+ public void onReply(Message message) {
+ MessageCompose.actionReply(this, message.getFolder().getAccount(), message, false, null);
+ }
- if (!mController.isMoveCapable(mAccount)) {
- menu.findItem(R.id.move).setVisible(false);
- menu.findItem(R.id.archive).setVisible(false);
- menu.findItem(R.id.spam).setVisible(false);
- }
+ @Override
+ public void onReplyAll(Message message) {
+ MessageCompose.actionReply(this, message.getFolder().getAccount(), message, true, null);
+ }
- if (!mAccount.hasArchiveFolder()) {
- menu.findItem(R.id.archive).setVisible(false);
- }
+ @Override
+ public void onCompose(Account account) {
+ MessageCompose.actionCompose(this, account);
+ }
- if (!mAccount.hasSpamFolder()) {
- menu.findItem(R.id.spam).setVisible(false);
- }
+ @Override
+ public void showMoreFromSameSender(String senderAddress) {
+ MessageListFragment fragment = MessageListFragment.newInstance("From " + senderAddress,
+ null, null, senderAddress, null, null, false);
+
+ addMessageListFragment(fragment);
+ }
+
+ @Override
+ public void onBackStackChanged() {
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ mMessageListFragment = (MessageListFragment) fragmentManager.findFragmentById(
+ R.id.message_list_container);
+
+ configureMenu(mMenu);
+ }
+
+ @Override
+ public void onSwipeRightToLeft(MotionEvent e1, MotionEvent e2) {
+ if (mMessageListFragment != null) {
+ mMessageListFragment.onSwipeRightToLeft(e1, e2);
}
+ }
- public void showSelectAll(boolean show) {
- if (mActionMode != null) {
- mSelectAll.setVisible(show);
- }
+ @Override
+ public void onSwipeLeftToRight(MotionEvent e1, MotionEvent e2) {
+ if (mMessageListFragment != null) {
+ mMessageListFragment.onSwipeLeftToRight(e1, e2);
}
+ }
- public void showMarkAsRead(boolean show) {
- if (mActionMode != null) {
- mMarkAsRead.setVisible(show);
- mMarkAsUnread.setVisible(!show);
- }
- }
-
- public void showFlag(boolean show) {
- if (mActionMode != null) {
- mFlag.setVisible(show);
- mUnflag.setVisible(!show);
+ private final class StorageListenerImplementation implements StorageManager.StorageListener {
+ @Override
+ public void onUnmount(String providerId) {
+ if (mAccount != null && providerId.equals(mAccount.getLocalStorageProviderId())) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ onAccountUnavailable();
+ }
+ });
}
}
@Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- final List selection = getSelectionFromCheckboxes();
-
- /*
- * In the following we assume that we can't move or copy
- * mails to the same folder. Also that spam isn't available if we are
- * in the spam folder,same for archive.
- *
- * This is the case currently so safe assumption.
- */
- switch (item.getItemId()) {
- case R.id.delete: {
- onDelete(selection);
- mSelectedCount = 0;
- break;
- }
- case R.id.mark_as_read: {
- setFlag(selection, Flag.SEEN, true);
- computeBatchDirection();
- break;
- }
- case R.id.mark_as_unread: {
- setFlag(selection, Flag.SEEN, false);
- computeBatchDirection();
- break;
- }
- case R.id.flag: {
- setFlag(selection, Flag.FLAGGED, true);
- computeBatchDirection();
- break;
- }
- case R.id.unflag: {
- setFlag(selection, Flag.FLAGGED, false);
- computeBatchDirection();
- break;
- }
- case R.id.select_all: {
- setAllSelected(true);
- break;
- }
-
- // only if the account supports this
- case R.id.archive: {
- onArchive(selection);
- mSelectedCount = 0;
- break;
- }
- case R.id.spam: {
- onSpam(selection);
- mSelectedCount = 0;
- break;
- }
- case R.id.move: {
- onMove(selection);
- mSelectedCount = 0;
- break;
- }
- case R.id.copy: {
- onCopy(selection);
- mSelectedCount = 0;
- break;
- }
- }
- if (mSelectedCount == 0) {
- mActionMode.finish();
- }
-
- return true;
+ public void onMount(String providerId) {
+ // no-op
}
}
+
+ @Override
+ public void remoteSearch(String searchAccount, String searchFolder, String queryString) {
+ MessageListFragment fragment = MessageListFragment.newInstance(searchAccount, searchFolder,
+ queryString, true);
+
+ addMessageListFragment(fragment);
+ }
+
+ private void addMessageListFragment(MessageListFragment fragment) {
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+
+ ft.replace(R.id.message_list_container, fragment);
+ ft.addToBackStack(null);
+
+ mMessageListFragment = fragment;
+ ft.commit();
+ }
+
+ @Override
+ public boolean startSearch(Account account, String folderName) {
+ // If this search was started from a MessageList of a single folder, pass along that folder info
+ // so that we can enable remote search.
+ if (account != null && folderName != null) {
+ final Bundle appData = new Bundle();
+ appData.putString(EXTRA_SEARCH_ACCOUNT, account.getUuid());
+ appData.putString(EXTRA_SEARCH_FOLDER, folderName);
+ startSearch(null, false, appData, false);
+ } else {
+ // TODO Handle the case where we're searching from within a search result.
+ startSearch(null, false, null, false);
+ }
+
+ return true;
+ }
}
-
diff --git a/src/com/fsck/k9/activity/MessageView.java b/src/com/fsck/k9/activity/MessageView.java
index 4fd8d94f8..760e37fac 100644
--- a/src/com/fsck/k9/activity/MessageView.java
+++ b/src/com/fsck/k9/activity/MessageView.java
@@ -1,27 +1,9 @@
package com.fsck.k9.activity;
-import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences.Editor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Toast;
-
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
@@ -30,100 +12,240 @@ import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
-import com.fsck.k9.controller.MessagingController;
-import com.fsck.k9.controller.MessagingListener;
+import com.fsck.k9.activity.misc.SwipeGestureDetector;
+import com.fsck.k9.activity.misc.SwipeGestureDetector.OnSwipeGestureListener;
import com.fsck.k9.crypto.PgpData;
-import com.fsck.k9.helper.FileBrowserHelper;
-import com.fsck.k9.helper.FileBrowserHelper.FileBrowserFailOverCallback;
-import com.fsck.k9.mail.Flag;
+import com.fsck.k9.fragment.MessageViewFragment;
+import com.fsck.k9.fragment.MessageViewFragment.MessageViewFragmentListener;
import com.fsck.k9.mail.Message;
-import com.fsck.k9.mail.MessagingException;
-import com.fsck.k9.mail.Part;
-import com.fsck.k9.mail.store.LocalStore.LocalMessage;
import com.fsck.k9.mail.store.StorageManager;
-import com.fsck.k9.view.AttachmentView;
-import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback;
+import com.fsck.k9.view.MessageHeader;
import com.fsck.k9.view.MessageTitleView;
-import com.fsck.k9.view.SingleMessageView;
-public class MessageView extends K9Activity implements OnClickListener {
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.Toast;
+
+
+public class MessageView extends K9FragmentActivity implements MessageViewFragmentListener,
+ OnSwipeGestureListener {
+
private static final String EXTRA_MESSAGE_REFERENCE = "com.fsck.k9.MessageView_messageReference";
private static final String EXTRA_MESSAGE_REFERENCES = "com.fsck.k9.MessageView_messageReferences";
private static final String EXTRA_MESSAGE_LIST_EXTRAS = "com.fsck.k9.MessageView_messageListExtras";
- private static final String STATE_PGP_DATA = "pgpData";
- private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
- private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;
- private static final int ACTIVITY_CHOOSE_DIRECTORY = 3;
- private SingleMessageView mMessageView;
- private MessageTitleView mTitleView;
-
- private PgpData mPgpData;
- private Menu mMenu;
- private Account mAccount;
- private MessageReference mMessageReference;
- private ArrayList mMessageReferences;
- private Message mMessage;
+ /**
+ * @see #mLastDirection
+ */
private static final int PREVIOUS = 1;
private static final int NEXT = 2;
- private int mLastDirection = (K9.messageViewShowNext()) ? NEXT : PREVIOUS;
- private MessagingController mController = MessagingController.getInstance(getApplication());
- private MessageReference mNextMessage = null;
- private MessageReference mPreviousMessage = null;
- private Listener mListener = new Listener();
- private MessageViewHandler mHandler = new MessageViewHandler();
+
+
+ public static void actionView(Context context, MessageReference messRef,
+ ArrayList messReferences, Bundle messageListExtras) {
+ Intent i = new Intent(context, MessageView.class);
+ i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ i.putExtra(EXTRA_MESSAGE_LIST_EXTRAS, messageListExtras);
+ i.putExtra(EXTRA_MESSAGE_REFERENCE, messRef);
+ i.putParcelableArrayListExtra(EXTRA_MESSAGE_REFERENCES, messReferences);
+ context.startActivity(i);
+ }
+
+
private StorageManager.StorageListener mStorageListener = new StorageListenerImplementation();
-
- private MenuItem mToggleMessageViewMenu;
-
- /** this variable is used to save the calling AttachmentView
- * until the onActivityResult is called.
- * => with this reference we can identity the caller
- */
- private AttachmentView attachmentTmpStore;
+ private Account mAccount;
+ private MessageTitleView mTitleView;
+ private MessageReference mMessageReference;
+ private ArrayList mMessageReferences;
+ private int mLastDirection = (K9.messageViewShowNext()) ? NEXT : PREVIOUS;
+ private MessageReference mNextMessage;
+ private MessageReference mPreviousMessage;
+ private MessageViewFragment mMessageViewFragment;
+ private Menu mMenu;
/**
- * Used to temporarily store the destination folder for refile operations if a confirmation
- * dialog is shown.
- */
- private String mDstFolder;
-
- /**
- * The extras used to create the {@link MessageList} instance that created this activity. May
- * be {@code null}.
- *
- * @see MessageList#actionHandleFolder(Context, Bundle)
- */
- private Bundle mMessageListExtras;
-
- /**
- * Screen width in pixels.
- *
- *
* Used to detect right-to-left bezel swipes.
- *
*
* @see #onSwipeRightToLeft(MotionEvent, MotionEvent)
*/
- private int mScreenWidthInPixels;
+ private int mRightBezelThreshold;
- private final class StorageListenerImplementation implements StorageManager.StorageListener {
- @Override
- public void onUnmount(String providerId) {
- if (!providerId.equals(mAccount.getLocalStorageProviderId())) {
- return;
- }
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- onAccountUnavailable();
- }
- });
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setTheme(K9.getK9ThemeResourceId(K9.getK9MessageViewTheme()));
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ setContentView(R.layout.message_view);
+
+ initializeActionBar();
+ setTitle("");
+
+ int screenWidth = getResources().getDisplayMetrics().widthPixels;
+ mRightBezelThreshold = screenWidth - SwipeGestureDetector.BEZEL_SWIPE_THRESHOLD;
+
+ // Enable gesture detection for MessageViews
+ if (K9.gesturesEnabled()) {
+ setupGestureDetector(this);
}
- @Override
- public void onMount(String providerId) { /* no-op */ }
+ final Intent intent = getIntent();
+
+ Uri uri = intent.getData();
+ if (savedInstanceState != null) {
+ mMessageReference = savedInstanceState.getParcelable(EXTRA_MESSAGE_REFERENCE);
+ mMessageReferences = savedInstanceState.getParcelableArrayList(EXTRA_MESSAGE_REFERENCES);
+ } else {
+ if (uri == null) {
+ mMessageReference = intent.getParcelableExtra(EXTRA_MESSAGE_REFERENCE);
+ mMessageReferences = intent.getParcelableArrayListExtra(EXTRA_MESSAGE_REFERENCES);
+ } else {
+ List segmentList = uri.getPathSegments();
+ if (segmentList.size() != 3) {
+ //TODO: Use resource to externalize message
+ Toast.makeText(this, "Invalid intent uri: " + uri.toString(), Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ String accountId = segmentList.get(0);
+ Collection accounts = Preferences.getPreferences(this).getAvailableAccounts();
+ for (Account account : accounts) {
+ if (String.valueOf(account.getAccountNumber()).equals(accountId)) {
+ mMessageReference = new MessageReference();
+ mMessageReference.accountUuid = account.getUuid();
+ mMessageReference.folderName = segmentList.get(1);
+ mMessageReference.uid = segmentList.get(2);
+ mMessageReferences = new ArrayList();
+ mAccount = account;
+ break;
+ }
+ }
+
+ if (mMessageReference == null) {
+ //TODO: Use resource to externalize message
+ Toast.makeText(this, "Invalid account id: " + accountId, Toast.LENGTH_LONG).show();
+ return;
+ }
+ }
+ }
+
+ if (mAccount == null) {
+ Preferences preferences = Preferences.getPreferences(getApplicationContext());
+ mAccount = preferences.getAccount(mMessageReference.accountUuid);
+ }
+
+ findSurroundingMessagesUid();
+
+
+ FragmentManager fragmentManager = getSupportFragmentManager();
+
+ mMessageViewFragment = (MessageViewFragment) fragmentManager.findFragmentById(R.id.message);
+
+ if (mMessageViewFragment == null) {
+ FragmentTransaction ft = fragmentManager.beginTransaction();
+ mMessageViewFragment = MessageViewFragment.newInstance(mMessageReference);
+ ft.add(R.id.message, mMessageViewFragment);
+ ft.commit();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (!mAccount.isAvailable(this)) {
+ onAccountUnavailable();
+ return;
+ }
+ StorageManager.getInstance(getApplication()).addListener(mStorageListener);
+ }
+
+ @Override
+ protected void onPause() {
+ StorageManager.getInstance(getApplication()).removeListener(mStorageListener);
+ super.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(EXTRA_MESSAGE_REFERENCE, mMessageReference);
+ outState.putParcelableArrayList(EXTRA_MESSAGE_REFERENCES, mMessageReferences);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+
+ getSupportMenuInflater().inflate(R.menu.message_view_option, menu);
+ mMenu = menu;
+ configureMenu(menu);
+
+ return true;
+ }
+
+ private void configureMenu(Menu menu) {
+ if (menu == null) {
+ return;
+ }
+
+ if (mNextMessage != null) {
+ menu.findItem(R.id.next_message).setEnabled(true);
+ menu.findItem(R.id.next_message).getIcon().setAlpha(255);
+ } else {
+ menu.findItem(R.id.next_message).getIcon().setAlpha(127);
+ menu.findItem(R.id.next_message).setEnabled(false);
+ }
+
+ if (mPreviousMessage != null) {
+ menu.findItem(R.id.previous_message).setEnabled(true);
+ menu.findItem(R.id.previous_message).getIcon().setAlpha(255);
+ } else {
+ menu.findItem(R.id.previous_message).getIcon().setAlpha(127);
+ menu.findItem(R.id.previous_message).setEnabled(false);
+ }
+ }
+
+ private void toggleActionsState(Menu menu, boolean state) {
+ for (int i = 0; i < menu.size(); ++i) {
+ menu.getItem(i).setEnabled(state);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ finish();
+ break;
+ }
+ case R.id.next_message: {
+ onNext();
+ break;
+ }
+ case R.id.previous_message: {
+ onPrevious();
+ break;
+ }
+ default: {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ return true;
}
@Override
@@ -169,44 +291,41 @@ public class MessageView extends K9Activity implements OnClickListener {
}
break;
}
- case KeyEvent.KEYCODE_DEL: {
- onDelete();
- return true;
- }
+ case KeyEvent.KEYCODE_DEL:
case KeyEvent.KEYCODE_D: {
- onDelete();
+ mMessageViewFragment.onDelete();
return true;
}
case KeyEvent.KEYCODE_F: {
- onForward();
+ mMessageViewFragment.onForward();
return true;
}
case KeyEvent.KEYCODE_A: {
- onReplyAll();
+ mMessageViewFragment.onReplyAll();
return true;
}
case KeyEvent.KEYCODE_R: {
- onReply();
+ mMessageViewFragment.onReply();
return true;
}
case KeyEvent.KEYCODE_G: {
- onFlag();
+ mMessageViewFragment.onFlag();
return true;
}
case KeyEvent.KEYCODE_M: {
- onMove();
+ mMessageViewFragment.onMove();
return true;
}
case KeyEvent.KEYCODE_S: {
- onRefile(mAccount.getSpamFolderName());
+ mMessageViewFragment.onRefile(mAccount.getSpamFolderName());
return true;
}
case KeyEvent.KEYCODE_V: {
- onRefile(mAccount.getArchiveFolderName());
+ mMessageViewFragment.onRefile(mAccount.getArchiveFolderName());
return true;
}
case KeyEvent.KEYCODE_Y: {
- onCopy();
+ mMessageViewFragment.onCopy();
return true;
}
case KeyEvent.KEYCODE_J:
@@ -220,11 +339,7 @@ public class MessageView extends K9Activity implements OnClickListener {
return true;
}
case KeyEvent.KEYCODE_Z: {
- mHandler.post(new Runnable() {
- public void run() {
- mMessageView.zoom(event);
- }
- });
+ mMessageViewFragment.zoom(event);
return true;
}
case KeyEvent.KEYCODE_H: {
@@ -241,174 +356,19 @@ public class MessageView extends K9Activity implements OnClickListener {
// Swallow these events too to avoid the audible notification of a volume change
if (K9.useVolumeKeysForNavigationEnabled()) {
if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
- if (K9.DEBUG)
+ if (K9.DEBUG) {
Log.v(K9.LOG_TAG, "Swallowed key up.");
+ }
return true;
}
}
return super.onKeyUp(keyCode, event);
}
- class MessageViewHandler extends Handler {
-
- public void progress(final boolean progress) {
- runOnUiThread(new Runnable() {
- public void run() {
- setSupportProgressBarIndeterminateVisibility(progress);
- }
- });
- }
-
- public void addAttachment(final View attachmentView) {
- runOnUiThread(new Runnable() {
- public void run() {
- mMessageView.addAttachment(attachmentView);
- }
- });
- }
-
- /* A helper for a set of "show a toast" methods */
- private void showToast(final String message, final int toastLength) {
- runOnUiThread(new Runnable() {
- public void run() {
- Toast.makeText(MessageView.this, message, toastLength).show();
- }
- });
- }
-
- public void networkError() {
- showToast(getString(R.string.status_network_error), Toast.LENGTH_LONG);
- }
-
- public void invalidIdError() {
- showToast(getString(R.string.status_invalid_id_error), Toast.LENGTH_LONG);
- }
-
-
- public void fetchingAttachment() {
- showToast(getString(R.string.message_view_fetching_attachment_toast), Toast.LENGTH_SHORT);
- }
- }
-
- public static void actionView(Context context, MessageReference messRef,
- ArrayList messReferences, Bundle messageListExtras) {
- Intent i = new Intent(context, MessageView.class);
- i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- i.putExtra(EXTRA_MESSAGE_LIST_EXTRAS, messageListExtras);
- i.putExtra(EXTRA_MESSAGE_REFERENCE, messRef);
- i.putParcelableArrayListExtra(EXTRA_MESSAGE_REFERENCES, messReferences);
- context.startActivity(i);
- }
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setTheme(K9.getK9ThemeResourceId(K9.getK9MessageViewTheme()));
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setContentView(R.layout.message_view);
-
- mMessageView = (SingleMessageView) findViewById(R.id.message_view);
-
- //set a callback for the attachment view. With this callback the attachmentview
- //request the start of a filebrowser activity.
- mMessageView.setAttachmentCallback(new AttachmentFileDownloadCallback() {
-
- @Override
- public void showFileBrowser(final AttachmentView caller) {
- FileBrowserHelper.getInstance()
- .showFileBrowserActivity(MessageView.this,
- null,
- MessageView.ACTIVITY_CHOOSE_DIRECTORY,
- callback);
- attachmentTmpStore = caller;
- }
- FileBrowserFailOverCallback callback = new FileBrowserFailOverCallback() {
-
- @Override
- public void onPathEntered(String path) {
- attachmentTmpStore.writeFile(new File(path));
- }
-
- @Override
- public void onCancel() {
- // canceled, do nothing
- }
- };
- });
-
- mMessageView.initialize(this);
- mMessageView.downloadRemainderButton().setOnClickListener(this);
- initializeActionBar();
-
- setTitle("");
- final Intent intent = getIntent();
-
- mMessageListExtras = intent.getParcelableExtra(EXTRA_MESSAGE_LIST_EXTRAS);
-
- Uri uri = intent.getData();
- if (icicle != null) {
- // TODO This code seems unnecessary since the icicle should already be thawed in onRestoreInstanceState().
- mMessageReference = icicle.getParcelable(EXTRA_MESSAGE_REFERENCE);
- mMessageReferences = icicle.getParcelableArrayList(EXTRA_MESSAGE_REFERENCES);
- mPgpData = (PgpData) icicle.getSerializable(STATE_PGP_DATA);
- } else {
- if (uri == null) {
- mMessageReference = intent.getParcelableExtra(EXTRA_MESSAGE_REFERENCE);
- mMessageReferences = intent.getParcelableArrayListExtra(EXTRA_MESSAGE_REFERENCES);
- } else {
- List segmentList = uri.getPathSegments();
- if (segmentList.size() != 3) {
- //TODO: Use resource to externalize message
- Toast.makeText(this, "Invalid intent uri: " + uri.toString(), Toast.LENGTH_LONG).show();
- return;
- }
-
- String accountId = segmentList.get(0);
- Collection accounts = Preferences.getPreferences(this).getAvailableAccounts();
- boolean found = false;
- for (Account account : accounts) {
- if (String.valueOf(account.getAccountNumber()).equals(accountId)) {
- mAccount = account;
- found = true;
- break;
- }
- }
- if (!found) {
- //TODO: Use resource to externalize message
- Toast.makeText(this, "Invalid account id: " + accountId, Toast.LENGTH_LONG).show();
- return;
- }
-
- mMessageReference = new MessageReference();
- mMessageReference.accountUuid = mAccount.getUuid();
- mMessageReference.folderName = segmentList.get(1);
- mMessageReference.uid = segmentList.get(2);
- mMessageReferences = new ArrayList();
- }
- }
-
- mAccount = Preferences.getPreferences(this).getAccount(mMessageReference.accountUuid);
- mScreenWidthInPixels = getResources().getDisplayMetrics().widthPixels;
-
- // Enable gesture detection for MessageViews
- mGestureDetector = new GestureDetector(new MyGestureDetector(false));
-
- displayMessage(mMessageReference);
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putParcelable(EXTRA_MESSAGE_REFERENCE, mMessageReference);
- outState.putParcelableArrayList(EXTRA_MESSAGE_REFERENCES, mMessageReferences);
- outState.putSerializable(STATE_PGP_DATA, mPgpData);
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- mPgpData = (PgpData) savedInstanceState.getSerializable(STATE_PGP_DATA);
- mMessageView.updateCryptoLayout(mAccount.getCryptoProvider(), mPgpData, mMessage);
+ protected void onAccountUnavailable() {
+ finish();
+ // TODO inform user about account unavailability using Toast
+ Accounts.listAccounts(this);
}
private void initializeActionBar() {
@@ -420,111 +380,55 @@ public class MessageView extends K9Activity implements OnClickListener {
final View customView = actionBar.getCustomView();
mTitleView = (MessageTitleView) customView.findViewById(android.R.id.title);
- mTitleView.setMessageHeader(mMessageView.getMessageHeaderView());
- }
-
- private void displayMessage(MessageReference ref) {
- mMessageReference = ref;
- if (K9.DEBUG)
- Log.d(K9.LOG_TAG, "MessageView displaying message " + mMessageReference);
- mAccount = Preferences.getPreferences(this).getAccount(mMessageReference.accountUuid);
- findSurroundingMessagesUid();
- // start with fresh, empty PGP data
- mPgpData = new PgpData();
-
- // Clear previous message
- mMessageView.resetView();
- mMessageView.resetHeaderView();
-
- mController.loadMessageForView(mAccount, mMessageReference.folderName, mMessageReference.uid, mListener);
- configureMenu(mMenu);
- }
-
- private void findSurroundingMessagesUid() {
- mNextMessage = mPreviousMessage = null;
- int i = mMessageReferences.indexOf(mMessageReference);
- if (i < 0)
- return;
- if (i != 0)
- mNextMessage = mMessageReferences.get(i - 1);
- if (i != (mMessageReferences.size() - 1))
- mPreviousMessage = mMessageReferences.get(i + 1);
}
@Override
- public void onResume() {
- super.onResume();
- if (!mAccount.isAvailable(this)) {
- onAccountUnavailable();
- return;
- }
- StorageManager.getInstance(getApplication()).addListener(mStorageListener);
- }
-
- @Override
- protected void onPause() {
- StorageManager.getInstance(getApplication()).removeListener(mStorageListener);
- super.onPause();
- }
-
- protected void onAccountUnavailable() {
- finish();
- // TODO inform user about account unavailability using Toast
- Accounts.listAccounts(this);
+ public void messageHeaderViewAvailable(MessageHeader header) {
+ mTitleView.setMessageHeader(header);
}
/**
- * Called from UI thread when user select Delete
+ * Set the title of the view.
+ *
+ *
Since we're using a custom ActionBar view, the normal {@code setTitle()} doesn't do what
+ * we think. This version sets the text value into the proper ActionBar title view.