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

Commit c8cd8cdd authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Controls API - Support action while device is locked"

parents 29c2b7df d689c6ad
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -38383,6 +38383,7 @@ package android.service.controls {
    method @NonNull public CharSequence getSubtitle();
    method @NonNull public CharSequence getTitle();
    method @Nullable public CharSequence getZone();
    method public boolean isAuthRequired();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.service.controls.Control> CREATOR;
    field public static final int STATUS_DISABLED = 4; // 0x4
@@ -38397,6 +38398,7 @@ package android.service.controls {
    ctor public Control.StatefulBuilder(@NonNull android.service.controls.Control);
    method @NonNull public android.service.controls.Control build();
    method @NonNull public android.service.controls.Control.StatefulBuilder setAppIntent(@NonNull android.app.PendingIntent);
    method @NonNull public android.service.controls.Control.StatefulBuilder setAuthRequired(boolean);
    method @NonNull public android.service.controls.Control.StatefulBuilder setControlId(@NonNull String);
    method @NonNull public android.service.controls.Control.StatefulBuilder setControlTemplate(@NonNull android.service.controls.templates.ControlTemplate);
    method @NonNull public android.service.controls.Control.StatefulBuilder setCustomColor(@Nullable android.content.res.ColorStateList);
+34 −3
Original line number Diff line number Diff line
@@ -121,6 +121,7 @@ public final class Control implements Parcelable {
    private final @Status int mStatus;
    private final @NonNull ControlTemplate mControlTemplate;
    private final @NonNull CharSequence mStatusText;
    private final boolean mAuthRequired;

    /**
     * @param controlId the unique persistent identifier for this object.
@@ -137,6 +138,8 @@ public final class Control implements Parcelable {
     * @param status
     * @param controlTemplate
     * @param statusText
     * @param authRequired true if the control can not be interacted with until the device is
     *                     unlocked
     */
    Control(@NonNull String controlId,
            @DeviceTypes.DeviceType int deviceType,
@@ -149,7 +152,8 @@ public final class Control implements Parcelable {
            @Nullable ColorStateList customColor,
            @Status int status,
            @NonNull ControlTemplate controlTemplate,
            @NonNull CharSequence statusText) {
            @NonNull CharSequence statusText,
            boolean authRequired) {
        Preconditions.checkNotNull(controlId);
        Preconditions.checkNotNull(title);
        Preconditions.checkNotNull(subtitle);
@@ -180,6 +184,7 @@ public final class Control implements Parcelable {
        }
        mControlTemplate = controlTemplate;
        mStatusText = statusText;
        mAuthRequired = authRequired;
    }

    /**
@@ -219,6 +224,7 @@ public final class Control implements Parcelable {
        ControlTemplateWrapper wrapper = ControlTemplateWrapper.CREATOR.createFromParcel(in);
        mControlTemplate = wrapper.getWrappedTemplate();
        mStatusText = in.readCharSequence();
        mAuthRequired = in.readBoolean();
    }

    /**
@@ -336,6 +342,13 @@ public final class Control implements Parcelable {
        return mStatusText;
    }

    /**
     * @return true if the control can not be interacted with until the device is unlocked
     */
    public boolean isAuthRequired() {
        return mAuthRequired;
    }

    @Override
    public int describeContents() {
        return 0;
@@ -376,6 +389,7 @@ public final class Control implements Parcelable {
        dest.writeInt(mStatus);
        new ControlTemplateWrapper(mControlTemplate).writeToParcel(dest, flags);
        dest.writeCharSequence(mStatusText);
        dest.writeBoolean(mAuthRequired);
    }

    public static final @NonNull Creator<Control> CREATOR = new Creator<Control>() {
@@ -409,6 +423,7 @@ public final class Control implements Parcelable {
     *     <li> Status: {@link Status#STATUS_UNKNOWN}
     *     <li> Control template: {@link ControlTemplate#getNoTemplateObject}
     *     <li> Status text: {@code ""}
     *     <li> Auth Required: {@code true}
     * </ul>
     */
    @SuppressLint("MutableBareField")
@@ -585,7 +600,8 @@ public final class Control implements Parcelable {
                    mCustomColor,
                    STATUS_UNKNOWN,
                    ControlTemplate.NO_TEMPLATE,
                    "");
                    "",
                    true /* authRequired */);
        }
    }

@@ -607,6 +623,7 @@ public final class Control implements Parcelable {
     *     <li> Status: {@link Status#STATUS_UNKNOWN}
     *     <li> Control template: {@link ControlTemplate#getNoTemplateObject}
     *     <li> Status text: {@code ""}
     *     <li> Auth Required: {@code true}
     * </ul>
     */
    public static final class StatefulBuilder {
@@ -623,6 +640,7 @@ public final class Control implements Parcelable {
        private @Status int mStatus = STATUS_UNKNOWN;
        private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
        private @NonNull CharSequence mStatusText = "";
        private boolean mAuthRequired = true;

        /**
         * @param controlId the identifier for the {@link Control}.
@@ -655,6 +673,7 @@ public final class Control implements Parcelable {
            mStatus = control.mStatus;
            mControlTemplate = control.mControlTemplate;
            mStatusText = control.mStatusText;
            mAuthRequired = control.mAuthRequired;
        }

        /**
@@ -820,6 +839,17 @@ public final class Control implements Parcelable {
            return this;
        }

        /**
         * @param authRequired true if the control can not be interacted with until the device is
         *                     unlocked
         * @return {@code this}
         */
        @NonNull
        public StatefulBuilder setAuthRequired(boolean authRequired) {
            mAuthRequired = authRequired;
            return this;
        }

        /**
         * @return a valid {@link Control}
         */
@@ -836,7 +866,8 @@ public final class Control implements Parcelable {
                    mCustomColor,
                    mStatus,
                    mControlTemplate,
                    mStatusText);
                    mStatusText,
                    mAuthRequired);
        }
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -116,6 +116,9 @@ public abstract class ControlsProviderService extends Service {
     * Calls to {@link Subscriber#onComplete} will not be expected. Instead, wait for the call from
     * {@link Subscription#cancel} to indicate that updates are no longer required. It is expected
     * that controls provided by this publisher were created using {@link Control.StatefulBuilder}.
     *
     * By default, all controls require the device to be unlocked in order for the user to interact
     * with it. This can be modified per Control by {@link Control.StatefulBuilder#setAuthRequired}.
     */
    @NonNull
    public abstract Publisher<Control> createPublisherFor(@NonNull List<String> controlIds);
@@ -127,6 +130,9 @@ public abstract class ControlsProviderService extends Service {
     * {@link ControlAction.ResponseResult}. The Integer should indicate whether the action
     * was received successfully, or if additional prompts should be presented to
     * the user. Any visual control updates should be sent via the Publisher.

     * By default, all invocations of this method will require the device be unlocked. This can
     * be modified per Control by {@link Control.StatefulBuilder#setAuthRequired}.
     */
    public abstract void performControlAction(@NonNull String controlId,
            @NonNull ControlAction action, @NonNull Consumer<Integer> consumer);
+1 −0
Original line number Diff line number Diff line
@@ -347,6 +347,7 @@ public class ControlProviderServiceTest {
                && Objects.equals(c1.getCustomColor(), c2.getCustomColor())
                && c1.getStatus() == c2.getStatus()
                && Objects.equals(c1.getControlTemplate(), c2.getControlTemplate())
                && Objects.equals(c1.isAuthRequired(), c2.isAuthRequired())
                && Objects.equals(c1.getStatusText(), c2.getStatusText());
    }

+53 −24
Original line number Diff line number Diff line
@@ -77,23 +77,37 @@ class ControlActionCoordinatorImpl @Inject constructor(

    override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
        controlsMetricsLogger.touch(cvh, isLocked)
        bouncerOrRun(createAction(cvh.cws.ci.controlId, {
        bouncerOrRun(
            createAction(
                cvh.cws.ci.controlId,
                {
                    cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    cvh.action(BooleanAction(templateId, !isChecked))
        }, true /* blockable */))
                },
                true /* blockable */
            ),
            isAuthRequired(cvh)
        )
    }

    override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
        controlsMetricsLogger.touch(cvh, isLocked)
        val blockable = cvh.usePanel()
        bouncerOrRun(createAction(cvh.cws.ci.controlId, {
        bouncerOrRun(
            createAction(
                cvh.cws.ci.controlId,
                {
                    cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    if (cvh.usePanel()) {
                        showDetail(cvh, control.getAppIntent())
                    } else {
                        cvh.action(CommandAction(templateId))
                    }
        }, blockable))
                },
                blockable
            ),
            isAuthRequired(cvh)
        )
    }

    override fun drag(isEdge: Boolean) {
@@ -106,20 +120,33 @@ class ControlActionCoordinatorImpl @Inject constructor(

    override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) {
        controlsMetricsLogger.drag(cvh, isLocked)
        bouncerOrRun(createAction(cvh.cws.ci.controlId, {
            cvh.action(FloatAction(templateId, newValue))
        }, false /* blockable */))
        bouncerOrRun(
            createAction(
                cvh.cws.ci.controlId,
                { cvh.action(FloatAction(templateId, newValue)) },
                false /* blockable */
            ),
            isAuthRequired(cvh)
        )
    }

    override fun longPress(cvh: ControlViewHolder) {
        controlsMetricsLogger.longPress(cvh, isLocked)
        bouncerOrRun(createAction(cvh.cws.ci.controlId, {
            // Long press snould only be called when there is valid control state, otherwise ignore
        bouncerOrRun(
            createAction(
                cvh.cws.ci.controlId,
                {
                    // Long press snould only be called when there is valid control state,
                    // otherwise ignore
                    cvh.cws.control?.let {
                        cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
                        showDetail(cvh, it.getAppIntent())
                    }
        }, false /* blockable */))
                },
                false /* blockable */
            ),
            isAuthRequired(cvh)
        )
    }

    override fun runPendingAction(controlId: String) {
@@ -135,6 +162,8 @@ class ControlActionCoordinatorImpl @Inject constructor(
        actionsInProgress.remove(controlId)
    }

    private fun isAuthRequired(cvh: ControlViewHolder) = cvh.cws.control?.isAuthRequired() ?: true

    private fun shouldRunAction(controlId: String) =
        if (actionsInProgress.add(controlId)) {
            uiExecutor.executeDelayed({
@@ -146,8 +175,8 @@ class ControlActionCoordinatorImpl @Inject constructor(
        }

    @VisibleForTesting
    fun bouncerOrRun(action: Action) {
        if (keyguardStateController.isShowing()) {
    fun bouncerOrRun(action: Action, authRequired: Boolean) {
        if (keyguardStateController.isShowing() && authRequired) {
            if (isLocked) {
                context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))

Loading