blob: 7c71051940ba69e6b7417638d63bd41015ff6156 [file] [log] [blame]
yuegf473e1d2018-01-02 16:23:14 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.newbubble;
17
18import android.content.Context;
19import android.graphics.PixelFormat;
20import android.support.v4.os.BuildCompat;
21import android.view.Gravity;
22import android.view.LayoutInflater;
23import android.view.View;
24import android.view.WindowManager;
25import android.view.WindowManager.LayoutParams;
26import android.view.animation.LinearInterpolator;
27
28/** Controller for showing and hiding bubble bottom action view. */
29final class BottomActionViewController {
30
31 // This delay controls how long to wait before we show the target when the user first moves
32 // the bubble, to prevent the bottom action view from animating if the user just wants to fling
33 // the bubble.
34 private static final int SHOW_TARGET_DELAY = 100;
35 private static final int SHOW_TARGET_DURATION = 350;
36 private static final int HIDE_TARGET_DURATION = 225;
37 private static final float HIGHLIGHT_TARGET_SCALE = 1.5f;
38
39 private final Context context;
40 private final WindowManager windowManager;
41 private final int gradientHeight;
42 private final int bottomActionViewTop;
43
44 private View bottomActionView;
45 private View dismissView;
46 private View endCallView;
47
48 private boolean dismissHighlighted;
49 private boolean endCallHighlighted;
50
51 public BottomActionViewController(Context context) {
52 this.context = context;
53 windowManager = context.getSystemService(WindowManager.class);
54 gradientHeight =
55 context.getResources().getDimensionPixelSize(R.dimen.bubble_bottom_action_view_height);
56 bottomActionViewTop = context.getResources().getDisplayMetrics().heightPixels - gradientHeight;
57 }
58
59 /** Creates and show the bottom action view. */
60 public void createAndShowBottomActionView() {
61 if (bottomActionView != null) {
62 return;
63 }
64
65 // Create a new view for the dismiss target
66 bottomActionView = LayoutInflater.from(context).inflate(R.layout.bottom_action_base, null);
67 bottomActionView.setAlpha(0);
68
69 // Sub views
70 dismissView = bottomActionView.findViewById(R.id.bottom_action_dismiss_layout);
71 endCallView = bottomActionView.findViewById(R.id.bottom_action_end_call_layout);
72
73 // Add the target to the window
74 // TODO(yueg): use TYPE_NAVIGATION_BAR_PANEL to draw over navigation bar
75 LayoutParams layoutParams =
76 new LayoutParams(
77 LayoutParams.MATCH_PARENT,
78 gradientHeight,
79 0,
80 bottomActionViewTop,
81 BuildCompat.isAtLeastO()
82 ? LayoutParams.TYPE_APPLICATION_OVERLAY
83 : LayoutParams.TYPE_SYSTEM_OVERLAY,
84 LayoutParams.FLAG_LAYOUT_IN_SCREEN
85 | LayoutParams.FLAG_NOT_TOUCHABLE
86 | LayoutParams.FLAG_NOT_FOCUSABLE,
87 PixelFormat.TRANSLUCENT);
88 layoutParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
89 windowManager.addView(bottomActionView, layoutParams);
90 bottomActionView.setSystemUiVisibility(
91 View.SYSTEM_UI_FLAG_LAYOUT_STABLE
92 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
93 | View.SYSTEM_UI_FLAG_FULLSCREEN);
94 bottomActionView
95 .getRootView()
96 .setSystemUiVisibility(
97 View.SYSTEM_UI_FLAG_LAYOUT_STABLE
98 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
99 | View.SYSTEM_UI_FLAG_FULLSCREEN);
100
101 // Shows the botton action view
102 bottomActionView
103 .animate()
104 .alpha(1f)
105 .setInterpolator(new LinearInterpolator())
106 .setStartDelay(SHOW_TARGET_DELAY)
107 .setDuration(SHOW_TARGET_DURATION)
108 .start();
109 }
110
111 /** Hides and destroys the bottom action view. */
112 public void destroyBottomActionView() {
113 if (bottomActionView == null) {
114 return;
115 }
116 bottomActionView
117 .animate()
118 .alpha(0f)
119 .setInterpolator(new LinearInterpolator())
120 .setDuration(HIDE_TARGET_DURATION)
121 .withEndAction(
122 () -> {
123 // Use removeViewImmediate instead of removeView to avoid view flashing before removed
124 windowManager.removeViewImmediate(bottomActionView);
125 bottomActionView = null;
126 })
127 .start();
128 }
129
130 /**
131 * Change highlight state of dismiss view and end call view according to current touch point.
132 * Highlight the view with touch point moving into its boundary. Unhighlight the view with touch
133 * point moving out of its boundary.
134 *
135 * @param x x position of current touch point
136 * @param y y position of current touch point
137 */
138 public void highlightIfHover(float x, float y) {
139 if (bottomActionView == null) {
140 return;
141 }
142 final int middle = context.getResources().getDisplayMetrics().widthPixels / 2;
143 boolean shouldHighlightDismiss = y > bottomActionViewTop && x < middle;
144 boolean shouldHighlightEndCall = y > bottomActionViewTop && x >= middle;
145
146 if (!shouldHighlightDismiss && dismissHighlighted) {
147 // Unhighlight dismiss
148 dismissView.animate().scaleX(1f).scaleY(1f).setDuration(HIDE_TARGET_DURATION).start();
149 dismissHighlighted = false;
150 } else if (!shouldHighlightEndCall && endCallHighlighted) {
151 // Unhighlight end call
152 endCallView.animate().scaleX(1f).scaleY(1f).setDuration(HIDE_TARGET_DURATION).start();
153 endCallHighlighted = false;
154 }
155
156 if (shouldHighlightDismiss && !dismissHighlighted) {
157 // Highlight dismiss
158 dismissView
159 .animate()
160 .scaleX(HIGHLIGHT_TARGET_SCALE)
161 .scaleY(HIGHLIGHT_TARGET_SCALE)
162 .setDuration(SHOW_TARGET_DURATION)
163 .start();
164 dismissHighlighted = true;
165 } else if (shouldHighlightEndCall && !endCallHighlighted) {
166 // Highlight end call
167 endCallView
168 .animate()
169 .scaleX(HIGHLIGHT_TARGET_SCALE)
170 .scaleY(HIGHLIGHT_TARGET_SCALE)
171 .setDuration(SHOW_TARGET_DURATION)
172 .start();
173 endCallHighlighted = true;
174 }
175 }
176
177 public boolean isDismissHighlighted() {
178 return dismissHighlighted;
179 }
180
181 public boolean isEndCallHighlighted() {
182 return endCallHighlighted;
183 }
184}