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

Commit 92701259 authored by Evan Laird's avatar Evan Laird
Browse files

[ScreenDecor CLI] ScreenDecorCommand based on ParseableCommand

This CL implements the ScreenDecorCommand class based on the new
ParseableCommand definitions. This approach lets us move all of the
parsing into the lib and handle the command object in a cleaner way.

Test: manual
Bug: 285941724
Change-Id: Ic801c654ade599dc4921994836ead46c2bba9f69
parent 02e51494
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -80,10 +80,12 @@ import com.android.systemui.decor.OverlayWindow;
import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerResDelegateImpl;
import com.android.systemui.decor.ScreenDecorCommand;
import com.android.systemui.log.ScreenDecorationsLogger;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.ThreadFactory;
@@ -131,6 +133,7 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
    protected boolean mIsRegistered;
    private final Context mContext;
    private final Executor mMainExecutor;
    private final CommandRegistry mCommandRegistry;
    private final SecureSettings mSecureSettings;
    @VisibleForTesting
    DisplayTracker.Callback mDisplayListener;
@@ -315,6 +318,7 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
    public ScreenDecorations(Context context,
            @Main Executor mainExecutor,
            SecureSettings secureSettings,
            CommandRegistry commandRegistry,
            UserTracker userTracker,
            DisplayTracker displayTracker,
            PrivacyDotViewController dotViewController,
@@ -326,6 +330,7 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
        mContext = context;
        mMainExecutor = mainExecutor;
        mSecureSettings = secureSettings;
        mCommandRegistry = commandRegistry;
        mUserTracker = userTracker;
        mDisplayTracker = displayTracker;
        mDotViewController = dotViewController;
@@ -350,6 +355,10 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
        }
    };

    private final ScreenDecorCommand.Callback mScreenDecorCommandCallback = (cmd, pw) -> {
        android.util.Log.d(TAG, cmd.toString());
    };

    @Override
    public void start() {
        if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
@@ -361,6 +370,8 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
        mExecutor.execute(this::startOnScreenDecorationsThread);
        mDotViewController.setUiExecutor(mExecutor);
        mAuthController.addCallback(mAuthControllerCallback);
        mCommandRegistry.registerCommand(ScreenDecorCommand.SCREEN_DECOR_CMD_NAME,
                () -> new ScreenDecorCommand(mScreenDecorCommandCallback));
    }

    /**
+171 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.decor

import android.graphics.Color
import android.graphics.Path
import android.util.PathParser
import com.android.systemui.statusbar.commandline.ParseableCommand
import com.android.systemui.statusbar.commandline.Type
import com.android.systemui.statusbar.commandline.map
import java.io.PrintWriter

/** Debug screen-decor command to be handled by the SystemUI command line interface */
class ScreenDecorCommand(
    private val callback: Callback,
) : ParseableCommand(SCREEN_DECOR_CMD_NAME) {
    val debug: Boolean? by
        param(
            longName = "debug",
            description =
                "Enter or exits debug mode. Effectively makes the corners visible and allows " +
                    "for overriding the path data for the anti-aliasing corner paths and display " +
                    "cutout.",
            valueParser = Type.Boolean,
        )

    val color: Int? by
        param(
            longName = "color",
            shortName = "c",
            description =
                "Set a specific color for the debug assets. See Color#parseString() for " +
                    "accepted inputs.",
            valueParser = Type.String.map { it.toColorIntOrNull() }
        )

    val roundedTop: RoundedCornerSubCommand? by subCommand(RoundedCornerSubCommand("rounded-top"))

    val roundedBottom: RoundedCornerSubCommand? by
        subCommand(RoundedCornerSubCommand("rounded-bottom"))

    override fun execute(pw: PrintWriter) {
        callback.onExecute(this, pw)
    }

    override fun toString(): String {
        return "ScreenDecorCommand(" +
            "debug=$debug, " +
            "color=$color, " +
            "roundedTop=$roundedTop, " +
            "roundedBottom=$roundedBottom)"
    }

    /** For use in ScreenDecorations.java, define a Callback */
    interface Callback {
        fun onExecute(cmd: ScreenDecorCommand, pw: PrintWriter)
    }

    companion object {
        const val SCREEN_DECOR_CMD_NAME = "screen-decor"
    }
}

