OSDN Git Service

Allow inline replying directly from the image
authorSelim Cinek <cinek@google.com>
Fri, 19 May 2017 05:16:00 +0000 (22:16 -0700)
committerSelim Cinek <cinek@google.com>
Sat, 3 Jun 2017 00:13:03 +0000 (17:13 -0700)
Test: runtest systemui
Fixes: 35853345
Change-Id: Id942392b8de5b24de6f4f5cf335fd7f28e48d49a

13 files changed:
core/java/android/app/Notification.java
core/java/android/widget/RemoteViews.java
core/res/res/drawable/ic_reply_notification.xml [new file with mode: 0644]
core/res/res/drawable/notification_reply_background.xml [new file with mode: 0644]
core/res/res/layout/notification_template_right_icon.xml
core/res/res/values/symbols.xml
packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
packages/SystemUI/src/com/android/systemui/statusbar/notification/TextViewTransformState.java
packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java

index 4587f28..df9b64c 100644 (file)
@@ -2742,6 +2742,11 @@ public class Notification implements Parcelable
         private int mBackgroundColor = COLOR_INVALID;
         private int mForegroundColor = COLOR_INVALID;
         private int mBackgroundColorHint = COLOR_INVALID;
+        /**
+         * A temporary location where actions are stored. If != null the view originally has action
+         * but doesn't have any for this inflation.
+         */
+        private ArrayList<Action> mOriginalActions;
         private boolean mRebuildStyledRemoteViews;
 
         /**
@@ -4054,7 +4059,53 @@ public class Notification implements Parcelable
                 contentView.setViewLayoutMarginEndDimen(R.id.line1, endMargin);
                 contentView.setViewLayoutMarginEndDimen(R.id.text, endMargin);
                 contentView.setViewLayoutMarginEndDimen(R.id.progress, endMargin);
+                // Bind the reply action
+                Action action = findReplyAction();
+                contentView.setViewVisibility(R.id.reply_icon_action, action != null
+                        ? View.VISIBLE
+                        : View.GONE);
+
+                if (action != null) {
+                    int contrastColor = resolveContrastColor();
+                    contentView.setDrawableParameters(R.id.reply_icon_action,
+                            true /* targetBackground */,
+                            -1,
+                            contrastColor,
+                            PorterDuff.Mode.SRC_ATOP, -1);
+                    int iconColor = NotificationColorUtil.isColorLight(contrastColor)
+                            ? Color.BLACK : Color.WHITE;
+                    contentView.setDrawableParameters(R.id.reply_icon_action,
+                            false /* targetBackground */,
+                            -1,
+                            iconColor,
+                            PorterDuff.Mode.SRC_ATOP, -1);
+                    contentView.setOnClickPendingIntent(R.id.right_icon,
+                            action.actionIntent);
+                    contentView.setOnClickPendingIntent(R.id.reply_icon_action,
+                            action.actionIntent);
+                    contentView.setRemoteInputs(R.id.right_icon, action.mRemoteInputs);
+                    contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs);
+
+                }
             }
+            contentView.setViewVisibility(R.id.right_icon_container, mN.mLargeIcon != null
+                    ? View.VISIBLE
+                    : View.GONE);
+        }
+
+        private Action findReplyAction() {
+            ArrayList<Action> actions = mActions;
+            if (mOriginalActions != null) {
+                actions = mOriginalActions;
+            }
+            int numActions = actions.size();
+            for (int i = 0; i < numActions; i++) {
+                Action action = actions.get(i);
+                if (hasValidRemoteInput(action)) {
+                    return action;
+                }
+            }
+            return null;
         }
 
         private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
@@ -5604,10 +5655,11 @@ public class Notification implements Parcelable
         @Override
         public RemoteViews makeContentView(boolean increasedHeight) {
             if (increasedHeight) {
-                ArrayList<Action> actions = mBuilder.mActions;
+                mBuilder.mOriginalActions = mBuilder.mActions;
                 mBuilder.mActions = new ArrayList<>();
                 RemoteViews remoteViews = makeBigContentView();
-                mBuilder.mActions = actions;
+                mBuilder.mActions = mBuilder.mOriginalActions;
+                mBuilder.mOriginalActions = null;
                 return remoteViews;
             }
             return super.makeContentView(increasedHeight);
@@ -5891,10 +5943,11 @@ public class Notification implements Parcelable
                 return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
                         mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
             } else {
-                ArrayList<Action> actions = mBuilder.mActions;
+                mBuilder.mOriginalActions = mBuilder.mActions;
                 mBuilder.mActions = new ArrayList<>();
                 RemoteViews remoteViews = makeBigContentView();
-                mBuilder.mActions = actions;
+                mBuilder.mActions = mBuilder.mOriginalActions;
+                mBuilder.mOriginalActions = null;
                 return remoteViews;
             }
         }
