Merge pull request #83 from andrewachen/messageview_save_location
Restore position in MessageView after rotation screen.
This commit is contained in:
commit
ef631e21d1
6 changed files with 159 additions and 5 deletions
|
@ -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_REFERENCES = "com.fsck.k9.MessageView_messageReferences";
|
||||
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 STATE_PGP_DATA = "pgpData";
|
||||
private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
|
||||
|
@ -372,13 +373,19 @@ public class MessageView extends K9Activity implements OnClickListener {
|
|||
}
|
||||
};
|
||||
});
|
||||
|
||||
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("");
|
||||
final Intent intent = getIntent();
|
||||
|
||||
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);
|
||||
|
@ -492,6 +499,7 @@ public class MessageView extends K9Activity implements OnClickListener {
|
|||
outState.putParcelableArrayList(EXTRA_MESSAGE_REFERENCES, mMessageReferences);
|
||||
outState.putSerializable(STATE_PGP_DATA, mPgpData);
|
||||
outState.putBoolean(SHOW_PICTURES, mMessageView.showPictures());
|
||||
outState.putDouble(EXTRA_SCROLL_PERCENTAGE, mTopView.getScrollPercentage());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -500,6 +508,7 @@ public class MessageView extends K9Activity implements OnClickListener {
|
|||
mPgpData = (PgpData) savedInstanceState.getSerializable(STATE_PGP_DATA);
|
||||
mMessageView.updateCryptoLayout(mAccount.getCryptoProvider(), mPgpData, mMessage);
|
||||
mMessageView.setLoadPictures(savedInstanceState.getBoolean(SHOW_PICTURES));
|
||||
mTopView.setScrollPercentage(savedInstanceState.getDouble(EXTRA_SCROLL_PERCENTAGE));
|
||||
}
|
||||
|
||||
private void displayMessage(MessageReference ref) {
|
||||
|
@ -619,11 +628,13 @@ public class MessageView extends K9Activity implements OnClickListener {
|
|||
onAccountUnavailable();
|
||||
return;
|
||||
}
|
||||
mController.addListener(mTopView.getListener());
|
||||
StorageManager.getInstance(getApplication()).addListener(mStorageListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
mController.removeListener(mTopView.getListener());
|
||||
StorageManager.getInstance(getApplication()).removeListener(mStorageListener);
|
||||
super.onPause();
|
||||
}
|
||||
|
@ -850,6 +861,8 @@ public class MessageView extends K9Activity implements OnClickListener {
|
|||
}
|
||||
|
||||
protected void onNext() {
|
||||
// Reset scroll percentage when we change messages
|
||||
mTopView.setScrollPercentage(0);
|
||||
if (mNextMessage == null) {
|
||||
Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
|
@ -864,6 +877,8 @@ public class MessageView extends K9Activity implements OnClickListener {
|
|||
}
|
||||
|
||||
protected void onPrevious() {
|
||||
// Reset scroll percentage when we change messages
|
||||
mTopView.setScrollPercentage(0);
|
||||
if (mPreviousMessage == null) {
|
||||
Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
|
@ -1091,8 +1106,8 @@ public class MessageView extends K9Activity implements OnClickListener {
|
|||
mTopView.scrollTo(0, 0);
|
||||
try {
|
||||
if (MessageView.this.mMessage != null
|
||||
&& MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL)
|
||||
&& message.isSet(Flag.X_DOWNLOADED_FULL)) {
|
||||
&& MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL)
|
||||
&& message.isSet(Flag.X_DOWNLOADED_FULL)) {
|
||||
mMessageView.setHeaders(message, account);
|
||||
}
|
||||
MessageView.this.mMessage = message;
|
||||
|
@ -1252,5 +1267,4 @@ public class MessageView extends K9Activity implements OnClickListener {
|
|||
// sometimes shows the original encrypted content
|
||||
mMessageView.loadBodyFromText(mAccount.getCryptoProvider(), mPgpData, mMessage, mPgpData.getDecryptedData(), "text/plain");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -112,6 +112,11 @@ public class MessagingListener {
|
|||
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) {
|
||||
}
|
||||
|
||||
|
|
|
@ -26,11 +26,15 @@ import android.webkit.WebView;
|
|||
import android.widget.TextView;
|
||||
|
||||
import com.fsck.k9.activity.AccessibleEmailContentActivity;
|
||||
import com.fsck.k9.controller.MessagingListener;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class AccessibleWebView extends TextView {
|
||||
private Context mContext;
|
||||
private String mHtmlSource;
|
||||
private WebView mDummyWebView;
|
||||
private Set<MessagingListener> mListeners = null;
|
||||
|
||||
public AccessibleWebView(Context context) {
|
||||
super(context);
|
||||
|
@ -68,6 +72,13 @@ public class AccessibleWebView extends TextView {
|
|||
String historyUrl) {
|
||||
mHtmlSource = data;
|
||||
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() {
|
||||
|
@ -92,4 +103,8 @@ public class AccessibleWebView extends TextView {
|
|||
i.putExtra("content", mHtmlSource);
|
||||
mContext.startActivity(i);
|
||||
}
|
||||
|
||||
public void setListeners(final Set<MessagingListener> listeners) {
|
||||
this.mListeners = listeners;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.fsck.k9.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Picture;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
@ -10,10 +11,15 @@ import android.webkit.WebView;
|
|||
import android.widget.Toast;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.controller.MessagingListener;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
|
||||
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
|
||||
|
@ -97,6 +103,18 @@ public class MessageWebView extends WebView {
|
|||
|
||||
// Disable network images by default. This is overridden by preferences.
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void setListeners(final Set<MessagingListener> listeners) {
|
||||
this.mListeners = listeners;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.fsck.k9.mail.internet.MimeUtility;
|
|||
import com.fsck.k9.mail.store.LocalStore;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -116,11 +117,10 @@ public class SingleMessageView extends LinearLayout {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean showPictures() {
|
||||
return mShowPictures;
|
||||
}
|
||||
|
||||
public void setShowPictures(Boolean show) {
|
||||
mShowPictures = show;
|
||||
}
|
||||
|
@ -312,4 +312,21 @@ public class SingleMessageView extends LinearLayout {
|
|||
AttachmentView.AttachmentFileDownloadCallback 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,22 @@ package com.fsck.k9.view;
|
|||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
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 {
|
||||
private GestureDetector mDetector;
|
||||
private boolean mScrolling = true;
|
||||
private int mCurrentYPosition;
|
||||
private double mScrollPercentage;
|
||||
private ScrollToLastLocationListener mListener;
|
||||
|
||||
public ToggleScrollView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
@ -59,4 +68,76 @@ public class ToggleScrollView extends ScrollView {
|
|||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue