diff options
9 files changed, 459 insertions, 13 deletions
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 8a1a8c554635..506035087ead 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -9646,7 +9646,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean canShare() { - return canCopy() && isDeviceProvisioned(); + if (!getContext().canStartActivityForResult() || !isDeviceProvisioned()) { + return false; + } + return canCopy(); } boolean isDeviceProvisioned() { @@ -9669,16 +9672,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean canProcessText() { - if (!getContext().canStartActivityForResult() || getId() == View.NO_ID - || hasPasswordTransformationMethod()) { + if (getId() == View.NO_ID) { return false; } - - if (mText.length() > 0 && hasSelection() && mEditor != null) { - return true; - } - - return false; + return canShare(); } boolean canSelectAllText() { diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml index 75195c4ffbcc..a95489b33013 100644 --- a/packages/SystemUI/res/layout/remote_input.xml +++ b/packages/SystemUI/res/layout/remote_input.xml @@ -43,6 +43,7 @@ android:singleLine="true" android:ellipsize="start" android:inputType="textShortMessage|textAutoCorrect|textCapSentences" + android:textIsSelectable="true" android:imeOptions="actionSend|flagNoExtractUi" /> <FrameLayout diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 8f1517d9dfe0..bb4a771ec3d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -1107,6 +1107,7 @@ public abstract class BaseStatusBar extends SystemUI implements }); a.start(); guts.setExposed(true); + row.closeRemoteInput(); mStackScroller.onHeightChanged(null, true /* needsAnimation */); mNotificationGutsExposed = guts; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 246f15ee196a..22bb8ebb2dc4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -579,6 +579,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } } + public void closeRemoteInput() { + mPrivateLayout.closeRemoteInput(); + mPublicLayout.closeRemoteInput(); + } + public interface ExpansionLogger { public void logNotificationExpansion(String key, boolean userAction, boolean expanded); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 3b875774681e..c2df292c0880 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -69,6 +69,9 @@ public class NotificationContentView extends FrameLayout { private View mHeadsUpChild; private HybridNotificationView mSingleLineView; + private RemoteInputView mExpandedRemoteInput; + private RemoteInputView mHeadsUpRemoteInput; + private NotificationViewWrapper mContractedWrapper; private NotificationViewWrapper mExpandedWrapper; private NotificationViewWrapper mHeadsUpWrapper; @@ -743,15 +746,19 @@ public class NotificationContentView extends FrameLayout { View bigContentView = mExpandedChild; if (bigContentView != null) { - applyRemoteInput(bigContentView, entry, hasRemoteInput); + mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput); + } else { + mExpandedRemoteInput = null; } View headsUpContentView = mHeadsUpChild; if (headsUpContentView != null) { - applyRemoteInput(headsUpContentView, entry, hasRemoteInput); + mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput); + } else { + mHeadsUpRemoteInput = null; } } - private void applyRemoteInput(View view, NotificationData.Entry entry, boolean hasRemoteInput) { + private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry, boolean hasRemoteInput) { View actionContainerCandidate = view.findViewById( com.android.internal.R.id.actions_container); if (actionContainerCandidate instanceof FrameLayout) { @@ -777,7 +784,20 @@ public class NotificationContentView extends FrameLayout { color = mContext.getColor(R.color.default_remote_input_background); } riv.setBackgroundColor(color); + + return riv; } + return existing; + } + return null; + } + + public void closeRemoteInput() { + if (mHeadsUpRemoteInput != null) { + mHeadsUpRemoteInput.close(); + } + if (mExpandedRemoteInput != null) { + mExpandedRemoteInput.close(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index eb5b57e4cb15..95f26d4cde87 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -16,24 +16,42 @@ package com.android.systemui.statusbar.phone; +import android.annotation.ColorInt; +import android.annotation.DrawableRes; +import android.annotation.LayoutRes; import android.app.StatusBarManager; import android.content.Context; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.media.session.MediaSessionLegacyHelper; +import android.net.Uri; +import android.os.Bundle; import android.os.IBinder; import android.util.AttributeSet; +import android.view.ActionMode; +import android.view.InputQueue; import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.MotionEvent; +import android.view.SurfaceHolder; import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.Window; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.widget.FrameLayout; +import com.android.internal.view.FloatingActionMode; +import com.android.internal.widget.FloatingToolbar; import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.statusbar.BaseStatusBar; @@ -57,6 +75,13 @@ public class StatusBarWindowView extends FrameLayout { private final Paint mTransparentSrcPaint = new Paint(); private FalsingManager mFalsingManager; + // Implements the floating action mode for TextView's Cut/Copy/Past menu. Normally provided by + // DecorView, but since this is a special window we have to roll our own. + private View mFloatingActionModeOriginatingView; + private ActionMode mFloatingActionMode; + private FloatingToolbar mFloatingToolbar; + private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener; + public StatusBarWindowView(Context context, AttributeSet attrs) { super(context, attrs); setMotionEventSplittingEnabled(false); @@ -301,5 +326,341 @@ public class StatusBarWindowView extends FrameLayout { a.recycle(); } } + + @Override + public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback, + int type) { + if (type == ActionMode.TYPE_FLOATING) { + return startActionMode(originalView, callback, type); + } + return super.startActionModeForChild(originalView, callback, type); + } + + private ActionMode createFloatingActionMode( + View originatingView, ActionMode.Callback2 callback) { + if (mFloatingActionMode != null) { + mFloatingActionMode.finish(); + } + cleanupFloatingActionModeViews(); + final FloatingActionMode mode = + new FloatingActionMode(mContext, callback, originatingView); + mFloatingActionModeOriginatingView = originatingView; + mFloatingToolbarPreDrawListener = + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + mode.updateViewLocationInWindow(); + return true; + } + }; + return mode; + } + + private void setHandledFloatingActionMode(ActionMode mode) { + mFloatingActionMode = mode; + mFloatingToolbar = new FloatingToolbar(mContext, mFakeWindow); + ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar); + mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary. + mFloatingActionModeOriginatingView.getViewTreeObserver() + .addOnPreDrawListener(mFloatingToolbarPreDrawListener); + } + + private void cleanupFloatingActionModeViews() { + if (mFloatingToolbar != null) { + mFloatingToolbar.dismiss(); + mFloatingToolbar = null; + } + if (mFloatingActionModeOriginatingView != null) { + if (mFloatingToolbarPreDrawListener != null) { + mFloatingActionModeOriginatingView.getViewTreeObserver() + .removeOnPreDrawListener(mFloatingToolbarPreDrawListener); + mFloatingToolbarPreDrawListener = null; + } + mFloatingActionModeOriginatingView = null; + } + } + + private ActionMode startActionMode( + View originatingView, ActionMode.Callback callback, int type) { + ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback); + ActionMode mode = createFloatingActionMode(originatingView, wrappedCallback); + if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) { + setHandledFloatingActionMode(mode); + } else { + mode = null; + } + return mode; + } + + private class ActionModeCallback2Wrapper extends ActionMode.Callback2 { + private final ActionMode.Callback mWrapped; + + public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) { + mWrapped = wrapped; + } + + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + return mWrapped.onCreateActionMode(mode, menu); + } + + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + requestFitSystemWindows(); + return mWrapped.onPrepareActionMode(mode, menu); + } + + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + return mWrapped.onActionItemClicked(mode, item); + } + + public void onDestroyActionMode(ActionMode mode) { + mWrapped.onDestroyActionMode(mode); + if (mode == mFloatingActionMode) { + cleanupFloatingActionModeViews(); + mFloatingActionMode = null; + } + requestFitSystemWindows(); + } + + @Override + public void onGetContentRect(ActionMode mode, View view, Rect outRect) { + if (mWrapped instanceof ActionMode.Callback2) { + ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect); + } else { + super.onGetContentRect(mode, view, outRect); + } + } + } + + /** + * Minimal window to satisfy FloatingToolbar. + */ + private Window mFakeWindow = new Window(mContext) { + @Override + public void takeSurface(SurfaceHolder.Callback2 callback) { + } + + @Override + public void takeInputQueue(InputQueue.Callback callback) { + } + + @Override + public boolean isFloating() { + return false; + } + + @Override + public void alwaysReadCloseOnTouchAttr() { + } + + @Override + public void setContentView(@LayoutRes int layoutResID) { + } + + @Override + public void setContentView(View view) { + } + + @Override + public void setContentView(View view, ViewGroup.LayoutParams params) { + } + + @Override + public void addContentView(View view, ViewGroup.LayoutParams params) { + } + + @Override + public void clearContentView() { + } + + @Override + public View getCurrentFocus() { + return null; + } + + @Override + public LayoutInflater getLayoutInflater() { + return null; + } + + @Override + public void setTitle(CharSequence title) { + } + + @Override + public void setTitleColor(@ColorInt int textColor) { + } + + @Override + public void openPanel(int featureId, KeyEvent event) { + } + + @Override + public void closePanel(int featureId) { + } + + @Override + public void togglePanel(int featureId, KeyEvent event) { + } + + @Override + public void invalidatePanelMenu(int featureId) { + } + + @Override + public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) { + return false; + } + + @Override + public boolean performPanelIdentifierAction(int featureId, int id, int flags) { + return false; + } + + @Override + public void closeAllPanels() { + } + + @Override + public boolean performContextMenuIdentifierAction(int id, int flags) { + return false; + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + } + + @Override + public void setBackgroundDrawable(Drawable drawable) { + } + + @Override + public void setFeatureDrawableResource(int featureId, @DrawableRes int resId) { + } + + @Override + public void setFeatureDrawableUri(int featureId, Uri uri) { + } + + @Override + public void setFeatureDrawable(int featureId, Drawable drawable) { + } + + @Override + public void setFeatureDrawableAlpha(int featureId, int alpha) { + } + + @Override + public void setFeatureInt(int featureId, int value) { + } + + @Override + public void takeKeyEvents(boolean get) { + } + + @Override + public boolean superDispatchKeyEvent(KeyEvent event) { + return false; + } + + @Override + public boolean superDispatchKeyShortcutEvent(KeyEvent event) { + return false; + } + + @Override + public boolean superDispatchTouchEvent(MotionEvent event) { + return false; + } + + @Override + public boolean superDispatchTrackballEvent(MotionEvent event) { + return false; + } + + @Override + public boolean superDispatchGenericMotionEvent(MotionEvent event) { + return false; + } + + @Override + public View getDecorView() { + return StatusBarWindowView.this; + } + + @Override + public View peekDecorView() { + return null; + } + + @Override + public Bundle saveHierarchyState() { + return null; + } + + @Override + public void restoreHierarchyState(Bundle savedInstanceState) { + } + + @Override + protected void onActive() { + } + + @Override + public void setChildDrawable(int featureId, Drawable drawable) { + } + + @Override + public void setChildInt(int featureId, int value) { + } + + @Override + public boolean isShortcutKey(int keyCode, KeyEvent event) { + return false; + } + + @Override + public void setVolumeControlStream(int streamType) { + } + + @Override + public int getVolumeControlStream() { + return 0; + } + + @Override + public int getStatusBarColor() { + return 0; + } + + @Override + public void setStatusBarColor(@ColorInt int color) { + } + + @Override + public int getNavigationBarColor() { + return 0; + } + + @Override + public void setNavigationBarColor(@ColorInt int color) { + } + + @Override + public void setDecorCaptionShade(int decorCaptionShade) { + } + + @Override + public void setResizingCaptionDrawable(Drawable drawable) { + } + + @Override + public void onMultiWindowChanged() { + } + + @Override + public void reportActivityRelaunched() { + } + }; + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 29b0f4b75876..3a0336bff905 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -29,8 +29,10 @@ import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -44,6 +46,7 @@ import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.stack.LongPressCancelable; /** * Host for the remote input. @@ -64,6 +67,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private RemoteInputController mController; private NotificationData.Entry mEntry; + private LongPressCancelable mLongPressCancelable; public RemoteInputView(Context context, AttributeSet attrs) { super(context, attrs); @@ -226,6 +230,30 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene updateSendButton(); } + public void close() { + mEditText.defocusIfNeeded(); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + if (mLongPressCancelable == null) { + ViewParent p = getParent(); + while (p != null) { + if (p instanceof LongPressCancelable) { + mLongPressCancelable = (LongPressCancelable) p; + break; + } + p = p.getParent(); + } + } + if (mLongPressCancelable != null) { + mLongPressCancelable.requestDisallowLongPress(); + } + } + return super.onInterceptTouchEvent(ev); + } + /** * An EditText that changes appearance based on whether it's focusable and becomes * un-focusable whenever the user navigates away from it or it becomes invisible. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/LongPressCancelable.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/LongPressCancelable.java new file mode 100644 index 000000000000..05f0c07574bb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/LongPressCancelable.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 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.systemui.statusbar.stack; + +/** + * Interface for container layouts that listen for long presses. A child that + * wants to handle long press can use this to cancel the parents long press logic. + */ +public interface LongPressCancelable { + /** + * Request that the view does not perform long press for the current touch. + */ + void requestDisallowLongPress(); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 59ec6112f4e1..686a71211dc9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -83,7 +83,7 @@ import java.util.HashSet; public class NotificationStackScrollLayout extends ViewGroup implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter, ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener, - SettingsIconRowListener { + SettingsIconRowListener, LongPressCancelable { public static final float BACKGROUND_ALPHA_DIMMED = 0.7f; private static final String TAG = "StackScroller"; @@ -2627,6 +2627,11 @@ public class NotificationStackScrollLayout extends ViewGroup } } + @Override + public void requestDisallowLongPress() { + removeLongPressCallback(); + } + public void removeLongPressCallback() { mSwipeHelper.removeLongPressCallback(); } |