blob: 662de3a5b5f5b7a23bed822b536d7854ae212f25 [file] [log] [blame]
/*
* Copyright (C) 2019 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.assist.ui;
import static com.android.systemui.assist.AssistManager.DISMISS_REASON_INVOCATION_CANCELLED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PixelFormat;
import android.metrics.LogMaker;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.WindowManager;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistManager;
/**
* Default UiController implementation. Shows white edge lights along the bottom of the phone,
* expanding from the corners to meet in the center.
*/
public class DefaultUiController implements AssistManager.UiController {
private static final String TAG = "DefaultUiController";
private static final long ANIM_DURATION_MS = 200;
protected final FrameLayout mRoot;
protected InvocationLightsView mInvocationLightsView;
private final WindowManager mWindowManager;
private final WindowManager.LayoutParams mLayoutParams;
private final PathInterpolator mProgressInterpolator = new PathInterpolator(.83f, 0, .84f, 1);
private boolean mAttached = false;
private boolean mInvocationInProgress = false;
private float mLastInvocationProgress = 0;
private ValueAnimator mInvocationAnimator = new ValueAnimator();
public DefaultUiController(Context context) {
mRoot = new FrameLayout(context);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mLayoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT, 0, 0,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
mLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
mLayoutParams.gravity = Gravity.BOTTOM;
mLayoutParams.setTitle("Assist");
mInvocationLightsView = (InvocationLightsView)
LayoutInflater.from(context).inflate(R.layout.invocation_lights, mRoot, false);
mRoot.addView(mInvocationLightsView);
}
@Override // AssistManager.UiController
public void processBundle(Bundle bundle) {
Log.e(TAG, "Bundle received but handling is not implemented; ignoring");
}
@Override // AssistManager.UiController
public void onInvocationProgress(int type, float progress) {
boolean invocationWasInProgress = mInvocationInProgress;
if (progress == 1) {
animateInvocationCompletion(type, 0);
} else if (progress == 0) {
hide();
} else {
if (!mInvocationInProgress) {
attach();
mInvocationInProgress = true;
updateAssistHandleVisibility();
}
setProgressInternal(type, progress);
}
mLastInvocationProgress = progress;
logInvocationProgressMetrics(type, progress, invocationWasInProgress);
}
@Override // AssistManager.UiController
public void onGestureCompletion(float velocity) {
animateInvocationCompletion(AssistManager.INVOCATION_TYPE_GESTURE, velocity);
}
@Override // AssistManager.UiController
public void hide() {
Dependency.get(AssistManager.class).hideAssist();
detach();
if (mInvocationAnimator.isRunning()) {
mInvocationAnimator.cancel();
}
mInvocationLightsView.hide();
mInvocationInProgress = false;
updateAssistHandleVisibility();
}
protected static void logInvocationProgressMetrics(
int type, float progress, boolean invocationWasInProgress) {
// Logs assistant invocation start.
if (!invocationWasInProgress && progress > 0.f) {
MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
.setType(MetricsEvent.TYPE_ACTION)
.setSubtype(Dependency.get(AssistManager.class).toLoggingSubType(type)));
}
// Logs assistant invocation cancelled.
if (invocationWasInProgress && progress == 0f) {
MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
.setType(MetricsEvent.TYPE_DISMISS)
.setSubtype(DISMISS_REASON_INVOCATION_CANCELLED));
}
}
private void updateAssistHandleVisibility() {
ScreenDecorations decorations = SysUiServiceProvider.getComponent(mRoot.getContext(),
ScreenDecorations.class);
decorations.setAssistHintBlocked(mInvocationInProgress);
}
private void attach() {
if (!mAttached) {
mWindowManager.addView(mRoot, mLayoutParams);
mAttached = true;
}
}
private void detach() {
if (mAttached) {
mWindowManager.removeViewImmediate(mRoot);
mAttached = false;
}
}
private void setProgressInternal(int type, float progress) {
mInvocationLightsView.onInvocationProgress(
mProgressInterpolator.getInterpolation(progress));
}
private void animateInvocationCompletion(int type, float velocity) {
mInvocationAnimator = ValueAnimator.ofFloat(mLastInvocationProgress, 1);
mInvocationAnimator.setStartDelay(1);
mInvocationAnimator.setDuration(ANIM_DURATION_MS);
mInvocationAnimator.addUpdateListener(
animation -> setProgressInternal(type, (float) animation.getAnimatedValue()));
mInvocationAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mInvocationInProgress = false;
mLastInvocationProgress = 0;
hide();
}
});
mInvocationAnimator.start();
}
}