Skip to content

Commit 7fc4fbc

Browse files
adampAndroid (Google) Code Review
authored andcommitted
Merge "Automatic persistent text selection for ListViews" into jb-dev
2 parents 0ae107f + 057a585 commit 7fc4fbc

File tree

4 files changed

+48
-11
lines changed

4 files changed

+48
-11
lines changed

core/java/android/view/View.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5584,7 +5584,7 @@ public void setHasTransientState(boolean hasTransientState) {
55845584
"unmatched pair of setHasTransientState calls");
55855585
}
55865586
if ((hasTransientState && mTransientStateCount == 1) ||
5587-
(hasTransientState && mTransientStateCount == 0)) {
5587+
(!hasTransientState && mTransientStateCount == 0)) {
55885588
// update flag if we've just incremented up from 0 or decremented down to 0
55895589
mPrivateFlags2 = (mPrivateFlags2 & ~HAS_TRANSIENT_STATE) |
55905590
(hasTransientState ? HAS_TRANSIENT_STATE : 0);

core/java/android/widget/AbsListView.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6317,6 +6317,7 @@ void addScrapView(View scrap, int position) {
63176317
if (mTransientStateViews == null) {
63186318
mTransientStateViews = new SparseArray<View>();
63196319
}
6320+
scrap.dispatchStartTemporaryDetach();
63206321
mTransientStateViews.put(position, scrap);
63216322
}
63226323
return;

core/java/android/widget/Editor.java

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package android.widget;
1818

19+
import com.android.internal.util.ArrayUtils;
20+
import com.android.internal.widget.EditableInputConnection;
21+
1922
import android.R;
2023
import android.content.ClipData;
2124
import android.content.ClipData.Item;
@@ -70,10 +73,10 @@
7073
import android.view.MenuItem;
7174
import android.view.MotionEvent;
7275
import android.view.View;
73-
import android.view.ViewConfiguration;
74-
import android.view.ViewGroup;
7576
import android.view.View.DragShadowBuilder;
7677
import android.view.View.OnClickListener;
78+
import android.view.ViewConfiguration;
79+
import android.view.ViewGroup;
7780
import android.view.ViewGroup.LayoutParams;
7881
import android.view.ViewParent;
7982
import android.view.ViewTreeObserver;
@@ -85,12 +88,12 @@
8588
import android.view.inputmethod.InputConnection;
8689
import android.view.inputmethod.InputMethodManager;
8790
import android.widget.AdapterView.OnItemClickListener;
91+
import android.widget.Editor.InputContentType;
92+
import android.widget.Editor.InputMethodState;
93+
import android.widget.Editor.SelectionModifierCursorController;
8894
import android.widget.TextView.Drawables;
8995
import android.widget.TextView.OnEditorActionListener;
9096

91-
import com.android.internal.util.ArrayUtils;
92-
import com.android.internal.widget.EditableInputConnection;
93-
9497
import java.text.BreakIterator;
9598
import java.util.Arrays;
9699
import java.util.Comparator;
@@ -102,6 +105,8 @@
102105
* @hide
103106
*/
104107
public class Editor {
108+
private static final String TAG = "Editor";
109+
105110
static final int BLINK = 500;
106111
private static final float[] TEMP_POSITION = new float[2];
107112
private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
@@ -151,6 +156,8 @@ public class Editor {
151156

152157
boolean mInBatchEditControllers;
153158
boolean mShowSoftInputOnFocus = true;
159+
boolean mPreserveDetachedSelection;
160+
boolean mTemporaryDetach;
154161

155162
SuggestionsPopupWindow mSuggestionsPopupWindow;
156163
SuggestionRangeSpan mSuggestionRangeSpan;
@@ -190,6 +197,7 @@ void onAttachedToWindow() {
190197
showError();
191198
mShowErrorAfterAttach = false;
192199
}
200+
mTemporaryDetach = false;
193201

194202
final ViewTreeObserver observer = mTextView.getViewTreeObserver();
195203
// No need to create the controller.
@@ -198,10 +206,22 @@ void onAttachedToWindow() {
198206
observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
199207
}
200208
if (mSelectionModifierCursorController != null) {
209+
mSelectionModifierCursorController.resetTouchOffsets();
201210
observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
202211
}
203212
updateSpellCheckSpans(0, mTextView.getText().length(),
204213
true /* create the spell checker if needed */);
214+
215+
if (mTextView.hasTransientState() &&
216+
mTextView.getSelectionStart() != mTextView.getSelectionEnd()) {
217+
// Since transient state is reference counted make sure it stays matched
218+
// with our own calls to it for managing selection.
219+
// The action mode callback will set this back again when/if the action mode starts.
220+
mTextView.setHasTransientState(false);
221+
222+
// We had an active selection from before, start the selection mode.
223+
startSelectionActionMode();
224+
}
205225
}
206226

207227
void onDetachedFromWindow() {
@@ -234,7 +254,10 @@ void onDetachedFromWindow() {
234254
mSpellChecker = null;
235255
}
236256

257+
mPreserveDetachedSelection = true;
237258
hideControllers();
259+
mPreserveDetachedSelection = false;
260+
mTemporaryDetach = false;
238261
}
239262

240263
private void showError() {
@@ -877,7 +900,9 @@ void onFocusChanged(boolean focused, int direction) {
877900
hideControllers();
878901
Selection.setSelection((Spannable) mTextView.getText(), selStart, selEnd);
879902
} else {
903+
if (mTemporaryDetach) mPreserveDetachedSelection = true;
880904
hideControllers();
905+
if (mTemporaryDetach) mPreserveDetachedSelection = false;
881906
downgradeEasyCorrectionSpans();
882907
}
883908

@@ -2679,6 +2704,7 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
26792704

26802705
if (menu.hasVisibleItems() || mode.getCustomView() != null) {
26812706
getSelectionController().show();
2707+
mTextView.setHasTransientState(true);
26822708
return true;
26832709
} else {
26842710
return false;
@@ -2707,7 +2733,17 @@ public void onDestroyActionMode(ActionMode mode) {
27072733
if (mCustomSelectionActionModeCallback != null) {
27082734
mCustomSelectionActionModeCallback.onDestroyActionMode(mode);
27092735
}
2710-
Selection.setSelection((Spannable) mTextView.getText(), mTextView.getSelectionEnd());
2736+
2737+
/*
2738+
* If we're ending this mode because we're detaching from a window,
2739+
* we still have selection state to preserve. Don't clear it, we'll
2740+
* bring back the selection mode when (if) we get reattached.
2741+
*/
2742+
if (!mPreserveDetachedSelection) {
2743+
Selection.setSelection((Spannable) mTextView.getText(),
2744+
mTextView.getSelectionEnd());
2745+
mTextView.setHasTransientState(false);
2746+
}
27112747

27122748
if (mSelectionModifierCursorController != null) {
27132749
mSelectionModifierCursorController.hide();

core/java/android/widget/TextView.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7257,10 +7257,9 @@ public void onStartTemporaryDetach() {
72577257
// usually because this instance is an editable field in a list
72587258
if (!mDispatchTemporaryDetach) mTemporaryDetach = true;
72597259

7260-
// Because of View recycling in ListView, there is no easy way to know when a TextView with
7261-
// selection becomes visible again. Until a better solution is found, stop text selection
7262-
// mode (if any) as soon as this TextView is recycled.
7263-
if (mEditor != null) mEditor.hideControllers();
7260+
// Tell the editor that we are temporarily detached. It can use this to preserve
7261+
// selection state as needed.
7262+
if (mEditor != null) mEditor.mTemporaryDetach = true;
72647263
}
72657264

72667265
@Override
@@ -7269,6 +7268,7 @@ public void onFinishTemporaryDetach() {
72697268
// Only track when onStartTemporaryDetach() is called directly,
72707269
// usually because this instance is an editable field in a list
72717270
if (!mDispatchTemporaryDetach) mTemporaryDetach = false;
7271+
if (mEditor != null) mEditor.mTemporaryDetach = false;
72727272
}
72737273

72747274
@Override

0 commit comments

Comments
 (0)