summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Fabian Kozynski <kozynski@google.com> 2019-12-05 14:15:24 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2019-12-05 14:15:24 +0000
commit1f6640123b73083769fcede4cb174e7e22a88e6f (patch)
tree47d24a47ca0336d615deff30b370bf37735cf575
parent819d8b77c4a40fa35e2977ba9fe051042602d97d (diff)
parentc57c1621cfd9b65d68392339d3ea9efbfde43bc0 (diff)
Merge "New Controls API @hide"
-rw-r--r--core/java/android/service/controls/BooleanAction.aidl19
-rw-r--r--core/java/android/service/controls/BooleanAction.java93
-rw-r--r--core/java/android/service/controls/Control.aidl19
-rw-r--r--core/java/android/service/controls/Control.java284
-rw-r--r--core/java/android/service/controls/ControlAction.aidl19
-rw-r--r--core/java/android/service/controls/ControlAction.java189
-rw-r--r--core/java/android/service/controls/ControlButton.aidl19
-rw-r--r--core/java/android/service/controls/ControlButton.java103
-rw-r--r--core/java/android/service/controls/ControlState.aidl19
-rw-r--r--core/java/android/service/controls/ControlState.java318
-rw-r--r--core/java/android/service/controls/ControlTemplate.aidl19
-rw-r--r--core/java/android/service/controls/ControlTemplate.java167
-rw-r--r--core/java/android/service/controls/DiscreteToggleTemplate.java110
-rw-r--r--core/java/android/service/controls/FloatAction.aidl19
-rw-r--r--core/java/android/service/controls/FloatAction.java90
-rw-r--r--core/java/android/service/controls/IControlsProvider.aidl30
-rw-r--r--core/java/android/service/controls/IControlsProviderCallback.aidl29
-rw-r--r--core/java/android/service/controls/RangeTemplate.aidl19
-rw-r--r--core/java/android/service/controls/RangeTemplate.java190
-rw-r--r--core/java/android/service/controls/ThumbnailTemplate.aidl19
-rw-r--r--core/java/android/service/controls/ThumbnailTemplate.java95
-rw-r--r--core/java/android/service/controls/ToggleTemplate.aidl19
-rw-r--r--core/java/android/service/controls/ToggleTemplate.java86
-rw-r--r--core/java/android/service/notification/IConditionProvider.aidl2
-rw-r--r--core/tests/coretests/src/android/service/controls/ControlActionTest.java69
-rw-r--r--core/tests/coretests/src/android/service/controls/ControlTemplateTest.java139
26 files changed, 2183 insertions, 1 deletions
diff --git a/core/java/android/service/controls/BooleanAction.aidl b/core/java/android/service/controls/BooleanAction.aidl
new file mode 100644
index 000000000000..730ad36749f7
--- /dev/null
+++ b/core/java/android/service/controls/BooleanAction.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+parcelable BooleanAction; \ No newline at end of file
diff --git a/core/java/android/service/controls/BooleanAction.java b/core/java/android/service/controls/BooleanAction.java
new file mode 100644
index 000000000000..8508c635142f
--- /dev/null
+++ b/core/java/android/service/controls/BooleanAction.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+/**
+ * Action sent by a {@link ToggleTemplate}
+ * @hide
+ */
+public final class BooleanAction extends ControlAction {
+
+ private final boolean mNewState;
+
+ /**
+ * @param templateId the identifier of the {@link ToggleTemplate} that produced this action.
+ * @param newState new value for the state displayed by the {@link ToggleTemplate}.
+ */
+ public BooleanAction(@NonNull String templateId, boolean newState) {
+ this(templateId, newState, null);
+ }
+
+ /**
+ * @param templateId the identifier of the {@link ToggleTemplate} that originated this action.
+ * @param newValue new value for the state displayed by the {@link ToggleTemplate}.
+ * @param challengeValue a value sent by the user along with the action to authenticate. {@code}
+ * null is sent when no authentication is needed or has not been
+ * requested.
+ */
+ public BooleanAction(@NonNull String templateId, boolean newValue,
+ @Nullable String challengeValue) {
+ super(templateId, challengeValue);
+ mNewState = newValue;
+ }
+
+ BooleanAction(Parcel in) {
+ super(in);
+ mNewState = in.readByte() == 1;
+ }
+
+ /**
+ * The new state set for the button in the corresponding {@link ToggleTemplate}.
+ *
+ * @return {@code true} if the button was toggled from an {@code off} state to an {@code on}
+ * state.
+ */
+ public boolean getNewState() {
+ return mNewState;
+ }
+
+ /**
+ * @return {@link ControlAction#TYPE_BOOLEAN}
+ */
+ @Override
+ public int getActionType() {
+ return ControlAction.TYPE_BOOLEAN;
+ }
+
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeByte(mNewState ? (byte) 1 : (byte) 0);
+ }
+
+ public static final @NonNull Creator<BooleanAction> CREATOR = new Creator<BooleanAction>() {
+ @Override
+ public BooleanAction createFromParcel(Parcel source) {
+ return new BooleanAction(source);
+ }
+
+ @Override
+ public BooleanAction[] newArray(int size) {
+ return new BooleanAction[size];
+ }
+ };
+}
diff --git a/core/java/android/service/controls/Control.aidl b/core/java/android/service/controls/Control.aidl
new file mode 100644
index 000000000000..f4964f2e15d7
--- /dev/null
+++ b/core/java/android/service/controls/Control.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+parcelable Control; \ No newline at end of file
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
new file mode 100644
index 000000000000..a69408c43df3
--- /dev/null
+++ b/core/java/android/service/controls/Control.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Represents a physical object that can be represented by a {@link ControlTemplate} and whose
+ * properties may be modified through a {@link ControlAction}.
+ *
+ * The information is provided by a {@link ControlProviderService} and represents static
+ * information (not current status) about the device.
+ * <p>
+ * Each control needs a unique (per provider) identifier that is persistent across reboots of the
+ * system.
+ * <p>
+ * Each {@link Control} will have a name and an icon. The name is usually set up by the user in the
+ * {@link ControlProvider} while the icon is usually decided by the {@link ControlProvider} based
+ * on the type of device.
+ * <p>
+ * The {@link ControlTemplate.TemplateType} provided will be used as a hint when displaying this in
+ * non-interactive situations (for example when there's no state to display). This template is not
+ * the one that will be shown with the current state and provide interactions. That template is set
+ * using {@link ControlState}.
+ * <p>
+ * An {@link Intent} linking to the provider Activity that expands this {@link Control} should be
+ * provided.
+ * @hide
+ */
+public class Control implements Parcelable {
+
+ private final @NonNull String mControlId;
+ private final @NonNull Icon mIcon;
+ private final @NonNull CharSequence mTitle;
+ private final @Nullable ColorStateList mTintColor;
+ private final @NonNull Intent mAppIntent;
+ private final @ControlTemplate.TemplateType int mPrimaryType;
+
+ /**
+ * @param controlId the unique persistent identifier for this object.
+ * @param icon an icon to display identifying the control.
+ * @param title the user facing name of this control (e.g. "Bedroom thermostat").
+ * @param tintColor the color to tint parts of the element UI. If {@code null} is passed, the
+ * system accent color will be used.
+ * @param appIntent an intent linking to a page to interact with the corresponding device.
+ * @param primaryType the primary template for this type.
+ */
+ public Control(@NonNull String controlId,
+ @NonNull Icon icon,
+ @NonNull CharSequence title,
+ @Nullable ColorStateList tintColor,
+ @NonNull Intent appIntent,
+ int primaryType) {
+ Preconditions.checkNotNull(controlId);
+ Preconditions.checkNotNull(icon);
+ Preconditions.checkNotNull(title);
+ Preconditions.checkNotNull(appIntent);
+ mControlId = controlId;
+ mIcon = icon;
+ mTitle = title;
+ mTintColor = tintColor;
+ mAppIntent = appIntent;
+ mPrimaryType = primaryType;
+ }
+
+ public Control(Parcel in) {
+ mControlId = in.readString();
+ mIcon = Icon.CREATOR.createFromParcel(in);
+ mTitle = in.readCharSequence();
+ if (in.readByte() == 1) {
+ mTintColor = ColorStateList.CREATOR.createFromParcel(in);
+ } else {
+ mTintColor = null;
+ }
+ mAppIntent = Intent.CREATOR.createFromParcel(in);
+ mPrimaryType = in.readInt();
+ }
+
+ @NonNull
+ public String getControlId() {
+ return mControlId;
+ }
+
+ @NonNull
+ public Icon getIcon() {
+ return mIcon;
+ }
+
+ @NonNull
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ @Nullable
+ public ColorStateList getTint() {
+ return mTintColor;
+ }
+
+ @NonNull
+ public Intent getAppIntent() {
+ return mAppIntent;
+ }
+
+ @ControlTemplate.TemplateType
+ public int getPrimaryType() {
+ return mPrimaryType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mControlId);
+ mIcon.writeToParcel(dest, flags);
+ dest.writeCharSequence(mTitle);
+ if (mTintColor != null) {
+ dest.writeByte((byte) 1);
+ mTintColor.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ mAppIntent.writeToParcel(dest, flags);
+ dest.writeInt(mPrimaryType);
+ }
+
+ public static final Creator<Control> CREATOR = new Creator<Control>() {
+ @Override
+ public Control createFromParcel(Parcel source) {
+ return new Control(source);
+ }
+
+ @Override
+ public Control[] newArray(int size) {
+ return new Control[size];
+ }
+ };
+
+ /**
+ * Builder class for {@link Control}.
+ *
+ * This class facilitates the creation of {@link Control}. It provides the following
+ * defaults for non-optional parameters:
+ * <ul>
+ * <li> Title: {@code ""}
+ * <li> Primary template: {@link ControlTemplate#TYPE_NONE}
+ * </ul>
+ */
+ public static class Builder {
+ private String mControlId;
+ private Icon mIcon;
+ private CharSequence mTitle = "";
+ private ColorStateList mTintColor;
+ private @Nullable Intent mAppIntent;
+ private @ControlTemplate.TemplateType int mPrimaryType = ControlTemplate.TYPE_NONE;
+
+ /**
+ * @param controlId the identifier for the {@link Control}.
+ * @param icon the icon for the {@link Control}.
+ * @param appIntent the intent linking to the device Activity.
+ */
+ public Builder(@NonNull String controlId,
+ @NonNull Icon icon,
+ @NonNull Intent appIntent) {
+ Preconditions.checkNotNull(controlId);
+ Preconditions.checkNotNull(icon);
+ Preconditions.checkNotNull(appIntent);
+ mControlId = controlId;
+ mIcon = icon;
+ mAppIntent = appIntent;
+ }
+
+ /**
+ * Creates a {@link Builder} using an existing {@link Control} as a base.
+ * @param control base for the builder.
+ */
+ public Builder(@NonNull Control control) {
+ Preconditions.checkNotNull(control);
+ mControlId = control.mControlId;
+ mIcon = control.mIcon;
+ mTitle = control.mTitle;
+ mTintColor = control.mTintColor;
+ mAppIntent = control.mAppIntent;
+ mPrimaryType = control.mPrimaryType;
+ }
+
+ /**
+ * @param controlId the identifier for the {@link Control}.
+ * @return {@code this}
+ */
+ public Builder setControlId(@NonNull String controlId) {
+ Preconditions.checkNotNull(controlId);
+ mControlId = controlId;
+ return this;
+ }
+
+ /**
+ * @param icon the icon for the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setIcon(@NonNull Icon icon) {
+ Preconditions.checkNotNull(icon);
+ mIcon = icon;
+ return this;
+ }
+
+ /**
+ * @param title the user facing name of the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setTitle(@NonNull CharSequence title) {
+ Preconditions.checkNotNull(title);
+ mTitle = title;
+ return this;
+ }
+
+ /**
+ * @param tint colors for tinting parts of the {@link Control} UI. Passing {@code null} will
+ * default to using the current color accent.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setTint(@Nullable ColorStateList tint) {
+ mTintColor = tint;
+ return this;
+ }
+
+ /**
+ * @param appIntent an {@link Intent} linking to an Activity for the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setAppIntent(@NonNull Intent appIntent) {
+ Preconditions.checkNotNull(appIntent);
+ mAppIntent = appIntent;
+ return this;
+ }
+
+ /**
+ * @param type type to use as default in the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setPrimaryType(@ControlTemplate.TemplateType int type) {
+ mPrimaryType = type;
+ return this;
+ }
+
+ /**
+ * Build a {@link Control}
+ * @return a valid {@link Control}
+ */
+ @NonNull
+ public Control build() {
+ return new Control(mControlId, mIcon, mTitle, mTintColor, mAppIntent, mPrimaryType);
+ }
+ }
+}
diff --git a/core/java/android/service/controls/ControlAction.aidl b/core/java/android/service/controls/ControlAction.aidl
new file mode 100644
index 000000000000..e1a5276b70d6
--- /dev/null
+++ b/core/java/android/service/controls/ControlAction.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+parcelable ControlAction; \ No newline at end of file
diff --git a/core/java/android/service/controls/ControlAction.java b/core/java/android/service/controls/ControlAction.java
new file mode 100644
index 000000000000..8b759556b597
--- /dev/null
+++ b/core/java/android/service/controls/ControlAction.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An abstract action that is executed from a {@link ControlTemplate}.
+ *
+ * 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 {
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ TYPE_BOOLEAN,
+ TYPE_FLOAT
+ })
+ public @interface ActionType {};
+
+ /**
+ * The identifier of {@link BooleanAction}.
+ */
+ public static final @ActionType int TYPE_BOOLEAN = 0;
+
+ /**
+ * The identifier of {@link FloatAction}.
+ */
+ public static final @ActionType int TYPE_FLOAT = 1;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ RESPONSE_OK,
+ RESPONSE_FAIL,
+ RESPONSE_CHALLENGE_ACK,
+ RESPONSE_CHALLENGE_PIN,
+ RESPONSE_CHALLENGE_PASSPHRASE
+ })
+ public @interface ResponseResult {};
+
+ /**
+ * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * the action has been performed. The action may still fail later and the state may not change.
+ */
+ public static final @ResponseResult int RESPONSE_OK = 0;
+ /**
+ * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * the action has failed.
+ */
+ public static final @ResponseResult int RESPONSE_FAIL = 1;
+ /**
+ * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * in order for the action to be performed, acknowledgment from the user is required.
+ */
+ public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 2;
+ /**
+ * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * in order for the action to be performed, a PIN is required.
+ */
+ public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 3;
+ /**
+ * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * in order for the action to be performed, an alphanumeric passphrase is required.
+ */
+ public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 4;
+
+ /**
+ * The {@link ActionType} associated with this class.
+ */
+ public abstract @ActionType int getActionType();
+
+ private final @NonNull String mTemplateId;
+ private final @Nullable String mChallengeValue;
+
+ private ControlAction() {
+ mTemplateId = "";
+ mChallengeValue = null;
+ }
+
+ /**
+ * @hide
+ */
+ ControlAction(@NonNull String templateId, @Nullable String challengeValue) {
+ Preconditions.checkNotNull(templateId);
+ mTemplateId = templateId;
+ mChallengeValue = challengeValue;
+ }
+
+ /**
+ * @hide
+ */
+ ControlAction(Parcel in) {
+ mTemplateId = in.readString();
+ if (in.readByte() == 1) {
+ mChallengeValue = in.readString();
+ } else {
+ mChallengeValue = null;
+ }
+ }
+
+ /**
+ * The identifier of the {@link ControlTemplate} that originated this action
+ */
+ @NonNull
+ public String getTemplateId() {
+ return mTemplateId;
+ }
+
+ /**
+ * The challenge value used to authenticate certain actions, if available.
+ */
+ @Nullable
+ public String getChallengeValue() {
+ return mChallengeValue;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(getActionType());
+ dest.writeString(mTemplateId);
+ if (mChallengeValue != null) {
+ dest.writeByte((byte) 1);
+ dest.writeString(mChallengeValue);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ }
+
+ public static final @NonNull Creator<ControlAction> CREATOR = new Creator<ControlAction>() {
+ @Override
+ public ControlAction createFromParcel(Parcel source) {
+ int type = source.readInt();
+ return createActionFromType(type, source);
+ }
+
+ @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 BooleanAction.CREATOR.createFromParcel(source);
+ case TYPE_FLOAT:
+ return FloatAction.CREATOR.createFromParcel(source);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/core/java/android/service/controls/ControlButton.aidl b/core/java/android/service/controls/ControlButton.aidl
new file mode 100644
index 000000000000..6a7262d9542e
--- /dev/null
+++ b/core/java/android/service/controls/ControlButton.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+parcelable ControlButton; \ No newline at end of file
diff --git a/core/java/android/service/controls/ControlButton.java b/core/java/android/service/controls/ControlButton.java
new file mode 100644
index 000000000000..fed31158be35
--- /dev/null
+++ b/core/java/android/service/controls/ControlButton.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.NonNull;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Button element for {@link ControlTemplate}.
+ * @hide
+ */
+public class ControlButton implements Parcelable {
+
+ private final boolean mActive;
+ private final @NonNull Icon mIcon;
+ private final @NonNull CharSequence mContentDescription;
+
+ /**
+ * @param active true if the button should be rendered as active.
+ * @param icon icon to display in the button.
+ * @param contentDescription content description for the button.
+ */
+ public ControlButton(boolean active, @NonNull Icon icon,
+ @NonNull CharSequence contentDescription) {
+ Preconditions.checkNotNull(icon);
+ Preconditions.checkNotNull(contentDescription);
+ mActive = active;
+ mIcon = icon;
+ mContentDescription = contentDescription;
+ }
+
+ /**
+ * Whether the button should be rendered in its active state.
+ */
+ public boolean isActive() {
+ return mActive;
+ }
+
+ /**
+ * The icon for this button.
+ */
+ @NonNull
+ public Icon getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * The content description for this button.
+ */
+ @NonNull
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte(mActive ? (byte) 1 : (byte) 0);
+ mIcon.writeToParcel(dest, flags);
+ dest.writeCharSequence(mContentDescription);
+ }
+
+ ControlButton(Parcel in) {
+ mActive = in.readByte() != 0;
+ mIcon = Icon.CREATOR.createFromParcel(in);
+ mContentDescription = in.readCharSequence();
+ }
+
+ public static final Creator<ControlButton> CREATOR = new Creator<ControlButton>() {
+ @Override
+ public ControlButton createFromParcel(Parcel source) {
+ return new ControlButton(source);
+ }
+
+ @Override
+ public ControlButton[] newArray(int size) {
+ return new ControlButton[size];
+ }
+ };
+}
diff --git a/core/java/android/service/controls/ControlState.aidl b/core/java/android/service/controls/ControlState.aidl
new file mode 100644
index 000000000000..520d85b47d3e
--- /dev/null
+++ b/core/java/android/service/controls/ControlState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+parcelable ControlState; \ No newline at end of file
diff --git a/core/java/android/service/controls/ControlState.java b/core/java/android/service/controls/ControlState.java
new file mode 100644
index 000000000000..804aef798eb5
--- /dev/null
+++ b/core/java/android/service/controls/ControlState.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Current state for a {@link Control}.
+ *
+ * Collects information to render the current state of a {@link Control} as well as possible action
+ * that can be performed on it. Some of the information may temporarily override the defaults
+ * provided by the corresponding {@link Control}, while this state is being displayed.
+ *
+ * Additionally, this can be used to modify information related to the corresponding
+ * {@link Control}.
+ * @hide
+ */
+public final class ControlState implements Parcelable {
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ STATUS_OK,
+ STATUS_NOT_FOUND,
+ STATUS_ERROR,
+ STATUS_DISABLED,
+ })
+ public @interface Status {};
+
+ /**
+ * The device corresponding to the {@link Control} is responding correctly.
+ */
+ public static final int STATUS_OK = 0;
+
+ /**
+ * The device corresponding to the {@link Control} cannot be found or was removed.
+ */
+ public static final int STATUS_NOT_FOUND = 1;
+
+ /**
+ * The device corresponding to the {@link Control} is in an error state.
+ */
+ public static final int STATUS_ERROR = 2;
+
+ /**
+ * The {@link Control} is currently disabled.
+ */
+ public static final int STATUS_DISABLED = 3;
+
+ private final @NonNull Control mControl;
+ private final @Status int mStatus;
+ private final @NonNull ControlTemplate mControlTemplate;
+ private final @NonNull CharSequence mStatusText;
+ private final @Nullable Icon mOverrideIcon;
+ private final @Nullable ColorStateList mOverrideTint;
+
+ /**
+ * @param control the {@link Control} this state should be applied to. Can be used to
+ * update information about the {@link Control}
+ * @param status the current status of the {@link Control}.
+ * @param controlTemplate the template to be used to render the {@link Control}.
+ * @param statusText the text describing the current status.
+ * @param overrideIcon the icon to temporarily override the one provided in
+ * {@link Control#getIcon()}. Pass {@code null} to use the icon in
+ * {@link Control#getIcon()}.
+ * @param overrideTint the colors to temporarily override those provided in
+ * {@link Control#getTint()}. Pass {@code null} to use the colors in
+ * {@link Control#getTint()}.
+ */
+ public ControlState(@NonNull Control control,
+ int status,
+ @NonNull ControlTemplate controlTemplate,
+ @NonNull CharSequence statusText,
+ @Nullable Icon overrideIcon,
+ @Nullable ColorStateList overrideTint) {
+ Preconditions.checkNotNull(control);
+ Preconditions.checkNotNull(controlTemplate);
+ Preconditions.checkNotNull(statusText);
+
+ mControl = control;
+ mStatus = status;
+ mControlTemplate = controlTemplate;
+ mOverrideIcon = overrideIcon;
+ mStatusText = statusText;
+ mOverrideTint = overrideTint;
+ }
+
+ ControlState(Parcel in) {
+ mControl = Control.CREATOR.createFromParcel(in);
+ mStatus = in.readInt();
+ mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
+ mStatusText = in.readCharSequence();
+ if (in.readByte() == 1) {
+ mOverrideIcon = Icon.CREATOR.createFromParcel(in);
+ } else {
+ mOverrideIcon = null;
+ }
+ if (in.readByte() == 1) {
+ mOverrideTint = ColorStateList.CREATOR.createFromParcel(in);
+ } else {
+ mOverrideTint = null;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Status
+ public int getStatus() {
+ return mStatus;
+ }
+
+ @NonNull
+ public ControlTemplate getControlTemplate() {
+ return mControlTemplate;
+ }
+
+ @Nullable
+ public Icon getOverrideIcon() {
+ return mOverrideIcon;
+ }
+
+ @NonNull
+ public CharSequence getStatusText() {
+ return mStatusText;
+ }
+
+ @Nullable
+ public ColorStateList getOverrideTint() {
+ return mOverrideTint;
+ }
+
+ @NonNull
+ public Control getControl() {
+ return mControl;
+ }
+
+ @NonNull
+ public String getControlId() {
+ return mControl.getControlId();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mControl.writeToParcel(dest, flags);
+ dest.writeInt(mStatus);
+ mControlTemplate.writeToParcel(dest, flags);
+ dest.writeCharSequence(mStatusText);
+ if (mOverrideIcon != null) {
+ dest.writeByte((byte) 1);
+ mOverrideIcon.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ if (mOverrideTint != null) {
+ dest.writeByte((byte) 1);
+ mOverrideTint.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ }
+
+ public static final Creator<ControlState> CREATOR = new Creator<ControlState>() {
+ @Override
+ public ControlState createFromParcel(Parcel source) {
+ return new ControlState(source);
+ }
+
+ @Override
+ public ControlState[] newArray(int size) {
+ return new ControlState[size];
+ }
+ };
+
+ /**
+ * Builder class for {@link ControlState}.
+ *
+ * This class facilitates the creation of {@link ControlState}. It provides the following
+ * defaults for non-optional parameters:
+ * <ul>
+ * <li> Status: {@link ControlState#STATUS_OK}
+ * <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
+ * <li> Status text: {@code ""}
+ * </ul>
+ */
+ public static class Builder {
+ private @NonNull Control mControl;
+ private @Status int mStatus = STATUS_OK;
+ private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
+ private @NonNull CharSequence mStatusText = "";
+ private @Nullable Icon mOverrideIcon;
+ private @Nullable ColorStateList mOverrideTint;
+
+ /**
+ * @param control the {@link Control} that the resulting {@link ControlState} refers to.
+ */
+ public Builder(@NonNull Control control) {
+ Preconditions.checkNotNull(control);
+ mControl = control;
+ }
+
+ /**
+ * Creates a {@link Builder} using an existing {@link ControlState} as a base.
+ * @param controlState base for the builder.
+ */
+ public Builder(@NonNull ControlState controlState) {
+ Preconditions.checkNotNull(controlState);
+ mControl = controlState.mControl;
+ mControlTemplate = controlState.mControlTemplate;
+ mOverrideIcon = controlState.mOverrideIcon;
+ mStatusText = controlState.mStatusText;
+ mOverrideTint = controlState.mOverrideTint;
+ }
+
+
+ /**
+ * @param control the updated {@link Control} information.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setControl(@NonNull Control control) {
+ mControl = control;
+ return this;
+ }
+
+ /**
+ * @param status the current status of the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setStatus(@Status int status) {
+ mStatus = status;
+ return this;
+ }
+
+ /**
+ * @param controlTemplate the template to use when rendering the {@code Control}.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
+ Preconditions.checkNotNull(controlTemplate);
+ mControlTemplate = controlTemplate;
+ return this;
+ }
+
+ /**
+ * @param statusText the user-visible description of the status.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setStatusText(@NonNull CharSequence statusText) {
+ Preconditions.checkNotNull(statusText);
+ mStatusText = statusText;
+ return this;
+ }
+
+ /**
+ * @param overrideIcon the icon to override the one defined in the corresponding
+ * {@code Control}. Pass {@code null} to remove the override.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setOverrideIcon(@Nullable Icon overrideIcon) {
+ mOverrideIcon = overrideIcon;
+ return this;
+ }
+
+ /**
+ * @param overrideTint the colors to override the ones defined in the corresponding
+ * {@code Control}. Pass {@code null} to remove the override.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setOverrideTint(@Nullable ColorStateList overrideTint) {
+ mOverrideTint = overrideTint;
+ return this;
+ }
+
+ /**
+ * @return a new {@link ControlState}
+ */
+ public ControlState build() {
+ return new ControlState(mControl, mStatus, mControlTemplate, mStatusText,
+ mOverrideIcon, mOverrideTint);
+ }
+ }
+}
+
diff --git a/core/java/android/service/controls/ControlTemplate.aidl b/core/java/android/service/controls/ControlTemplate.aidl
new file mode 100644
index 000000000000..ecb948c8a306
--- /dev/null
+++ b/core/java/android/service/controls/ControlTemplate.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+parcelable ControlTemplate; \ No newline at end of file
diff --git a/core/java/android/service/controls/ControlTemplate.java b/core/java/android/service/controls/ControlTemplate.java
new file mode 100644
index 000000000000..e559862e86d6
--- /dev/null
+++ b/core/java/android/service/controls/ControlTemplate.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An abstract input template for a {@link Control}.
+ *
+ * Specifies what layout is presented to the user when a {@link ControlState} is assigned to a
+ * particular {@link Control}.
+ * <p>
+ * Some instances of {@link Control} can originate actions (via user interaction) to modify its
+ * associated state. The actions available to a given {@link Control} in a particular
+ * {@link ControlState} are determined by its {@link ControlTemplate}.
+ * @see ControlAction
+ * @hide
+ */
+public abstract class ControlTemplate implements Parcelable {
+
+ /**
+ * Singleton representing a {@link Control} with no input.
+ */
+ public static final ControlTemplate NO_TEMPLATE = new ControlTemplate("") {
+ @Override
+ public int getTemplateType() {
+ return TYPE_NONE;
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ TYPE_NONE,
+ TYPE_TOGGLE,
+ TYPE_RANGE,
+ TYPE_THUMBNAIL,
+ TYPE_DISCRETE_TOGGLE,
+ TYPE_COORD_RANGE
+ })
+ public @interface TemplateType {}
+
+ /**
+ * Type identifier of {@link ControlTemplate#NO_TEMPLATE}.
+ */
+ public static final int TYPE_NONE = 0;
+
+ /**
+ * Type identifier of {@link ToggleTemplate}.
+ */
+ public static final int TYPE_TOGGLE = 1;
+
+ /**
+ * Type identifier of {@link RangeTemplate}.
+ */
+ public static final int TYPE_RANGE = 2;
+
+ /**
+ * Type identifier of {@link ThumbnailTemplate}.
+ */
+ public static final int TYPE_THUMBNAIL = 3;
+
+ /**
+ * Type identifier of {@link DiscreteToggleTemplate}.
+ */
+ public static final int TYPE_DISCRETE_TOGGLE = 4;
+
+ /**
+ * @hide
+ */
+ public static final int TYPE_COORD_RANGE = 5;
+
+ private @NonNull final String mTemplateId;
+
+ /**
+ * @return the identifier for this object.
+ */
+ public String getTemplateId() {
+ return mTemplateId;
+ }
+
+ /**
+ * The {@link TemplateType} associated with this class.
+ */
+ public abstract @TemplateType int getTemplateType();
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(getTemplateType());
+ dest.writeString(mTemplateId);
+ }
+
+ private ControlTemplate() {
+ mTemplateId = "";
+ }
+
+ ControlTemplate(Parcel in) {
+ mTemplateId = in.readString();
+ }
+
+ /**
+ * @hide
+ */
+ ControlTemplate(@NonNull String templateId) {
+ Preconditions.checkNotNull(templateId);
+ 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);
+ }
+
+ @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 ToggleTemplate.CREATOR.createFromParcel(source);
+ case TYPE_RANGE:
+ return RangeTemplate.CREATOR.createFromParcel(source);
+ case TYPE_THUMBNAIL:
+ return ThumbnailTemplate.CREATOR.createFromParcel(source);
+ case TYPE_DISCRETE_TOGGLE:
+ return DiscreteToggleTemplate.CREATOR.createFromParcel(source);
+ case TYPE_NONE:
+ return NO_TEMPLATE;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/service/controls/DiscreteToggleTemplate.java b/core/java/android/service/controls/DiscreteToggleTemplate.java
new file mode 100644
index 000000000000..5167af41c2f0
--- /dev/null
+++ b/core/java/android/service/controls/DiscreteToggleTemplate.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A template for a {@link Control} with two discrete inputs.
+ *
+ * The two inputs represent a <i>Negative</i> input and a <i>Positive</i> input.
+ * <p>
+ * When one of the buttons is actioned, a {@link BooleanAction} will be sent.
+ * {@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 {
+
+ private final @NonNull ControlButton mNegativeButton;
+ private final @NonNull ControlButton mPositiveButton;
+
+ /**
+ * @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
+ */
+ public DiscreteToggleTemplate(@NonNull String templateId,
+ @NonNull ControlButton negativeButton,
+ @NonNull ControlButton positiveButton) {
+ super(templateId);
+ Preconditions.checkNotNull(negativeButton);
+ Preconditions.checkNotNull(positiveButton);
+ mNegativeButton = negativeButton;
+ mPositiveButton = positiveButton;
+ }
+
+ DiscreteToggleTemplate(Parcel in) {
+ super(in);
+ this.mNegativeButton = ControlButton.CREATOR.createFromParcel(in);
+ this.mPositiveButton = ControlButton.CREATOR.createFromParcel(in);
+ }
+
+ /**
+ * The {@link ControlButton} associated with the <i>Negative</i> action.
+ */
+ @NonNull
+ public ControlButton getNegativeButton() {
+ return mNegativeButton;
+ }
+
+ /**
+ * The {@link ControlButton} associated with the <i>Positive</i> action.
+ */
+ @NonNull
+ public ControlButton getPositiveButton() {
+ return mPositiveButton;
+ }
+
+ /**
+ * @return {@link ControlTemplate#TYPE_DISCRETE_TOGGLE}
+ */
+ @Override
+ public int getTemplateType() {
+ return TYPE_DISCRETE_TOGGLE;
+ }
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ mNegativeButton.writeToParcel(dest, flags);
+ mPositiveButton.writeToParcel(dest, flags);
+ }
+
+ public static final Creator<DiscreteToggleTemplate> CREATOR =
+ new Creator<DiscreteToggleTemplate>() {
+ @Override
+ public DiscreteToggleTemplate createFromParcel(Parcel source) {
+ return new DiscreteToggleTemplate(source);
+ }
+
+ @Override
+ public DiscreteToggleTemplate[] newArray(int size) {
+ return new DiscreteToggleTemplate[size];
+ }
+ };
+}
diff --git a/core/java/android/service/controls/FloatAction.aidl b/core/java/android/service/controls/FloatAction.aidl
new file mode 100644
index 000000000000..dbc0f726880c
--- /dev/null
+++ b/core/java/android/service/controls/FloatAction.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+parcelable FloatAction; \ No newline at end of file
diff --git a/core/java/android/service/controls/FloatAction.java b/core/java/android/service/controls/FloatAction.java
new file mode 100644
index 000000000000..fe6db10a98cd
--- /dev/null
+++ b/core/java/android/service/controls/FloatAction.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+/**
+ * Action sent by a {@link RangeTemplate}.
+ * @hide
+ */
+public final class FloatAction extends ControlAction {
+
+ private final float mNewValue;
+
+ /**
+ * @param templateId the identifier of the {@link RangeTemplate} that produced this action.
+ * @param newValue new value for the state displayed by the {@link RangeTemplate}.
+ */
+ public FloatAction(@NonNull String templateId, float newValue) {
+ this(templateId, newValue, null);
+ }
+
+ /**
+ * @param templateId the identifier of the {@link RangeTemplate} that originated this action.
+ * @param newValue new value for the state of the {@link RangeTemplate}.
+ * @param challengeValue a value sent by the user along with the action to authenticate. {@code}
+ * null is sent when no authentication is needed or has not been
+ * requested.
+ */
+
+ public FloatAction(@NonNull String templateId, float newValue,
+ @Nullable String challengeValue) {
+ super(templateId, challengeValue);
+ mNewValue = newValue;
+ }
+
+ public FloatAction(Parcel in) {
+ super(in);
+ mNewValue = in.readFloat();
+ }
+
+ /**
+ * The new value set for the range in the corresponding {@link RangeTemplate}.
+ */
+ public float getNewValue() {
+ return mNewValue;
+ }
+
+ /**
+ * @return {@link ControlAction#TYPE_FLOAT}
+ */
+ @Override
+ public int getActionType() {
+ return TYPE_FLOAT;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeFloat(mNewValue);
+ }
+
+ public static final @NonNull Creator<FloatAction> CREATOR = new Creator<FloatAction>() {
+ @Override
+ public FloatAction createFromParcel(Parcel source) {
+ return new FloatAction(source);
+ }
+
+ @Override
+ public FloatAction[] newArray(int size) {
+ return new FloatAction[size];
+ }
+ };
+}
diff --git a/core/java/android/service/controls/IControlsProvider.aidl b/core/java/android/service/controls/IControlsProvider.aidl
new file mode 100644
index 000000000000..f778653eb3d3
--- /dev/null
+++ b/core/java/android/service/controls/IControlsProvider.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.service.controls.ControlAction;
+
+/** @hide */
+oneway interface IControlsProvider {
+ void load();
+
+ void subscribe(in List<String> controlIds);
+
+ void unsubscribe();
+
+ void onAction(in String controlId, in ControlAction action);
+} \ No newline at end of file
diff --git a/core/java/android/service/controls/IControlsProviderCallback.aidl b/core/java/android/service/controls/IControlsProviderCallback.aidl
new file mode 100644
index 000000000000..3dbb68c1c7f0
--- /dev/null
+++ b/core/java/android/service/controls/IControlsProviderCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.service.controls.Control;
+import android.service.controls.ControlState;
+
+/** @hide */
+oneway interface IControlsProviderCallback {
+ void onLoad(in List<Control> controls);
+
+ void onRefreshState(in List<ControlState> controlStates);
+
+ void onControlActionResponse(in String controlId, int response);
+} \ No newline at end of file
diff --git a/core/java/android/service/controls/RangeTemplate.aidl b/core/java/android/service/controls/RangeTemplate.aidl
new file mode 100644
index 000000000000..a3d1ca074276
--- /dev/null
+++ b/core/java/android/service/controls/RangeTemplate.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+parcelable RangeTemplate; \ No newline at end of file
diff --git a/core/java/android/service/controls/RangeTemplate.java b/core/java/android/service/controls/RangeTemplate.java
new file mode 100644
index 000000000000..70bf2dd4aad4
--- /dev/null
+++ b/core/java/android/service/controls/RangeTemplate.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+import java.security.InvalidParameterException;
+
+/**
+ * A template for a {@link Control} with inputs in a "continuous" range of values.
+ *
+ * @see FloatAction
+ * @hide
+ */
+public final class RangeTemplate extends ControlTemplate {
+
+ private final float mMinValue;
+ private final float mMaxValue;
+ private final float mCurrentValue;
+ private final float mStepValue;
+ private final @NonNull CharSequence mFormatString;
+
+ /**
+ * Construct a new {@link RangeTemplate}.
+ *
+ * The range must be valid, meaning:
+ * <ul>
+ * <li> {@code minValue} < {@code maxValue}
+ * <li> {@code minValue} < {@code currentValue}
+ * <li> {@code currentValue} < {@code maxValue}
+ * <li> 0 < {@code stepValue}
+ * </ul>
+ * <p>
+ * The current value of the Control will be formatted accordingly.
+ *
+ * @param templateId the identifier for this template object
+ * @param minValue minimum value for the input
+ * @param maxValue maximum value for the input
+ * @param currentValue the current value of the {@link ControlState} containing this object.
+ * @param stepValue minimum value of increments/decrements when interacting with this control.
+ * @param formatString a formatting string as per {@link String#format} used to display the
+ * {@code currentValue}. If {@code null} is passed, the "%.1f" is used.
+ * @throws InvalidParameterException if the parameters passed do not make a valid range.
+ */
+ public RangeTemplate(@NonNull String templateId,
+ float minValue,
+ float maxValue,
+ float currentValue,
+ float stepValue,
+ @Nullable CharSequence formatString) {
+ super(templateId);
+ Preconditions.checkNotNull(formatString);
+ mMinValue = minValue;
+ mMaxValue = maxValue;
+ mCurrentValue = currentValue;
+ mStepValue = stepValue;
+ if (formatString != null) {
+ mFormatString = formatString;
+ } else {
+ mFormatString = "%.1f";
+ }
+ validate();
+ }
+
+ /**
+ * Construct a new {@link RangeTemplate} from a {@link Parcel}.
+ *
+ * @throws InvalidParameterException if the parameters passed do not make a valid range
+ * @see RangeTemplate#RangeTemplate(String, float, float, float, float, CharSequence)
+ * @hide
+ */
+ RangeTemplate(Parcel in) {
+ super(in);
+ mMinValue = in.readFloat();
+ mMaxValue = in.readFloat();
+ mCurrentValue = in.readFloat();
+ mStepValue = in.readFloat();
+ mFormatString = in.readCharSequence();
+ validate();
+ }
+
+ /**
+ * The minimum value for this range.
+ */
+ public float getMinValue() {
+ return mMinValue;
+ }
+
+ /**
+ * The maximum value for this range.
+ */
+ public float getMaxValue() {
+ return mMaxValue;
+ }
+
+ /**
+ * The current value for this range.
+ */
+ public float getCurrentValue() {
+ return mCurrentValue;
+ }
+
+ /**
+ * The value of the smallest increment or decrement that can be performed on this range.
+ */
+ public float getStepValue() {
+ return mStepValue;
+ }
+
+ /**
+ * Formatter for generating a user visible {@link String} representing the value
+ * returned by {@link RangeTemplate#getCurrentValue}.
+ * @return a formatting string as specified in {@link String#format}
+ */
+ @NonNull
+ public CharSequence getFormatString() {
+ return mFormatString;
+ }
+
+ /**
+ * @return {@link ControlTemplate#TYPE_RANGE}
+ */
+ @Override
+ public int getTemplateType() {
+ return TYPE_RANGE;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeFloat(mMinValue);
+ dest.writeFloat(mMaxValue);
+ dest.writeFloat(mCurrentValue);
+ dest.writeFloat(mStepValue);
+ dest.writeCharSequence(mFormatString);
+ }
+
+ /**
+ * Validate constructor parameters
+ *
+ * @throws InvalidParameterException if the parameters passed do not make a valid range
+ */
+ private void validate() {
+ if (Float.compare(mMinValue, mMaxValue) > 0) {
+ throw new InvalidParameterException(
+ String.format("minValue=%f > maxValue=%f", mMinValue, mMaxValue));
+ }
+ if (Float.compare(mMinValue, mCurrentValue) > 0) {
+ throw new InvalidParameterException(
+ String.format("minValue=%f > currentValue=%f", mMinValue, mCurrentValue));
+ }
+ if (Float.compare(mCurrentValue, mMaxValue) > 0) {
+ throw new InvalidParameterException(
+ String.format("currentValue=%f > maxValue=%f", mCurrentValue, mMaxValue));
+ }
+ if (mStepValue <= 0) {
+ throw new InvalidParameterException(String.format("stepValue=%f <= 0", mStepValue));
+ }
+ }
+
+ public static final Creator<RangeTemplate> CREATOR = new Creator<RangeTemplate>() {
+ @Override
+ public RangeTemplate createFromParcel(Parcel source) {
+ return new RangeTemplate(source);
+ }
+
+ @Override
+ public RangeTemplate[] newArray(int size) {
+ return new RangeTemplate[size];
+ }
+ };
+}
diff --git a/core/java/android/service/controls/ThumbnailTemplate.aidl b/core/java/android/service/controls/ThumbnailTemplate.aidl
new file mode 100644
index 000000000000..fe8c7fed7c89
--- /dev/null
+++ b/core/java/android/service/controls/ThumbnailTemplate.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+parcelable ThumbnailTemplate; \ No newline at end of file
diff --git a/core/java/android/service/controls/ThumbnailTemplate.java b/core/java/android/service/controls/ThumbnailTemplate.java
new file mode 100644
index 000000000000..796d2de89576
--- /dev/null
+++ b/core/java/android/service/controls/ThumbnailTemplate.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.NonNull;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A template for a {@link Control} that displays an image.
+ * @hide
+ */
+public final class ThumbnailTemplate extends ControlTemplate {
+
+ private final @NonNull Icon mThumbnail;
+ private final @NonNull CharSequence mContentDescription;
+
+ /**
+ * @param templateId the identifier for this template object
+ * @param thumbnail an image to display on the {@link Control}
+ * @param contentDescription a description of the image for accessibility.
+ */
+ public ThumbnailTemplate(@NonNull String templateId, @NonNull Icon thumbnail,
+ @NonNull CharSequence contentDescription) {
+ super(templateId);
+ Preconditions.checkNotNull(thumbnail);
+ Preconditions.checkNotNull(contentDescription);
+ mThumbnail = thumbnail;
+ mContentDescription = contentDescription;
+ }
+
+ ThumbnailTemplate(Parcel in) {
+ super(in);
+ mThumbnail = Icon.CREATOR.createFromParcel(in);
+ mContentDescription = in.readCharSequence();
+ }
+
+ /**
+ * The {@link Icon} (image) displayed by this template.
+ */
+ @NonNull
+ public Icon getThumbnail() {
+ return mThumbnail;
+ }
+
+ /**
+ * The description of the image returned by {@link ThumbnailTemplate#getThumbnail()}
+ */
+ @NonNull
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
+ * @return {@link ControlTemplate#TYPE_THUMBNAIL}
+ */
+ @Override
+ public int getTemplateType() {
+ return TYPE_THUMBNAIL;
+ }
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ mThumbnail.writeToParcel(dest, flags);
+ dest.writeCharSequence(mContentDescription);
+ }
+
+ public static final Creator<ThumbnailTemplate> CREATOR = new Creator<ThumbnailTemplate>() {
+ @Override
+ public ThumbnailTemplate createFromParcel(Parcel source) {
+ return new ThumbnailTemplate(source);
+ }
+
+ @Override
+ public ThumbnailTemplate[] newArray(int size) {
+ return new ThumbnailTemplate[size];
+ }
+ };
+}
diff --git a/core/java/android/service/controls/ToggleTemplate.aidl b/core/java/android/service/controls/ToggleTemplate.aidl
new file mode 100644
index 000000000000..1c823d9aee6d
--- /dev/null
+++ b/core/java/android/service/controls/ToggleTemplate.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+parcelable ToggleTemplate; \ No newline at end of file
diff --git a/core/java/android/service/controls/ToggleTemplate.java b/core/java/android/service/controls/ToggleTemplate.java
new file mode 100644
index 000000000000..3766bd168477
--- /dev/null
+++ b/core/java/android/service/controls/ToggleTemplate.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A template for a {@link Control} with a single button that can be toggled between two states.
+ *
+ * The states for the toggle correspond to the states in {@link ControlButton#isActive()}.
+ * An action on this template will originate a {@link BooleanAction} to change that state.
+ *
+ * @see BooleanAction
+ * @hide
+ */
+public final class ToggleTemplate extends ControlTemplate {
+
+ private final @NonNull ControlButton mButton;
+
+ /**
+ * @param templateId the identifier for this template object
+ * @param button a {@ControlButton} that can show the current state and toggle it
+ */
+ public ToggleTemplate(@NonNull String templateId, @NonNull ControlButton button) {
+ super(templateId);
+ Preconditions.checkNotNull(button);
+ mButton = button;
+ }
+
+ ToggleTemplate(Parcel in) {
+ super(in);
+ mButton = ControlButton.CREATOR.createFromParcel(in);
+ }
+
+ /**
+ * The button provided to this object in {@link ToggleTemplate#ToggleTemplate}
+ */
+ @NonNull
+ public ControlButton getButton() {
+ return mButton;
+ }
+
+ /**
+ * @return {@link ControlTemplate#TYPE_TOGGLE}
+ */
+ @Override
+ public int getTemplateType() {
+ return TYPE_TOGGLE;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ mButton.writeToParcel(dest, flags);
+ }
+
+ public static final Creator<ToggleTemplate> CREATOR = new Creator<ToggleTemplate>() {
+ @Override
+ public ToggleTemplate createFromParcel(Parcel source) {
+ return new ToggleTemplate(source);
+ }
+
+ @Override
+ public ToggleTemplate[] newArray(int size) {
+ return new ToggleTemplate[size];
+ }
+ };
+
+}
diff --git a/core/java/android/service/notification/IConditionProvider.aidl b/core/java/android/service/notification/IConditionProvider.aidl
index 3f3c6b80286d..dd3904fc28ce 100644
--- a/core/java/android/service/notification/IConditionProvider.aidl
+++ b/core/java/android/service/notification/IConditionProvider.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2014, The Android Open Source Project
+ * 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.
diff --git a/core/tests/coretests/src/android/service/controls/ControlActionTest.java b/core/tests/coretests/src/android/service/controls/ControlActionTest.java
new file mode 100644
index 000000000000..ef4912fe1f74
--- /dev/null
+++ b/core/tests/coretests/src/android/service/controls/ControlActionTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ControlActionTest {
+
+ private static final String TEST_ID = "TEST_ID";
+
+ @Test
+ public void testUnparcelingCorrectClass_boolean() {
+ ControlAction toParcel = new BooleanAction(TEST_ID, true);
+
+ ControlAction fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlAction.TYPE_BOOLEAN, fromParcel.getActionType());
+ assertTrue(fromParcel instanceof BooleanAction);
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_float() {
+ ControlAction toParcel = new FloatAction(TEST_ID, 1);
+
+ ControlAction fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlAction.TYPE_FLOAT, fromParcel.getActionType());
+ assertTrue(fromParcel instanceof FloatAction);
+ }
+
+ private ControlAction parcelAndUnparcel(ControlAction toParcel) {
+ Parcel parcel = Parcel.obtain();
+
+ assertNotNull(parcel);
+
+ parcel.setDataPosition(0);
+ toParcel.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ return ControlAction.CREATOR.createFromParcel(parcel);
+ }
+}
diff --git a/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java b/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java
new file mode 100644
index 000000000000..4fa4e1d7b733
--- /dev/null
+++ b/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.DrawableRes;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.security.InvalidParameterException;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ControlTemplateTest {
+
+ private static final String PACKAGE_NAME = "com.android.frameworks.coretests";
+ private static final @DrawableRes int TEST_ICON_ID = R.drawable.box;
+ private static final String TEST_ID = "TEST_ID";
+ private static final CharSequence TEST_CONTENT_DESCRIPTION = "TEST_CONTENT_DESCRIPTION";
+ private Icon mIcon;
+ private ControlButton mControlButton;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mIcon = Icon.createWithResource(PACKAGE_NAME, TEST_ICON_ID);
+ mControlButton = new ControlButton(true, mIcon, TEST_CONTENT_DESCRIPTION);
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_none() {
+ ControlTemplate toParcel = ControlTemplate.NO_TEMPLATE;
+
+ ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlTemplate.NO_TEMPLATE, fromParcel);
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_toggle() {
+ ControlTemplate toParcel = new ToggleTemplate(TEST_ID, mControlButton);
+
+ ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlTemplate.TYPE_TOGGLE, fromParcel.getTemplateType());
+ assertTrue(fromParcel instanceof ToggleTemplate);
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_range() {
+ ControlTemplate toParcel = new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f");
+
+ ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlTemplate.TYPE_RANGE, fromParcel.getTemplateType());
+ assertTrue(fromParcel instanceof RangeTemplate);
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testRangeParameters_minMax() {
+ RangeTemplate range = new RangeTemplate(TEST_ID, 2, 0, 1, 1, "%f");
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testRangeParameters_minCurrent() {
+ RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, -1, 1, "%f");
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testRangeParameters_maxCurrent() {
+ RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, 3, 1, "%f");
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testRangeParameters_negativeStep() {
+ RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, 1, -1, "%f");
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_thumbnail() {
+ ControlTemplate toParcel = new ThumbnailTemplate(TEST_ID, mIcon, TEST_CONTENT_DESCRIPTION);
+
+ ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlTemplate.TYPE_THUMBNAIL, fromParcel.getTemplateType());
+ assertTrue(fromParcel instanceof ThumbnailTemplate);
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_discreteToggle() {
+ ControlTemplate toParcel =
+ new DiscreteToggleTemplate(TEST_ID, mControlButton, mControlButton);
+
+ ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlTemplate.TYPE_DISCRETE_TOGGLE, fromParcel.getTemplateType());
+ assertTrue(fromParcel instanceof DiscreteToggleTemplate);
+ }
+
+ private ControlTemplate parcelAndUnparcel(ControlTemplate toParcel) {
+ Parcel parcel = Parcel.obtain();
+
+ assertNotNull(parcel);
+
+ parcel.setDataPosition(0);
+ toParcel.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ return ControlTemplate.CREATOR.createFromParcel(parcel);
+ }
+}