Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 433c8117 authored by Miranda Kephart's avatar Miranda Kephart
Browse files

Add white edgelights to AOSP

Moves most of the invocation code (as well as PerimeterPathGuide,
CornerPathRenderer, and EdgeLight) into base SystemUI. Shows
white edge lights coming in from the corners into the center, from
gesture or squeeze.

Bug: 132984557
Test: manual

Change-Id: Icbe611c3513f24f0ac13b68bd4d65f7cb4d402d6
parent 7692d975
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ 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
  -->

<com.android.systemui.assist.ui.InvocationLightsView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom"
    android:visibility="gone"/>
+6 −3
Original line number Diff line number Diff line
@@ -174,6 +174,9 @@
    <!-- Logout button -->
    <color name="logout_button_bg_color">#ccffffff</color>

    <!-- Color for the Assistant invocation lights -->
    <color name="default_invocation_lights_color">#ffffffff</color>         <!-- white -->

    <!-- GM2 colors -->
    <color name="GM2_grey_50">#F8F9FA</color>
    <color name="GM2_grey_100">#F1F3F4</color>
+73 −16
Original line number Diff line number Diff line
@@ -40,8 +40,11 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.ConfigurationChangedReceiver;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.ui.DefaultUiController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;

@@ -50,6 +53,40 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 */
public class AssistManager implements ConfigurationChangedReceiver {

    /**
     * Controls the UI for showing Assistant invocation progress.
     */
    public interface UiController {
        /**
         * Updates the invocation progress.
         *
         * @param type     one of INVOCATION_TYPE_GESTURE, INVOCATION_TYPE_ACTIVE_EDGE,
         *                 INVOCATION_TYPE_VOICE, INVOCATION_TYPE_QUICK_SEARCH_BAR,
         *                 INVOCATION_HOME_BUTTON_LONG_PRESS
         * @param progress a float between 0 and 1 inclusive. 0 represents the beginning of the
         *                 gesture; 1 represents the end.
         */
        void onInvocationProgress(int type, float progress);

        /**
         * Called when an invocation gesture completes.
         *
         * @param velocity the speed of the invocation gesture, in pixels per millisecond. For
         *                 drags, this is 0.
         */
        void onGestureCompletion(float velocity);

        /**
         * Called with the Bundle from VoiceInteractionSessionListener.onSetUiHints.
         */
        void processBundle(Bundle hints);

        /**
         * Hides the UI.
         */
        void hide();
    }

    private static final String TAG = "AssistManager";

