blob: 7c71051940ba69e6b7417638d63bd41015ff6156 [file] [log] [blame]
/*
* 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;
}
}