/**
 * Defines a subcommand suitable for `rounded-top` and `rounded-bottom`. They both have the same
 * API.
 */
class RoundedCornerSubCommand(name: String) : ParseableCommand(name) {
    val height by
        param(
                longName = "height",
                description = "The height of a corner, in pixels.",
                valueParser = Type.Int,
            )
            .required()

    val width by
        param(
                longName = "width",
                description =
                    "The width of the corner, in pixels. Likely should be equal to the height.",
                valueParser = Type.Int,
            )
            .required()

    val pathData by
        param(
                longName = "path-data",
                shortName = "d",
                description =
                    "PathParser-compatible path string to be rendered as the corner drawable. " +
                        "This path should be a closed arc oriented as the top-left corner " +
                        "of the device",
                valueParser = Type.String.map { it.toPathOrNull() }
            )
            .required()

    val viewportHeight: Float? by
        param(
            longName = "viewport-height",
            description =
                "The height of the viewport for the given path string. " +
                    "If null, the corner height will be used.",
            valueParser = Type.Float,
        )

    val scaleY: Float
        get() = viewportHeight?.let { height.toFloat() / it } ?: 1.0f

    val viewportWidth: Float? by
        param(
            longName = "viewport-width",
            description =
                "The width of the viewport for the given path string. " +
                    "If null, the corner width will be used.",
            valueParser = Type.Float,
        )

    val scaleX: Float
        get() = viewportWidth?.let { width.toFloat() / it } ?: 1.0f

    override fun execute(pw: PrintWriter) {
        // Not needed for a subcommand
    }

    override fun toString(): String {
        return "RoundedCornerSubCommand(" +
            "height=$height," +
            " width=$width," +
            " pathData='$pathData'," +
            " viewportHeight=$viewportHeight," +
            " viewportWidth=$viewportWidth)"
    }

    fun toRoundedCornerDebugModel(): DebugRoundedCornerModel =
        DebugRoundedCornerModel(
            path = pathData,
            width = width,
            height = height,
            scaleX = scaleX,
            scaleY = scaleY,
        )
}

fun String.toPathOrNull(): Path? =
    try {
        PathParser.createPathFromPathData(this)
    } catch (e: Exception) {
        null
    }

fun String.toColorIntOrNull(): Int? =
    try {
        Color.parseColor(this)
    } catch (e: Exception) {
        null
    }
+7 −2
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ import com.android.systemui.log.ScreenDecorationsLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
@@ -139,6 +140,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
    @Mock
    private Display mDisplay;
    @Mock
    private CommandRegistry mCommandRegistry;
    @Mock
    private UserTracker mUserTracker;
    @Mock
    private PrivacyDotViewController mDotViewController;
@@ -232,7 +235,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
                new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer"))));

        mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
                mUserTracker, mDisplayTracker, mDotViewController, mThreadFactory,
                mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController,
                mThreadFactory,
                mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
                new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
                mAuthController) {
@@ -1227,7 +1231,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
        when(mFaceScanningProviderFactory.getProviders()).thenReturn(mFaceScanningProviders);
        when(mFaceScanningProviderFactory.getHasProviders()).thenReturn(true);
        ScreenDecorations screenDecorations = new ScreenDecorations(mContext, mExecutor,
                mSecureSettings, mUserTracker, mDisplayTracker, mDotViewController,
                mSecureSettings, mCommandRegistry, mUserTracker, mDisplayTracker,
                mDotViewController,
                mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
                new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mAuthController);
        screenDecorations.start();