    // Note that VERBOSE logging may leak PII (e.g. transcription contents).
@@ -76,6 +113,7 @@ public class AssistManager implements ConfigurationChangedReceiver {
    private final InterestingConfigChanges mInterestingConfigChanges;
    private final PhoneStateMonitor mPhoneStateMonitor;
    private final AssistHandleBehaviorController mHandleController;
    private final UiController mUiController;

    private AssistOrbContainer mView;
    private final DeviceProvisionedController mDeviceProvisionedController;
@@ -119,6 +157,23 @@ public class AssistManager implements ConfigurationChangedReceiver {
                | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
        onConfigurationChanged(context.getResources().getConfiguration());
        mShouldEnableOrb = !ActivityManager.isLowRamDeviceStatic();

        mUiController = new DefaultUiController(mContext);

        OverviewProxyService overviewProxy = Dependency.get(OverviewProxyService.class);
        overviewProxy.addCallback(new OverviewProxyService.OverviewProxyListener() {
            @Override
            public void onAssistantProgress(float progress) {
                // Progress goes from 0 to 1 to indicate how close the assist gesture is to
                // completion.
                onInvocationProgress(INVOCATION_TYPE_GESTURE, progress);
            }

            @Override
            public void onAssistantGestureCompletion(float velocity) {
                onGestureCompletion(velocity);
            }
        });
    }

    protected void registerVoiceInteractionSessionListener() {
@@ -196,21 +251,23 @@ public class AssistManager implements ConfigurationChangedReceiver {
        // Logs assistant start with invocation type.
        MetricsLogger.action(
                new LogMaker(MetricsEvent.ASSISTANT)
                    .setType(MetricsEvent.TYPE_OPEN).setSubtype(args.getInt(INVOCATION_TYPE_KEY)));
                        .setType(MetricsEvent.TYPE_OPEN).setSubtype(
                        args.getInt(INVOCATION_TYPE_KEY)));
        startAssistInternal(args, assistComponent, isService);
    }

    /** Called when the user is performing an assistant invocation action (e.g. Active Edge) */
    public void onInvocationProgress(int type, float progress) {
        // intentional no-op, vendor's AssistManager implementation should override if needed.
        mUiController.onInvocationProgress(type, progress);
    }

    /** Called when the user has invoked the assistant with the incoming velocity, in pixels per
    /**
     * Called when the user has invoked the assistant with the incoming velocity, in pixels per
     * millisecond. For invocations without a velocity (e.g. slow drag), the velocity is set to
     * zero.
     */
    public void onAssistantGestureCompletion(float velocity) {
        // intentional no-op, vendor's AssistManager implementation should override if needed.
    public void onGestureCompletion(float velocity) {
        mUiController.onGestureCompletion(velocity);
    }

    public void hideAssist() {
+65 −0
Original line number Diff line number Diff line
/*
 * 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 android.graphics.Path;

/**
 * Describes paths for circular rounded device corners.
 */
public final class CircularCornerPathRenderer extends CornerPathRenderer {

    private final int mCornerRadiusBottom;
    private final int mCornerRadiusTop;
    private final int mHeight;
    private final int mWidth;
    private final Path mPath = new Path();

    public CircularCornerPathRenderer(int cornerRadiusBottom, int cornerRadiusTop,
            int width, int height) {
        mCornerRadiusBottom = cornerRadiusBottom;
        mCornerRadiusTop = cornerRadiusTop;
        mHeight = height;
        mWidth = width;
    }

    @Override // CornerPathRenderer
    public Path getCornerPath(Corner corner) {
        mPath.reset();
        switch (corner) {
            case BOTTOM_LEFT:
                mPath.moveTo(0, mHeight - mCornerRadiusBottom);
                mPath.arcTo(0, mHeight - mCornerRadiusBottom * 2, mCornerRadiusBottom * 2, mHeight,
                        180, -90, true);
                break;
            case BOTTOM_RIGHT:
                mPath.moveTo(mWidth - mCornerRadiusBottom, mHeight);
                mPath.arcTo(mWidth - mCornerRadiusBottom * 2, mHeight - mCornerRadiusBottom * 2,
                        mWidth, mHeight, 90, -90, true);
                break;
            case TOP_RIGHT:
                mPath.moveTo(mWidth, mCornerRadiusTop);
                mPath.arcTo(mWidth - mCornerRadiusTop, 0, mWidth, mCornerRadiusTop, 0, -90, true);
                break;
            case TOP_LEFT:
                mPath.moveTo(mCornerRadiusTop, 0);
                mPath.arcTo(0, 0, mCornerRadiusTop, mCornerRadiusTop, 270, -90, true);
                break;
        }
        return mPath;
    }
}
+140 −0
Original line number Diff line number Diff line
/*
 * 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 android.graphics.Path;
import android.graphics.PointF;

import java.util.ArrayList;
import java.util.List;

/**
 * Handles paths along device corners.
 */
public abstract class CornerPathRenderer {

    // The maximum delta between the corner curve and points approximating the corner curve.
    private static final float ACCEPTABLE_ERROR = 0.1f;

    /**
     * For convenience, labels the four device corners.
     *
     * Corners must be listed in CCW order, otherwise we'll break rotation.
     */
    public enum Corner {
        BOTTOM_LEFT,
        BOTTOM_RIGHT,
        TOP_RIGHT,
        TOP_LEFT
    }

    /**
     * Returns the path along the inside of a corner (centered insetAmountPx from the corner's
     * edge).
     */
    public Path getInsetPath(Corner corner, float insetAmountPx) {
        return approximateInnerPath(getCornerPath(corner), -insetAmountPx);
    }

    /**
     * Returns the path of a corner (centered on the exact corner). Must be implemented by extending
     * classes, based on the device-specific rounded corners. A default implementation for circular
     * corners is provided by CircularCornerPathRenderer.
     */
    public abstract Path getCornerPath(Corner corner);

    private Path approximateInnerPath(Path input, float delta) {
        List<PointF> points = shiftBy(getApproximatePoints(input), delta);
        return toPath(points);
    }

    private ArrayList<PointF> getApproximatePoints(Path path) {
        float[] rawInput = path.approximate(ACCEPTABLE_ERROR);

        ArrayList<PointF> output = new ArrayList<>();

        for (int i = 0; i < rawInput.length; i = i + 3) {
            output.add(new PointF(rawInput[i + 1], rawInput[i + 2]));
        }

        return output;
    }

    private ArrayList<PointF> shiftBy(ArrayList<PointF> input, float delta) {
        ArrayList<PointF> output = new ArrayList<>();

        for (int i = 0; i < input.size(); i++) {
            PointF point = input.get(i);
            PointF normal = normalAt(input, i);
            PointF shifted =
                    new PointF(point.x + (normal.x * delta), point.y + (normal.y * delta));
            output.add(shifted);
        }
        return output;
    }

    private Path toPath(List<PointF> points) {
        Path path = new Path();
        if (points.size() > 0) {
            path.moveTo(points.get(0).x, points.get(0).y);
            for (PointF point : points.subList(1, points.size())) {
                path.lineTo(point.x, point.y);
            }
        }
        return path;
    }

    private PointF normalAt(List<PointF> points, int index) {
        PointF d1;
        if (index == 0) {
            d1 = new PointF(0, 0);
        } else {
            PointF point = points.get(index);
            PointF previousPoint = points.get(index - 1);
            d1 = new PointF((point.x - previousPoint.x), (point.y - previousPoint.y));
        }

        PointF d2;
        if (index == (points.size() - 1)) {
            d2 = new PointF(0, 0);
        } else {
            PointF point = points.get(index);
            PointF nextPoint = points.get(index + 1);
            d2 = new PointF((nextPoint.x - point.x), (nextPoint.y - point.y));
        }

        return rotate90Ccw(normalize(new PointF(d1.x + d2.x, d1.y + d2.y)));
    }

    private PointF rotate90Ccw(PointF input) {
        return new PointF(-input.y, input.x);
    }

    private float magnitude(PointF point) {
        return (float) Math.sqrt((point.x * point.x) + (point.y * point.y));
    }

    private PointF normalize(PointF point) {
        float magnitude = magnitude(point);
        if (magnitude == 0.f) {
            return point;
        }

        float normal = 1 / magnitude;
        return new PointF((point.x * normal), (point.y * normal));
    }
}
Loading