1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-08-13 17:03:48 -04:00

Merge pull request #83 from andrewachen/messageview_save_location

Restore position in MessageView after rotation screen.
This commit is contained in:
Andrew Chen 2011-11-07 09:57:18 -08:00
commit ef631e21d1
6 changed files with 159 additions and 5 deletions

View File

@ -35,6 +35,7 @@ public class MessageView extends K9Activity implements OnClickListener {
private static final String EXTRA_MESSAGE_REFERENCE = "com.fsck.k9.MessageView_messageReference"; 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_REFERENCES = "com.fsck.k9.MessageView_messageReferences";
private static final String EXTRA_NEXT = "com.fsck.k9.MessageView_next"; private static final String EXTRA_NEXT = "com.fsck.k9.MessageView_next";
private static final String EXTRA_SCROLL_PERCENTAGE = "com.fsck.k9.MessageView_scrollPercentage";
private static final String SHOW_PICTURES = "showPictures"; private static final String SHOW_PICTURES = "showPictures";
private static final String STATE_PGP_DATA = "pgpData"; private static final String STATE_PGP_DATA = "pgpData";
private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1; private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
@ -372,13 +373,19 @@ public class MessageView extends K9Activity implements OnClickListener {
} }
}; };
}); });
mMessageView.initialize(this); mMessageView.initialize(this);
// Register the ScrollView's listener to handle scrolling to last known location on resume.
mController.addListener(mTopView.getListener());
mMessageView.setListeners(mController.getListeners());
setTitle(""); setTitle("");
final Intent intent = getIntent(); final Intent intent = getIntent();
Uri uri = intent.getData(); Uri uri = intent.getData();
if (icicle != null) { if (icicle != null) {
// TODO This code seems unnecessary since the icicle should already be thawed in onRestoreInstanceState().
mMessageReference = icicle.getParcelable(EXTRA_MESSAGE_REFERENCE); mMessageReference = icicle.getParcelable(EXTRA_MESSAGE_REFERENCE);
mMessageReferences = icicle.getParcelableArrayList(EXTRA_MESSAGE_REFERENCES); mMessageReferences = icicle.getParcelableArrayList(EXTRA_MESSAGE_REFERENCES);
mPgpData = (PgpData) icicle.getSerializable(STATE_PGP_DATA); mPgpData = (PgpData) icicle.getSerializable(STATE_PGP_DATA);
@ -492,6 +499,7 @@ public class MessageView extends K9Activity implements OnClickListener {
outState.putParcelableArrayList(EXTRA_MESSAGE_REFERENCES, mMessageReferences); outState.putParcelableArrayList(EXTRA_MESSAGE_REFERENCES, mMessageReferences);
outState.putSerializable(STATE_PGP_DATA, mPgpData); outState.putSerializable(STATE_PGP_DATA, mPgpData);
outState.putBoolean(SHOW_PICTURES, mMessageView.showPictures()); outState.putBoolean(SHOW_PICTURES, mMessageView.showPictures());
outState.putDouble(EXTRA_SCROLL_PERCENTAGE, mTopView.getScrollPercentage());
} }
@Override @Override
@ -500,6 +508,7 @@ public class MessageView extends K9Activity implements OnClickListener {
mPgpData = (PgpData) savedInstanceState.getSerializable(STATE_PGP_DATA); mPgpData = (PgpData) savedInstanceState.getSerializable(STATE_PGP_DATA);
mMessageView.updateCryptoLayout(mAccount.getCryptoProvider(), mPgpData, mMessage); mMessageView.updateCryptoLayout(mAccount.getCryptoProvider(), mPgpData, mMessage);
mMessageView.setLoadPictures(savedInstanceState.getBoolean(SHOW_PICTURES)); mMessageView.setLoadPictures(savedInstanceState.getBoolean(SHOW_PICTURES));
mTopView.setScrollPercentage(savedInstanceState.getDouble(EXTRA_SCROLL_PERCENTAGE));
} }
private void displayMessage(MessageReference ref) { private void displayMessage(MessageReference ref) {
@ -619,11 +628,13 @@ public class MessageView extends K9Activity implements OnClickListener {
onAccountUnavailable(); onAccountUnavailable();
return; return;
} }
mController.addListener(mTopView.getListener());
StorageManager.getInstance(getApplication()).addListener(mStorageListener); StorageManager.getInstance(getApplication()).addListener(mStorageListener);
} }
@Override @Override
protected void onPause() { protected void onPause() {
mController.removeListener(mTopView.getListener());
StorageManager.getInstance(getApplication()).removeListener(mStorageListener); StorageManager.getInstance(getApplication()).removeListener(mStorageListener);
super.onPause(); super.onPause();
} }
@ -850,6 +861,8 @@ public class MessageView extends K9Activity implements OnClickListener {
} }
protected void onNext() { protected void onNext() {
// Reset scroll percentage when we change messages
mTopView.setScrollPercentage(0);
if (mNextMessage == null) { if (mNextMessage == null) {
Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show(); Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
return; return;
@ -864,6 +877,8 @@ public class MessageView extends K9Activity implements OnClickListener {
} }
protected void onPrevious() { protected void onPrevious() {
// Reset scroll percentage when we change messages
mTopView.setScrollPercentage(0);
if (mPreviousMessage == null) { if (mPreviousMessage == null) {
Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show(); Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
return; return;
@ -1091,8 +1106,8 @@ public class MessageView extends K9Activity implements OnClickListener {
mTopView.scrollTo(0, 0); mTopView.scrollTo(0, 0);
try { try {
if (MessageView.this.mMessage != null if (MessageView.this.mMessage != null
&& MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL) && MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL)
&& message.isSet(Flag.X_DOWNLOADED_FULL)) { && message.isSet(Flag.X_DOWNLOADED_FULL)) {
mMessageView.setHeaders(message, account); mMessageView.setHeaders(message, account);
} }
MessageView.this.mMessage = message; MessageView.this.mMessage = message;
@ -1252,5 +1267,4 @@ public class MessageView extends K9Activity implements OnClickListener {
// sometimes shows the original encrypted content // sometimes shows the original encrypted content
mMessageView.loadBodyFromText(mAccount.getCryptoProvider(), mPgpData, mMessage, mPgpData.getDecryptedData(), "text/plain"); mMessageView.loadBodyFromText(mAccount.getCryptoProvider(), mPgpData, mMessage, mPgpData.getDecryptedData(), "text/plain");
} }
} }

View File

@ -112,6 +112,11 @@ public class MessagingListener {
public void loadMessageForViewFailed(Account account, String folder, String uid, Throwable t) { public void loadMessageForViewFailed(Account account, String folder, String uid, Throwable t) {
} }
/**
* Called when a message for view has been fully displayed on the screen.
*/
public void messageViewFinished() {}
public void checkMailStarted(Context context, Account account) { public void checkMailStarted(Context context, Account account) {
} }

View File

@ -26,11 +26,15 @@ import android.webkit.WebView;
import android.widget.TextView; import android.widget.TextView;
import com.fsck.k9.activity.AccessibleEmailContentActivity; import com.fsck.k9.activity.AccessibleEmailContentActivity;
import com.fsck.k9.controller.MessagingListener;
import java.util.Set;
public class AccessibleWebView extends TextView { public class AccessibleWebView extends TextView {
private Context mContext; private Context mContext;
private String mHtmlSource; private String mHtmlSource;
private WebView mDummyWebView; private WebView mDummyWebView;
private Set<MessagingListener> mListeners = null;
public AccessibleWebView(Context context) { public AccessibleWebView(Context context) {
super(context); super(context);
@ -68,6 +72,13 @@ public class AccessibleWebView extends TextView {
String historyUrl) { String historyUrl) {
mHtmlSource = data; mHtmlSource = data;
this.setText(Html.fromHtml(mHtmlSource, null, null)); this.setText(Html.fromHtml(mHtmlSource, null, null));
// Let everyone know that loading has finished.
if (mListeners != null) {
for (MessagingListener l : mListeners) {
l.messageViewFinished();
}
}
} }
public boolean zoomIn() { public boolean zoomIn() {
@ -92,4 +103,8 @@ public class AccessibleWebView extends TextView {
i.putExtra("content", mHtmlSource); i.putExtra("content", mHtmlSource);
mContext.startActivity(i); mContext.startActivity(i);
} }
public void setListeners(final Set<MessagingListener> listeners) {
this.mListeners = listeners;
}
} }

View File

@ -1,6 +1,7 @@
package com.fsck.k9.view; package com.fsck.k9.view;
import android.content.Context; import android.content.Context;
import android.graphics.Picture;
import android.os.Build; import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
@ -10,10 +11,15 @@ import android.webkit.WebView;
import android.widget.Toast; import android.widget.Toast;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.R; import com.fsck.k9.R;
import com.fsck.k9.controller.MessagingListener;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Set;
public class MessageWebView extends WebView { public class MessageWebView extends WebView {
// Store a reference to the listeners in MessagingController. We can't fetch it directly since
// we don't know the application name.
private Set<MessagingListener> mListeners = null;
/** /**
* We use WebSettings.getBlockNetworkLoads() to prevent the WebView that displays email * We use WebSettings.getBlockNetworkLoads() to prevent the WebView that displays email
@ -97,6 +103,18 @@ public class MessageWebView extends WebView {
// Disable network images by default. This is overridden by preferences. // Disable network images by default. This is overridden by preferences.
blockNetworkData(true); blockNetworkData(true);
// Listen for when the screen has finished drawing.
setPictureListener(new PictureListener() {
@Override
public void onNewPicture(WebView webView, Picture picture) {
if (mListeners != null) {
for (MessagingListener l : mListeners) {
l.messageViewFinished();
}
}
}
});
} }
/* /*
@ -115,4 +133,8 @@ public class MessageWebView extends WebView {
Log.e(K9.LOG_TAG, "Exception in emulateShiftHeld()", e); Log.e(K9.LOG_TAG, "Exception in emulateShiftHeld()", e);
} }
} }
public void setListeners(final Set<MessagingListener> listeners) {
this.mListeners = listeners;
}
} }

View File

@ -29,6 +29,7 @@ import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.store.LocalStore; import com.fsck.k9.mail.store.LocalStore;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
@ -116,11 +117,10 @@ public class SingleMessageView extends LinearLayout {
return false; return false;
} }
public boolean showPictures() { public boolean showPictures() {
return mShowPictures; return mShowPictures;
} }
public void setShowPictures(Boolean show) { public void setShowPictures(Boolean show) {
mShowPictures = show; mShowPictures = show;
} }
@ -312,4 +312,21 @@ public class SingleMessageView extends LinearLayout {
AttachmentView.AttachmentFileDownloadCallback attachmentCallback) { AttachmentView.AttachmentFileDownloadCallback attachmentCallback) {
this.attachmentCallback = attachmentCallback; this.attachmentCallback = attachmentCallback;
} }
/**
* Save a copy of the {@link com.fsck.k9.controller.MessagingController#getListeners()}. This method will also
* pass along these listeners to the underlying views.
* @param listeners Set of listeners.
*/
public void setListeners(final Set<MessagingListener> listeners) {
if(!mScreenReaderEnabled) {
if(mMessageContentView != null) {
mMessageContentView.setListeners(listeners);
}
} else {
if(mAccessibleMessageContentView != null) {
mAccessibleMessageContentView.setListeners(listeners);
}
}
}
} }

View File

@ -2,13 +2,22 @@ package com.fsck.k9.view;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.widget.ScrollView; import android.widget.ScrollView;
import com.fsck.k9.K9;
import com.fsck.k9.controller.MessagingListener;
/**
* An extension of {@link ScrollView} that allows scrolling to be selectively disabled.
*/
public class ToggleScrollView extends ScrollView { public class ToggleScrollView extends ScrollView {
private GestureDetector mDetector; private GestureDetector mDetector;
private boolean mScrolling = true; private boolean mScrolling = true;
private int mCurrentYPosition;
private double mScrollPercentage;
private ScrollToLastLocationListener mListener;
public ToggleScrollView(Context context, AttributeSet attrs) { public ToggleScrollView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@ -59,4 +68,76 @@ public class ToggleScrollView extends ScrollView {
return false; return false;
} }
} }
/**
* Fetch the current percentage by which this view has been scrolled.
* @return Scroll percentage based on the top edge of the screen, from 0 to 100. This number should never really
* be 100, unless the screen is of 0 height...
*/
public double getScrollPercentage() {
// We save only the Y coordinate instead of the percentage because I don't know how expensive the
// computeVerticalScrollRange() call is.
final int scrollRange = computeVerticalScrollRange();
if(scrollRange == 0) {
return 0;
}
return (double) mCurrentYPosition / scrollRange;
}
/**
* Set the percentage by which we should scroll the page once we get the load complete event. This is
* based on the top edge of the view.
* @param percentage Percentage of page to scroll to.
*/
public void setScrollPercentage(final double percentage) {
Log.d(K9.LOG_TAG, "ToggleView: Setting last scroll percentage to " + percentage);
this.mScrollPercentage = percentage;
}
/**
* Override {@link ScrollView#onScrollChanged(int, int, int, int)} to record the current x/y position. We use this
* to save our current position for future scrolling.
*
* @param x
* @param y
* @param oldx
* @param oldy
*/
@Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
this.mCurrentYPosition = y;
// I wish Android has a TRACE log level so I wouldn't have to comment this out. This one is really noisy.
// Log.d(K9.LOG_TAG, "ToggleScrollView: mCurrentYPosition=" + y + " scrollRange=" + computeVerticalScrollRange() + " pct=" + getScrollPercentage());
}
/**
* This is a {@link MessagingListener} which listens for when the a message has finished being displayed on the
* screen. We'll scroll the message to the user's last known location once it's done.
*/
class ScrollToLastLocationListener extends MessagingListener {
public void messageViewFinished() {
// Don't scroll if our last position was at the top.
if(mScrollPercentage != 0.0) {
final int scrollRange = computeVerticalScrollRange();
final int newY = (int)(mScrollPercentage * scrollRange);
Log.d(K9.LOG_TAG, "ToggleScrollView: requested " + (100 * mScrollPercentage) + "%, " +
"scrolling to " + newY + "/" + scrollRange);
scrollTo(0, newY);
}
}
}
/**
* Fetch the {@link MessagingListener} for this <code>ScrollView</code>.
* @return
*/
public MessagingListener getListener() {
if(this.mListener != null) {
return this.mListener;
} else {
return this.mListener = new ScrollToLastLocationListener();
}
}
} }