summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryueg <yueg@google.com>2018-01-02 16:23:14 -0800
committerCopybara-Service <copybara-piper@google.com>2018-01-02 16:47:06 -0800
commitf473e1d0988bb13874a0774db9cbcd66777f9150 (patch)
tree0bdad6c3221ca608439b5b518d746ccce89cb120
parent31586c7946fdc1a238d64bba07fbab9b0c4ea872 (diff)
Bubble v2 dismiss.
Drag and drop bubble to bottom to hide or end call. Flinging to bottom does not trigger the actions. Color/text is not final. Navigation bar is not hiden and the change will be in a following CL. Bug: 67605985 Test: NewBubbleTest PiperOrigin-RevId: 180608133 Change-Id: Iff4cb32226d8fbf0f8e5319f6876a1d74c336b4a
-rw-r--r--assets/quantum/res/drawable/quantum_ic_clear_vd_theme_24.xml25
-rw-r--r--java/com/android/dialer/logging/dialer_impression.proto6
-rw-r--r--java/com/android/newbubble/BottomActionViewController.java184
-rw-r--r--java/com/android/newbubble/NewBubble.java43
-rw-r--r--java/com/android/newbubble/NewMoveHandler.java10
-rw-r--r--java/com/android/newbubble/res/drawable/bottom_action_scrim.xml24
-rw-r--r--java/com/android/newbubble/res/layout/bottom_action_base.xml63
-rw-r--r--java/com/android/newbubble/res/values/values.xml2
8 files changed, 351 insertions, 6 deletions
diff --git a/assets/quantum/res/drawable/quantum_ic_clear_vd_theme_24.xml b/assets/quantum/res/drawable/quantum_ic_clear_vd_theme_24.xml
new file mode 100644
index 000000000..f55a394dd
--- /dev/null
+++ b/assets/quantum/res/drawable/quantum_ic_clear_vd_theme_24.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector> \ No newline at end of file
diff --git a/java/com/android/dialer/logging/dialer_impression.proto b/java/com/android/dialer/logging/dialer_impression.proto
index 2d2eebf67..a17d365b1 100644
--- a/java/com/android/dialer/logging/dialer_impression.proto
+++ b/java/com/android/dialer/logging/dialer_impression.proto
@@ -12,7 +12,7 @@ message DialerImpression {
// Event enums to be used for Impression Logging in Dialer.
// It's perfectly acceptable for this enum to be large
// Values should be from 1000 to 100000.
- // Next Tag: 1320
+ // Next Tag: 1322
enum Type {
UNKNOWN_AOSP_EVENT_TYPE = 1000;
@@ -644,5 +644,9 @@ message DialerImpression {
BUBBLE_V2_BLUETOOTH = 1318;
// User ended call from bubble call action menu
BUBBLE_V2_END_CALL = 1319;
+ // Drag bubble to bottom and dismiss
+ BUBBLE_V2_BOTTOM_ACTION_DISMISS = 1320;
+ // Drag bubble to bottom and end call
+ BUBBLE_V2_BOTTOM_ACTION_END_CALL = 1321;
}
}
diff --git a/java/com/android/newbubble/BottomActionViewController.java b/java/com/android/newbubble/BottomActionViewController.java
new file mode 100644
index 000000000..7c7105194
--- /dev/null
+++ b/java/com/android/newbubble/BottomActionViewController.java
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+package com.android.newbubble;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.support.v4.os.BuildCompat;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.view.animation.LinearInterpolator;
+
+/** Controller for showing and hiding bubble bottom action view. */
+final class BottomActionViewController {
+
+ // This delay controls how long to wait before we show the target when the user first moves
+ // the bubble, to prevent the bottom action view from animating if the user just wants to fling
+ // the bubble.
+ private static final int SHOW_TARGET_DELAY = 100;
+ private static final int SHOW_TARGET_DURATION = 350;
+ private static final int HIDE_TARGET_DURATION = 225;
+ private static final float HIGHLIGHT_TARGET_SCALE = 1.5f;
+
+ private final Context context;
+ private final WindowManager windowManager;
+ private final int gradientHeight;
+ private final int bottomActionViewTop;
+
+ private View bottomActionView;
+ private View dismissView;
+ private View endCallView;
+
+ private boolean dismissHighlighted;
+ private boolean endCallHighlighted;
+
+ public BottomActionViewController(Context context) {
+ this.context = context;
+ windowManager = context.getSystemService(WindowManager.class);
+ gradientHeight =
+ context.getResources().getDimensionPixelSize(R.dimen.bubble_bottom_action_view_height);
+ bottomActionViewTop = context.getResources().getDisplayMetrics().heightPixels - gradientHeight;
+ }
+
+ /** Creates and show the bottom action view. */
+ public void createAndShowBottomActionView() {
+ if (bottomActionView != null) {
+ return;
+ }
+
+ // Create a new view for the dismiss target
+ bottomActionView = LayoutInflater.from(context).inflate(R.layout.bottom_action_base, null);
+ bottomActionView.setAlpha(0);
+
+ // Sub views
+ dismissView = bottomActionView.findViewById(R.id.bottom_action_dismiss_layout);
+ endCallView = bottomActionView.findViewById(R.id.bottom_action_end_call_layout);
+
+ // Add the target to the window
+ // TODO(yueg): use TYPE_NAVIGATION_BAR_PANEL to draw over navigation bar
+ LayoutParams layoutParams =
+ new LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ gradientHeight,
+ 0,
+ bottomActionViewTop,
+ BuildCompat.isAtLeastO()
+ ? LayoutParams.TYPE_APPLICATION_OVERLAY
+ : LayoutParams.TYPE_SYSTEM_OVERLAY,
+ LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | LayoutParams.FLAG_NOT_TOUCHABLE
+ | LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+ windowManager.addView(bottomActionView, layoutParams);
+ bottomActionView.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_FULLSCREEN);
+ bottomActionView
+ .getRootView()
+ .setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_FULLSCREEN);
+
+ // Shows the botton action view
+ bottomActionView
+ .animate()
+ .alpha(1f)
+ .setInterpolator(new LinearInterpolator())
+ .setStartDelay(SHOW_TARGET_DELAY)
+ .setDuration(SHOW_TARGET_DURATION)
+ .start();
+ }
+
+ /** Hides and destroys the bottom action view. */
+ public void destroyBottomActionView() {
+ if (bottomActionView == null) {
+ return;
+ }
+ bottomActionView
+ .animate()
+ .alpha(0f)
+ .setInterpolator(new LinearInterpolator())
+ .setDuration(HIDE_TARGET_DURATION)
+ .withEndAction(
+ () -> {
+ // Use removeViewImmediate instead of removeView to avoid view flashing before removed
+ windowManager.removeViewImmediate(bottomActionView);
+ bottomActionView = null;
+ })
+ .start();
+ }
+
+ /**
+ * Change highlight state of dismiss view and end call view according to current touch point.
+ * Highlight the view with touch point moving into its boundary. Unhighlight the view with touch
+ * point moving out of its boundary.
+ *
+ * @param x x position of current touch point
+ * @param y y position of current touch point
+ */
+ public void highlightIfHover(float x, float y) {
+ if (bottomActionView == null) {
+ return;
+ }
+ final int middle = context.getResources().getDisplayMetrics().widthPixels / 2;
+ boolean shouldHighlightDismiss = y > bottomActionViewTop && x < middle;
+ boolean shouldHighlightEndCall = y > bottomActionViewTop && x >= middle;
+
+ if (!shouldHighlightDismiss && dismissHighlighted) {
+ // Unhighlight dismiss
+ dismissView.animate().scaleX(1f).scaleY(1f).setDuration(HIDE_TARGET_DURATION).start();
+ dismissHighlighted = false;
+ } else if (!shouldHighlightEndCall && endCallHighlighted) {
+ // Unhighlight end call
+ endCallView.animate().scaleX(1f).scaleY(1f).setDuration(HIDE_TARGET_DURATION).start();
+ endCallHighlighted = false;
+ }
+
+ if (shouldHighlightDismiss && !dismissHighlighted) {
+ // Highlight dismiss
+ dismissView
+ .animate()
+ .scaleX(HIGHLIGHT_TARGET_SCALE)
+ .scaleY(HIGHLIGHT_TARGET_SCALE)
+ .setDuration(SHOW_TARGET_DURATION)
+ .start();
+ dismissHighlighted = true;
+ } else if (shouldHighlightEndCall && !endCallHighlighted) {
+ // Highlight end call
+ endCallView
+ .animate()
+ .scaleX(HIGHLIGHT_TARGET_SCALE)
+ .scaleY(HIGHLIGHT_TARGET_SCALE)
+ .setDuration(SHOW_TARGET_DURATION)
+ .start();
+ endCallHighlighted = true;
+ }
+ }
+
+ public boolean isDismissHighlighted() {
+ return dismissHighlighted;
+ }
+
+ public boolean isEndCallHighlighted() {
+ return endCallHighlighted;
+ }
+}
diff --git a/java/com/android/newbubble/NewBubble.java b/java/com/android/newbubble/NewBubble.java
index 469c15d71..f5a036f93 100644
--- a/java/com/android/newbubble/NewBubble.java
+++ b/java/com/android/newbubble/NewBubble.java
@@ -60,6 +60,7 @@ import android.view.animation.AnticipateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.Toast;
import android.widget.ViewAnimator;
import com.android.dialer.common.LogUtil;
import com.android.dialer.logging.DialerImpression;
@@ -830,6 +831,12 @@ public class NewBubble {
*/
void replaceViewHolder() {
LogUtil.enterBlock("NewBubble.replaceViewHolder");
+ // Don't do it. If windowParams is null, either we haven't initialized it or we set it to null.
+ // There is no need to recreate bubble.
+ if (windowParams == null) {
+ return;
+ }
+
ViewHolder oldViewHolder = viewHolder;
// Create a new ViewHolder and copy needed info.
@@ -873,6 +880,27 @@ public class NewBubble {
return viewHolder.getExpandedView().getVisibility();
}
+ void bottomActionDismiss() {
+ logBasicOrCallImpression(DialerImpression.Type.BUBBLE_V2_BOTTOM_ACTION_DISMISS);
+ // Create bubble at default location at next time
+ hideAndReset();
+ windowParams = null;
+ }
+
+ void bottomActionEndCall() {
+ logBasicOrCallImpression(DialerImpression.Type.BUBBLE_V2_BOTTOM_ACTION_END_CALL);
+ // Hide without animation
+ hideHelper(
+ () -> {
+ defaultAfterHidingAnimation();
+ DialerCall call = getCall();
+ if (call != null) {
+ call.disconnect();
+ Toast.makeText(context, R.string.incall_call_ended, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
private boolean isDrawingFromRight() {
return (windowParams.gravity & Gravity.RIGHT) == Gravity.RIGHT;
}
@@ -896,11 +924,7 @@ public class NewBubble {
}
private void logBasicOrCallImpression(DialerImpression.Type impressionType) {
- // Bubble is shown for outgoing, active or background call
- DialerCall call = CallList.getInstance().getOutgoingCall();
- if (call == null) {
- call = CallList.getInstance().getActiveOrBackgroundCall();
- }
+ DialerCall call = getCall();
if (call != null) {
Logger.get(context)
.logCallImpression(impressionType, call.getUniqueCallId(), call.getTimeAddedMs());
@@ -909,6 +933,15 @@ public class NewBubble {
}
}
+ private DialerCall getCall() {
+ // Bubble is shown for outgoing, active or background call
+ DialerCall call = CallList.getInstance().getOutgoingCall();
+ if (call == null) {
+ call = CallList.getInstance().getActiveOrBackgroundCall();
+ }
+ return call;
+ }
+
private void setPrimaryButtonAccessibilityAction(String description) {
viewHolder
.getPrimaryButton()
diff --git a/java/com/android/newbubble/NewMoveHandler.java b/java/com/android/newbubble/NewMoveHandler.java
index 9e6d95553..c00c10729 100644
--- a/java/com/android/newbubble/NewMoveHandler.java
+++ b/java/com/android/newbubble/NewMoveHandler.java
@@ -51,6 +51,7 @@ class NewMoveHandler implements OnTouchListener {
private final int bubbleShadowPaddingHorizontal;
private final int bubbleExpandedViewWidth;
private final float touchSlopSquared;
+ private final BottomActionViewController bottomActionViewController;
private boolean clickable = true;
private boolean isMoving;
@@ -156,6 +157,8 @@ class NewMoveHandler implements OnTouchListener {
// efficient than needing to take a square root.
touchSlopSquared = (float) Math.pow(ViewConfiguration.get(context).getScaledTouchSlop(), 2);
+ bottomActionViewController = new BottomActionViewController(context);
+
targetView.setOnTouchListener(this);
}
@@ -200,7 +203,9 @@ class NewMoveHandler implements OnTouchListener {
if (!isMoving) {
isMoving = true;
bubble.onMoveStart();
+ bottomActionViewController.createAndShowBottomActionView();
}
+ bottomActionViewController.highlightIfHover(eventX, eventY);
ensureSprings();
@@ -229,11 +234,16 @@ class NewMoveHandler implements OnTouchListener {
moveXAnimation.animateToFinalPosition(target.x);
moveYAnimation.animateToFinalPosition(target.y);
+ } else if (bottomActionViewController.isDismissHighlighted()) {
+ bubble.bottomActionDismiss();
+ } else if (bottomActionViewController.isEndCallHighlighted()) {
+ bubble.bottomActionEndCall();
} else {
snapX();
}
isMoving = false;
bubble.onMoveFinish();
+ bottomActionViewController.destroyBottomActionView();
} else {
v.performClick();
if (clickable) {
diff --git a/java/com/android/newbubble/res/drawable/bottom_action_scrim.xml b/java/com/android/newbubble/res/drawable/bottom_action_scrim.xml
new file mode 100644
index 000000000..bd13382ec
--- /dev/null
+++ b/java/com/android/newbubble/res/drawable/bottom_action_scrim.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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="rectangle">
+ <gradient
+ android:angle="90"
+ android:endColor="@android:color/transparent"
+ android:startColor="#AA000000"/>
+</shape>
diff --git a/java/com/android/newbubble/res/layout/bottom_action_base.xml b/java/com/android/newbubble/res/layout/bottom_action_base.xml
new file mode 100644
index 000000000..bf08e1be5
--- /dev/null
+++ b/java/com/android/newbubble/res/layout/bottom_action_base.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/bubble_bottom_action_view_height"
+ android:orientation="horizontal"
+ android:background="@drawable/bottom_action_scrim">
+
+ <LinearLayout
+ android:id="@+id/bottom_action_dismiss_layout"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center_horizontal|center_vertical">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/bubble_button_icon_padding"
+ android:src="@drawable/quantum_ic_clear_vd_theme_24"
+ android:tint="@color/bubble_button_color_white"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="true"
+ android:textColor="@color/bubble_button_color_white"
+ android:text="Hide"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/bottom_action_end_call_layout"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center_horizontal|center_vertical">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/bubble_button_icon_padding"
+ android:src="@drawable/quantum_ic_call_end_vd_theme_24"
+ android:tint="@color/bubble_button_color_white"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAllCaps="true"
+ android:textColor="@color/bubble_button_color_white"
+ android:text="End call"/>
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/java/com/android/newbubble/res/values/values.xml b/java/com/android/newbubble/res/values/values.xml
index 71f813ac6..040a5be1c 100644
--- a/java/com/android/newbubble/res/values/values.xml
+++ b/java/com/android/newbubble/res/values/values.xml
@@ -41,4 +41,6 @@
<dimen name="bubble_expanded_separator_height">8dp</dimen>
<dimen name="bubble_small_icon_size">24dp</dimen>
<dimen name="bubble_small_icon_padding">4dp</dimen>
+
+ <dimen name="bubble_bottom_action_view_height">180dp</dimen>
</resources>