diff options
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); + } +} |