Merge "FloatingActionMode.setOutsideTouchable" into pi-dev

This commit is contained in:
Abodunrinwa Toki
2018-04-27 19:46:10 +00:00
committed by Android (Google) Code Review
5 changed files with 104 additions and 18 deletions

View File

@@ -123,6 +123,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.EditableInputConnection;
import java.lang.annotation.Retention;
@@ -2215,6 +2216,15 @@ public class Editor {
ActionMode.Callback actionModeCallback = new TextActionModeCallback(actionMode);
mTextActionMode = mTextView.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
final boolean selectableText = mTextView.isTextEditable() || mTextView.isTextSelectable();
if (actionMode == TextActionMode.TEXT_LINK && !selectableText
&& mTextActionMode instanceof FloatingActionMode) {
// Make the toolbar outside-touchable so that it can be dismissed when the user clicks
// outside of it.
((FloatingActionMode) mTextActionMode).setOutsideTouchable(true,
() -> stopTextActionMode());
}
final boolean selectionStarted = mTextActionMode != null;
if (selectionStarted
&& mTextView.isTextEditable() && !mTextView.isTextSelectable()

View File

@@ -17,6 +17,7 @@
package com.android.internal.view;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
@@ -30,6 +31,7 @@ import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.WindowManager;
import android.widget.PopupWindow;
import com.android.internal.R;
import com.android.internal.util.Preconditions;
import com.android.internal.view.menu.MenuBuilder;
@@ -241,6 +243,23 @@ public final class FloatingActionMode extends ActionMode {
}
}
/**
* If this is set to true, the action mode view will dismiss itself on touch events outside of
* its window. This only makes sense if the action mode view is a PopupWindow that is touchable
* but not focusable, which means touches outside of the window will be delivered to the window
* behind. The default is false.
*
* This is for internal use only and the approach to this may change.
* @hide
*
* @param outsideTouchable whether or not this action mode is "outside touchable"
* @param onDismiss optional. Sets a callback for when this action mode popup dismisses itself
*/
public void setOutsideTouchable(
boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
mFloatingToolbar.setOutsideTouchable(outsideTouchable, onDismiss);
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
mFloatingToolbarVisibilityHelper.setWindowFocused(hasWindowFocus);

View File

@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
@@ -259,6 +260,22 @@ public final class FloatingToolbar {
return mPopup.isHidden();
}
/**
* If this is set to true, the action mode view will dismiss itself on touch events outside of
* its window. If the toolbar is already showing, it will be re-shown so that this setting takes
* effect immediately.
*
* @param outsideTouchable whether or not this action mode is "outside touchable"
* @param onDismiss optional. Sets a callback for when this action mode popup dismisses itself
*/
public void setOutsideTouchable(
boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
if (mPopup.setOutsideTouchable(outsideTouchable, onDismiss) && isShowing()) {
dismiss();
doShow();
}
}
private void doShow() {
List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
menuItems.sort(mMenuItemComparator);
@@ -512,6 +529,32 @@ public final class FloatingToolbar {
});
}
/**
* Makes this toolbar "outside touchable" and sets the onDismissListener.
* This will take effect the next time the toolbar is re-shown.
*
* @param outsideTouchable if true, the popup will be made "outside touchable" and
* "non focusable". The reverse will happen if false.
* @param onDismiss
*
* @return true if the "outsideTouchable" setting was modified. Otherwise returns false
*
* @see PopupWindow#setOutsideTouchable(boolean)
* @see PopupWindow#setFocusable(boolean)
* @see PopupWindow.OnDismissListener
*/
public boolean setOutsideTouchable(
boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
boolean ret = false;
if (mPopupWindow.isOutsideTouchable() ^ outsideTouchable) {
mPopupWindow.setOutsideTouchable(outsideTouchable);
mPopupWindow.setFocusable(!outsideTouchable);
ret = true;
}
mPopupWindow.setOnDismissListener(onDismiss);
return ret;
}
/**
* Lays out buttons for the specified menu items.
* Requires a subsequent call to {@link #show()} to show the items.

View File

@@ -31,6 +31,8 @@
<TextView
android:id="@+id/nonselectable_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:focusable="false"
android:focusableInTouchMode="false" />
</LinearLayout>

View File

@@ -318,40 +318,52 @@ public class TextViewActivityTest {
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(position));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
}
@Test
public void testToolbarAppearsAfterLinkClickedNonselectable() throws Throwable {
TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
int position = (textLink.getStart() + textLink.getEnd()) / 2;
final TextView textView = mActivity.findViewById(R.id.nonselectable_textview);
final TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
final int position = (textLink.getStart() + textLink.getEnd()) / 2;
onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
assertTrue(textView.hasSelection());
// toggle
onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
sleepForFloatingToolbarPopup();
assertFalse(textView.hasSelection());
onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
assertTrue(textView.hasSelection());
// click outside
onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(0));
sleepForFloatingToolbarPopup();
assertFalse(textView.hasSelection());
}
@Test
public void testSelectionRemovedWhenNonselectableTextLosesFocus() throws Throwable {
// Add a link to both selectable and nonselectable TextViews:
TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.textview);
int selectablePosition = (textLink.getStart() + textLink.getEnd()) / 2;
textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
int nonselectablePosition = (textLink.getStart() + textLink.getEnd()) / 2;
TextView selectableTextView = mActivity.findViewById(R.id.textview);
TextView nonselectableTextView = mActivity.findViewById(R.id.nonselectable_textview);
final TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
final int position = (textLink.getStart() + textLink.getEnd()) / 2;
final TextView textView = mActivity.findViewById(R.id.nonselectable_textview);
mActivityRule.runOnUiThread(() -> textView.setFocusableInTouchMode(true));
onView(withId(R.id.nonselectable_textview))
.perform(clickOnTextAtIndex(nonselectablePosition));
onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
assertTrue(nonselectableTextView.hasSelection());
assertTrue(textView.hasSelection());
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(selectablePosition));
mActivityRule.runOnUiThread(() -> textView.clearFocus());
mInstrumentation.waitForIdleSync();
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
assertTrue(selectableTextView.hasSelection());
assertFalse(nonselectableTextView.hasSelection());
assertFalse(textView.hasSelection());
}
@Test