index 985584e..49e1d44 100644 (file)
@@ -2191,7 +2191,7 @@ public class RemoteViews implements Parcelable, Filter {
 
         @Override
         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
-            final TextView target = (TextView) root.findViewById(viewId);
+            final View target = root.findViewById(viewId);
             if (target == null) return;
 
             target.setTagInternal(R.id.remote_input_tag, remoteInputs);
diff --git a/core/res/res/drawable/ic_reply_notification.xml b/core/res/res/drawable/ic_reply_notification.xml
new file mode 100644 (file)
index 0000000..88b8c5b
--- /dev/null
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="10dp"
+        android:height="10dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M10.0,9.0L10.0,5.0l-7.0,7.0 7.0,7.0l0.0,-4.1c5.0,0.0 8.5,1.6 11.0,5.1 -1.0,-5.0 -4.0,-10.0 -11.0,-11.0z"/>
+    <path
+        android:pathData="M0 0h24v24H0z"
+        android:fillColor="#00000000"/>
+</vector>
diff --git a/core/res/res/drawable/notification_reply_background.xml b/core/res/res/drawable/notification_reply_background.xml
new file mode 100644 (file)
index 0000000..08e22f2
--- /dev/null
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid
+        android:color="#ff757575"/>
+    <size
+        android:width="16dp"
+        android:height="16dp"/>
+</shape>
index 65a5015..aa4e05b 100644 (file)
   ~ limitations under the License
   -->
 
-<ImageView android:id="@+id/right_icon" xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="40dp"
-    android:layout_height="40dp"
-    android:layout_marginEnd="@dimen/notification_content_margin_end"
-    android:layout_marginTop="36dp"
-    android:layout_gravity="top|end"
-    android:scaleType="centerCrop"
-    />
-
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:id="@+id/right_icon_container"
+             android:layout_width="wrap_content"
+             android:layout_height="wrap_content"
+             android:layout_gravity="top|end">
+    <ImageView android:id="@+id/right_icon"
+               android:layout_width="40dp"
+               android:layout_height="40dp"
+               android:layout_gravity="top|end"
+               android:layout_marginTop="36dp"
+               android:layout_marginEnd="@dimen/notification_content_margin_end"
+               android:scaleType="centerCrop"/>
+    <ImageView android:id="@+id/reply_icon_action"
+               android:layout_width="16dp"
+               android:layout_height="16dp"
+               android:layout_gravity="top|end"
+               android:layout_marginTop="64dp"
+               android:layout_marginEnd="12dp"
+               android:background="@drawable/notification_reply_background"
+               android:src="@drawable/ic_reply_notification"
+               android:scaleType="center"
+               visiblity="gone"/>
+</FrameLayout>
 
index 6554d44..9763827 100644 (file)
   <java-symbol type="string" name="config_defaultTrustAgent" />
 
   <!-- Time picker -->
+  <java-symbol type="id" name="right_icon_container"/>
+  <java-symbol type="id" name="reply_icon_action"/>
   <java-symbol type="id" name="toggle_mode"/>
   <java-symbol type="id" name="input_mode"/>
   <java-symbol type="id" name="input_header"/>
index 4a5f83f..7f2f5f6 100644 (file)
@@ -1487,6 +1487,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
         mHasUserChangedExpansion = true;
         mUserExpanded = userExpanded;
         onExpansionChanged(true /* userAction */, wasExpanded);
+        if (!wasExpanded && isExpanded()
+                && getActualHeight() != getIntrinsicHeight()) {
+            notifyHeightChanged(true /* needsAnimation */);
+        }
     }
 
     public void resetUserExpansion() {
@@ -2097,8 +2101,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
         float x = event.getX();
         float y = event.getY();
         NotificationHeaderView header = getVisibleNotificationHeader();
-        if (header != null) {
-            return header.isInTouchRect(x - getTranslation(), y);
+        if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
+            return true;
+        }
+        if ((!mIsSummaryWithChildren || mShowingPublic)
+                && getShowingLayout().disallowSingleClick(x, y)) {
+            return true;
         }
         return super.disallowSingleClick(event);
     }
index baf0c2c..15f3908 100644 (file)
@@ -1440,4 +1440,15 @@ public class NotificationContentView extends FrameLayout {
         }
         return true;
     }
+
+    /**
+     * Should a single click be disallowed on this view when on the keyguard?
+     */
+    public boolean disallowSingleClick(float x, float y) {
+        NotificationViewWrapper visibleWrapper = getVisibleWrapper(getVisibleType());
+        if (visibleWrapper != null) {
+            return visibleWrapper.disallowSingleClick(x, y);
+        }
+        return false;
+    }
 }
