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

Commit 10eae318 authored by Matt Pietal's avatar Matt Pietal
Browse files

Controls API - Adding service impl

Adding a basic service implementation for apps to implement if they
would like to contribute to the new controls space. This is a WIP
while the controls design is worked.

Test: atest + Home Mock Service
Change-Id: Iae3de21ebf5b15b0df498dce2add51f53bbd229d
parent 10197b12
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -213,6 +213,8 @@ public final class ControlState implements Parcelable {
        if (mAppIntent != null) {
            dest.writeByte((byte) 1);
            mAppIntent.writeToParcel(dest, flags);
        } else {
            dest.writeByte((byte) 0);
        }
        if (mIcon != null) {
            dest.writeByte((byte) 1);
+147 −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 android.service.controls;

import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;

import java.util.List;

/**
 * Service implementation allowing applications to contribute controls to the
 * System UI.
 * @hide
 */
public abstract class ControlsProviderService extends Service {

    @SdkConstant(SdkConstantType.SERVICE_ACTION)
    public static final String CONTROLS_ACTION = "android.service.controls.ControlsProviderService";

    private IControlsProviderCallback mCallback;
    private RequestHandler mHandler;

    /**
     * Signal to retrieve all Controls. When complete, call
     * {@link IControlsProviderCallback#onLoad} to inform the caller.
     */
    public abstract void load();

    /**
     * Informs the service that the caller is listening for updates to the given controlIds.
     * {@link IControlsProviderCallback#onRefreshState} should be called any time
     * there are Control updates to render.
     */
    public abstract void subscribe(@NonNull List<String> controlIds);

    /**
     * Informs the service that the caller is done listening for updates,
     * and any calls to {@link IControlsProviderCallback#onRefreshState} will be ignored.
     */
    public abstract void unsubscribe();

    /**
     * The user has interacted with a Control. The action is dictated by the type of
     * {@link ControlAction} that was sent.
     */
    public abstract void onAction(@NonNull String controlId, @NonNull ControlAction action);

    protected IControlsProviderCallback getControlsProviderCallback() {
        return mCallback;
    }

    @Override
    public IBinder onBind(Intent intent) {
        mHandler = new RequestHandler(Looper.getMainLooper());

        Bundle bundle = intent.getBundleExtra("CALLBACK_BUNDLE");
        IBinder callbackBinder = bundle.getBinder("CALLBACK_BINDER");
        mCallback = IControlsProviderCallback.Stub.asInterface(callbackBinder);

        return new IControlsProvider.Stub() {
            public void load() {
                mHandler.sendEmptyMessage(RequestHandler.MSG_LOAD);
            }

            public void subscribe(List<String> ids) {
                mHandler.obtainMessage(RequestHandler.MSG_SUBSCRIBE, ids).sendToTarget();
            }

            public void unsubscribe() {
                mHandler.sendEmptyMessage(RequestHandler.MSG_UNSUBSCRIBE);
            }

            public void onAction(String id, ControlAction action) {
                ActionMessage msg = new ActionMessage(id, action);
                mHandler.obtainMessage(RequestHandler.MSG_SUBSCRIBE, msg).sendToTarget();
            }
        };
    }

    @Override
    public boolean onUnbind(Intent intent) {
        mCallback = null;
        mHandler = null;
        return true;
    }

    private class RequestHandler extends Handler {
        private static final int MSG_LOAD = 1;
        private static final int MSG_SUBSCRIBE = 2;
        private static final int MSG_UNSUBSCRIBE = 3;
        private static final int MSG_ON_ACTION = 4;

        RequestHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message msg) {
            switch(msg.what) {
                case MSG_LOAD:
                    ControlsProviderService.this.load();
                    break;
                case MSG_SUBSCRIBE:
                    List<String> ids = (List<String>) msg.obj;
                    ControlsProviderService.this.subscribe(ids);
                    break;
                case MSG_UNSUBSCRIBE:
                    ControlsProviderService.this.unsubscribe();
                    break;
                case MSG_ON_ACTION:
                    ActionMessage aMsg = (ActionMessage) msg.obj;
                    ControlsProviderService.this.onAction(aMsg.mId, aMsg.mAction);
                    break;
            }
        }
    }

    private class ActionMessage {
        final String mId;
        final ControlAction mAction;

        ActionMessage(String id, ControlAction action) {
            this.mId = id;
            this.mAction = action;
        }
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -2933,6 +2933,14 @@
    <permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE"
        android:protectionLevel="signature" />

    <!-- Allows SystemUI to request third party controls.
         <p>Should only be requested by the System and required by
         ControlsService declarations.
         @hide
    -->
    <permission android:name="android.permission.BIND_CONTROLS"
        android:protectionLevel="signature" />

    <!-- @SystemApi Allows an application to force a BACK operation on whatever is the
         top activity.
         <p>Not for use by third-party applications.
+3 −0
Original line number Diff line number Diff line
@@ -174,6 +174,9 @@
    <!-- Adding Quick Settings tiles -->
    <uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" />

    <!-- Adding Controls to SystemUI -->
    <uses-permission android:name="android.permission.BIND_CONTROLS" />

    <!-- Quick Settings tile: Night Mode / Dark Theme -->
    <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE" />