Smooth scrolling to selection after new/edit/copy
Change-Id: I8dd5702c6dc3a46b35fea1a3e851238f6e8a38f0
diff --git a/src/com/android/contacts/activities/ContactBrowserActivity.java b/src/com/android/contacts/activities/ContactBrowserActivity.java
index 6a68547..55e9e37 100644
--- a/src/com/android/contacts/activities/ContactBrowserActivity.java
+++ b/src/com/android/contacts/activities/ContactBrowserActivity.java
@@ -75,6 +75,7 @@
private static final int SUBACTIVITY_NEW_CONTACT = 2;
private static final int SUBACTIVITY_DISPLAY_GROUP = 3;
+ private static final int SUBACTIVITY_EDIT_CONTACT = 4;
private DialogManager mDialogManager = new DialogManager(this);
@@ -190,6 +191,7 @@
}
mListFragment.setSelectedContactUri(uri);
setupContactDetailFragment(uri);
+ mListFragment.scrollToSelectedContact();
}
}
@@ -281,6 +283,10 @@
return;
}
+ // If we are closing the editor, it's a good idea to scroll the list
+ // to the contact we have just finished editing.
+ boolean scrollToSelection = mEditorFragment != null;
+
// No editor here
closeEditorFragment(true);
@@ -308,6 +314,9 @@
.replace(R.id.detail_container, mEmptyFragment)
.commit();
}
+ if (scrollToSelection) {
+ mListFragment.scrollToSelectedContact();
+ }
}
private void setupContactEditorFragment(Uri contactLookupUri) {
@@ -470,7 +479,7 @@
if (extras != null) {
intent.putExtras(extras);
}
- startActivity(intent);
+ startActivityForResult(intent, SUBACTIVITY_EDIT_CONTACT);
}
}
@@ -511,7 +520,8 @@
@Override
public void onEditRequested(Uri contactLookupUri) {
- startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri));
+ startActivityForResult(
+ new Intent(Intent.ACTION_EDIT, contactLookupUri), SUBACTIVITY_EDIT_CONTACT);
}
@Override
@@ -715,6 +725,11 @@
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
+ case SUBACTIVITY_EDIT_CONTACT: {
+ mListFragment.scrollToSelectedContact();
+ break;
+ }
+
case SUBACTIVITY_NEW_CONTACT: {
if (resultCode == RESULT_OK && mContactContentDisplayed) {
final Uri newContactUri = data.getData();
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index fe82129..2dfe10d 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -16,6 +16,7 @@
package com.android.contacts.list;
import com.android.contacts.R;
+import com.android.contacts.widget.ListViewUtils;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.CursorLoader;
@@ -27,6 +28,7 @@
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Directory;
import android.text.TextUtils;
+import android.widget.ListView;
/**
* Fragment containing a contact list used for browsing (as compared to
@@ -42,6 +44,7 @@
private Uri mSelectedContactUri;
private long mSelectedContactDirectoryId;
private String mSelectedContactLookupKey;
+ private boolean mScrollToSelectionRequested;
private OnContactBrowserActionListener mListener;
@@ -229,4 +232,34 @@
super.finish();
mListener.onFinishAction();
}
+
+ public void scrollToSelectedContact() {
+ mScrollToSelectionRequested = true;
+ scrollToSelectedContactIfNeeded();
+ }
+
+ @Override
+ protected void completeRestoreInstanceState() {
+ super.completeRestoreInstanceState();
+ scrollToSelectedContactIfNeeded();
+ }
+
+ private void scrollToSelectedContactIfNeeded() {
+ if (!mScrollToSelectionRequested) {
+ return;
+ }
+
+ ContactListAdapter adapter = getAdapter();
+ if (adapter == null) {
+ return;
+ }
+
+ int position = adapter.getSelectedContactPosition();
+ if (position != -1) {
+ mScrollToSelectionRequested = false;
+ ListView listView = getListView();
+ ListViewUtils.smartSmoothScrollToPosition(
+ listView, position + listView.getHeaderViewsCount());
+ }
+ }
}
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 235f8f3..ca5a9a8 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -228,7 +228,8 @@
outState.putParcelable(KEY_REQUEST, mRequest);
if (mListView != null) {
- outState.putParcelable(KEY_LIST_STATE, mListView.onSaveInstanceState());
+ mListState = mListView.onSaveInstanceState();
+ outState.putParcelable(KEY_LIST_STATE, mListState);
}
}
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
index c6ceb18..f2b0da7 100644
--- a/src/com/android/contacts/list/ContactListAdapter.java
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -228,4 +228,48 @@
view.showSnippet(cursor, CONTACT_SNIPPET_MIMETYPE_COLUMN_INDEX,
CONTACT_SNIPPET_DATA1_COLUMN_INDEX, CONTACT_SNIPPET_DATA4_COLUMN_INDEX);
}
+
+ public int getSelectedContactPosition() {
+ if (mSelectedContactLookupKey == null) {
+ return -1;
+ }
+
+ Cursor cursor = null;
+ int partitionIndex = -1;
+ int partitionCount = getPartitionCount();
+ for (int i = 0; i < partitionCount; i++) {
+ DirectoryPartition partition = (DirectoryPartition) getPartition(i);
+ if (partition.getDirectoryId() == mSelectedContactDirectoryId) {
+ partitionIndex = i;
+ break;
+ }
+ }
+ if (partitionIndex == -1) {
+ return -1;
+ }
+
+ cursor = getCursor(partitionIndex);
+ if (cursor == null) {
+ return -1;
+ }
+
+ cursor.moveToPosition(-1); // Reset cursor
+ int offset = -1;
+ while (cursor.moveToNext()) {
+ String lookupKey = cursor.getString(CONTACT_LOOKUP_KEY_COLUMN_INDEX);
+ if (mSelectedContactLookupKey.equals(lookupKey)) {
+ offset = cursor.getPosition();
+ break;
+ }
+ }
+ if (offset == -1) {
+ return -1;
+ }
+
+ int position = getPositionForPartition(partitionIndex) + offset;
+ if (hasHeader(partitionIndex)) {
+ position++;
+ }
+ return position;
+ }
}
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index 26f5375..2d4af4f 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -668,6 +668,11 @@
return false;
}
+ // If we are about to close the editor - there is no need to refresh the data
+ if (saveMode == SaveMode.CLOSE) {
+ getLoaderManager().stopLoader(LOADER_DATA);
+ }
+
mStatus = Status.SAVING;
final PersistTask task = new PersistTask(this, saveMode);
task.execute(mState);
diff --git a/src/com/android/contacts/widget/ListViewUtils.java b/src/com/android/contacts/widget/ListViewUtils.java
new file mode 100644
index 0000000..4003122
--- /dev/null
+++ b/src/com/android/contacts/widget/ListViewUtils.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.widget;
+
+import android.widget.ListView;
+
+/**
+ * Utility methods for working with ListView.
+ */
+public final class ListViewUtils {
+
+ /**
+ * Brings the specified position to view by performing a jump-scroll maneuver:
+ * first it jumps to some position near the one requested and then does a smooth
+ * scroll to the requested position. This creates an impression of full smooth
+ * scrolling without actually traversing the entire list.
+ */
+ public static void smartSmoothScrollToPosition(final ListView listView, final int position) {
+ listView.post(new Runnable() {
+
+ @Override
+ public void run() {
+ int firstPosition = listView.getFirstVisiblePosition();
+ int lastPosition = listView.getLastVisiblePosition();
+ if (position >= firstPosition && position <= lastPosition) {
+ return; // Already on screen
+ }
+
+ // We will first position the list a couple of screens before or after
+ // the new selection and then scroll smoothly to it.
+ int twoScreens = (lastPosition - firstPosition) * 2;
+ int preliminaryPosition;
+ if (position < firstPosition) {
+ preliminaryPosition = position + twoScreens;
+ if (preliminaryPosition >= listView.getCount()) {
+ preliminaryPosition = listView.getCount() - 1;
+ }
+ } else {
+ preliminaryPosition = position - twoScreens;
+ if (preliminaryPosition < 0) {
+ preliminaryPosition = 0;
+ }
+ }
+ listView.setSelection(preliminaryPosition);
+ scrollToFinalPosition(listView, position);
+ }
+ });
+ }
+
+ private static void scrollToFinalPosition(final ListView listView, final int position) {
+ // Position the element at about 1/3 of the list height
+ final int offset = (int) (listView.getHeight() * 0.33);
+ listView.post(new Runnable() {
+
+ @Override
+ public void run() {
+ listView.smoothScrollToPositionFromTop(position, offset);
+ }
+ });
+ }
+}