1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-30 13:12:25 -05:00

Scroll to the last known position in the MessageView when rotating the screen.

This commit is contained in:
Andrew Chen 2011-11-02 16:39:23 -07:00
parent 5ff6341a84
commit 763ff2752d
6 changed files with 145 additions and 4 deletions

View File

@ -38,6 +38,7 @@ public class MessageView extends K9Activity implements OnClickListener {
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_ORIGINATING_INTENT = "com.fsck.k9.MessageView_originatingIntent"; private static final String EXTRA_ORIGINATING_INTENT = "com.fsck.k9.MessageView_originatingIntent";
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;
@ -96,6 +97,7 @@ public class MessageView extends K9Activity implements OnClickListener {
private Listener mListener = new Listener(); private Listener mListener = new Listener();
private MessageViewHandler mHandler = new MessageViewHandler(); private MessageViewHandler mHandler = new MessageViewHandler();
private StorageManager.StorageListener mStorageListener = new StorageListenerImplementation(); private StorageManager.StorageListener mStorageListener = new StorageListenerImplementation();
private MessagingListener mLoadCompleteListener = new ScrollToLastLocationListener();
/** this variable is used to save the calling AttachmentView /** this variable is used to save the calling AttachmentView
* until the onActivityResult is called. * until the onActivityResult is called.
@ -109,6 +111,11 @@ public class MessageView extends K9Activity implements OnClickListener {
*/ */
private String mDstFolder; private String mDstFolder;
/**
* Used after restore/rotation to scroll our message to the last known location.
*/
private double mScrollPercentage;
private final class StorageListenerImplementation implements StorageManager.StorageListener { private final class StorageListenerImplementation implements StorageManager.StorageListener {
@Override @Override
public void onUnmount(String providerId) { public void onUnmount(String providerId) {
@ -408,8 +415,14 @@ public class MessageView extends K9Activity implements OnClickListener {
} }
}; };
}); });
mMessageView.initialize(this); mMessageView.initialize(this);
// Add listener for message load completion. We'll use this to scroll the page to user's last known
// location.
mController.addListener(mLoadCompleteListener);
mMessageView.setListeners(mController.getListeners());
setTitle(""); setTitle("");
final Intent intent = getIntent(); final Intent intent = getIntent();
@ -530,6 +543,9 @@ 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());
if(mTopView != null) {
outState.putDouble(EXTRA_SCROLL_PERCENTAGE, mTopView.getScrollPercentage());
}
} }
@Override @Override
@ -538,6 +554,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));
mScrollPercentage = savedInstanceState.getDouble(EXTRA_SCROLL_PERCENTAGE);
} }
private void displayMessage(MessageReference ref) { private void displayMessage(MessageReference ref) {
@ -657,11 +674,13 @@ public class MessageView extends K9Activity implements OnClickListener {
onAccountUnavailable(); onAccountUnavailable();
return; return;
} }
mController.addListener(mLoadCompleteListener);
StorageManager.getInstance(getApplication()).addListener(mStorageListener); StorageManager.getInstance(getApplication()).addListener(mStorageListener);
} }
@Override @Override
protected void onPause() { protected void onPause() {
mController.removeListener(mLoadCompleteListener);
StorageManager.getInstance(getApplication()).removeListener(mStorageListener); StorageManager.getInstance(getApplication()).removeListener(mStorageListener);
super.onPause(); super.onPause();
} }
@ -873,6 +892,8 @@ public class MessageView extends K9Activity implements OnClickListener {
@Override @Override
protected void onNext() { protected void onNext() {
// Reset scroll percentage when we change messages
mScrollPercentage = 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;
@ -888,6 +909,8 @@ public class MessageView extends K9Activity implements OnClickListener {
@Override @Override
protected void onPrevious() { protected void onPrevious() {
// Reset scroll percentage when we change messages
mScrollPercentage = 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;
@ -1277,4 +1300,17 @@ public class MessageView extends K9Activity implements OnClickListener {
mMessageView.loadBodyFromText(mAccount.getCryptoProvider(), mPgpData, mMessage, mPgpData.getDecryptedData(), "text/plain"); mMessageView.loadBodyFromText(mAccount.getCryptoProvider(), mPgpData, mMessage, mPgpData.getDecryptedData(), "text/plain");
} }
/**
* 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(mTopView != null && mScrollPercentage != 0.0) {
Log.d(K9.LOG_TAG, "MessageView has finished loading, scrolling to last known location.");
mTopView.setScrollPercentage(mScrollPercentage);
}
}
}
} }

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,19 @@ 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;
/**
* 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 currentYPosition;
public ToggleScrollView(Context context, AttributeSet attrs) { public ToggleScrollView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@ -59,4 +65,44 @@ 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() {
if(computeVerticalScrollRange() == 0) {
return 0;
}
return (double)currentYPosition / computeVerticalScrollRange();
}
/**
* Scroll the screen to a specific percentage of the page. This is based on the top edge of the page.
* @param percentage Percentage of page to scroll to.
*/
public void setScrollPercentage(final double percentage) {
final int newY = (int)(percentage * computeVerticalScrollRange());
Log.d(K9.LOG_TAG, "ToggleScrollView: requested " + (100 * percentage) + "%, scrolling to " + newY + "/" + computeVerticalScrollRange());
scrollTo(0, newY);
}
/**
* 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.currentYPosition = 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: currentYPosition=" + y + " scrollRange=" + computeVerticalScrollRange() + " pct=" + getScrollPercentage());
}
} }