Removed @hide and Parcelable hierarchy
This CL removes @hide from all the controls API as well as introduce some minor changes: * ControlTemplate and ControlAction are not Parcelable. There are Parcelable wrappers that stores all the information in the Bundle produced by #getDataBundle. * Control supports a customIcon and customColor to override those defined by deviceType. Missing: * Javadoc in some classes/methods * CTS testing Test: atest ControlProviderServiceTest Test: atest ControlActionTest Test: atest ControlTemplateTest Bug: 147760868 Change-Id: I3279ecfcc4fabe05a4b278385eb89ea6a92e3a33
This commit is contained in:
committed by
Matt Pietal
parent
15cc12ccf4
commit
1bb26b5046
301
api/current.txt
301
api/current.txt
@@ -43006,6 +43006,307 @@ package android.service.chooser {
|
||||
|
||||
}
|
||||
|
||||
package android.service.controls {
|
||||
|
||||
public final class Control implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method @NonNull public android.app.PendingIntent getAppIntent();
|
||||
method @NonNull public String getControlId();
|
||||
method @NonNull public android.service.controls.templates.ControlTemplate getControlTemplate();
|
||||
method @Nullable public android.content.res.ColorStateList getCustomColor();
|
||||
method @Nullable public android.graphics.drawable.Icon getCustomIcon();
|
||||
method public int getDeviceType();
|
||||
method public int getStatus();
|
||||
method @NonNull public CharSequence getStatusText();
|
||||
method @Nullable public CharSequence getStructure();
|
||||
method @NonNull public CharSequence getSubtitle();
|
||||
method @NonNull public CharSequence getTitle();
|
||||
method @Nullable public CharSequence getZone();
|
||||
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
|
||||
field public static final int STATUS_ERROR = 3; // 0x3
|
||||
field public static final int STATUS_NOT_FOUND = 2; // 0x2
|
||||
field public static final int STATUS_OK = 1; // 0x1
|
||||
field public static final int STATUS_UNKNOWN = 0; // 0x0
|
||||
}
|
||||
|
||||
public static final class Control.StatefulBuilder {
|
||||
ctor public Control.StatefulBuilder(@NonNull String, @NonNull android.app.PendingIntent);
|
||||
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 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);
|
||||
method @NonNull public android.service.controls.Control.StatefulBuilder setCustomIcon(@Nullable android.graphics.drawable.Icon);
|
||||
method @NonNull public android.service.controls.Control.StatefulBuilder setDeviceType(int);
|
||||
method @NonNull public android.service.controls.Control.StatefulBuilder setStatus(int);
|
||||
method @NonNull public android.service.controls.Control.StatefulBuilder setStatusText(@NonNull CharSequence);
|
||||
method @NonNull public android.service.controls.Control.StatefulBuilder setStructure(@Nullable CharSequence);
|
||||
method @NonNull public android.service.controls.Control.StatefulBuilder setSubtitle(@NonNull CharSequence);
|
||||
method @NonNull public android.service.controls.Control.StatefulBuilder setTitle(@NonNull CharSequence);
|
||||
method @NonNull public android.service.controls.Control.StatefulBuilder setZone(@Nullable CharSequence);
|
||||
}
|
||||
|
||||
public static final class Control.StatelessBuilder {
|
||||
ctor public Control.StatelessBuilder(@NonNull String, @NonNull android.app.PendingIntent);
|
||||
ctor public Control.StatelessBuilder(@NonNull android.service.controls.Control);
|
||||
method @NonNull public android.service.controls.Control build();
|
||||
method @NonNull public android.service.controls.Control.StatelessBuilder setAppIntent(@NonNull android.app.PendingIntent);
|
||||
method @NonNull public android.service.controls.Control.StatelessBuilder setControlId(@NonNull String);
|
||||
method @NonNull public android.service.controls.Control.StatelessBuilder setCustomColor(@Nullable android.content.res.ColorStateList);
|
||||
method @NonNull public android.service.controls.Control.StatelessBuilder setCustomIcon(@Nullable android.graphics.drawable.Icon);
|
||||
method @NonNull public android.service.controls.Control.StatelessBuilder setDeviceType(int);
|
||||
method @NonNull public android.service.controls.Control.StatelessBuilder setStructure(@Nullable CharSequence);
|
||||
method @NonNull public android.service.controls.Control.StatelessBuilder setSubtitle(@NonNull CharSequence);
|
||||
method @NonNull public android.service.controls.Control.StatelessBuilder setTitle(@NonNull CharSequence);
|
||||
method @NonNull public android.service.controls.Control.StatelessBuilder setZone(@Nullable CharSequence);
|
||||
}
|
||||
|
||||
public abstract class ControlsProviderService extends android.app.Service {
|
||||
ctor public ControlsProviderService();
|
||||
method public abstract void loadAvailableControls(@NonNull java.util.function.Consumer<java.util.List<android.service.controls.Control>>);
|
||||
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
|
||||
method public abstract void performControlAction(@NonNull String, @NonNull android.service.controls.actions.ControlAction, @NonNull java.util.function.Consumer<java.lang.Integer>);
|
||||
method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherFor(@NonNull java.util.List<java.lang.String>);
|
||||
field public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService";
|
||||
field @NonNull public static final String TAG = "ControlsProviderService";
|
||||
}
|
||||
|
||||
public class DeviceTypes {
|
||||
method public static boolean validDeviceType(int);
|
||||
field public static final int TYPE_AC_HEATER = 1; // 0x1
|
||||
field public static final int TYPE_AC_UNIT = 2; // 0x2
|
||||
field public static final int TYPE_AIR_FRESHENER = 3; // 0x3
|
||||
field public static final int TYPE_AIR_PURIFIER = 4; // 0x4
|
||||
field public static final int TYPE_AWNING = 33; // 0x21
|
||||
field public static final int TYPE_BLINDS = 34; // 0x22
|
||||
field public static final int TYPE_CAMERA = 50; // 0x32
|
||||
field public static final int TYPE_CLOSET = 35; // 0x23
|
||||
field public static final int TYPE_COFFEE_MAKER = 5; // 0x5
|
||||
field public static final int TYPE_CURTAIN = 36; // 0x24
|
||||
field public static final int TYPE_DEHUMIDIFIER = 6; // 0x6
|
||||
field public static final int TYPE_DISHWASHER = 24; // 0x18
|
||||
field public static final int TYPE_DISPLAY = 7; // 0x7
|
||||
field public static final int TYPE_DOOR = 37; // 0x25
|
||||
field public static final int TYPE_DOORBELL = 51; // 0x33
|
||||
field public static final int TYPE_DRAWER = 38; // 0x26
|
||||
field public static final int TYPE_DRYER = 25; // 0x19
|
||||
field public static final int TYPE_FAN = 8; // 0x8
|
||||
field public static final int TYPE_GARAGE = 39; // 0x27
|
||||
field public static final int TYPE_GATE = 40; // 0x28
|
||||
field public static final int TYPE_GENERIC_ARM_DISARM = -5; // 0xfffffffb
|
||||
field public static final int TYPE_GENERIC_LOCK_UNLOCK = -4; // 0xfffffffc
|
||||
field public static final int TYPE_GENERIC_ON_OFF = -1; // 0xffffffff
|
||||
field public static final int TYPE_GENERIC_OPEN_CLOSE = -3; // 0xfffffffd
|
||||
field public static final int TYPE_GENERIC_START_STOP = -2; // 0xfffffffe
|
||||
field public static final int TYPE_GENERIC_TEMP_SETTING = -6; // 0xfffffffa
|
||||
field public static final int TYPE_GENERIC_VIEWSTREAM = -7; // 0xfffffff9
|
||||
field public static final int TYPE_HEATER = 47; // 0x2f
|
||||
field public static final int TYPE_HOOD = 10; // 0xa
|
||||
field public static final int TYPE_HUMIDIFIER = 11; // 0xb
|
||||
field public static final int TYPE_KETTLE = 12; // 0xc
|
||||
field public static final int TYPE_LIGHT = 13; // 0xd
|
||||
field public static final int TYPE_LOCK = 45; // 0x2d
|
||||
field public static final int TYPE_MICROWAVE = 14; // 0xe
|
||||
field public static final int TYPE_MOP = 26; // 0x1a
|
||||
field public static final int TYPE_MOWER = 27; // 0x1b
|
||||
field public static final int TYPE_MULTICOOKER = 28; // 0x1c
|
||||
field public static final int TYPE_OUTLET = 15; // 0xf
|
||||
field public static final int TYPE_PERGOLA = 41; // 0x29
|
||||
field public static final int TYPE_RADIATOR = 16; // 0x10
|
||||
field public static final int TYPE_REFRIGERATOR = 48; // 0x30
|
||||
field public static final int TYPE_REMOTE_CONTROL = 17; // 0x11
|
||||
field public static final int TYPE_SECURITY_SYSTEM = 46; // 0x2e
|
||||
field public static final int TYPE_SET_TOP = 18; // 0x12
|
||||
field public static final int TYPE_SHOWER = 29; // 0x1d
|
||||
field public static final int TYPE_SHUTTER = 42; // 0x2a
|
||||
field public static final int TYPE_SPRINKLER = 30; // 0x1e
|
||||
field public static final int TYPE_STANDMIXER = 19; // 0x13
|
||||
field public static final int TYPE_STYLER = 20; // 0x14
|
||||
field public static final int TYPE_SWITCH = 21; // 0x15
|
||||
field public static final int TYPE_THERMOSTAT = 49; // 0x31
|
||||
field public static final int TYPE_TV = 22; // 0x16
|
||||
field public static final int TYPE_UNKNOWN = 0; // 0x0
|
||||
field public static final int TYPE_VACUUM = 32; // 0x20
|
||||
field public static final int TYPE_VALVE = 44; // 0x2c
|
||||
field public static final int TYPE_WASHER = 31; // 0x1f
|
||||
field public static final int TYPE_WATER_HEATER = 23; // 0x17
|
||||
field public static final int TYPE_WINDOW = 43; // 0x2b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.service.controls.actions {
|
||||
|
||||
public final class BooleanAction extends android.service.controls.actions.ControlAction {
|
||||
ctor public BooleanAction(@NonNull String, boolean);
|
||||
ctor public BooleanAction(@NonNull String, boolean, @Nullable String);
|
||||
method public int getActionType();
|
||||
method public boolean getNewState();
|
||||
}
|
||||
|
||||
public final class CommandAction extends android.service.controls.actions.ControlAction {
|
||||
ctor public CommandAction(@NonNull String, @Nullable String);
|
||||
ctor public CommandAction(@NonNull String);
|
||||
method public int getActionType();
|
||||
}
|
||||
|
||||
public abstract class ControlAction {
|
||||
method public abstract int getActionType();
|
||||
method @Nullable public String getChallengeValue();
|
||||
method @NonNull public String getTemplateId();
|
||||
method public static final boolean isValidResponse(int);
|
||||
field @NonNull public static final android.service.controls.actions.ControlAction ERROR_ACTION;
|
||||
field public static final int RESPONSE_CHALLENGE_ACK = 3; // 0x3
|
||||
field public static final int RESPONSE_CHALLENGE_PASSPHRASE = 5; // 0x5
|
||||
field public static final int RESPONSE_CHALLENGE_PIN = 4; // 0x4
|
||||
field public static final int RESPONSE_FAIL = 2; // 0x2
|
||||
field public static final int RESPONSE_OK = 1; // 0x1
|
||||
field public static final int RESPONSE_UNKNOWN = 0; // 0x0
|
||||
field public static final int TYPE_BOOLEAN = 1; // 0x1
|
||||
field public static final int TYPE_COMMAND = 5; // 0x5
|
||||
field public static final int TYPE_ERROR = -1; // 0xffffffff
|
||||
field public static final int TYPE_FLOAT = 2; // 0x2
|
||||
field public static final int TYPE_MODE = 4; // 0x4
|
||||
field public static final int TYPE_MULTI_FLOAT = 3; // 0x3
|
||||
}
|
||||
|
||||
public final class FloatAction extends android.service.controls.actions.ControlAction {
|
||||
ctor public FloatAction(@NonNull String, float);
|
||||
ctor public FloatAction(@NonNull String, float, @Nullable String);
|
||||
method public int getActionType();
|
||||
method public float getNewValue();
|
||||
}
|
||||
|
||||
public final class ModeAction extends android.service.controls.actions.ControlAction {
|
||||
ctor public ModeAction(@NonNull String, int, @Nullable String);
|
||||
ctor public ModeAction(@NonNull String, int);
|
||||
method public int getActionType();
|
||||
method public int getNewMode();
|
||||
}
|
||||
|
||||
public final class MultiFloatAction extends android.service.controls.actions.ControlAction {
|
||||
ctor public MultiFloatAction(@NonNull String, @NonNull float[], @Nullable String);
|
||||
ctor public MultiFloatAction(@NonNull String, @NonNull float[]);
|
||||
method public int getActionType();
|
||||
method @NonNull public float[] getNewValues();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.service.controls.templates {
|
||||
|
||||
public final class ControlButton implements android.os.Parcelable {
|
||||
ctor public ControlButton(boolean, @NonNull CharSequence);
|
||||
method public int describeContents();
|
||||
method @NonNull public CharSequence getActionDescription();
|
||||
method public boolean isChecked();
|
||||
method @NonNull public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.service.controls.templates.ControlButton> CREATOR;
|
||||
}
|
||||
|
||||
public abstract class ControlTemplate {
|
||||
method @NonNull public String getTemplateId();
|
||||
method public abstract int getTemplateType();
|
||||
field @NonNull public static final android.service.controls.templates.ControlTemplate ERROR_TEMPLATE;
|
||||
field @NonNull public static final android.service.controls.templates.ControlTemplate NO_TEMPLATE;
|
||||
field public static final int TYPE_DISCRETE_TOGGLE = 4; // 0x4
|
||||
field public static final int TYPE_ERROR = -1; // 0xffffffff
|
||||
field public static final int TYPE_NONE = 0; // 0x0
|
||||
field public static final int TYPE_RANGE = 2; // 0x2
|
||||
field public static final int TYPE_STATELESS = 8; // 0x8
|
||||
field public static final int TYPE_TEMPERATURE = 7; // 0x7
|
||||
field public static final int TYPE_THUMBNAIL = 3; // 0x3
|
||||
field public static final int TYPE_TOGGLE = 1; // 0x1
|
||||
field public static final int TYPE_TOGGLE_RANGE = 6; // 0x6
|
||||
}
|
||||
|
||||
public final class CoordinatedRangeTemplate extends android.service.controls.templates.ControlTemplate {
|
||||
ctor public CoordinatedRangeTemplate(@NonNull String, float, @NonNull android.service.controls.templates.RangeTemplate, @NonNull android.service.controls.templates.RangeTemplate);
|
||||
ctor public CoordinatedRangeTemplate(@NonNull String, float, float, float, float, float, float, float, float, @Nullable CharSequence);
|
||||
method public float getCurrentValueHigh();
|
||||
method public float getCurrentValueLow();
|
||||
method @NonNull public CharSequence getFormatString();
|
||||
method public float getMaxValueHigh();
|
||||
method public float getMaxValueLow();
|
||||
method public float getMinGap();
|
||||
method public float getMinValueHigh();
|
||||
method public float getMinValueLow();
|
||||
method @NonNull public android.service.controls.templates.RangeTemplate getRangeHigh();
|
||||
method @NonNull public android.service.controls.templates.RangeTemplate getRangeLow();
|
||||
method public float getStepValue();
|
||||
method public int getTemplateType();
|
||||
}
|
||||
|
||||
public final class DiscreteToggleTemplate extends android.service.controls.templates.ControlTemplate {
|
||||
ctor public DiscreteToggleTemplate(@NonNull String, @NonNull android.service.controls.templates.ControlButton, @NonNull android.service.controls.templates.ControlButton);
|
||||
method @NonNull public android.service.controls.templates.ControlButton getNegativeButton();
|
||||
method @NonNull public android.service.controls.templates.ControlButton getPositiveButton();
|
||||
method public int getTemplateType();
|
||||
}
|
||||
|
||||
public final class RangeTemplate extends android.service.controls.templates.ControlTemplate {
|
||||
ctor public RangeTemplate(@NonNull String, float, float, float, float, @Nullable CharSequence);
|
||||
method public float getCurrentValue();
|
||||
method @NonNull public CharSequence getFormatString();
|
||||
method public float getMaxValue();
|
||||
method public float getMinValue();
|
||||
method public float getStepValue();
|
||||
method public int getTemplateType();
|
||||
}
|
||||
|
||||
public final class StatelessTemplate extends android.service.controls.templates.ControlTemplate {
|
||||
ctor public StatelessTemplate(@NonNull String);
|
||||
method public int getTemplateType();
|
||||
}
|
||||
|
||||
public final class TemperatureControlTemplate extends android.service.controls.templates.ControlTemplate {
|
||||
ctor public TemperatureControlTemplate(@NonNull String, @NonNull android.service.controls.templates.ControlTemplate, int, int, int);
|
||||
method public int getCurrentActiveMode();
|
||||
method public int getCurrentMode();
|
||||
method public int getModes();
|
||||
method @NonNull public android.service.controls.templates.ControlTemplate getTemplate();
|
||||
method public int getTemplateType();
|
||||
field public static final int FLAG_MODE_COOL = 8; // 0x8
|
||||
field public static final int FLAG_MODE_ECO = 32; // 0x20
|
||||
field public static final int FLAG_MODE_HEAT = 4; // 0x4
|
||||
field public static final int FLAG_MODE_HEAT_COOL = 16; // 0x10
|
||||
field public static final int FLAG_MODE_OFF = 2; // 0x2
|
||||
field public static final int MODE_COOL = 3; // 0x3
|
||||
field public static final int MODE_ECO = 5; // 0x5
|
||||
field public static final int MODE_HEAT = 2; // 0x2
|
||||
field public static final int MODE_HEAT_COOL = 4; // 0x4
|
||||
field public static final int MODE_OFF = 1; // 0x1
|
||||
field public static final int MODE_UNKNOWN = 0; // 0x0
|
||||
}
|
||||
|
||||
public final class ThumbnailTemplate extends android.service.controls.templates.ControlTemplate {
|
||||
ctor public ThumbnailTemplate(@NonNull String, @NonNull android.graphics.drawable.Icon, @NonNull CharSequence);
|
||||
method @NonNull public CharSequence getContentDescription();
|
||||
method public int getTemplateType();
|
||||
method @NonNull public android.graphics.drawable.Icon getThumbnail();
|
||||
}
|
||||
|
||||
public final class ToggleRangeTemplate extends android.service.controls.templates.ControlTemplate {
|
||||
ctor public ToggleRangeTemplate(@NonNull String, @NonNull android.service.controls.templates.ControlButton, @NonNull android.service.controls.templates.RangeTemplate);
|
||||
ctor public ToggleRangeTemplate(@NonNull String, boolean, @NonNull CharSequence, @NonNull android.service.controls.templates.RangeTemplate);
|
||||
method @NonNull public CharSequence getActionDescription();
|
||||
method @NonNull public android.service.controls.templates.RangeTemplate getRange();
|
||||
method public int getTemplateType();
|
||||
method public boolean isChecked();
|
||||
}
|
||||
|
||||
public final class ToggleTemplate extends android.service.controls.templates.ControlTemplate {
|
||||
ctor public ToggleTemplate(@NonNull String, @NonNull android.service.controls.templates.ControlButton);
|
||||
method @NonNull public CharSequence getContentDescription();
|
||||
method public int getTemplateType();
|
||||
method public boolean isChecked();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.service.dreams {
|
||||
|
||||
public class DreamService extends android.app.Service implements android.view.Window.Callback {
|
||||
|
||||
@@ -19,12 +19,16 @@ package android.service.controls;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.service.controls.actions.ControlAction;
|
||||
import android.service.controls.templates.ControlTemplate;
|
||||
import android.service.controls.templates.ControlTemplateWrapper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
@@ -51,9 +55,8 @@ import java.lang.annotation.RetentionPolicy;
|
||||
* <p>
|
||||
* An {@link Intent} linking to the provider Activity that expands on this {@link Control} and
|
||||
* allows for further actions should be provided.
|
||||
* @hide
|
||||
*/
|
||||
public class Control implements Parcelable {
|
||||
public final class Control implements Parcelable {
|
||||
private static final String TAG = "Control";
|
||||
|
||||
private static final int NUM_STATUS = 5;
|
||||
@@ -99,6 +102,10 @@ public class Control implements Parcelable {
|
||||
private final @Nullable CharSequence mStructure;
|
||||
private final @Nullable CharSequence mZone;
|
||||
private final @NonNull PendingIntent mAppIntent;
|
||||
|
||||
private final @Nullable Icon mCustomIcon;
|
||||
private final @Nullable ColorStateList mCustomColor;
|
||||
|
||||
private final @Status int mStatus;
|
||||
private final @NonNull ControlTemplate mControlTemplate;
|
||||
private final @NonNull CharSequence mStatusText;
|
||||
@@ -113,14 +120,21 @@ public class Control implements Parcelable {
|
||||
* @param zone
|
||||
* @param appIntent a {@link PendingIntent} linking to a page to interact with the
|
||||
* corresponding device.
|
||||
* @param customIcon
|
||||
* @param customColor
|
||||
* @param status
|
||||
* @param controlTemplate
|
||||
* @param statusText
|
||||
*/
|
||||
public Control(@NonNull String controlId,
|
||||
Control(@NonNull String controlId,
|
||||
@DeviceTypes.DeviceType int deviceType,
|
||||
@NonNull CharSequence title,
|
||||
@NonNull CharSequence subtitle,
|
||||
@Nullable CharSequence structure,
|
||||
@Nullable CharSequence zone,
|
||||
@NonNull PendingIntent appIntent,
|
||||
@Nullable Icon customIcon,
|
||||
@Nullable ColorStateList customColor,
|
||||
@Status int status,
|
||||
@NonNull ControlTemplate controlTemplate,
|
||||
@NonNull CharSequence statusText) {
|
||||
@@ -142,6 +156,10 @@ public class Control implements Parcelable {
|
||||
mStructure = structure;
|
||||
mZone = zone;
|
||||
mAppIntent = appIntent;
|
||||
|
||||
mCustomColor = customColor;
|
||||
mCustomIcon = customIcon;
|
||||
|
||||
if (status < 0 || status >= NUM_STATUS) {
|
||||
mStatus = STATUS_UNKNOWN;
|
||||
Log.e(TAG, "Status unknown:" + status);
|
||||
@@ -152,7 +170,11 @@ public class Control implements Parcelable {
|
||||
mStatusText = statusText;
|
||||
}
|
||||
|
||||
public Control(Parcel in) {
|
||||
/**
|
||||
* @param in
|
||||
* @hide
|
||||
*/
|
||||
Control(Parcel in) {
|
||||
mControlId = in.readString();
|
||||
mDeviceType = in.readInt();
|
||||
mTitle = in.readCharSequence();
|
||||
@@ -168,8 +190,22 @@ public class Control implements Parcelable {
|
||||
mZone = null;
|
||||
}
|
||||
mAppIntent = PendingIntent.CREATOR.createFromParcel(in);
|
||||
|
||||
if (in.readByte() == (byte) 1) {
|
||||
mCustomIcon = Icon.CREATOR.createFromParcel(in);
|
||||
} else {
|
||||
mCustomIcon = null;
|
||||
}
|
||||
|
||||
if (in.readByte() == (byte) 1) {
|
||||
mCustomColor = ColorStateList.CREATOR.createFromParcel(in);
|
||||
} else {
|
||||
mCustomColor = null;
|
||||
}
|
||||
|
||||
mStatus = in.readInt();
|
||||
mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
|
||||
ControlTemplateWrapper wrapper = ControlTemplateWrapper.CREATOR.createFromParcel(in);
|
||||
mControlTemplate = wrapper.getWrappedTemplate();
|
||||
mStatusText = in.readCharSequence();
|
||||
}
|
||||
|
||||
@@ -208,6 +244,16 @@ public class Control implements Parcelable {
|
||||
return mAppIntent;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Icon getCustomIcon() {
|
||||
return mCustomIcon;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ColorStateList getCustomColor() {
|
||||
return mCustomColor;
|
||||
}
|
||||
|
||||
@Status
|
||||
public int getStatus() {
|
||||
return mStatus;
|
||||
@@ -229,7 +275,7 @@ public class Control implements Parcelable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeString(mControlId);
|
||||
dest.writeInt(mDeviceType);
|
||||
dest.writeCharSequence(mTitle);
|
||||
@@ -247,14 +293,27 @@ public class Control implements Parcelable {
|
||||
dest.writeByte((byte) 0);
|
||||
}
|
||||
mAppIntent.writeToParcel(dest, flags);
|
||||
if (mCustomIcon != null) {
|
||||
dest.writeByte((byte) 1);
|
||||
mCustomIcon.writeToParcel(dest, flags);
|
||||
} else {
|
||||
dest.writeByte((byte) 0);
|
||||
}
|
||||
if (mCustomColor != null) {
|
||||
dest.writeByte((byte) 1);
|
||||
mCustomColor.writeToParcel(dest, flags);
|
||||
} else {
|
||||
dest.writeByte((byte) 0);
|
||||
}
|
||||
|
||||
dest.writeInt(mStatus);
|
||||
mControlTemplate.writeToParcel(dest, flags);
|
||||
new ControlTemplateWrapper(mControlTemplate).writeToParcel(dest, flags);
|
||||
dest.writeCharSequence(mStatusText);
|
||||
}
|
||||
|
||||
public static final Creator<Control> CREATOR = new Creator<Control>() {
|
||||
public static final @NonNull Creator<Control> CREATOR = new Creator<Control>() {
|
||||
@Override
|
||||
public Control createFromParcel(Parcel source) {
|
||||
public Control createFromParcel(@NonNull Parcel source) {
|
||||
return new Control(source);
|
||||
}
|
||||
|
||||
@@ -275,25 +334,25 @@ public class Control implements Parcelable {
|
||||
* <li> Subtitle: {@code ""}
|
||||
* </ul>
|
||||
* This fixes the values relating to state of the {@link Control} as required by
|
||||
* {@link ControlsProviderService#onLoad}:
|
||||
* {@link ControlsProviderService#loadAvailableControls}:
|
||||
* <ul>
|
||||
* <li> Status: {@link Status#STATUS_UNKNOWN}
|
||||
* <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
|
||||
* <li> Status text: {@code ""}
|
||||
* </ul>
|
||||
*/
|
||||
public static class StatelessBuilder {
|
||||
@SuppressLint("MutableBareField")
|
||||
public static final 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 = "";
|
||||
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 @Nullable Icon mCustomIcon;
|
||||
private @Nullable ColorStateList mCustomColor;
|
||||
|
||||
/**
|
||||
* @param controlId the identifier for the {@link Control}.
|
||||
@@ -320,6 +379,8 @@ public class Control implements Parcelable {
|
||||
mStructure = control.mStructure;
|
||||
mZone = control.mZone;
|
||||
mAppIntent = control.mAppIntent;
|
||||
mCustomIcon = control.mCustomIcon;
|
||||
mCustomColor = control.mCustomColor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,6 +446,18 @@ public class Control implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public StatelessBuilder setCustomIcon(@Nullable Icon customIcon) {
|
||||
mCustomIcon = customIcon;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public StatelessBuilder setCustomColor(@Nullable ColorStateList customColor) {
|
||||
mCustomColor = customColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link Control}
|
||||
* @return a valid {@link Control}
|
||||
@@ -398,14 +471,42 @@ public class Control implements Parcelable {
|
||||
mStructure,
|
||||
mZone,
|
||||
mAppIntent,
|
||||
mStatus,
|
||||
mControlTemplate,
|
||||
mStatusText);
|
||||
mCustomIcon,
|
||||
mCustomColor,
|
||||
STATUS_UNKNOWN,
|
||||
ControlTemplate.NO_TEMPLATE,
|
||||
"");
|
||||
}
|
||||
}
|
||||
|
||||
public static class StatefulBuilder extends StatelessBuilder {
|
||||
/**
|
||||
* Builder class for {@link Control}.
|
||||
*
|
||||
* This class facilitates the creation of {@link Control}.
|
||||
* It provides the following defaults for non-optional parameters:
|
||||
* <ul>
|
||||
* <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
|
||||
* <li> Title: {@code ""}
|
||||
* <li> Subtitle: {@code ""}
|
||||
* <li> Status: {@link Status#STATUS_UNKNOWN}
|
||||
* <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
|
||||
* <li> Status text: {@code ""}
|
||||
* </ul>
|
||||
*/
|
||||
public static final class StatefulBuilder {
|
||||
private static final String TAG = "StatefulBuilder";
|
||||
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 @Nullable Icon mCustomIcon;
|
||||
private @Nullable ColorStateList mCustomColor;
|
||||
private @Status int mStatus = STATUS_UNKNOWN;
|
||||
private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
|
||||
private @NonNull CharSequence mStatusText = "";
|
||||
|
||||
/**
|
||||
* @param controlId the identifier for the {@link Control}.
|
||||
@@ -413,11 +514,27 @@ public class Control implements Parcelable {
|
||||
*/
|
||||
public StatefulBuilder(@NonNull String controlId,
|
||||
@NonNull PendingIntent appIntent) {
|
||||
super(controlId, appIntent);
|
||||
Preconditions.checkNotNull(controlId);
|
||||
Preconditions.checkNotNull(appIntent);
|
||||
mControlId = controlId;
|
||||
mAppIntent = appIntent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link StatelessBuilder} using an existing {@link Control} as a base.
|
||||
* @param control base for the builder.
|
||||
*/
|
||||
public StatefulBuilder(@NonNull Control control) {
|
||||
super(control);
|
||||
Preconditions.checkNotNull(control);
|
||||
mControlId = control.mControlId;
|
||||
mDeviceType = control.mDeviceType;
|
||||
mTitle = control.mTitle;
|
||||
mSubtitle = control.mSubtitle;
|
||||
mStructure = control.mStructure;
|
||||
mZone = control.mZone;
|
||||
mAppIntent = control.mAppIntent;
|
||||
mCustomIcon = control.mCustomIcon;
|
||||
mCustomColor = control.mCustomColor;
|
||||
mStatus = control.mStatus;
|
||||
mControlTemplate = control.mControlTemplate;
|
||||
mStatusText = control.mStatusText;
|
||||
@@ -429,13 +546,19 @@ public class Control implements Parcelable {
|
||||
*/
|
||||
@NonNull
|
||||
public StatefulBuilder setControlId(@NonNull String controlId) {
|
||||
super.setControlId(controlId);
|
||||
Preconditions.checkNotNull(controlId);
|
||||
mControlId = controlId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public StatefulBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
|
||||
super.setDeviceType(deviceType);
|
||||
if (!DeviceTypes.validDeviceType(deviceType)) {
|
||||
Log.e(TAG, "Invalid device type:" + deviceType);
|
||||
mDeviceType = DeviceTypes.TYPE_UNKNOWN;
|
||||
} else {
|
||||
mDeviceType = deviceType;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -445,25 +568,27 @@ public class Control implements Parcelable {
|
||||
*/
|
||||
@NonNull
|
||||
public StatefulBuilder setTitle(@NonNull CharSequence title) {
|
||||
super.setTitle(title);
|
||||
Preconditions.checkNotNull(title);
|
||||
mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public StatefulBuilder setSubtitle(@NonNull CharSequence subtitle) {
|
||||
super.setSubtitle(subtitle);
|
||||
Preconditions.checkNotNull(subtitle);
|
||||
mSubtitle = subtitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public StatefulBuilder setStructure(@Nullable CharSequence structure) {
|
||||
super.setStructure(structure);
|
||||
mStructure = structure;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public StatefulBuilder setZone(@Nullable CharSequence zone) {
|
||||
super.setZone(zone);
|
||||
mZone = zone;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -473,7 +598,20 @@ public class Control implements Parcelable {
|
||||
*/
|
||||
@NonNull
|
||||
public StatefulBuilder setAppIntent(@NonNull PendingIntent appIntent) {
|
||||
super.setAppIntent(appIntent);
|
||||
Preconditions.checkNotNull(appIntent);
|
||||
mAppIntent = appIntent;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public StatefulBuilder setCustomIcon(@Nullable Icon customIcon) {
|
||||
mCustomIcon = customIcon;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public StatefulBuilder setCustomColor(@Nullable ColorStateList customColor) {
|
||||
mCustomColor = customColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -501,5 +639,21 @@ public class Control implements Parcelable {
|
||||
mStatusText = statusText;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Control build() {
|
||||
return new Control(mControlId,
|
||||
mDeviceType,
|
||||
mTitle,
|
||||
mSubtitle,
|
||||
mStructure,
|
||||
mZone,
|
||||
mAppIntent,
|
||||
mCustomIcon,
|
||||
mCustomColor,
|
||||
mStatus,
|
||||
mControlTemplate,
|
||||
mStatusText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.service.controls.actions.ControlAction;
|
||||
import android.service.controls.actions.ControlActionWrapper;
|
||||
import android.service.controls.templates.ControlTemplate;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
@@ -35,150 +36,90 @@ import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Flow.Publisher;
|
||||
import java.util.concurrent.Flow.Subscriber;
|
||||
import java.util.concurrent.Flow.Subscription;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 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";
|
||||
public static final String SERVICE_CONTROLS =
|
||||
"android.service.controls.ControlsProviderService";
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static final String CALLBACK_BUNDLE = "CALLBACK_BUNDLE";
|
||||
public static final String CALLBACK_BINDER = "CALLBACK_BINDER";
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static final String CALLBACK_TOKEN = "CALLBACK_TOKEN";
|
||||
|
||||
public final String TAG = getClass().getSimpleName();
|
||||
public static final @NonNull String TAG = "ControlsProviderService";
|
||||
|
||||
private IControlsProviderCallback mCallback;
|
||||
private IBinder mToken;
|
||||
private RequestHandler mHandler;
|
||||
|
||||
/**
|
||||
* Signal to retrieve all Controls. When complete, call
|
||||
* {@link IControlsProviderCallback#onLoad} to inform the caller.
|
||||
* Retrieve all available controls, using the stateless builder
|
||||
* {@link Control.StatelessBuilder} to build each Control, then use the
|
||||
* provided consumer to callback to the call originator.
|
||||
*/
|
||||
public abstract void load();
|
||||
public abstract void loadAvailableControls(@NonNull Consumer<List<Control>> consumer);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Return a valid Publisher for the given controlIds. This publisher will be asked
|
||||
* to provide updates for the given list of controlIds as long as the Subscription
|
||||
* is valid.
|
||||
*/
|
||||
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();
|
||||
@NonNull
|
||||
public abstract Publisher<Control> publisherFor(@NonNull List<String> controlIds);
|
||||
|
||||
/**
|
||||
* The user has interacted with a Control. The action is dictated by the type of
|
||||
* {@link ControlAction} that was sent.
|
||||
* {@link ControlAction} that was sent. A response can be sent via
|
||||
* {@link Consumer#accept}, with the Integer argument being one of the provided
|
||||
* {@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.
|
||||
*/
|
||||
public abstract void onAction(@NonNull String controlId, @NonNull ControlAction action);
|
||||
|
||||
/**
|
||||
* 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 final 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 final 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 final 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()));
|
||||
}
|
||||
public abstract void performControlAction(@NonNull String controlId,
|
||||
@NonNull ControlAction action, @NonNull Consumer<Integer> consumer);
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
@NonNull
|
||||
public final IBinder onBind(@NonNull Intent intent) {
|
||||
mHandler = new RequestHandler(Looper.getMainLooper());
|
||||
|
||||
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() {
|
||||
public void load() {
|
||||
mHandler.sendEmptyMessage(RequestHandler.MSG_LOAD);
|
||||
public void load(IControlsLoadCallback cb) {
|
||||
mHandler.obtainMessage(RequestHandler.MSG_LOAD, cb).sendToTarget();
|
||||
}
|
||||
|
||||
public void subscribe(List<String> ids) {
|
||||
mHandler.obtainMessage(RequestHandler.MSG_SUBSCRIBE, ids).sendToTarget();
|
||||
public void subscribe(List<String> controlIds,
|
||||
IControlsSubscriber subscriber) {
|
||||
SubscribeMessage msg = new SubscribeMessage(controlIds, subscriber);
|
||||
mHandler.obtainMessage(RequestHandler.MSG_SUBSCRIBE, msg).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_ON_ACTION, msg).sendToTarget();
|
||||
public void action(String controlId, ControlActionWrapper action,
|
||||
IControlsActionCallback cb) {
|
||||
ActionMessage msg = new ActionMessage(controlId, action.getWrappedAction(), cb);
|
||||
mHandler.obtainMessage(RequestHandler.MSG_ACTION, msg).sendToTarget();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUnbind(Intent intent) {
|
||||
mCallback = null;
|
||||
public boolean onUnbind(@NonNull Intent intent) {
|
||||
mHandler = null;
|
||||
return true;
|
||||
}
|
||||
@@ -186,8 +127,7 @@ public abstract class ControlsProviderService extends Service {
|
||||
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;
|
||||
private static final int MSG_ACTION = 3;
|
||||
|
||||
RequestHandler(Looper looper) {
|
||||
super(looper);
|
||||
@@ -196,30 +136,136 @@ public abstract class ControlsProviderService extends Service {
|
||||
public void handleMessage(Message msg) {
|
||||
switch(msg.what) {
|
||||
case MSG_LOAD:
|
||||
ControlsProviderService.this.load();
|
||||
final IControlsLoadCallback cb = (IControlsLoadCallback) msg.obj;
|
||||
ControlsProviderService.this.loadAvailableControls(consumerFor(cb));
|
||||
break;
|
||||
|
||||
case MSG_SUBSCRIBE:
|
||||
List<String> ids = (List<String>) msg.obj;
|
||||
ControlsProviderService.this.subscribe(ids);
|
||||
final SubscribeMessage sMsg = (SubscribeMessage) msg.obj;
|
||||
final IControlsSubscriber cs = sMsg.mSubscriber;
|
||||
Subscriber<Control> s = new Subscriber<Control>() {
|
||||
public void onSubscribe(Subscription subscription) {
|
||||
try {
|
||||
cs.onSubscribe(mToken, new SubscriptionAdapter(subscription));
|
||||
} catch (RemoteException ex) {
|
||||
ex.rethrowAsRuntimeException();
|
||||
}
|
||||
}
|
||||
public void onNext(@NonNull Control statefulControl) {
|
||||
Preconditions.checkNotNull(statefulControl);
|
||||
try {
|
||||
cs.onNext(mToken, statefulControl);
|
||||
} catch (RemoteException ex) {
|
||||
ex.rethrowAsRuntimeException();
|
||||
}
|
||||
}
|
||||
public void onError(Throwable t) {
|
||||
try {
|
||||
cs.onError(mToken, t.toString());
|
||||
} catch (RemoteException ex) {
|
||||
ex.rethrowAsRuntimeException();
|
||||
}
|
||||
}
|
||||
public void onComplete() {
|
||||
try {
|
||||
cs.onComplete(mToken);
|
||||
} catch (RemoteException ex) {
|
||||
ex.rethrowAsRuntimeException();
|
||||
}
|
||||
}
|
||||
};
|
||||
ControlsProviderService.this.publisherFor(sMsg.mControlIds).subscribe(s);
|
||||
break;
|
||||
case MSG_UNSUBSCRIBE:
|
||||
ControlsProviderService.this.unsubscribe();
|
||||
break;
|
||||
case MSG_ON_ACTION:
|
||||
ActionMessage aMsg = (ActionMessage) msg.obj;
|
||||
ControlsProviderService.this.onAction(aMsg.mId, aMsg.mAction);
|
||||
|
||||
case MSG_ACTION:
|
||||
final ActionMessage aMsg = (ActionMessage) msg.obj;
|
||||
ControlsProviderService.this.performControlAction(aMsg.mControlId,
|
||||
aMsg.mAction, consumerFor(aMsg.mControlId, aMsg.mCb));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private Consumer<Integer> consumerFor(final String controlId,
|
||||
final IControlsActionCallback cb) {
|
||||
return (@NonNull Integer response) -> {
|
||||
Preconditions.checkNotNull(response);
|
||||
if (!ControlAction.isValidResponse(response)) {
|
||||
Log.e(TAG, "Not valid response result: " + response);
|
||||
response = ControlAction.RESPONSE_UNKNOWN;
|
||||
}
|
||||
try {
|
||||
cb.accept(mToken, controlId, response);
|
||||
} catch (RemoteException ex) {
|
||||
ex.rethrowAsRuntimeException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Consumer<List<Control>> consumerFor(IControlsLoadCallback cb) {
|
||||
return (@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 (isStatelessControl(control)) {
|
||||
list.add(control);
|
||||
} else {
|
||||
Log.w(TAG, "onLoad: control is not stateless.");
|
||||
list.add(new Control.StatelessBuilder(control).build());
|
||||
}
|
||||
}
|
||||
try {
|
||||
cb.accept(mToken, list);
|
||||
} catch (RemoteException ex) {
|
||||
ex.rethrowAsRuntimeException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isStatelessControl(Control control) {
|
||||
return (control.getStatus() == Control.STATUS_UNKNOWN
|
||||
&& control.getControlTemplate().getTemplateType() == ControlTemplate.TYPE_NONE
|
||||
&& TextUtils.isEmpty(control.getStatusText()));
|
||||
}
|
||||
}
|
||||
|
||||
private class ActionMessage {
|
||||
final String mId;
|
||||
final ControlAction mAction;
|
||||
private static class SubscriptionAdapter extends IControlsSubscription.Stub {
|
||||
final Subscription mSubscription;
|
||||
|
||||
ActionMessage(String id, ControlAction action) {
|
||||
this.mId = id;
|
||||
SubscriptionAdapter(Subscription s) {
|
||||
this.mSubscription = s;
|
||||
}
|
||||
|
||||
public void request(long n) {
|
||||
mSubscription.request(n);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
mSubscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ActionMessage {
|
||||
final String mControlId;
|
||||
final ControlAction mAction;
|
||||
final IControlsActionCallback mCb;
|
||||
|
||||
ActionMessage(String controlId, ControlAction action, IControlsActionCallback cb) {
|
||||
this.mControlId = controlId;
|
||||
this.mAction = action;
|
||||
this.mCb = cb;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SubscribeMessage {
|
||||
final List<String> mControlIds;
|
||||
final IControlsSubscriber mSubscriber;
|
||||
|
||||
SubscribeMessage(List<String> controlIds, IControlsSubscriber subscriber) {
|
||||
this.mControlIds = controlIds;
|
||||
this.mSubscriber = subscriber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@ import android.annotation.IntDef;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class DeviceTypes {
|
||||
|
||||
// Update this when adding new concrete types. Does not count TYPE_UNKNOWN
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, The Android Open Source Project
|
||||
* Copyright (c) 2020, 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.
|
||||
@@ -14,6 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.service.controls.templates;
|
||||
package android.service.controls;
|
||||
|
||||
parcelable RangeTemplate;
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IControlsActionCallback {
|
||||
void accept(in IBinder token, in String controlId, int response);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, The Android Open Source Project
|
||||
* Copyright (c) 2020, 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.
|
||||
@@ -14,6 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.service.controls.templates;
|
||||
package android.service.controls;
|
||||
|
||||
parcelable ToggleTemplate;
|
||||
import android.service.controls.Control;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IControlsLoadCallback {
|
||||
void accept(in IBinder token, in List<Control> controls);
|
||||
}
|
||||
@@ -16,15 +16,20 @@
|
||||
|
||||
package android.service.controls;
|
||||
|
||||
import android.service.controls.actions.ControlAction;
|
||||
import android.service.controls.IControlsActionCallback;
|
||||
import android.service.controls.IControlsLoadCallback;
|
||||
import android.service.controls.IControlsSubscriber;
|
||||
import android.service.controls.actions.ControlActionWrapper;
|
||||
|
||||
/** @hide */
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IControlsProvider {
|
||||
void load();
|
||||
void load(IControlsLoadCallback cb);
|
||||
|
||||
void subscribe(in List<String> controlIds);
|
||||
void subscribe(in List<String> controlIds,
|
||||
IControlsSubscriber subscriber);
|
||||
|
||||
void unsubscribe();
|
||||
|
||||
void onAction(in String controlId, in ControlAction action);
|
||||
void action(in String controlId, in ControlActionWrapper action,
|
||||
IControlsActionCallback cb);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, The Android Open Source Project
|
||||
* Copyright (c) 2020, 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.
|
||||
@@ -17,12 +17,14 @@
|
||||
package android.service.controls;
|
||||
|
||||
import android.service.controls.Control;
|
||||
import android.service.controls.IControlsSubscription;
|
||||
|
||||
/** @hide */
|
||||
oneway interface IControlsProviderCallback {
|
||||
void onLoad(in IBinder token, in List<Control> controls);
|
||||
|
||||
void onRefreshState(in IBinder token, in List<Control> statefulControls);
|
||||
|
||||
void onControlActionResponse(in IBinder token, in String controlId, int response);
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IControlsSubscriber {
|
||||
void onSubscribe(in IBinder token, in IControlsSubscription cs);
|
||||
void onNext(in IBinder token, in Control c);
|
||||
void onError(in IBinder token, in String s);
|
||||
void onComplete(in IBinder token);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, The Android Open Source Project
|
||||
* Copyright (c) 2020, 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.
|
||||
@@ -14,6 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.service.controls.templates;
|
||||
package android.service.controls;
|
||||
|
||||
parcelable ControlTemplate;
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IControlsSubscription {
|
||||
void request(long n);
|
||||
void cancel();
|
||||
}
|
||||
@@ -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.actions;
|
||||
|
||||
parcelable BooleanAction;
|
||||
@@ -19,12 +19,10 @@ package android.service.controls.actions;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.service.controls.templates.ToggleTemplate;
|
||||
|
||||
/**
|
||||
* Action sent by a {@link ToggleTemplate}
|
||||
* @hide
|
||||
*/
|
||||
public final class BooleanAction extends ControlAction {
|
||||
|
||||
@@ -54,6 +52,10 @@ public final class BooleanAction extends ControlAction {
|
||||
mNewState = newState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
BooleanAction(Bundle b) {
|
||||
super(b);
|
||||
mNewState = b.getBoolean(KEY_NEW_STATE);
|
||||
@@ -77,24 +79,15 @@ public final class BooleanAction extends ControlAction {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = super.getDataBundle();
|
||||
b.putBoolean(KEY_NEW_STATE, mNewState);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<BooleanAction> CREATOR = new Creator<BooleanAction>() {
|
||||
@Override
|
||||
public BooleanAction createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new BooleanAction(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanAction[] newArray(int size) {
|
||||
return new BooleanAction[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,18 +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.actions;
|
||||
|
||||
parcelable CommandAction;
|
||||
@@ -19,11 +19,7 @@ package android.service.controls.actions;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class CommandAction extends ControlAction {
|
||||
|
||||
private static final @ActionType int TYPE = TYPE_COMMAND;
|
||||
@@ -36,7 +32,11 @@ public final class CommandAction extends ControlAction {
|
||||
this(templateId, null);
|
||||
}
|
||||
|
||||
public CommandAction(Bundle b) {
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
CommandAction(Bundle b) {
|
||||
super(b);
|
||||
}
|
||||
|
||||
@@ -44,18 +44,4 @@ public final class CommandAction extends ControlAction {
|
||||
public int getActionType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public static final Creator<CommandAction> CREATOR = new Creator<CommandAction>() {
|
||||
@Override
|
||||
public CommandAction createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new CommandAction(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandAction[] newArray(int size) {
|
||||
return new CommandAction[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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.actions;
|
||||
|
||||
parcelable ControlAction;
|
||||
@@ -21,10 +21,9 @@ import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.service.controls.IControlsProviderCallback;
|
||||
import android.service.controls.IControlsActionCallback;
|
||||
import android.service.controls.templates.ControlTemplate;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
@@ -36,20 +35,21 @@ import java.lang.annotation.RetentionPolicy;
|
||||
*
|
||||
* The action may have a value to authenticate the input, when the provider has requested it to
|
||||
* complete the action.
|
||||
* @hide
|
||||
*/
|
||||
public abstract class ControlAction implements Parcelable {
|
||||
public abstract class ControlAction {
|
||||
|
||||
private static final String TAG = "ControlAction";
|
||||
|
||||
private static final String KEY_ACTION_TYPE = "key_action_type";
|
||||
private static final String KEY_TEMPLATE_ID = "key_template_id";
|
||||
private static final String KEY_CHALLENGE_VALUE = "key_challenge_value";
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
TYPE_UNKNOWN,
|
||||
TYPE_ERROR,
|
||||
TYPE_BOOLEAN,
|
||||
TYPE_FLOAT,
|
||||
TYPE_MULTI_FLOAT,
|
||||
@@ -57,15 +57,16 @@ public abstract class ControlAction implements Parcelable {
|
||||
TYPE_COMMAND
|
||||
})
|
||||
public @interface ActionType {};
|
||||
public static final ControlAction UNKNOWN_ACTION = new ControlAction() {
|
||||
|
||||
public static final @NonNull ControlAction ERROR_ACTION = new ControlAction() {
|
||||
@Override
|
||||
public int getActionType() {
|
||||
return TYPE_UNKNOWN;
|
||||
return TYPE_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
public static final @ActionType int TYPE_UNKNOWN = 0;
|
||||
public static final @ActionType int TYPE_ERROR = -1;
|
||||
|
||||
/**
|
||||
* The identifier of {@link BooleanAction}.
|
||||
*/
|
||||
@@ -104,27 +105,27 @@ public abstract class ControlAction implements Parcelable {
|
||||
public static final @ResponseResult int RESPONSE_UNKNOWN = 0;
|
||||
|
||||
/**
|
||||
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
|
||||
* Response code for {@link IControlsActionCallback#accept} 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 = 1;
|
||||
/**
|
||||
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
|
||||
* Response code for {@link IControlsActionCallback#accept} indicating that
|
||||
* the action has failed.
|
||||
*/
|
||||
public static final @ResponseResult int RESPONSE_FAIL = 2;
|
||||
/**
|
||||
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
|
||||
* Response code for {@link IControlsActionCallback#accept} indicating that
|
||||
* in order for the action to be performed, acknowledgment from the user is required.
|
||||
*/
|
||||
public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 3;
|
||||
/**
|
||||
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
|
||||
* Response code for {@link IControlsActionCallback#accept} indicating that
|
||||
* in order for the action to be performed, a PIN is required.
|
||||
*/
|
||||
public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 4;
|
||||
/**
|
||||
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
|
||||
* Response code for {@link IControlsActionCallback#accept} indicating that
|
||||
* in order for the action to be performed, an alphanumeric passphrase is required.
|
||||
*/
|
||||
public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 5;
|
||||
@@ -175,68 +176,55 @@ public abstract class ControlAction implements Parcelable {
|
||||
return mChallengeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(getActionType());
|
||||
dest.writeBundle(getDataBundle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a {@link Bundle} describing this object populated with data.
|
||||
*
|
||||
* Implementations in subclasses should populate the {@link Bundle} returned by
|
||||
* {@link ControlAction}.
|
||||
* @return a {@link Bundle} containing the data that represents this object.
|
||||
* @hide
|
||||
*/
|
||||
@CallSuper
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = new Bundle();
|
||||
b.putInt(KEY_ACTION_TYPE, getActionType());
|
||||
b.putString(KEY_TEMPLATE_ID, mTemplateId);
|
||||
b.putString(KEY_CHALLENGE_VALUE, mChallengeValue);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<ControlAction> CREATOR = new Creator<ControlAction>() {
|
||||
@Override
|
||||
public ControlAction createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
return createActionFromType(type, source);
|
||||
/**
|
||||
* @param bundle
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
static ControlAction createActionFromBundle(@NonNull Bundle bundle) {
|
||||
if (bundle == null) {
|
||||
Log.e(TAG, "Null bundle");
|
||||
return ERROR_ACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlAction[] newArray(int size) {
|
||||
return new ControlAction[size];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private static ControlAction createActionFromType(@ActionType int type, Parcel source) {
|
||||
switch(type) {
|
||||
case TYPE_BOOLEAN:
|
||||
return new BooleanAction(source.readBundle());
|
||||
case TYPE_FLOAT:
|
||||
return new FloatAction(source.readBundle());
|
||||
case TYPE_MULTI_FLOAT:
|
||||
return new MultiFloatAction(source.readBundle());
|
||||
case TYPE_MODE:
|
||||
return new ModeAction(source.readBundle());
|
||||
case TYPE_COMMAND:
|
||||
return new CommandAction(source.readBundle());
|
||||
default:
|
||||
source.readBundle();
|
||||
return UNKNOWN_ACTION;
|
||||
int type = bundle.getInt(KEY_ACTION_TYPE, TYPE_ERROR);
|
||||
try {
|
||||
switch (type) {
|
||||
case TYPE_BOOLEAN:
|
||||
return new BooleanAction(bundle);
|
||||
case TYPE_FLOAT:
|
||||
return new FloatAction(bundle);
|
||||
case TYPE_MULTI_FLOAT:
|
||||
return new MultiFloatAction(bundle);
|
||||
case TYPE_MODE:
|
||||
return new ModeAction(bundle);
|
||||
case TYPE_COMMAND:
|
||||
return new CommandAction(bundle);
|
||||
case TYPE_ERROR:
|
||||
default:
|
||||
return ERROR_ACTION;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error creating action", e);
|
||||
return ERROR_ACTION;
|
||||
}
|
||||
}
|
||||
|
||||
protected static void verifyType(@ActionType int type, @ActionType int thisType) {
|
||||
if (type != thisType) {
|
||||
throw new IllegalStateException("The type " + type + "does not match " + thisType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
* Copyright (C) 2020 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.
|
||||
@@ -16,4 +16,4 @@
|
||||
|
||||
package android.service.controls.actions;
|
||||
|
||||
parcelable FloatAction;
|
||||
parcelable ControlActionWrapper;
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.actions;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
/**
|
||||
* Wrapper for parceling/unparceling {@link ControlAction}.
|
||||
* @hide
|
||||
*/
|
||||
public final class ControlActionWrapper implements Parcelable {
|
||||
|
||||
private final @NonNull ControlAction mControlAction;
|
||||
|
||||
public ControlActionWrapper(@NonNull ControlAction controlAction) {
|
||||
Preconditions.checkNotNull(controlAction);
|
||||
|
||||
mControlAction = controlAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeBundle(mControlAction.getDataBundle());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ControlAction getWrappedAction() {
|
||||
return mControlAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<ControlActionWrapper> CREATOR =
|
||||
new Creator<ControlActionWrapper>() {
|
||||
@Override
|
||||
public ControlActionWrapper createFromParcel(@NonNull Parcel in) {
|
||||
return new ControlActionWrapper(
|
||||
ControlAction.createActionFromBundle(in.readBundle()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlActionWrapper[] newArray(int size) {
|
||||
return new ControlActionWrapper[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -19,13 +19,11 @@ package android.service.controls.actions;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.service.controls.templates.RangeTemplate;
|
||||
import android.service.controls.templates.ToggleRangeTemplate;
|
||||
|
||||
/**
|
||||
* Action sent by a {@link RangeTemplate}, {@link ToggleRangeTemplate}.
|
||||
* @hide
|
||||
*/
|
||||
public final class FloatAction extends ControlAction {
|
||||
|
||||
@@ -56,7 +54,11 @@ public final class FloatAction extends ControlAction {
|
||||
mNewValue = newValue;
|
||||
}
|
||||
|
||||
public FloatAction(Bundle b) {
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
FloatAction(Bundle b) {
|
||||
super(b);
|
||||
mNewValue = b.getFloat(KEY_NEW_VALUE);
|
||||
}
|
||||
@@ -76,24 +78,15 @@ public final class FloatAction extends ControlAction {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = super.getDataBundle();
|
||||
b.putFloat(KEY_NEW_VALUE, mNewValue);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<FloatAction> CREATOR = new Creator<FloatAction>() {
|
||||
@Override
|
||||
public FloatAction createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new FloatAction(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatAction[] newArray(int size) {
|
||||
return new FloatAction[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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.actions;
|
||||
|
||||
parcelable ModeAction;
|
||||
@@ -19,11 +19,7 @@ package android.service.controls.actions;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class ModeAction extends ControlAction {
|
||||
|
||||
private static final @ActionType int TYPE = TYPE_MODE;
|
||||
@@ -45,13 +41,22 @@ public final class ModeAction extends ControlAction {
|
||||
this(templateId, newMode, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
ModeAction(Bundle b) {
|
||||
super(b);
|
||||
mNewMode = b.getInt(KEY_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = super.getDataBundle();
|
||||
b.putInt(KEY_MODE, mNewMode);
|
||||
return b;
|
||||
@@ -60,18 +65,4 @@ public final class ModeAction extends ControlAction {
|
||||
public int getNewMode() {
|
||||
return mNewMode;
|
||||
}
|
||||
|
||||
public static final Creator<ModeAction> CREATOR = new Creator<ModeAction>() {
|
||||
@Override
|
||||
public ModeAction createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new ModeAction(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModeAction[] newArray(int size) {
|
||||
return new ModeAction[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,18 +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.actions;
|
||||
|
||||
parcelable MultiFloatAction;
|
||||
@@ -19,14 +19,10 @@ package android.service.controls.actions;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class MultiFloatAction extends ControlAction {
|
||||
|
||||
private static final String TAG = "MultiFloatAction";
|
||||
@@ -58,6 +54,10 @@ public final class MultiFloatAction extends ControlAction {
|
||||
this(templateId, newValues, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
MultiFloatAction(Bundle b) {
|
||||
super(b);
|
||||
mNewValues = b.getFloatArray(KEY_VALUES);
|
||||
@@ -68,24 +68,15 @@ public final class MultiFloatAction extends ControlAction {
|
||||
return mNewValues.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = super.getDataBundle();
|
||||
b.putFloatArray(KEY_VALUES, mNewValues);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static final Creator<MultiFloatAction> CREATOR = new Creator<MultiFloatAction>() {
|
||||
@Override
|
||||
public MultiFloatAction createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new MultiFloatAction(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiFloatAction[] newArray(int size) {
|
||||
return new MultiFloatAction[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import com.android.internal.util.Preconditions;
|
||||
|
||||
/**
|
||||
* Button element for {@link ControlTemplate}.
|
||||
* @hide
|
||||
*/
|
||||
public final class ControlButton implements Parcelable {
|
||||
|
||||
@@ -64,7 +63,8 @@ public final class ControlButton implements Parcelable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
@NonNull
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeByte(mChecked ? (byte) 1 : (byte) 0);
|
||||
dest.writeCharSequence(mActionDescription);
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public final class ControlButton implements Parcelable {
|
||||
mActionDescription = in.readCharSequence();
|
||||
}
|
||||
|
||||
public static final Creator<ControlButton> CREATOR = new Creator<ControlButton>() {
|
||||
public static final @NonNull Creator<ControlButton> CREATOR = new Creator<ControlButton>() {
|
||||
@Override
|
||||
public ControlButton createFromParcel(Parcel source) {
|
||||
return new ControlButton(source);
|
||||
|
||||
@@ -19,11 +19,11 @@ package android.service.controls.templates;
|
||||
import android.annotation.CallSuper;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.service.controls.Control;
|
||||
import android.service.controls.actions.ControlAction;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
@@ -39,27 +39,37 @@ import java.lang.annotation.RetentionPolicy;
|
||||
* associated state. The actions available to a given {@link Control} are determined by its
|
||||
* {@link ControlTemplate}.
|
||||
* @see ControlAction
|
||||
* @hide
|
||||
*/
|
||||
public abstract class ControlTemplate implements Parcelable {
|
||||
public abstract class ControlTemplate {
|
||||
|
||||
private static final String TAG = "ControlTemplate";
|
||||
|
||||
private static final String KEY_TEMPLATE_ID = "key_template_id";
|
||||
private static final String KEY_TEMPLATE_TYPE = "key_template_type";
|
||||
|
||||
/**
|
||||
* Singleton representing a {@link Control} with no input.
|
||||
*/
|
||||
public static final ControlTemplate NO_TEMPLATE = new ControlTemplate("") {
|
||||
public static final @NonNull ControlTemplate NO_TEMPLATE = new ControlTemplate("") {
|
||||
@Override
|
||||
public int getTemplateType() {
|
||||
return TYPE_NONE;
|
||||
}
|
||||
};
|
||||
|
||||
public static final @NonNull ControlTemplate ERROR_TEMPLATE = new ControlTemplate("") {
|
||||
@Override
|
||||
public int getTemplateType() {
|
||||
return TYPE_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
TYPE_ERROR,
|
||||
TYPE_NONE,
|
||||
TYPE_TOGGLE,
|
||||
TYPE_RANGE,
|
||||
@@ -72,47 +82,50 @@ public abstract class ControlTemplate implements Parcelable {
|
||||
})
|
||||
public @interface TemplateType {}
|
||||
|
||||
public static final @TemplateType int TYPE_ERROR = -1;
|
||||
|
||||
/**
|
||||
* Type identifier of {@link ControlTemplate#NO_TEMPLATE}.
|
||||
*/
|
||||
public static final int TYPE_NONE = 0;
|
||||
public static final @TemplateType int TYPE_NONE = 0;
|
||||
|
||||
/**
|
||||
* Type identifier of {@link ToggleTemplate}.
|
||||
*/
|
||||
public static final int TYPE_TOGGLE = 1;
|
||||
public static final @TemplateType int TYPE_TOGGLE = 1;
|
||||
|
||||
/**
|
||||
* Type identifier of {@link RangeTemplate}.
|
||||
*/
|
||||
public static final int TYPE_RANGE = 2;
|
||||
public static final @TemplateType int TYPE_RANGE = 2;
|
||||
|
||||
/**
|
||||
* Type identifier of {@link ThumbnailTemplate}.
|
||||
*/
|
||||
public static final int TYPE_THUMBNAIL = 3;
|
||||
public static final @TemplateType int TYPE_THUMBNAIL = 3;
|
||||
|
||||
/**
|
||||
* Type identifier of {@link DiscreteToggleTemplate}.
|
||||
*/
|
||||
public static final int TYPE_DISCRETE_TOGGLE = 4;
|
||||
public static final @TemplateType int TYPE_DISCRETE_TOGGLE = 4;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static final int TYPE_COORD_RANGE = 5;
|
||||
public static final @TemplateType int TYPE_COORD_RANGE = 5;
|
||||
|
||||
public static final int TYPE_TOGGLE_RANGE = 6;
|
||||
public static final @TemplateType int TYPE_TOGGLE_RANGE = 6;
|
||||
|
||||
public static final int TYPE_TEMPERATURE = 7;
|
||||
public static final @TemplateType int TYPE_TEMPERATURE = 7;
|
||||
|
||||
public static final int TYPE_STATELESS = 8;
|
||||
public static final @TemplateType int TYPE_STATELESS = 8;
|
||||
|
||||
private @NonNull final String mTemplateId;
|
||||
|
||||
/**
|
||||
* @return the identifier for this object.
|
||||
*/
|
||||
@NonNull
|
||||
public String getTemplateId() {
|
||||
return mTemplateId;
|
||||
}
|
||||
@@ -122,24 +135,16 @@ public abstract class ControlTemplate implements Parcelable {
|
||||
*/
|
||||
public abstract @TemplateType int getTemplateType();
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(getTemplateType());
|
||||
dest.writeBundle(getDataBundle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a {@link Bundle} describing this object populated with data.
|
||||
* @return a {@link Bundle} containing the data that represents this object.
|
||||
* @hide
|
||||
*/
|
||||
@CallSuper
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = new Bundle();
|
||||
b.putInt(KEY_TEMPLATE_TYPE, getTemplateType());
|
||||
b.putString(KEY_TEMPLATE_ID, mTemplateId);
|
||||
return b;
|
||||
}
|
||||
@@ -148,6 +153,10 @@ public abstract class ControlTemplate implements Parcelable {
|
||||
mTemplateId = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
ControlTemplate(@NonNull Bundle b) {
|
||||
mTemplateId = b.getString(KEY_TEMPLATE_ID);
|
||||
}
|
||||
@@ -160,48 +169,46 @@ public abstract class ControlTemplate implements Parcelable {
|
||||
mTemplateId = templateId;
|
||||
}
|
||||
|
||||
public static final Creator<ControlTemplate> CREATOR = new Creator<ControlTemplate>() {
|
||||
@Override
|
||||
public ControlTemplate createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
return createTemplateFromType(type, source);
|
||||
/**
|
||||
*
|
||||
* @param bundle
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
static ControlTemplate createTemplateFromBundle(@Nullable Bundle bundle) {
|
||||
if (bundle == null) {
|
||||
Log.e(TAG, "Null bundle");
|
||||
return ERROR_TEMPLATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlTemplate[] newArray(int size) {
|
||||
return new ControlTemplate[size];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private static ControlTemplate createTemplateFromType(@TemplateType int type, Parcel source) {
|
||||
switch(type) {
|
||||
case TYPE_TOGGLE:
|
||||
return new ToggleTemplate(source.readBundle());
|
||||
case TYPE_RANGE:
|
||||
return new RangeTemplate(source.readBundle());
|
||||
case TYPE_THUMBNAIL:
|
||||
return new ThumbnailTemplate(source.readBundle());
|
||||
case TYPE_DISCRETE_TOGGLE:
|
||||
return new DiscreteToggleTemplate(source.readBundle());
|
||||
case TYPE_COORD_RANGE:
|
||||
return new CoordinatedRangeTemplate(source.readBundle());
|
||||
case TYPE_TOGGLE_RANGE:
|
||||
return new ToggleRangeTemplate(source.readBundle());
|
||||
case TYPE_TEMPERATURE:
|
||||
return new TemperatureControlTemplate(source.readBundle());
|
||||
case TYPE_STATELESS:
|
||||
return new StatelessTemplate(source.readBundle());
|
||||
case TYPE_NONE:
|
||||
default:
|
||||
source.readBundle();
|
||||
return NO_TEMPLATE;
|
||||
}
|
||||
}
|
||||
|
||||
protected static void verifyType(@TemplateType int type, @TemplateType int thisType) {
|
||||
if (type != thisType) {
|
||||
throw new IllegalStateException("The type " + type + "does not match " + thisType);
|
||||
int type = bundle.getInt(KEY_TEMPLATE_TYPE, TYPE_ERROR);
|
||||
try {
|
||||
switch (type) {
|
||||
case TYPE_TOGGLE:
|
||||
return new ToggleTemplate(bundle);
|
||||
case TYPE_RANGE:
|
||||
return new RangeTemplate(bundle);
|
||||
case TYPE_THUMBNAIL:
|
||||
return new ThumbnailTemplate(bundle);
|
||||
case TYPE_DISCRETE_TOGGLE:
|
||||
return new DiscreteToggleTemplate(bundle);
|
||||
case TYPE_COORD_RANGE:
|
||||
return new CoordinatedRangeTemplate(bundle);
|
||||
case TYPE_TOGGLE_RANGE:
|
||||
return new ToggleRangeTemplate(bundle);
|
||||
case TYPE_TEMPERATURE:
|
||||
return new TemperatureControlTemplate(bundle);
|
||||
case TYPE_STATELESS:
|
||||
return new StatelessTemplate(bundle);
|
||||
case TYPE_NONE:
|
||||
return NO_TEMPLATE;
|
||||
case TYPE_ERROR:
|
||||
default:
|
||||
return ERROR_TEMPLATE;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error creating template", e);
|
||||
return ERROR_TEMPLATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
* Copyright (C) 2020 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.
|
||||
@@ -16,4 +16,4 @@
|
||||
|
||||
package android.service.controls.templates;
|
||||
|
||||
parcelable StatelessTemplate;
|
||||
parcelable ControlTemplateWrapper;
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.templates;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
/**
|
||||
* Wrapper for parceling/unparceling {@link ControlTemplate}.
|
||||
* @hide
|
||||
*/
|
||||
public final class ControlTemplateWrapper implements Parcelable {
|
||||
|
||||
private final @NonNull ControlTemplate mControlTemplate;
|
||||
|
||||
public ControlTemplateWrapper(@NonNull ControlTemplate template) {
|
||||
Preconditions.checkNotNull(template);
|
||||
mControlTemplate = template;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ControlTemplate getWrappedTemplate() {
|
||||
return mControlTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeBundle(mControlTemplate.getDataBundle());
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<ControlTemplateWrapper> CREATOR =
|
||||
new Creator<ControlTemplateWrapper>() {
|
||||
@Override
|
||||
public ControlTemplateWrapper createFromParcel(@NonNull Parcel source) {
|
||||
return new ControlTemplateWrapper(
|
||||
ControlTemplate.createTemplateFromBundle(source.readBundle()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlTemplateWrapper[] newArray(int size) {
|
||||
return new ControlTemplateWrapper[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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.templates;
|
||||
|
||||
parcelable CoordinatedRangeTemplate;
|
||||
@@ -19,12 +19,8 @@ package android.service.controls.templates;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class CoordinatedRangeTemplate extends ControlTemplate {
|
||||
|
||||
private static final String TAG = "CoordinatedRangeTemplate";
|
||||
@@ -74,10 +70,14 @@ public final class CoordinatedRangeTemplate extends ControlTemplate {
|
||||
minValueHigh, maxValueHigh, currentValueHigh, stepValue, formatString));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
CoordinatedRangeTemplate(Bundle b) {
|
||||
super(b);
|
||||
mRangeLow = b.getParcelable(KEY_RANGE_LOW);
|
||||
mRangeHigh = b.getParcelable(KEY_RANGE_HIGH);
|
||||
mRangeLow = new RangeTemplate(b.getBundle(KEY_RANGE_LOW));
|
||||
mRangeHigh = new RangeTemplate(b.getBundle(KEY_RANGE_HIGH));
|
||||
mMinGap = b.getFloat(KEY_MIN_GAP);
|
||||
validateRanges();
|
||||
}
|
||||
@@ -134,11 +134,16 @@ public final class CoordinatedRangeTemplate extends ControlTemplate {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = super.getDataBundle();
|
||||
b.putParcelable(KEY_RANGE_LOW, mRangeLow);
|
||||
b.putParcelable(KEY_RANGE_HIGH, mRangeHigh);
|
||||
b.putBundle(KEY_RANGE_LOW, mRangeLow.getDataBundle());
|
||||
b.putBundle(KEY_RANGE_HIGH, mRangeHigh.getDataBundle());
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -160,18 +165,4 @@ public final class CoordinatedRangeTemplate extends ControlTemplate {
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<CoordinatedRangeTemplate> CREATOR =
|
||||
new Creator<CoordinatedRangeTemplate>() {
|
||||
@Override
|
||||
public CoordinatedRangeTemplate createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new CoordinatedRangeTemplate(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CoordinatedRangeTemplate[] newArray(int size) {
|
||||
return new CoordinatedRangeTemplate[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,18 +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.templates;
|
||||
|
||||
parcelable DiscreteToggleTemplate;
|
||||
@@ -18,7 +18,6 @@ package android.service.controls.templates;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.service.controls.Control;
|
||||
import android.service.controls.actions.BooleanAction;
|
||||
|
||||
@@ -33,9 +32,8 @@ import com.android.internal.util.Preconditions;
|
||||
* {@link BooleanAction#getNewState} will be {@code false} if the button was
|
||||
* {@link DiscreteToggleTemplate#getNegativeButton} and {@code true} if the button was
|
||||
* {@link DiscreteToggleTemplate#getPositiveButton}.
|
||||
* @hide
|
||||
*/
|
||||
public class DiscreteToggleTemplate extends ControlTemplate {
|
||||
public final class DiscreteToggleTemplate extends ControlTemplate {
|
||||
|
||||
private static final @TemplateType int TYPE = TYPE_DISCRETE_TOGGLE;
|
||||
private static final String KEY_NEGATIVE_BUTTON = "key_negative_button";
|
||||
@@ -46,8 +44,8 @@ public class DiscreteToggleTemplate extends ControlTemplate {
|
||||
|
||||
/**
|
||||
* @param templateId the identifier for this template object
|
||||
* @param negativeButton a {@ControlButton} for the <i>Negative</i> input
|
||||
* @param positiveButton a {@ControlButton} for the <i>Positive</i> input
|
||||
* @param negativeButton a {@link ControlButton} for the <i>Negative</i> input
|
||||
* @param positiveButton a {@link ControlButton} for the <i>Positive</i> input
|
||||
*/
|
||||
public DiscreteToggleTemplate(@NonNull String templateId,
|
||||
@NonNull ControlButton negativeButton,
|
||||
@@ -59,6 +57,10 @@ public class DiscreteToggleTemplate extends ControlTemplate {
|
||||
mPositiveButton = positiveButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
DiscreteToggleTemplate(Bundle b) {
|
||||
super(b);
|
||||
mNegativeButton = b.getParcelable(KEY_NEGATIVE_BUTTON);
|
||||
@@ -89,32 +91,17 @@ public class DiscreteToggleTemplate extends ControlTemplate {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = super.getDataBundle();
|
||||
b.putParcelable(KEY_NEGATIVE_BUTTON, mNegativeButton);
|
||||
b.putParcelable(KEY_POSITIVE_BUTTON, mPositiveButton);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static final Creator<DiscreteToggleTemplate> CREATOR =
|
||||
new Creator<DiscreteToggleTemplate>() {
|
||||
@Override
|
||||
public DiscreteToggleTemplate createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new DiscreteToggleTemplate(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiscreteToggleTemplate[] newArray(int size) {
|
||||
return new DiscreteToggleTemplate[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import android.service.controls.actions.FloatAction;
|
||||
* A template for a {@link Control} with inputs in a "continuous" range of values.
|
||||
*
|
||||
* @see FloatAction
|
||||
* @hide
|
||||
*/
|
||||
public final class RangeTemplate extends ControlTemplate {
|
||||
|
||||
@@ -148,8 +147,13 @@ public final class RangeTemplate extends ControlTemplate {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = super.getDataBundle();
|
||||
b.putFloat(KEY_MIN_VALUE, mMinValue);
|
||||
b.putFloat(KEY_MAX_VALUE, mMaxValue);
|
||||
@@ -181,18 +185,4 @@ public final class RangeTemplate extends ControlTemplate {
|
||||
throw new IllegalArgumentException(String.format("stepValue=%f <= 0", mStepValue));
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<RangeTemplate> CREATOR = new Creator<RangeTemplate>() {
|
||||
@Override
|
||||
public RangeTemplate createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new RangeTemplate(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RangeTemplate[] newArray(int size) {
|
||||
return new RangeTemplate[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,11 +18,7 @@ package android.service.controls.templates;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class StatelessTemplate extends ControlTemplate {
|
||||
|
||||
@Override
|
||||
@@ -30,23 +26,15 @@ public final class StatelessTemplate extends ControlTemplate {
|
||||
return TYPE_STATELESS;
|
||||
}
|
||||
|
||||
public StatelessTemplate(@NonNull Bundle b) {
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
StatelessTemplate(@NonNull Bundle b) {
|
||||
super(b);
|
||||
}
|
||||
|
||||
public StatelessTemplate(@NonNull String templateId) {
|
||||
super(templateId);
|
||||
}
|
||||
|
||||
public static final Creator<StatelessTemplate> CREATOR = new Creator<StatelessTemplate>() {
|
||||
@Override
|
||||
public StatelessTemplate createFromParcel(Parcel source) {
|
||||
return new StatelessTemplate(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatelessTemplate[] newArray(int size) {
|
||||
return new StatelessTemplate[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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.templates;
|
||||
|
||||
parcelable TemperatureControlTemplate;
|
||||
@@ -19,7 +19,6 @@ package android.service.controls.templates;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
@@ -27,9 +26,6 @@ import com.android.internal.util.Preconditions;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class TemperatureControlTemplate extends ControlTemplate {
|
||||
|
||||
private static final String TAG = "ThermostatTemplate";
|
||||
@@ -67,6 +63,9 @@ public final class TemperatureControlTemplate extends ControlTemplate {
|
||||
|
||||
public static final @Mode int MODE_ECO = 5;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(flag = true, value = {
|
||||
FLAG_MODE_OFF,
|
||||
@@ -136,18 +135,27 @@ public final class TemperatureControlTemplate extends ControlTemplate {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
TemperatureControlTemplate(@NonNull Bundle b) {
|
||||
super(b);
|
||||
mTemplate = b.getParcelable(KEY_TEMPLATE);
|
||||
mTemplate = ControlTemplate.createTemplateFromBundle(b.getBundle(KEY_TEMPLATE));
|
||||
mCurrentMode = b.getInt(KEY_CURRENT_MODE);
|
||||
mCurrentActiveMode = b.getInt(KEY_CURRENT_ACTIVE_MODE);
|
||||
mModes = b.getInt(KEY_MODES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = super.getDataBundle();
|
||||
b.putParcelable(KEY_TEMPLATE, mTemplate);
|
||||
b.putBundle(KEY_TEMPLATE, mTemplate.getDataBundle());
|
||||
b.putInt(KEY_CURRENT_MODE, mCurrentMode);
|
||||
b.putInt(KEY_CURRENT_ACTIVE_MODE, mCurrentActiveMode);
|
||||
b.putInt(KEY_MODES, mModes);
|
||||
@@ -175,18 +183,4 @@ public final class TemperatureControlTemplate extends ControlTemplate {
|
||||
public int getTemplateType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public static final Creator<TemperatureControlTemplate> CREATOR = new Creator<TemperatureControlTemplate>() {
|
||||
@Override
|
||||
public TemperatureControlTemplate createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new TemperatureControlTemplate(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TemperatureControlTemplate[] newArray(int size) {
|
||||
return new TemperatureControlTemplate[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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.templates;
|
||||
|
||||
parcelable ThumbnailTemplate;
|
||||
@@ -19,15 +19,12 @@ package android.service.controls.templates;
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.service.controls.Control;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A template for a {@link Control} that displays an image.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class ThumbnailTemplate extends ControlTemplate {
|
||||
|
||||
@@ -52,6 +49,10 @@ public final class ThumbnailTemplate extends ControlTemplate {
|
||||
mContentDescription = contentDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
ThumbnailTemplate(Bundle b) {
|
||||
super(b);
|
||||
mThumbnail = b.getParcelable(KEY_ICON);
|
||||
@@ -82,25 +83,16 @@ public final class ThumbnailTemplate extends ControlTemplate {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = super.getDataBundle();
|
||||
b.putObject(KEY_ICON, mThumbnail);
|
||||
b.putObject(KEY_CONTENT_DESCRIPTION, mContentDescription);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static final Creator<ThumbnailTemplate> CREATOR = new Creator<ThumbnailTemplate>() {
|
||||
@Override
|
||||
public ThumbnailTemplate createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new ThumbnailTemplate(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThumbnailTemplate[] newArray(int size) {
|
||||
return new ThumbnailTemplate[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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.templates;
|
||||
|
||||
parcelable ToggleRangeTemplate;
|
||||
@@ -18,13 +18,9 @@ package android.service.controls.templates;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class ToggleRangeTemplate extends ControlTemplate {
|
||||
|
||||
private static final @TemplateType int TYPE = TYPE_TOGGLE_RANGE;
|
||||
@@ -34,11 +30,14 @@ public final class ToggleRangeTemplate extends ControlTemplate {
|
||||
private @NonNull final ControlButton mControlButton;
|
||||
private @NonNull final RangeTemplate mRangeTemplate;
|
||||
|
||||
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
ToggleRangeTemplate(@NonNull Bundle b) {
|
||||
super(b);
|
||||
mControlButton = b.getParcelable(KEY_BUTTON);
|
||||
mRangeTemplate = b.getParcelable(KEY_RANGE);
|
||||
mRangeTemplate = new RangeTemplate(b.getBundle(KEY_RANGE));
|
||||
}
|
||||
|
||||
public ToggleRangeTemplate(@NonNull String templateId,
|
||||
@@ -60,11 +59,16 @@ public final class ToggleRangeTemplate extends ControlTemplate {
|
||||
range);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = super.getDataBundle();
|
||||
b.putParcelable(KEY_BUTTON, mControlButton);
|
||||
b.putParcelable(KEY_RANGE, mRangeTemplate);
|
||||
b.putBundle(KEY_RANGE, mRangeTemplate.getDataBundle());
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -86,19 +90,4 @@ public final class ToggleRangeTemplate extends ControlTemplate {
|
||||
public int getTemplateType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public static final Creator<ToggleRangeTemplate> CREATOR = new Creator<ToggleRangeTemplate>() {
|
||||
|
||||
@Override
|
||||
public ToggleRangeTemplate createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new ToggleRangeTemplate(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToggleRangeTemplate[] newArray(int size) {
|
||||
return new ToggleRangeTemplate[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package android.service.controls.templates;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.service.controls.Control;
|
||||
import android.service.controls.actions.BooleanAction;
|
||||
|
||||
@@ -31,7 +30,6 @@ import com.android.internal.util.Preconditions;
|
||||
* An action on this template will originate a {@link BooleanAction} to change that state.
|
||||
*
|
||||
* @see BooleanAction
|
||||
* @hide
|
||||
*/
|
||||
public final class ToggleTemplate extends ControlTemplate {
|
||||
|
||||
@@ -41,7 +39,7 @@ public final class ToggleTemplate extends ControlTemplate {
|
||||
|
||||
/**
|
||||
* @param templateId the identifier for this template object
|
||||
* @param button a {@ControlButton} that can show the current state and toggle it
|
||||
* @param button a {@link ControlButton} that can show the current state and toggle it
|
||||
*/
|
||||
public ToggleTemplate(@NonNull String templateId, @NonNull ControlButton button) {
|
||||
super(templateId);
|
||||
@@ -49,6 +47,10 @@ public final class ToggleTemplate extends ControlTemplate {
|
||||
mButton = button;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param b
|
||||
* @hide
|
||||
*/
|
||||
ToggleTemplate(Bundle b) {
|
||||
super(b);
|
||||
mButton = b.getParcelable(KEY_BUTTON);
|
||||
@@ -58,6 +60,7 @@ public final class ToggleTemplate extends ControlTemplate {
|
||||
return mButton.isChecked();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public CharSequence getContentDescription() {
|
||||
return mButton.getActionDescription();
|
||||
}
|
||||
@@ -70,25 +73,15 @@ public final class ToggleTemplate extends ControlTemplate {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
protected Bundle getDataBundle() {
|
||||
@NonNull
|
||||
Bundle getDataBundle() {
|
||||
Bundle b = super.getDataBundle();
|
||||
b.putParcelable(KEY_BUTTON, mButton);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static final Creator<ToggleTemplate> CREATOR = new Creator<ToggleTemplate>() {
|
||||
@Override
|
||||
public ToggleTemplate createFromParcel(Parcel source) {
|
||||
int type = source.readInt();
|
||||
verifyType(type, TYPE);
|
||||
return new ToggleTemplate(source.readBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToggleTemplate[] newArray(int size) {
|
||||
return new ToggleTemplate[size];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.IIntentSender;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.service.controls.actions.CommandAction;
|
||||
import android.service.controls.actions.ControlAction;
|
||||
import android.service.controls.actions.ControlActionWrapper;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Flow.Publisher;
|
||||
import java.util.concurrent.Flow.Subscriber;
|
||||
import java.util.concurrent.Flow.Subscription;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ControlProviderServiceTest {
|
||||
|
||||
private IBinder mToken = new Binder();
|
||||
@Mock
|
||||
private IControlsActionCallback.Stub mActionCallback;
|
||||
@Mock
|
||||
private IControlsLoadCallback.Stub mLoadCallback;
|
||||
@Mock
|
||||
private IControlsSubscriber.Stub mSubscriber;
|
||||
@Mock
|
||||
private IIntentSender mIIntentSender;
|
||||
|
||||
private PendingIntent mPendingIntent;
|
||||
private FakeControlsProviderService mControlsProviderService;
|
||||
|
||||
private IControlsProvider mControlsProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(mActionCallback.asBinder()).thenCallRealMethod();
|
||||
when(mActionCallback.queryLocalInterface(any())).thenReturn(mActionCallback);
|
||||
when(mLoadCallback.asBinder()).thenCallRealMethod();
|
||||
when(mLoadCallback.queryLocalInterface(any())).thenReturn(mLoadCallback);
|
||||
when(mSubscriber.asBinder()).thenCallRealMethod();
|
||||
when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber);
|
||||
|
||||
Bundle b = new Bundle();
|
||||
b.putBinder(ControlsProviderService.CALLBACK_TOKEN, mToken);
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(ControlsProviderService.CALLBACK_BUNDLE, b);
|
||||
|
||||
mPendingIntent = new PendingIntent(mIIntentSender);
|
||||
|
||||
mControlsProviderService = new FakeControlsProviderService();
|
||||
mControlsProvider = IControlsProvider.Stub.asInterface(
|
||||
mControlsProviderService.onBind(intent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnLoad_allStateless() throws RemoteException {
|
||||
Control control1 = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build();
|
||||
Control control2 = new Control.StatelessBuilder("TEST_ID_2", mPendingIntent)
|
||||
.setDeviceType(DeviceTypes.TYPE_AIR_FRESHENER).build();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ArgumentCaptor<List<Control>> captor = ArgumentCaptor.forClass(List.class);
|
||||
|
||||
ArrayList<Control> list = new ArrayList<>();
|
||||
list.add(control1);
|
||||
list.add(control2);
|
||||
|
||||
mControlsProviderService.setControls(list);
|
||||
mControlsProvider.load(mLoadCallback);
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
|
||||
verify(mLoadCallback).accept(eq(mToken), captor.capture());
|
||||
List<Control> l = captor.getValue();
|
||||
assertEquals(2, l.size());
|
||||
assertTrue(equals(control1, l.get(0)));
|
||||
assertTrue(equals(control2, l.get(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnLoad_statefulConvertedToStateless() throws RemoteException {
|
||||
Control control = new Control.StatefulBuilder("TEST_ID", mPendingIntent)
|
||||
.setTitle("TEST_TITLE")
|
||||
.setStatus(Control.STATUS_OK)
|
||||
.build();
|
||||
Control statelessControl = new Control.StatelessBuilder(control).build();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ArgumentCaptor<List<Control>> captor = ArgumentCaptor.forClass(List.class);
|
||||
|
||||
ArrayList<Control> list = new ArrayList<>();
|
||||
list.add(control);
|
||||
|
||||
mControlsProviderService.setControls(list);
|
||||
mControlsProvider.load(mLoadCallback);
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
|
||||
verify(mLoadCallback).accept(eq(mToken), captor.capture());
|
||||
List<Control> l = captor.getValue();
|
||||
assertEquals(1, l.size());
|
||||
assertFalse(equals(control, l.get(0)));
|
||||
assertTrue(equals(statelessControl, l.get(0)));
|
||||
assertEquals(Control.STATUS_UNKNOWN, l.get(0).getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscribe() throws RemoteException {
|
||||
Control control = new Control.StatefulBuilder("TEST_ID", mPendingIntent)
|
||||
.setTitle("TEST_TITLE")
|
||||
.setStatus(Control.STATUS_OK)
|
||||
.build();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ArgumentCaptor<Control> controlCaptor =
|
||||
ArgumentCaptor.forClass(Control.class);
|
||||
ArgumentCaptor<IControlsSubscription.Stub> subscriptionCaptor =
|
||||
ArgumentCaptor.forClass(IControlsSubscription.Stub.class);
|
||||
|
||||
ArrayList<Control> list = new ArrayList<>();
|
||||
list.add(control);
|
||||
|
||||
mControlsProviderService.setControls(list);
|
||||
|
||||
mControlsProvider.subscribe(new ArrayList<String>(), mSubscriber);
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
|
||||
verify(mSubscriber).onSubscribe(eq(mToken), subscriptionCaptor.capture());
|
||||
subscriptionCaptor.getValue().request(1);
|
||||
|
||||
verify(mSubscriber).onNext(eq(mToken), controlCaptor.capture());
|
||||
Control c = controlCaptor.getValue();
|
||||
assertTrue(equals(c, list.get(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnAction() throws RemoteException {
|
||||
mControlsProvider.action("TEST_ID", new ControlActionWrapper(
|
||||
new CommandAction("", null)), mActionCallback);
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
|
||||
verify(mActionCallback).accept(mToken, "TEST_ID",
|
||||
ControlAction.RESPONSE_OK);
|
||||
}
|
||||
|
||||
private static boolean equals(Control c1, Control c2) {
|
||||
if (c1 == c2) return true;
|
||||
if (c1 == null || c2 == null) return false;
|
||||
return Objects.equals(c1.getControlId(), c2.getControlId())
|
||||
&& c1.getDeviceType() == c2.getDeviceType()
|
||||
&& Objects.equals(c1.getTitle(), c2.getTitle())
|
||||
&& Objects.equals(c1.getSubtitle(), c2.getSubtitle())
|
||||
&& Objects.equals(c1.getStructure(), c2.getStructure())
|
||||
&& Objects.equals(c1.getZone(), c2.getZone())
|
||||
&& Objects.equals(c1.getAppIntent(), c2.getAppIntent())
|
||||
&& Objects.equals(c1.getCustomIcon(), c2.getCustomIcon())
|
||||
&& Objects.equals(c1.getCustomColor(), c2.getCustomColor())
|
||||
&& c1.getStatus() == c2.getStatus()
|
||||
&& Objects.equals(c1.getControlTemplate(), c2.getControlTemplate())
|
||||
&& Objects.equals(c1.getStatusText(), c2.getStatusText());
|
||||
}
|
||||
|
||||
static class FakeControlsProviderService extends ControlsProviderService {
|
||||
|
||||
private List<Control> mControls;
|
||||
|
||||
public void setControls(List<Control> controls) {
|
||||
mControls = controls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAvailableControls(Consumer<List<Control>> cb) {
|
||||
cb.accept(mControls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Publisher<Control> publisherFor(List<String> ids) {
|
||||
return new Publisher<Control>() {
|
||||
public void subscribe(final Subscriber s) {
|
||||
s.onSubscribe(new Subscription() {
|
||||
public void request(long n) {
|
||||
for (Control c : mControls) {
|
||||
s.onNext(c);
|
||||
}
|
||||
}
|
||||
public void cancel() {}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performControlAction(String controlId, ControlAction action,
|
||||
Consumer<Integer> cb) {
|
||||
cb.accept(ControlAction.RESPONSE_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
* Copyright (C) 2020 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.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.service.controls;
|
||||
package android.service.controls.actions;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
@@ -22,12 +22,6 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.service.controls.actions.BooleanAction;
|
||||
import android.service.controls.actions.CommandAction;
|
||||
import android.service.controls.actions.ControlAction;
|
||||
import android.service.controls.actions.FloatAction;
|
||||
import android.service.controls.actions.ModeAction;
|
||||
import android.service.controls.actions.MultiFloatAction;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
@@ -97,9 +91,9 @@ public class ControlActionTest {
|
||||
assertNotNull(parcel);
|
||||
|
||||
parcel.setDataPosition(0);
|
||||
toParcel.writeToParcel(parcel, 0);
|
||||
new ControlActionWrapper(toParcel).writeToParcel(parcel, 0);
|
||||
parcel.setDataPosition(0);
|
||||
|
||||
return ControlAction.CREATOR.createFromParcel(parcel);
|
||||
return ControlActionWrapper.CREATOR.createFromParcel(parcel).getWrappedAction();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
* Copyright (C) 2020 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.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.service.controls;
|
||||
package android.service.controls.templates;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
@@ -24,16 +24,6 @@ import static org.junit.Assert.assertNotNull;
|
||||
import android.annotation.DrawableRes;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Parcel;
|
||||
import android.service.controls.templates.ControlButton;
|
||||
import android.service.controls.templates.ControlTemplate;
|
||||
import android.service.controls.templates.CoordinatedRangeTemplate;
|
||||
import android.service.controls.templates.DiscreteToggleTemplate;
|
||||
import android.service.controls.templates.RangeTemplate;
|
||||
import android.service.controls.templates.StatelessTemplate;
|
||||
import android.service.controls.templates.TemperatureControlTemplate;
|
||||
import android.service.controls.templates.ThumbnailTemplate;
|
||||
import android.service.controls.templates.ToggleRangeTemplate;
|
||||
import android.service.controls.templates.ToggleTemplate;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
@@ -65,8 +55,7 @@ public class ControlTemplateTest {
|
||||
|
||||
@Test
|
||||
public void testUnparcelingCorrectClass_none() {
|
||||
ControlTemplate
|
||||
toParcel = ControlTemplate.NO_TEMPLATE;
|
||||
ControlTemplate toParcel = ControlTemplate.NO_TEMPLATE;
|
||||
|
||||
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
|
||||
|
||||
@@ -75,8 +64,7 @@ public class ControlTemplateTest {
|
||||
|
||||
@Test
|
||||
public void testUnparcelingCorrectClass_toggle() {
|
||||
ControlTemplate
|
||||
toParcel = new android.service.controls.templates.ToggleTemplate(TEST_ID, mControlButton);
|
||||
ControlTemplate toParcel = new ToggleTemplate(TEST_ID, mControlButton);
|
||||
|
||||
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
|
||||
|
||||
@@ -86,8 +74,7 @@ public class ControlTemplateTest {
|
||||
|
||||
@Test
|
||||
public void testUnparcelingCorrectClass_range() {
|
||||
ControlTemplate
|
||||
toParcel = new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f");
|
||||
ControlTemplate toParcel = new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f");
|
||||
|
||||
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
|
||||
|
||||
@@ -117,8 +104,7 @@ public class ControlTemplateTest {
|
||||
|
||||
@Test
|
||||
public void testUnparcelingCorrectClass_thumbnail() {
|
||||
ControlTemplate
|
||||
toParcel = new ThumbnailTemplate(TEST_ID, mIcon, TEST_ACTION_DESCRIPTION);
|
||||
ControlTemplate toParcel = new ThumbnailTemplate(TEST_ID, mIcon, TEST_ACTION_DESCRIPTION);
|
||||
|
||||
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
|
||||
|
||||
@@ -140,7 +126,7 @@ public class ControlTemplateTest {
|
||||
@Test
|
||||
public void testUnparcelingCorrectClass_coordRange() {
|
||||
ControlTemplate toParcel =
|
||||
new CoordinatedRangeTemplate(TEST_ID,0.1f, 0, 1, 0.5f, 1, 2, 1.5f, 0.1f, "%f");
|
||||
new CoordinatedRangeTemplate(TEST_ID, 0.1f, 0, 1, 0.5f, 1, 2, 1.5f, 0.1f, "%f");
|
||||
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
|
||||
assertEquals(ControlTemplate.TYPE_COORD_RANGE, fromParcel.getTemplateType());
|
||||
assertTrue(fromParcel instanceof CoordinatedRangeTemplate);
|
||||
@@ -149,7 +135,7 @@ public class ControlTemplateTest {
|
||||
@Test
|
||||
public void testCoordRangeParameters_negativeMinGap() {
|
||||
CoordinatedRangeTemplate template =
|
||||
new CoordinatedRangeTemplate(TEST_ID,-0.1f, 0, 1, 0.5f, 1, 2, 1.5f, 0.1f, "%f");
|
||||
new CoordinatedRangeTemplate(TEST_ID, -0.1f, 0, 1, 0.5f, 1, 2, 1.5f, 0.1f, "%f");
|
||||
assertEquals(0, template.getMinGap(), 0);
|
||||
}
|
||||
|
||||
@@ -176,9 +162,8 @@ public class ControlTemplateTest {
|
||||
|
||||
@Test
|
||||
public void testUnparcelingCorrectClass_toggleRange() {
|
||||
ControlTemplate toParcel =
|
||||
new ToggleRangeTemplate(TEST_ID, mControlButton,
|
||||
new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f"));
|
||||
ControlTemplate toParcel = new ToggleRangeTemplate(TEST_ID, mControlButton,
|
||||
new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f"));
|
||||
|
||||
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
|
||||
|
||||
@@ -198,11 +183,12 @@ public class ControlTemplateTest {
|
||||
|
||||
@Test
|
||||
public void testUnparcelingCorrectClass_thermostat() {
|
||||
ControlTemplate toParcel = new TemperatureControlTemplate(TEST_ID,
|
||||
new ToggleTemplate("", mControlButton),
|
||||
TemperatureControlTemplate.MODE_OFF,
|
||||
TemperatureControlTemplate.MODE_OFF,
|
||||
TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
ControlTemplate toParcel = new TemperatureControlTemplate(
|
||||
TEST_ID,
|
||||
new ToggleTemplate("", mControlButton),
|
||||
TemperatureControlTemplate.MODE_OFF,
|
||||
TemperatureControlTemplate.MODE_OFF,
|
||||
TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
|
||||
ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
|
||||
|
||||
@@ -212,48 +198,70 @@ public class ControlTemplateTest {
|
||||
|
||||
@Test
|
||||
public void testThermostatParams_wrongMode() {
|
||||
TemperatureControlTemplate thermostat = new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE, -1,
|
||||
TemperatureControlTemplate.MODE_OFF, TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
TemperatureControlTemplate thermostat = new TemperatureControlTemplate(
|
||||
TEST_ID,
|
||||
ControlTemplate.NO_TEMPLATE,
|
||||
-1,
|
||||
TemperatureControlTemplate.MODE_OFF,
|
||||
TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
assertEquals(TemperatureControlTemplate.MODE_UNKNOWN, thermostat.getCurrentMode());
|
||||
|
||||
thermostat = new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE, 100,
|
||||
TemperatureControlTemplate.MODE_OFF, TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
thermostat = new TemperatureControlTemplate(
|
||||
TEST_ID,
|
||||
ControlTemplate.NO_TEMPLATE,
|
||||
100,
|
||||
TemperatureControlTemplate.MODE_OFF,
|
||||
TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
assertEquals(TemperatureControlTemplate.MODE_UNKNOWN, thermostat.getCurrentMode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThermostatParams_wrongActiveMode() {
|
||||
TemperatureControlTemplate thermostat = new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE,
|
||||
TemperatureControlTemplate.MODE_OFF,-1, TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
TemperatureControlTemplate thermostat = new TemperatureControlTemplate(
|
||||
TEST_ID,
|
||||
ControlTemplate.NO_TEMPLATE,
|
||||
TemperatureControlTemplate.MODE_OFF,
|
||||
-1,
|
||||
TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
assertEquals(TemperatureControlTemplate.MODE_UNKNOWN, thermostat.getCurrentActiveMode());
|
||||
|
||||
thermostat = new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE,
|
||||
TemperatureControlTemplate.MODE_OFF,100, TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
thermostat = new TemperatureControlTemplate(
|
||||
TEST_ID,
|
||||
ControlTemplate.NO_TEMPLATE,
|
||||
TemperatureControlTemplate.MODE_OFF,
|
||||
100,
|
||||
TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
assertEquals(TemperatureControlTemplate.MODE_UNKNOWN, thermostat.getCurrentActiveMode());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testThermostatParams_wrongFlags_currentMode() {
|
||||
new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE, TemperatureControlTemplate.MODE_HEAT,
|
||||
TemperatureControlTemplate.MODE_OFF, TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
new TemperatureControlTemplate(
|
||||
TEST_ID,
|
||||
ControlTemplate.NO_TEMPLATE,
|
||||
TemperatureControlTemplate.MODE_HEAT,
|
||||
TemperatureControlTemplate.MODE_OFF,
|
||||
TemperatureControlTemplate.FLAG_MODE_OFF);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testThermostatParams_wrongFlags_currentActiveMode() {
|
||||
new TemperatureControlTemplate(TEST_ID, ControlTemplate.NO_TEMPLATE, TemperatureControlTemplate.MODE_HEAT,
|
||||
TemperatureControlTemplate.MODE_OFF, TemperatureControlTemplate.FLAG_MODE_HEAT);
|
||||
new TemperatureControlTemplate(TEST_ID,
|
||||
ControlTemplate.NO_TEMPLATE,
|
||||
TemperatureControlTemplate.MODE_HEAT,
|
||||
TemperatureControlTemplate.MODE_OFF,
|
||||
TemperatureControlTemplate.FLAG_MODE_HEAT);
|
||||
}
|
||||
|
||||
private ControlTemplate parcelAndUnparcel(
|
||||
ControlTemplate toParcel) {
|
||||
private ControlTemplate parcelAndUnparcel(ControlTemplate toParcel) {
|
||||
Parcel parcel = Parcel.obtain();
|
||||
|
||||
assertNotNull(parcel);
|
||||
|
||||
parcel.setDataPosition(0);
|
||||
toParcel.writeToParcel(parcel, 0);
|
||||
new ControlTemplateWrapper(toParcel).writeToParcel(parcel, 0);
|
||||
parcel.setDataPosition(0);
|
||||
|
||||
return ControlTemplate.CREATOR.createFromParcel(parcel);
|
||||
return ControlTemplateWrapper.CREATOR.createFromParcel(parcel).getWrappedTemplate();
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,10 @@ import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.os.IBinder
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.IControlsProviderCallback
|
||||
import android.service.controls.IControlsActionCallback
|
||||
import android.service.controls.IControlsLoadCallback
|
||||
import android.service.controls.IControlsSubscriber
|
||||
import android.service.controls.IControlsSubscription
|
||||
import android.service.controls.actions.ControlAction
|
||||
import android.util.ArrayMap
|
||||
import android.util.Log
|
||||
@@ -54,20 +57,14 @@ open class ControlsBindingControllerImpl @Inject constructor(
|
||||
private val componentMap: MutableMap<ComponentName, ControlsProviderLifecycleManager> =
|
||||
ArrayMap<ComponentName, ControlsProviderLifecycleManager>()
|
||||
|
||||
private val serviceCallback = object : IControlsProviderCallback.Stub() {
|
||||
override fun onLoad(token: IBinder, controls: MutableList<Control>) {
|
||||
private val loadCallbackService = object : IControlsLoadCallback.Stub() {
|
||||
override fun accept(token: IBinder, controls: MutableList<Control>) {
|
||||
backgroundExecutor.execute(OnLoadRunnable(token, controls))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRefreshState(token: IBinder, controlStates: List<Control>) {
|
||||
if (!refreshing.get()) {
|
||||
Log.d(TAG, "Refresh outside of window for token:$token")
|
||||
} else {
|
||||
backgroundExecutor.execute(OnRefreshStateRunnable(token, controlStates))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onControlActionResponse(
|
||||
private val actionCallbackService = object : IControlsActionCallback.Stub() {
|
||||
override fun accept(
|
||||
token: IBinder,
|
||||
controlId: String,
|
||||
@ControlAction.ResponseResult response: Int
|
||||
@@ -76,13 +73,36 @@ open class ControlsBindingControllerImpl @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private val subscriberService = object : IControlsSubscriber.Stub() {
|
||||
override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
|
||||
backgroundExecutor.execute(OnSubscribeRunnable(token, subs))
|
||||
}
|
||||
|
||||
override fun onNext(token: IBinder, c: Control) {
|
||||
if (!refreshing.get()) {
|
||||
Log.d(TAG, "Refresh outside of window for token:$token")
|
||||
} else {
|
||||
backgroundExecutor.execute(OnNextRunnable(token, c))
|
||||
}
|
||||
}
|
||||
override fun onError(token: IBinder, s: String) {
|
||||
backgroundExecutor.execute(OnErrorRunnable(token, s))
|
||||
}
|
||||
|
||||
override fun onComplete(token: IBinder) {
|
||||
backgroundExecutor.execute(OnCompleteRunnable(token))
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal open fun createProviderManager(component: ComponentName):
|
||||
ControlsProviderLifecycleManager {
|
||||
return ControlsProviderLifecycleManager(
|
||||
context,
|
||||
backgroundExecutor,
|
||||
serviceCallback,
|
||||
loadCallbackService,
|
||||
actionCallbackService,
|
||||
subscriberService,
|
||||
component
|
||||
)
|
||||
}
|
||||
@@ -176,16 +196,51 @@ open class ControlsBindingControllerImpl @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private inner class OnRefreshStateRunnable(
|
||||
private inner class OnNextRunnable(
|
||||
token: IBinder,
|
||||
val list: List<Control>
|
||||
val control: Control
|
||||
) : CallbackRunnable(token) {
|
||||
override fun run() {
|
||||
if (!refreshing.get()) {
|
||||
Log.d(TAG, "onRefresh outside of window from:${provider?.componentName}")
|
||||
}
|
||||
provider?.let {
|
||||
lazyController.get().refreshStatus(it.componentName, list)
|
||||
lazyController.get().refreshStatus(it.componentName, control)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class OnSubscribeRunnable(
|
||||
token: IBinder,
|
||||
val subscription: IControlsSubscription
|
||||
) : CallbackRunnable(token) {
|
||||
override fun run() {
|
||||
if (!refreshing.get()) {
|
||||
Log.d(TAG, "onRefresh outside of window from '${provider?.componentName}'")
|
||||
}
|
||||
provider?.let {
|
||||
it.startSubscription(subscription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class OnCompleteRunnable(
|
||||
token: IBinder
|
||||
) : CallbackRunnable(token) {
|
||||
override fun run() {
|
||||
provider?.let {
|
||||
Log.i(TAG, "onComplete receive from '${provider?.componentName}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class OnErrorRunnable(
|
||||
token: IBinder,
|
||||
val error: String
|
||||
) : CallbackRunnable(token) {
|
||||
override fun run() {
|
||||
provider?.let {
|
||||
Log.e(TAG, "onError receive from '${provider?.componentName}': $error")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,4 +256,4 @@ open class ControlsBindingControllerImpl @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ interface ControlsController {
|
||||
fun changeFavoriteStatus(controlInfo: ControlInfo, state: Boolean)
|
||||
fun unsubscribe()
|
||||
fun action(controlInfo: ControlInfo, action: ControlAction)
|
||||
fun refreshStatus(componentName: ComponentName, controls: List<Control>)
|
||||
fun refreshStatus(componentName: ComponentName, control: Control)
|
||||
fun onActionResponse(
|
||||
componentName: ComponentName,
|
||||
controlId: String,
|
||||
@ControlAction.ResponseResult response: Int
|
||||
)
|
||||
fun clearFavorites()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,20 +210,20 @@ class ControlsControllerImpl @Inject constructor (
|
||||
}
|
||||
}
|
||||
|
||||
override fun refreshStatus(componentName: ComponentName, controls: List<Control>) {
|
||||
override fun refreshStatus(componentName: ComponentName, control: Control) {
|
||||
if (!available) {
|
||||
Log.d(TAG, "Controls not available")
|
||||
return
|
||||
}
|
||||
executor.execute {
|
||||
synchronized(currentFavorites) {
|
||||
val changed = updateFavoritesLocked(componentName, controls)
|
||||
val changed = updateFavoritesLocked(componentName, listOf(control))
|
||||
if (changed) {
|
||||
persistenceWrapper.storeFavorites(favoritesAsListLocked())
|
||||
}
|
||||
}
|
||||
}
|
||||
uiController.onRefreshState(componentName, controls)
|
||||
uiController.onRefreshState(componentName, listOf(control))
|
||||
}
|
||||
|
||||
override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
|
||||
@@ -270,4 +270,4 @@ class ControlsControllerImpl @Inject constructor (
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,13 @@ import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.os.RemoteException
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.ControlsProviderService.CALLBACK_BINDER
|
||||
import android.service.controls.ControlsProviderService.CALLBACK_BUNDLE
|
||||
import android.service.controls.ControlsProviderService.CALLBACK_TOKEN
|
||||
import android.service.controls.IControlsActionCallback
|
||||
import android.service.controls.IControlsLoadCallback
|
||||
import android.service.controls.IControlsProvider
|
||||
import android.service.controls.IControlsProviderCallback
|
||||
import android.service.controls.IControlsSubscriber
|
||||
import android.service.controls.IControlsSubscription
|
||||
import android.service.controls.actions.ControlAction
|
||||
import android.util.ArraySet
|
||||
import android.util.Log
|
||||
@@ -41,19 +43,23 @@ typealias LoadCallback = (List<Control>) -> Unit
|
||||
class ControlsProviderLifecycleManager(
|
||||
private val context: Context,
|
||||
private val executor: DelayableExecutor,
|
||||
private val serviceCallback: IControlsProviderCallback.Stub,
|
||||
private val loadCallbackService: IControlsLoadCallback.Stub,
|
||||
private val actionCallbackService: IControlsActionCallback.Stub,
|
||||
private val subscriberService: IControlsSubscriber.Stub,
|
||||
val componentName: ComponentName
|
||||
) : IBinder.DeathRecipient {
|
||||
|
||||
var lastLoadCallback: LoadCallback? = null
|
||||
private set
|
||||
val token: IBinder = Binder()
|
||||
@GuardedBy("subscriptions")
|
||||
private val subscriptions = mutableListOf<IControlsSubscription>()
|
||||
private var unbindImmediate = false
|
||||
private var requiresBound = false
|
||||
private var isBound = false
|
||||
@GuardedBy("queuedMessages")
|
||||
private val queuedMessages: MutableSet<Message> = ArraySet()
|
||||
private var wrapper: ControlsProviderServiceWrapper? = null
|
||||
private var wrapper: ServiceWrapper? = null
|
||||
private var bindTryCount = 0
|
||||
private val TAG = javaClass.simpleName
|
||||
private var onLoadCanceller: Runnable? = null
|
||||
@@ -61,12 +67,12 @@ class ControlsProviderLifecycleManager(
|
||||
companion object {
|
||||
private const val MSG_LOAD = 0
|
||||
private const val MSG_SUBSCRIBE = 1
|
||||
private const val MSG_UNSUBSCRIBE = 2
|
||||
private const val MSG_ON_ACTION = 3
|
||||
private const val MSG_UNBIND = 4
|
||||
private const val MSG_ACTION = 2
|
||||
private const val MSG_UNBIND = 3
|
||||
private const val BIND_RETRY_DELAY = 1000L // ms
|
||||
private const val LOAD_TIMEOUT = 5000L // ms
|
||||
private const val MAX_BIND_RETRIES = 5
|
||||
private const val MAX_CONTROLS_REQUEST = 100000L
|
||||
private const val DEBUG = true
|
||||
private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE or
|
||||
Context.BIND_WAIVE_PRIORITY
|
||||
@@ -75,7 +81,6 @@ class ControlsProviderLifecycleManager(
|
||||
private val intent = Intent().apply {
|
||||
component = componentName
|
||||
putExtra(CALLBACK_BUNDLE, Bundle().apply {
|
||||
putBinder(CALLBACK_BINDER, serviceCallback)
|
||||
putBinder(CALLBACK_TOKEN, token)
|
||||
})
|
||||
}
|
||||
@@ -119,7 +124,7 @@ class ControlsProviderLifecycleManager(
|
||||
override fun onServiceConnected(name: ComponentName, service: IBinder) {
|
||||
if (DEBUG) Log.d(TAG, "onServiceConnected $name")
|
||||
bindTryCount = 0
|
||||
wrapper = ControlsProviderServiceWrapper(IControlsProvider.Stub.asInterface(service))
|
||||
wrapper = ServiceWrapper(IControlsProvider.Stub.asInterface(service))
|
||||
try {
|
||||
service.linkToDeath(this@ControlsProviderLifecycleManager, 0)
|
||||
} catch (_: RemoteException) {}
|
||||
@@ -151,7 +156,7 @@ class ControlsProviderLifecycleManager(
|
||||
}
|
||||
queue.filter { it is Message.Action }.forEach {
|
||||
val msg = it as Message.Action
|
||||
onAction(msg.id, msg.action)
|
||||
action(msg.id, msg.action)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +187,7 @@ class ControlsProviderLifecycleManager(
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "load $componentName")
|
||||
}
|
||||
if (!(wrapper?.load() ?: false)) {
|
||||
if (!(wrapper?.load(loadCallbackService) ?: false)) {
|
||||
queueMessage(Message.Load)
|
||||
binderDied()
|
||||
}
|
||||
@@ -194,7 +199,7 @@ class ControlsProviderLifecycleManager(
|
||||
onLoadCanceller = executor.executeDelayed({
|
||||
// Didn't receive a response in time, log and send back empty list
|
||||
Log.d(TAG, "Timeout waiting onLoad for $componentName")
|
||||
serviceCallback.onLoad(token, emptyList())
|
||||
loadCallbackService.accept(token, emptyList())
|
||||
}, LOAD_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
if (isBound) {
|
||||
load()
|
||||
@@ -218,7 +223,7 @@ class ControlsProviderLifecycleManager(
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "subscribe $componentName - $controlIds")
|
||||
}
|
||||
if (!(wrapper?.subscribe(controlIds) ?: false)) {
|
||||
if (!(wrapper?.subscribe(controlIds, subscriberService) ?: false)) {
|
||||
queueMessage(Message.Subscribe(controlIds))
|
||||
binderDied()
|
||||
}
|
||||
@@ -226,29 +231,45 @@ class ControlsProviderLifecycleManager(
|
||||
|
||||
fun maybeBindAndSendAction(controlId: String, action: ControlAction) {
|
||||
if (isBound) {
|
||||
onAction(controlId, action)
|
||||
action(controlId, action)
|
||||
} else {
|
||||
queueMessage(Message.Action(controlId, action))
|
||||
bindService(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onAction(controlId: String, action: ControlAction) {
|
||||
private fun action(controlId: String, action: ControlAction) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onAction $componentName - $controlId")
|
||||
}
|
||||
if (!(wrapper?.onAction(controlId, action) ?: false)) {
|
||||
if (!(wrapper?.action(controlId, action, actionCallbackService) ?: false)) {
|
||||
queueMessage(Message.Action(controlId, action))
|
||||
binderDied()
|
||||
}
|
||||
}
|
||||
|
||||
fun startSubscription(subscription: IControlsSubscription) {
|
||||
synchronized(subscriptions) {
|
||||
subscriptions.add(subscription)
|
||||
}
|
||||
wrapper?.request(subscription, MAX_CONTROLS_REQUEST)
|
||||
}
|
||||
|
||||
fun unsubscribe() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "unsubscribe $componentName")
|
||||
}
|
||||
unqueueMessage(Message.Subscribe(emptyList())) // Removes all subscribe messages
|
||||
wrapper?.unsubscribe()
|
||||
|
||||
val subs = synchronized(subscriptions) {
|
||||
ArrayList(subscriptions).also {
|
||||
subscriptions.clear()
|
||||
}
|
||||
}
|
||||
|
||||
subs.forEach {
|
||||
wrapper?.cancel(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun maybeUnbindAndRemoveCallback() {
|
||||
@@ -277,7 +298,7 @@ class ControlsProviderLifecycleManager(
|
||||
override val type = MSG_SUBSCRIBE
|
||||
}
|
||||
class Action(val id: String, val action: ControlAction) : Message() {
|
||||
override val type = MSG_ON_ACTION
|
||||
override val type = MSG_ACTION
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,17 @@
|
||||
package com.android.systemui.controls.controller
|
||||
|
||||
import android.service.controls.actions.ControlAction
|
||||
import android.service.controls.IControlsActionCallback
|
||||
import android.service.controls.IControlsLoadCallback
|
||||
import android.service.controls.IControlsProvider
|
||||
import android.service.controls.IControlsSubscriber
|
||||
import android.service.controls.IControlsSubscription
|
||||
import android.service.controls.actions.ControlActionWrapper
|
||||
import android.util.Log
|
||||
|
||||
class ControlsProviderServiceWrapper(val service: IControlsProvider) {
|
||||
class ServiceWrapper(val service: IControlsProvider) {
|
||||
companion object {
|
||||
private const val TAG = "ControlsProviderServiceWrapper"
|
||||
private const val TAG = "ServiceWrapper"
|
||||
}
|
||||
|
||||
private fun callThroughService(block: () -> Unit): Boolean {
|
||||
@@ -30,32 +35,42 @@ class ControlsProviderServiceWrapper(val service: IControlsProvider) {
|
||||
block()
|
||||
return true
|
||||
} catch (ex: Exception) {
|
||||
Log.d(TAG, "Caught exception from ControlsProviderService", ex)
|
||||
Log.e(TAG, "Caught exception from ControlsProviderService", ex)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fun load(): Boolean {
|
||||
fun load(cb: IControlsLoadCallback): Boolean {
|
||||
return callThroughService {
|
||||
service.load()
|
||||
service.load(cb)
|
||||
}
|
||||
}
|
||||
|
||||
fun subscribe(controlIds: List<String>): Boolean {
|
||||
fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber): Boolean {
|
||||
return callThroughService {
|
||||
service.subscribe(controlIds)
|
||||
service.subscribe(controlIds, subscriber)
|
||||
}
|
||||
}
|
||||
|
||||
fun unsubscribe(): Boolean {
|
||||
fun request(subscription: IControlsSubscription, num: Long): Boolean {
|
||||
return callThroughService {
|
||||
service.unsubscribe()
|
||||
subscription.request(num)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAction(controlId: String, action: ControlAction): Boolean {
|
||||
fun cancel(subscription: IControlsSubscription): Boolean {
|
||||
return callThroughService {
|
||||
service.onAction(controlId, action)
|
||||
subscription.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun action(
|
||||
controlId: String,
|
||||
action: ControlAction,
|
||||
cb: IControlsActionCallback
|
||||
): Boolean {
|
||||
return callThroughService {
|
||||
service.action(controlId, ControlActionWrapper(action), cb)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
|
||||
context,
|
||||
executor,
|
||||
ServiceListing.Builder(context)
|
||||
.setIntentAction(ControlsProviderService.CONTROLS_ACTION)
|
||||
.setIntentAction(ControlsProviderService.SERVICE_CONTROLS)
|
||||
.setPermission("android.permission.BIND_CONTROLS")
|
||||
.setNoun("Controls Provider")
|
||||
.setSetting("controls_providers")
|
||||
|
||||
@@ -224,8 +224,9 @@ class ControlsControllerImplTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testRefreshStatus() {
|
||||
val list = listOf(Control.StatefulBuilder(TEST_CONTROL_ID, pendingIntent).build())
|
||||
controller.refreshStatus(TEST_COMPONENT, list)
|
||||
val control = Control.StatefulBuilder(TEST_CONTROL_ID, pendingIntent).build()
|
||||
val list = listOf(control)
|
||||
controller.refreshStatus(TEST_COMPONENT, control)
|
||||
|
||||
verify(uiController).onRefreshState(TEST_COMPONENT, list)
|
||||
}
|
||||
@@ -340,7 +341,7 @@ class ControlsControllerImplTest : SysuiTestCase() {
|
||||
val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2)
|
||||
val control = builderFromInfo(newControlInfo).build()
|
||||
|
||||
controller.refreshStatus(TEST_COMPONENT, listOf(control))
|
||||
controller.refreshStatus(TEST_COMPONENT, control)
|
||||
|
||||
delayableExecutor.runAllReady()
|
||||
|
||||
@@ -357,4 +358,4 @@ class ControlsControllerImplTest : SysuiTestCase() {
|
||||
controller.clearFavorites()
|
||||
assertTrue(controller.getFavoriteControls().isEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,12 @@ package com.android.systemui.controls.controller
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.IControlsActionCallback
|
||||
import android.service.controls.IControlsLoadCallback
|
||||
import android.service.controls.IControlsProvider
|
||||
import android.service.controls.IControlsProviderCallback
|
||||
import android.service.controls.IControlsSubscriber
|
||||
import android.service.controls.actions.ControlAction
|
||||
import android.service.controls.actions.ControlActionWrapper
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
@@ -35,7 +38,10 @@ import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers
|
||||
import org.mockito.ArgumentMatchers.eq
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.verify
|
||||
@@ -46,14 +52,25 @@ import org.mockito.MockitoAnnotations
|
||||
class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
|
||||
|
||||
@Mock
|
||||
private lateinit var serviceCallback: IControlsProviderCallback.Stub
|
||||
private lateinit var actionCallback: IControlsActionCallback.Stub
|
||||
@Mock
|
||||
private lateinit var loadCallback: IControlsLoadCallback.Stub
|
||||
@Mock
|
||||
private lateinit var subscriber: IControlsSubscriber.Stub
|
||||
@Mock
|
||||
private lateinit var service: IControlsProvider.Stub
|
||||
|
||||
@Captor
|
||||
private lateinit var wrapperCaptor: ArgumentCaptor<ControlActionWrapper>
|
||||
|
||||
private val componentName = ComponentName("test.pkg", "test.cls")
|
||||
private lateinit var manager: ControlsProviderLifecycleManager
|
||||
private lateinit var executor: DelayableExecutor
|
||||
|
||||
companion object {
|
||||
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
@@ -66,7 +83,9 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
|
||||
manager = ControlsProviderLifecycleManager(
|
||||
context,
|
||||
executor,
|
||||
serviceCallback,
|
||||
loadCallback,
|
||||
actionCallback,
|
||||
subscriber,
|
||||
componentName
|
||||
)
|
||||
}
|
||||
@@ -94,7 +113,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
|
||||
val callback: (List<Control>) -> Unit = {}
|
||||
manager.maybeBindAndLoad(callback)
|
||||
|
||||
verify(service).load()
|
||||
verify(service).load(loadCallback)
|
||||
|
||||
assertTrue(mContext.isBound(componentName))
|
||||
assertEquals(callback, manager.lastLoadCallback)
|
||||
@@ -109,30 +128,24 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
|
||||
assertNull(manager.lastLoadCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnsubscribe() {
|
||||
manager.bindPermanently()
|
||||
manager.unsubscribe()
|
||||
|
||||
verify(service).unsubscribe()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMaybeBindAndSubscribe() {
|
||||
val list = listOf("TEST_ID")
|
||||
manager.maybeBindAndSubscribe(list)
|
||||
|
||||
assertTrue(mContext.isBound(componentName))
|
||||
verify(service).subscribe(list)
|
||||
verify(service).subscribe(list, subscriber)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMaybeBindAndAction() {
|
||||
val controlId = "TEST_ID"
|
||||
val action = ControlAction.UNKNOWN_ACTION
|
||||
val action = ControlAction.ERROR_ACTION
|
||||
manager.maybeBindAndSendAction(controlId, action)
|
||||
|
||||
assertTrue(mContext.isBound(componentName))
|
||||
verify(service).onAction(controlId, action)
|
||||
verify(service).action(eq(controlId), capture(wrapperCaptor),
|
||||
eq(actionCallback))
|
||||
assertEquals(action, wrapperCaptor.getValue().getWrappedAction())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.controls.controller
|
||||
|
||||
import android.os.RemoteException
|
||||
import android.service.controls.IControlsProvider
|
||||
import android.service.controls.actions.ControlAction
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.any
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class ControlsProviderServiceWrapperTest : SysuiTestCase() {
|
||||
|
||||
@Mock
|
||||
private lateinit var service: IControlsProvider
|
||||
|
||||
private val exception = RemoteException()
|
||||
|
||||
private lateinit var wrapper: ControlsProviderServiceWrapper
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
wrapper = ControlsProviderServiceWrapper(service)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLoad_happyPath() {
|
||||
val result = wrapper.load()
|
||||
|
||||
assertTrue(result)
|
||||
verify(service).load()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLoad_error() {
|
||||
`when`(service.load()).thenThrow(exception)
|
||||
val result = wrapper.load()
|
||||
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubscribe_happyPath() {
|
||||
val list = listOf("TEST_ID")
|
||||
val result = wrapper.subscribe(list)
|
||||
|
||||
assertTrue(result)
|
||||
verify(service).subscribe(list)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubscribe_error() {
|
||||
`when`(service.subscribe(any())).thenThrow(exception)
|
||||
|
||||
val list = listOf("TEST_ID")
|
||||
val result = wrapper.subscribe(list)
|
||||
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnsubscribe_happyPath() {
|
||||
val result = wrapper.unsubscribe()
|
||||
|
||||
assertTrue(result)
|
||||
verify(service).unsubscribe()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnsubscribe_error() {
|
||||
`when`(service.unsubscribe()).thenThrow(exception)
|
||||
val result = wrapper.unsubscribe()
|
||||
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnAction_happyPath() {
|
||||
val id = "TEST_ID"
|
||||
val action = ControlAction.UNKNOWN_ACTION
|
||||
|
||||
val result = wrapper.onAction(id, action)
|
||||
|
||||
assertTrue(result)
|
||||
verify(service).onAction(id, action)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnAction_error() {
|
||||
`when`(service.onAction(any(), any())).thenThrow(exception)
|
||||
|
||||
val id = "TEST_ID"
|
||||
val action = ControlAction.UNKNOWN_ACTION
|
||||
|
||||
val result = wrapper.onAction(id, action)
|
||||
|
||||
assertFalse(result)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.controls.controller
|
||||
|
||||
import android.os.RemoteException
|
||||
import android.service.controls.IControlsActionCallback
|
||||
import android.service.controls.IControlsLoadCallback
|
||||
import android.service.controls.IControlsProvider
|
||||
import android.service.controls.IControlsSubscriber
|
||||
import android.service.controls.IControlsSubscription
|
||||
import android.service.controls.actions.ControlAction
|
||||
import android.service.controls.actions.ControlActionWrapper
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.eq
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.any
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class ServiceWrapperTest : SysuiTestCase() {
|
||||
|
||||
@Mock
|
||||
private lateinit var service: IControlsProvider
|
||||
|
||||
@Mock
|
||||
private lateinit var subscription: IControlsSubscription
|
||||
|
||||
@Mock
|
||||
private lateinit var subscriber: IControlsSubscriber
|
||||
|
||||
@Mock
|
||||
private lateinit var loadCallback: IControlsLoadCallback
|
||||
|
||||
@Mock
|
||||
private lateinit var actionCallback: IControlsActionCallback
|
||||
|
||||
@Captor
|
||||
private lateinit var wrapperCaptor: ArgumentCaptor<ControlActionWrapper>
|
||||
|
||||
private val exception = RemoteException()
|
||||
|
||||
private lateinit var wrapper: ServiceWrapper
|
||||
|
||||
companion object {
|
||||
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
wrapper = ServiceWrapper(service)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLoad_happyPath() {
|
||||
val result = wrapper.load(loadCallback)
|
||||
|
||||
assertTrue(result)
|
||||
verify(service).load(loadCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLoad_error() {
|
||||
`when`(service.load(any())).thenThrow(exception)
|
||||
val result = wrapper.load(loadCallback)
|
||||
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubscribe_happyPath() {
|
||||
val list = listOf("TEST_ID")
|
||||
val result = wrapper.subscribe(list, subscriber)
|
||||
|
||||
assertTrue(result)
|
||||
verify(service).subscribe(list, subscriber)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubscribe_error() {
|
||||
`when`(service.subscribe(any(), any())).thenThrow(exception)
|
||||
|
||||
val list = listOf("TEST_ID")
|
||||
val result = wrapper.subscribe(list, subscriber)
|
||||
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCancel_happyPath() {
|
||||
val result = wrapper.cancel(subscription)
|
||||
|
||||
assertTrue(result)
|
||||
verify(subscription).cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCancel_error() {
|
||||
`when`(subscription.cancel()).thenThrow(exception)
|
||||
val result = wrapper.cancel(subscription)
|
||||
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnAction_happyPath() {
|
||||
val id = "TEST_ID"
|
||||
val action = ControlAction.ERROR_ACTION
|
||||
|
||||
val result = wrapper.action(id, action, actionCallback)
|
||||
|
||||
assertTrue(result)
|
||||
verify(service).action(eq(id), capture(wrapperCaptor),
|
||||
eq(actionCallback))
|
||||
assertEquals(action, wrapperCaptor.getValue().getWrappedAction())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnAction_error() {
|
||||
`when`(service.action(any(), any(), any())).thenThrow(exception)
|
||||
|
||||
val id = "TEST_ID"
|
||||
val action = ControlAction.ERROR_ACTION
|
||||
|
||||
val result = wrapper.action(id, action, actionCallback)
|
||||
|
||||
assertFalse(result)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user