index e6b3fb8..c891418 100644 (file)
@@ -48,10 +48,13 @@ public class ImageTransformState extends TransformState {
 
     @Override
     protected boolean sameAs(TransformState otherState) {
+        if (super.sameAs(otherState)) {
+            return true;
+        }
         if (otherState instanceof ImageTransformState) {
             return mIcon != null && mIcon.sameAs(((ImageTransformState) otherState).getIcon());
         }
-        return super.sameAs(otherState);
+        return false;
     }
 
     @Override
index f0b6b2e..f5f92c8 100644 (file)
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification;
 
 import android.content.Context;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
 import android.widget.ImageView;
@@ -41,6 +42,8 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
     private TextView mTitle;
     private TextView mText;
     private View mActionsContainer;
+    private View mReplyAction;
+    private Rect mTmpRect = new Rect();
 
     private int mContentHeight;
     private int mMinHeightHint;
@@ -130,6 +133,29 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
             mProgressBar = null;
         }
         mActionsContainer = mView.findViewById(com.android.internal.R.id.actions_container);
+        mReplyAction = mView.findViewById(com.android.internal.R.id.reply_icon_action);
+    }
+
+    @Override
+    public boolean disallowSingleClick(float x, float y) {
+        if (mReplyAction != null && mReplyAction.getVisibility() == View.VISIBLE) {
+            if (isOnView(mReplyAction, x, y) || isOnView(mPicture, x, y)) {
+                return true;
+            }
+        }
+        return super.disallowSingleClick(x, y);
+    }
+
+    private boolean isOnView(View view, float x, float y) {
+        View searchView = (View) view.getParent();
+        while (searchView != null && !(searchView instanceof ExpandableNotificationRow)) {
+            searchView.getHitRect(mTmpRect);
+            x -= mTmpRect.left;
+            y -= mTmpRect.top;
+            searchView = (View) searchView.getParent();
+        }
+        view.getHitRect(mTmpRect);
+        return mTmpRect.contains((int) x,(int) y);
     }
 
     @Override
index 5cc39cc..07f5044 100644 (file)
@@ -182,4 +182,8 @@ public abstract class NotificationViewWrapper implements TransformableView {
     public boolean isDimmable() {
         return true;
     }
+
+    public boolean disallowSingleClick(float x, float y) {
+        return false;
+    }
 }
index 6fe5756..fb4e2ec 100644 (file)
@@ -41,6 +41,9 @@ public class TextViewTransformState extends TransformState {
 
     @Override
     protected boolean sameAs(TransformState otherState) {
+        if (super.sameAs(otherState)) {
+            return true;
+        }
         if (otherState instanceof TextViewTransformState) {
             TextViewTransformState otherTvs = (TextViewTransformState) otherState;
             if(TextUtils.equals(otherTvs.mText.getText(), mText.getText())) {
@@ -50,7 +53,7 @@ public class TextViewTransformState extends TransformState {
                         && getInnerHeight(mText) == getInnerHeight(otherTvs.mText);
             }
         }
-        return super.sameAs(otherState);
+        return false;
     }
 
     private int getInnerHeight(TextView text) {
index c3f1cb1..a77dd4e 100644 (file)
@@ -54,6 +54,7 @@ public class TransformState {
 
     protected View mTransformedView;
     private int[] mOwnPosition = new int[2];
+    private boolean mSameAsAny;
     private float mTransformationEndY = UNDEFINED;
     private float mTransformationEndX = UNDEFINED;
 
@@ -418,7 +419,7 @@ public class TransformState {
     }
 
     protected boolean sameAs(TransformState otherState) {
-        return false;
+        return mSameAsAny;
     }
 
     public void appear(float transformationAmount, TransformableView otherView) {
@@ -449,6 +450,9 @@ public class TransformState {
         if (view instanceof ImageView) {
             ImageTransformState result = ImageTransformState.obtain();
             result.initFrom(view);
+            if (view.getId() == com.android.internal.R.id.reply_icon_action) {
+                ((TransformState) result).setIsSameAsAnyView(true);
+            }
             return result;
         }
         if (view instanceof ProgressBar) {
@@ -461,6 +465,10 @@ public class TransformState {
         return result;
     }
 
+    private void setIsSameAsAnyView(boolean sameAsAny) {
+        mSameAsAny = sameAsAny;
+    }
+
     public void recycle() {
         reset();
         if (getClass() == TransformState.class) {
@@ -514,6 +522,7 @@ public class TransformState {
 
     protected void reset() {
         mTransformedView = null;
+        mSameAsAny = false;
         mTransformationEndX = UNDEFINED;
         mTransformationEndY = UNDEFINED;
     }