Merge "Merge ControlState into Control"

This commit is contained in:
Fabian Kozynski
2020-01-02 15:19:13 +00:00
committed by Android (Google) Code Review
8 changed files with 313 additions and 430 deletions

View File

@@ -16,6 +16,7 @@
package android.service.controls;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
@@ -28,28 +29,26 @@ import android.util.Log;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Represents a physical object that can be represented by a {@link ControlTemplate} and whose
* properties may be modified through a {@link ControlAction}.
*
* The information is provided by a {@link ControlProviderService} and represents static
* The information is provided by a {@link ControlsProviderService} and represents static
* information (not current status) about the device.
* <p>
* Each control needs a unique (per provider) identifier that is persistent across reboots of the
* system.
* <p>
* Each {@link Control} will have a name, a subtitle and will optionally belong to a structure
* and zone. Some of these values are defined by the user and/or the {@link ControlProviderService}
* and zone. Some of these values are defined by the user and/or the {@link ControlsProviderService}
* and will be used to display the control as well as group them for management.
* <p>
* Each object will have an associated {@link DeviceTypes.DeviceType}. This will determine the icons and colors
* used to display it.
* <p>
* The {@link ControlTemplate.TemplateType} provided will be used as a hint when displaying this in
* non-interactive situations (for example when there's no state to display). This template is not
* the one that will be shown with the current state and provide interactions. That template is set
* using {@link ControlState}.
* <p>
* An {@link Intent} linking to the provider Activity that expands on this {@link Control} and
* allows for further actions should be provided.
* @hide
@@ -57,17 +56,52 @@ import com.android.internal.util.Preconditions;
public class Control implements Parcelable {
private static final String TAG = "Control";
;
private static final int NUM_STATUS = 5;
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
STATUS_UNKNOWN,
STATUS_OK,
STATUS_NOT_FOUND,
STATUS_ERROR,
STATUS_DISABLED,
})
public @interface Status {};
public static final int STATUS_UNKNOWN = 0;
/**
* The device corresponding to the {@link Control} is responding correctly.
*/
public static final int STATUS_OK = 1;
/**
* The device corresponding to the {@link Control} cannot be found or was removed.
*/
public static final int STATUS_NOT_FOUND = 2;
/**
* The device corresponding to the {@link Control} is in an error state.
*/
public static final int STATUS_ERROR = 3;
/**
* The {@link Control} is currently disabled.
*/
public static final int STATUS_DISABLED = 4;
private final @NonNull String mControlId;
private final @DeviceTypes.DeviceType
int mDeviceType;
private final @DeviceTypes.DeviceType int mDeviceType;
private final @NonNull CharSequence mTitle;
private final @NonNull CharSequence mSubtitle;
private final @Nullable CharSequence mStructure;
private final @Nullable CharSequence mZone;
private final @NonNull PendingIntent mAppIntent;
private final @ControlTemplate.TemplateType int mPrimaryType;
private final @Status int mStatus;
private final @NonNull ControlTemplate mControlTemplate;
private final @NonNull CharSequence mStatusText;
/**
* @param controlId the unique persistent identifier for this object.
@@ -79,7 +113,6 @@ public class Control implements Parcelable {
* @param zone
* @param appIntent a {@link PendingIntent} linking to a page to interact with the
* corresponding device.
* @param primaryType the primary template for this type.
*/
public Control(@NonNull String controlId,
@DeviceTypes.DeviceType int deviceType,
@@ -88,11 +121,15 @@ public class Control implements Parcelable {
@Nullable CharSequence structure,
@Nullable CharSequence zone,
@NonNull PendingIntent appIntent,
int primaryType) {
@Status int status,
@NonNull ControlTemplate controlTemplate,
@NonNull CharSequence statusText) {
Preconditions.checkNotNull(controlId);
Preconditions.checkNotNull(title);
Preconditions.checkNotNull(subtitle);
Preconditions.checkNotNull(appIntent);
Preconditions.checkNotNull(controlTemplate);
Preconditions.checkNotNull(statusText);
mControlId = controlId;
if (!DeviceTypes.validDeviceType(deviceType)) {
Log.e(TAG, "Invalid device type:" + deviceType);
@@ -105,7 +142,14 @@ public class Control implements Parcelable {
mStructure = structure;
mZone = zone;
mAppIntent = appIntent;
mPrimaryType = primaryType;
if (status < 0 || status >= NUM_STATUS) {
mStatus = STATUS_UNKNOWN;
Log.e(TAG, "Status unknown:" + status);
} else {
mStatus = status;
}
mControlTemplate = controlTemplate;
mStatusText = statusText;
}
public Control(Parcel in) {
@@ -124,7 +168,9 @@ public class Control implements Parcelable {
mZone = null;
}
mAppIntent = PendingIntent.CREATOR.createFromParcel(in);
mPrimaryType = in.readInt();
mStatus = in.readInt();
mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
mStatusText = in.readCharSequence();
}
@NonNull
@@ -162,9 +208,19 @@ public class Control implements Parcelable {
return mAppIntent;
}
@android.service.controls.templates.ControlTemplate.TemplateType
public int getPrimaryType() {
return mPrimaryType;
@Status
public int getStatus() {
return mStatus;
}
@NonNull
public ControlTemplate getControlTemplate() {
return mControlTemplate;
}
@NonNull
public CharSequence getStatusText() {
return mStatusText;
}
@Override
@@ -191,7 +247,9 @@ public class Control implements Parcelable {
dest.writeByte((byte) 0);
}
mAppIntent.writeToParcel(dest, flags);
dest.writeInt(mPrimaryType);
dest.writeInt(mStatus);
mControlTemplate.writeToParcel(dest, flags);
dest.writeCharSequence(mStatusText);
}
public static final Creator<Control> CREATOR = new Creator<Control>() {
@@ -209,32 +267,39 @@ public class Control implements Parcelable {
/**
* Builder class for {@link Control}.
*
* This class facilitates the creation of {@link Control}. It provides the following
* defaults for non-optional parameters:
* This class facilitates the creation of {@link Control} with no state.
* It provides the following defaults for non-optional parameters:
* <ul>
* <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
* <li> Title: {@code ""}
* <li> Subtitle: {@code ""}
* <li> Primary template: {@link ControlTemplate#TYPE_NONE}
* </ul>
* This fixes the values relating to state of the {@link Control} as required by
* {@link ControlsProviderService#onLoad}:
* <ul>
* <li> Status: {@link Status#STATUS_UNKNOWN}
* <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
* <li> Status text: {@code ""}
* </ul>
*/
public static class Builder {
private static final String TAG = "Control.Builder";
private @NonNull String mControlId;
private @DeviceTypes.DeviceType
int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
private @NonNull CharSequence mTitle = "";
private @NonNull CharSequence mSubtitle = "";
private @Nullable CharSequence mStructure;
private @Nullable CharSequence mZone;
private @NonNull PendingIntent mAppIntent;
private @ControlTemplate.TemplateType int mPrimaryType = ControlTemplate.TYPE_NONE;
public static class StatelessBuilder {
private static final String TAG = "StatelessBuilder";
protected @NonNull String mControlId;
protected @DeviceTypes.DeviceType int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
protected @NonNull CharSequence mTitle = "";
protected @NonNull CharSequence mSubtitle = "";
protected @Nullable CharSequence mStructure;
protected @Nullable CharSequence mZone;
protected @NonNull PendingIntent mAppIntent;
protected @Status int mStatus = STATUS_UNKNOWN;
protected @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
protected @NonNull CharSequence mStatusText = "";
/**
* @param controlId the identifier for the {@link Control}.
* @param appIntent the pending intent linking to the device Activity.
*/
public Builder(@NonNull String controlId,
public StatelessBuilder(@NonNull String controlId,
@NonNull PendingIntent appIntent) {
Preconditions.checkNotNull(controlId);
Preconditions.checkNotNull(appIntent);
@@ -243,10 +308,10 @@ public class Control implements Parcelable {
}
/**
* Creates a {@link Builder} using an existing {@link Control} as a base.
* Creates a {@link StatelessBuilder} using an existing {@link Control} as a base.
* @param control base for the builder.
*/
public Builder(@NonNull Control control) {
public StatelessBuilder(@NonNull Control control) {
Preconditions.checkNotNull(control);
mControlId = control.mControlId;
mDeviceType = control.mDeviceType;
@@ -255,7 +320,6 @@ public class Control implements Parcelable {
mStructure = control.mStructure;
mZone = control.mZone;
mAppIntent = control.mAppIntent;
mPrimaryType = control.mPrimaryType;
}
/**
@@ -263,14 +327,14 @@ public class Control implements Parcelable {
* @return {@code this}
*/
@NonNull
public Builder setControlId(@NonNull String controlId) {
public StatelessBuilder setControlId(@NonNull String controlId) {
Preconditions.checkNotNull(controlId);
mControlId = controlId;
return this;
}
@NonNull
public Builder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
public StatelessBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
if (!DeviceTypes.validDeviceType(deviceType)) {
Log.e(TAG, "Invalid device type:" + deviceType);
mDeviceType = DeviceTypes.TYPE_UNKNOWN;
@@ -285,27 +349,27 @@ public class Control implements Parcelable {
* @return {@code this}
*/
@NonNull
public Builder setTitle(@NonNull CharSequence title) {
public StatelessBuilder setTitle(@NonNull CharSequence title) {
Preconditions.checkNotNull(title);
mTitle = title;
return this;
}
@NonNull
public Builder setSubtitle(@NonNull CharSequence subtitle) {
public StatelessBuilder setSubtitle(@NonNull CharSequence subtitle) {
Preconditions.checkNotNull(subtitle);
mSubtitle = subtitle;
return this;
}
@NonNull
public Builder setStructure(@Nullable CharSequence structure) {
public StatelessBuilder setStructure(@Nullable CharSequence structure) {
mStructure = structure;
return this;
}
@NonNull
public Builder setZone(@Nullable CharSequence zone) {
public StatelessBuilder setZone(@Nullable CharSequence zone) {
mZone = zone;
return this;
}
@@ -315,22 +379,12 @@ public class Control implements Parcelable {
* @return {@code this}
*/
@NonNull
public Builder setAppIntent(@NonNull PendingIntent appIntent) {
public StatelessBuilder setAppIntent(@NonNull PendingIntent appIntent) {
Preconditions.checkNotNull(appIntent);
mAppIntent = appIntent;
return this;
}
/**
* @param type type to use as default in the {@link Control}
* @return {@code this}
*/
@NonNull
public Builder setPrimaryType(@ControlTemplate.TemplateType int type) {
mPrimaryType = type;
return this;
}
/**
* Build a {@link Control}
* @return a valid {@link Control}
@@ -344,7 +398,108 @@ public class Control implements Parcelable {
mStructure,
mZone,
mAppIntent,
mPrimaryType);
mStatus,
mControlTemplate,
mStatusText);
}
}
public static class StatefulBuilder extends StatelessBuilder {
private static final String TAG = "StatefulBuilder";
/**
* @param controlId the identifier for the {@link Control}.
* @param appIntent the pending intent linking to the device Activity.
*/
public StatefulBuilder(@NonNull String controlId,
@NonNull PendingIntent appIntent) {
super(controlId, appIntent);
}
public StatefulBuilder(@NonNull Control control) {
super(control);
mStatus = control.mStatus;
mControlTemplate = control.mControlTemplate;
mStatusText = control.mStatusText;
}
/**
* @param controlId the identifier for the {@link Control}.
* @return {@code this}
*/
@NonNull
public StatefulBuilder setControlId(@NonNull String controlId) {
super.setControlId(controlId);
return this;
}
@NonNull
public StatefulBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
super.setDeviceType(deviceType);
return this;
}
/**
* @param title the user facing name of the {@link Control}
* @return {@code this}
*/
@NonNull
public StatefulBuilder setTitle(@NonNull CharSequence title) {
super.setTitle(title);
return this;
}
@NonNull
public StatefulBuilder setSubtitle(@NonNull CharSequence subtitle) {
super.setSubtitle(subtitle);
return this;
}
@NonNull
public StatefulBuilder setStructure(@Nullable CharSequence structure) {
super.setStructure(structure);
return this;
}
@NonNull
public StatefulBuilder setZone(@Nullable CharSequence zone) {
super.setZone(zone);
return this;
}
/**
* @param appIntent an {@link Intent} linking to an Activity for the {@link Control}
* @return {@code this}
*/
@NonNull
public StatefulBuilder setAppIntent(@NonNull PendingIntent appIntent) {
super.setAppIntent(appIntent);
return this;
}
@NonNull
public StatefulBuilder setStatus(@Status int status) {
if (status < 0 || status >= NUM_STATUS) {
mStatus = STATUS_UNKNOWN;
Log.e(TAG, "Status unknown:" + status);
} else {
mStatus = status;
}
return this;
}
@NonNull
public StatefulBuilder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
Preconditions.checkNotNull(controlTemplate);
mControlTemplate = controlTemplate;
return this;
}
@NonNull
public StatefulBuilder setStatusText(@NonNull CharSequence statusText) {
Preconditions.checkNotNull(statusText);
mStatusText = statusText;
return this;
}
}
}

View File

@@ -1,19 +0,0 @@
/*
* 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;
parcelable ControlState;

View File

@@ -1,335 +0,0 @@
/*
* 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.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.controls.templates.ControlTemplate;
import android.util.Log;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Current state for a {@link Control}.
*
* Collects information to render the current state of a {@link Control} as well as possible action
* that can be performed on it.
* <p>
* Additionally, this object is used to modify elements from the {@link Control} such as device
* type, and intents. This information will last until it is again modified by a
* {@link ControlState}.
* @hide
*/
public final class ControlState implements Parcelable {
private static final String TAG = "ControlState";
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
STATUS_OK,
STATUS_NOT_FOUND,
STATUS_ERROR,
STATUS_DISABLED,
})
public @interface Status {};
/**
* The device corresponding to the {@link Control} is responding correctly.
*/
public static final int STATUS_OK = 0;
/**
* The device corresponding to the {@link Control} cannot be found or was removed.
*/
public static final int STATUS_NOT_FOUND = 1;
/**
* The device corresponding to the {@link Control} is in an error state.
*/
public static final int STATUS_ERROR = 2;
/**
* The {@link Control} is currently disabled.
*/
public static final int STATUS_DISABLED = 3;
private final @NonNull String mControlId;
private final @DeviceTypes.DeviceType int mDeviceType;
private final @Status int mStatus;
private final @NonNull ControlTemplate mControlTemplate;
private final @NonNull CharSequence mStatusText;
private final @Nullable PendingIntent mAppIntent;
/**
* @param controlId the identifier of the {@link Control} this object refers to.
* @param status the current status of the {@link Control}.
* @param deviceType the {@link DeviceTypes.DeviceType} to replace the one set in the
* {@link Control} or set in the last {@link ControlState}. In order to keep
* the current device type for this {@link Control}, the old value must be
* passed.
* @param controlTemplate the template to be used to render the {@link Control}. This can be
* of a different {@link ControlTemplate.TemplateType} than the
* one defined in {@link Control#getPrimaryType}
* @param statusText the user facing text describing the current status.
* @param appIntent the {@link PendingIntent} to replace the one set in the {@link Control} or
* set in the last {@link ControlState}. Pass {@code null} to use the last
* value set for this {@link Control}.
*/
public ControlState(@NonNull String controlId,
@DeviceTypes.DeviceType int deviceType,
@Status int status,
@NonNull ControlTemplate controlTemplate,
@NonNull CharSequence statusText,
@Nullable PendingIntent appIntent) {
Preconditions.checkNotNull(controlId);
Preconditions.checkNotNull(controlTemplate);
Preconditions.checkNotNull(statusText);
mControlId = controlId;
if (!DeviceTypes.validDeviceType(deviceType)) {
Log.e(TAG, "Invalid device type:" + deviceType);
mDeviceType = DeviceTypes.TYPE_UNKNOWN;
} else {
mDeviceType = deviceType;
}
mStatus = status;
mControlTemplate = controlTemplate;
mStatusText = statusText;
mAppIntent = appIntent;
}
ControlState(Parcel in) {
mControlId = in.readString();
mDeviceType = in.readInt();
mStatus = in.readInt();
mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
mStatusText = in.readCharSequence();
if (in.readByte() == 1) {
mAppIntent = PendingIntent.CREATOR.createFromParcel(in);
} else {
mAppIntent = null;
}
}
@Override
public int describeContents() {
return 0;
}
@NonNull
public String getControlId() {
return mControlId;
}
@DeviceTypes.DeviceType
public int getDeviceType() {
return mDeviceType;
}
@Nullable
public PendingIntent getAppIntent() {
return mAppIntent;
}
@Status
public int getStatus() {
return mStatus;
}
@NonNull
public ControlTemplate getControlTemplate() {
return mControlTemplate;
}
@NonNull
public CharSequence getStatusText() {
return mStatusText;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mControlId);
dest.writeInt(mDeviceType);
dest.writeInt(mStatus);
mControlTemplate.writeToParcel(dest, flags);
dest.writeCharSequence(mStatusText);
if (mAppIntent != null) {
dest.writeByte((byte) 1);
mAppIntent.writeToParcel(dest, flags);
} else {
dest.writeByte((byte) 0);
}
}
public static final Creator<ControlState> CREATOR = new Creator<ControlState>() {
@Override
public ControlState createFromParcel(Parcel source) {
return new ControlState(source);
}
@Override
public ControlState[] newArray(int size) {
return new ControlState[size];
}
};
/**
* Builder class for {@link ControlState}.
*
* This class facilitates the creation of {@link ControlState}. It provides the following
* defaults for non-optional parameters:
* <ul>
* <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
* <li> Status: {@link ControlState#STATUS_OK}
* <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
* <li> Status text: {@code ""}
* </ul>
*/
public static class Builder {
private static final String TAG = "ControlState.Builder";
private @NonNull String mControlId;
private @DeviceTypes.DeviceType
int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
private @Status int mStatus = STATUS_OK;
private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
private @NonNull CharSequence mStatusText = "";
private @Nullable PendingIntent mAppIntent;
/**
* @param controlId the identifier of the {@link Control} that the resulting
* {@link ControlState} refers to.
*/
public Builder(@NonNull String controlId) {
Preconditions.checkNotNull(controlId);
mControlId = controlId;
}
/**
* Creates a {@link Builder} using an existing {@link ControlState} as a base.
* @param controlState base for the builder.
*/
public Builder(@NonNull ControlState controlState) {
Preconditions.checkNotNull(controlState);
mControlId = controlState.mControlId;
mDeviceType = controlState.mDeviceType;
mStatus = controlState.mStatus;
mControlTemplate = controlState.mControlTemplate;
mStatusText = controlState.mStatusText;
mAppIntent = controlState.mAppIntent;
}
/**
* @param controlId the identifier of the {@link Control} for the resulting object.
* @return {@code this}
*/
@NonNull
public Builder setControlId(@NonNull String controlId) {
mControlId = controlId;
return this;
}
/**
* @param deviceType the device type of the {@link Control}.
* @return {@code this}
*/
@NonNull
public Builder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
if (!DeviceTypes.validDeviceType(deviceType)) {
Log.e(TAG, "Invalid device type:" + deviceType);
mDeviceType = DeviceTypes.TYPE_UNKNOWN;
} else {
mDeviceType = deviceType;
}
return this;
}
/**
* @param status the current status of the {@link Control}
* @return {@code this}
*/
@NonNull
public Builder setStatus(@Status int status) {
mStatus = status;
return this;
}
/**
* @param controlTemplate the template to use when rendering the {@code Control}.
* @return {@code this}
*/
@NonNull
public Builder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
Preconditions.checkNotNull(controlTemplate);
mControlTemplate = controlTemplate;
return this;
}
/**
* @param statusText the user-visible description of the status.
* @return {@code this}
*/
@NonNull
public Builder setStatusText(@NonNull CharSequence statusText) {
Preconditions.checkNotNull(statusText);
mStatusText = statusText;
return this;
}
/**
* @param appIntent the Pending Intent to replace the one defined in the corresponding
* {@link Control} or set by the last {@link ControlState}. Pass
* {@code null} to keep the last value.
* @return {@code this}
*/
@NonNull
public Builder setAppIntent(@Nullable PendingIntent appIntent) {
mAppIntent = appIntent;
return this;
}
/**
* @return a new {@link ControlState}
*/
public ControlState build() {
return new ControlState(mControlId, mDeviceType, mStatus, mControlTemplate, mStatusText,
mAppIntent);
}
/**
* Creates a new {@link ControlState.Builder} for the given {@link Control}.
*
* This will set the corresponding identifier as well as the device type.
* @param control the {@link Control} to create a state for.
* @return a {@link ControlState.Builder} for a {@link Control}
*/
public static Builder createForControl(Control control) {
return new Builder(control.getControlId()).setDeviceType(control.getDeviceType());
}
}
}

View File

@@ -20,13 +20,21 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.service.controls.actions.ControlAction;
import android.service.controls.templates.ControlTemplate;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.List;
/**
@@ -38,8 +46,14 @@ public abstract class ControlsProviderService extends Service {
@SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String CONTROLS_ACTION = "android.service.controls.ControlsProviderService";
public static final String CALLBACK_BUNDLE = "CALLBACK_BUNDLE";
public static final String CALLBACK_BINDER = "CALLBACK_BINDER";
public static final String CALLBACK_TOKEN = "CALLBACK_TOKEN";
public final String TAG = getClass().getSimpleName();
private IControlsProviderCallback mCallback;
private IBinder mToken;
private RequestHandler mHandler;
/**
@@ -67,16 +81,80 @@ public abstract class ControlsProviderService extends Service {
*/
public abstract void onAction(@NonNull String controlId, @NonNull ControlAction action);
protected IControlsProviderCallback getControlsProviderCallback() {
return mCallback;
/**
* Sends a list of the controls available from this service.
*
* The items in the list must not have state information (as created by
* {@link Control.StatelessBuilder}).
* @param controls
*/
public void onLoad(@NonNull List<Control> controls) {
Preconditions.checkNotNull(controls);
List<Control> list = new ArrayList<>();
for (Control control: controls) {
if (control == null) {
Log.e(TAG, "onLoad: null control.");
}
if (isStateless(control)) {
list.add(control);
} else {
Log.w(TAG, "onLoad: control is not stateless.");
list.add(new Control.StatelessBuilder(control).build());
}
}
try {
mCallback.onLoad(mToken, list);
} catch (RemoteException ex) {
ex.rethrowAsRuntimeException();
}
}
/**
* Sends a list of the controls requested by {@link ControlsProviderService#subscribe} with
* their state.
* @param statefulControls
*/
public void onRefreshState(@NonNull List<Control> statefulControls) {
Preconditions.checkNotNull(statefulControls);
try {
mCallback.onRefreshState(mToken, statefulControls);
} catch (RemoteException ex) {
ex.rethrowAsRuntimeException();
}
}
/**
* Sends the response of a command in the specified {@link Control}.
* @param controlId
* @param response
*/
public void onControlActionResponse(
@NonNull String controlId, @ControlAction.ResponseResult int response) {
Preconditions.checkNotNull(controlId);
if (!ControlAction.isValidResponse(response)) {
Log.e(TAG, "Not valid response result: " + response);
response = ControlAction.RESPONSE_UNKNOWN;
}
try {
mCallback.onControlActionResponse(mToken, controlId, response);
} catch (RemoteException ex) {
ex.rethrowAsRuntimeException();
}
}
private boolean isStateless(Control control) {
return (control.getStatus() == Control.STATUS_UNKNOWN
&& control.getControlTemplate().getTemplateType() == ControlTemplate.TYPE_NONE
&& TextUtils.isEmpty(control.getStatusText()));
}
@Override
public IBinder onBind(Intent intent) {
mHandler = new RequestHandler(Looper.getMainLooper());
Bundle bundle = intent.getBundleExtra("CALLBACK_BUNDLE");
IBinder callbackBinder = bundle.getBinder("CALLBACK_BINDER");
Bundle bundle = intent.getBundleExtra(CALLBACK_BUNDLE);
IBinder callbackBinder = bundle.getBinder(CALLBACK_BINDER);
mToken = bundle.getBinder(CALLBACK_TOKEN);
mCallback = IControlsProviderCallback.Stub.asInterface(callbackBinder);
return new IControlsProvider.Stub() {

View File

@@ -17,13 +17,12 @@
package android.service.controls;
import android.service.controls.Control;
import android.service.controls.ControlState;
/** @hide */
oneway interface IControlsProviderCallback {
void onLoad(in List<Control> controls);
void onLoad(in IBinder token, in List<Control> controls);
void onRefreshState(in List<ControlState> controlStates);
void onRefreshState(in IBinder token, in List<Control> statefulControls);
void onControlActionResponse(in String controlId, int response);
void onControlActionResponse(in IBinder token, in String controlId, int response);
}

View File

@@ -23,8 +23,8 @@ import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.controls.templates.ControlTemplate;
import android.service.controls.IControlsProviderCallback;
import android.service.controls.templates.ControlTemplate;
import com.android.internal.util.Preconditions;
@@ -82,11 +82,17 @@ public abstract class ControlAction implements Parcelable {
public static final @ActionType int TYPE_COMMAND = 5;
public static final boolean isValidResponse(@ResponseResult int response) {
return (response >= 0 && response < NUM_RESPONSE_TYPES);
}
private static final int NUM_RESPONSE_TYPES = 6;
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
RESPONSE_UNKNOWN,
RESPONSE_OK,
RESPONSE_FAIL,
RESPONSE_CHALLENGE_ACK,
@@ -95,31 +101,33 @@ public abstract class ControlAction implements Parcelable {
})
public @interface ResponseResult {};
public static final @ResponseResult int RESPONSE_UNKNOWN = 0;
/**
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
* the action has been performed. The action may still fail later and the state may not change.
*/
public static final @ResponseResult int RESPONSE_OK = 0;
public static final @ResponseResult int RESPONSE_OK = 1;
/**
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
* the action has failed.
*/
public static final @ResponseResult int RESPONSE_FAIL = 1;
public static final @ResponseResult int RESPONSE_FAIL = 2;
/**
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
* in order for the action to be performed, acknowledgment from the user is required.
*/
public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 2;
public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 3;
/**
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
* in order for the action to be performed, a PIN is required.
*/
public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 3;
public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 4;
/**
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
* in order for the action to be performed, an alphanumeric passphrase is required.
*/
public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 4;
public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 5;
/**
* The {@link ActionType} associated with this class.

View File

@@ -23,7 +23,6 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.controls.Control;
import android.service.controls.ControlState;
import android.service.controls.actions.ControlAction;
import com.android.internal.util.Preconditions;
@@ -34,12 +33,11 @@ import java.lang.annotation.RetentionPolicy;
/**
* An abstract input template for a {@link Control}.
*
* Specifies what layout is presented to the user when a {@link ControlState} is assigned to a
* particular {@link Control}.
* Specifies what layout is presented to the user for a given {@link Control}.
* <p>
* Some instances of {@link Control} can originate actions (via user interaction) to modify its
* associated state. The actions available to a given {@link Control} in a particular
* {@link ControlState} are determined by its {@link ControlTemplate}.
* associated state. The actions available to a given {@link Control} are determined by its
* {@link ControlTemplate}.
* @see ControlAction
* @hide
*/

View File

@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Parcel;
import android.service.controls.Control;
import android.service.controls.ControlState;
import android.service.controls.actions.FloatAction;
/**
@@ -61,7 +60,7 @@ public final class RangeTemplate extends ControlTemplate {
* @param templateId the identifier for this template object
* @param minValue minimum value for the input
* @param maxValue maximum value for the input
* @param currentValue the current value of the {@link ControlState} containing this object.
* @param currentValue the current value of the {@link Control} containing this object.
* @param stepValue minimum value of increments/decrements when interacting with this control.
* @param formatString a formatting string as per {@link String#format} used to display the
* {@code currentValue}. If {@code null} is passed, the "%.1f" is used.