|  | /* | 
|  | * Copyright (C) 2017 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.companion; | 
|  |  | 
|  | import android.annotation.NonNull; | 
|  | import android.annotation.Nullable; | 
|  | import android.compat.annotation.UnsupportedAppUsage; | 
|  | import android.os.Parcel; | 
|  | import android.os.Parcelable; | 
|  | import android.provider.OneTimeUseBuilder; | 
|  |  | 
|  | import com.android.internal.util.ArrayUtils; | 
|  | import com.android.internal.util.CollectionUtils; | 
|  |  | 
|  | import java.util.ArrayList; | 
|  | import java.util.List; | 
|  | import java.util.Objects; | 
|  |  | 
|  | /** | 
|  | * A request for the user to select a companion device to associate with. | 
|  | * | 
|  | * You can optionally set {@link Builder#addDeviceFilter filters} for which devices to show to the | 
|  | * user to select from. | 
|  | * The exact type and fields of the filter you can set depend on the | 
|  | * medium type. See {@link Builder}'s static factory methods for specific protocols that are | 
|  | * supported. | 
|  | * | 
|  | * You can also set {@link Builder#setSingleDevice single device} to request a popup with single | 
|  | * device to be shown instead of a list to choose from | 
|  | */ | 
|  | public final class AssociationRequest implements Parcelable { | 
|  |  | 
|  | private final boolean mSingleDevice; | 
|  | private final List<DeviceFilter<?>> mDeviceFilters; | 
|  |  | 
|  | private AssociationRequest( | 
|  | boolean singleDevice, @Nullable List<DeviceFilter<?>> deviceFilters) { | 
|  | this.mSingleDevice = singleDevice; | 
|  | this.mDeviceFilters = CollectionUtils.emptyIfNull(deviceFilters); | 
|  | } | 
|  |  | 
|  | private AssociationRequest(Parcel in) { | 
|  | this( | 
|  | in.readByte() != 0, | 
|  | in.readParcelableList(new ArrayList<>(), AssociationRequest.class.getClassLoader())); | 
|  | } | 
|  |  | 
|  | /** @hide */ | 
|  | @UnsupportedAppUsage | 
|  | public boolean isSingleDevice() { | 
|  | return mSingleDevice; | 
|  | } | 
|  |  | 
|  | /** @hide */ | 
|  | @NonNull | 
|  | @UnsupportedAppUsage | 
|  | public List<DeviceFilter<?>> getDeviceFilters() { | 
|  | return mDeviceFilters; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object o) { | 
|  | if (this == o) return true; | 
|  | if (o == null || getClass() != o.getClass()) return false; | 
|  | AssociationRequest that = (AssociationRequest) o; | 
|  | return mSingleDevice == that.mSingleDevice && | 
|  | Objects.equals(mDeviceFilters, that.mDeviceFilters); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return Objects.hash(mSingleDevice, mDeviceFilters); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "AssociationRequest{" + | 
|  | "mSingleDevice=" + mSingleDevice + | 
|  | ", mDeviceFilters=" + mDeviceFilters + | 
|  | '}'; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeToParcel(Parcel dest, int flags) { | 
|  | dest.writeByte((byte) (mSingleDevice ? 1 : 0)); | 
|  | dest.writeParcelableList(mDeviceFilters, flags); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int describeContents() { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | public static final @android.annotation.NonNull Creator<AssociationRequest> CREATOR = new Creator<AssociationRequest>() { | 
|  | @Override | 
|  | public AssociationRequest createFromParcel(Parcel in) { | 
|  | return new AssociationRequest(in); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AssociationRequest[] newArray(int size) { | 
|  | return new AssociationRequest[size]; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * A builder for {@link AssociationRequest} | 
|  | */ | 
|  | public static final class Builder extends OneTimeUseBuilder<AssociationRequest> { | 
|  | private boolean mSingleDevice = false; | 
|  | @Nullable private ArrayList<DeviceFilter<?>> mDeviceFilters = null; | 
|  |  | 
|  | public Builder() {} | 
|  |  | 
|  | /** | 
|  | * Whether only a single device should match the provided filter. | 
|  | * | 
|  | * When scanning for a single device with a specifc {@link BluetoothDeviceFilter} mac | 
|  | * address, bonded devices are also searched among. This allows to obtain the necessary app | 
|  | * privileges even if the device is already paired. | 
|  | * | 
|  | * @param singleDevice if true, scanning for a device will stop as soon as at least one | 
|  | *                     fitting device is found | 
|  | */ | 
|  | @NonNull | 
|  | public Builder setSingleDevice(boolean singleDevice) { | 
|  | checkNotUsed(); | 
|  | this.mSingleDevice = singleDevice; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param deviceFilter if set, only devices matching the given filter will be shown to the | 
|  | *                     user | 
|  | */ | 
|  | @NonNull | 
|  | public Builder addDeviceFilter(@Nullable DeviceFilter<?> deviceFilter) { | 
|  | checkNotUsed(); | 
|  | if (deviceFilter != null) { | 
|  | mDeviceFilters = ArrayUtils.add(mDeviceFilters, deviceFilter); | 
|  | } | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /** @inheritDoc */ | 
|  | @NonNull | 
|  | @Override | 
|  | public AssociationRequest build() { | 
|  | markUsed(); | 
|  | return new AssociationRequest(mSingleDevice, mDeviceFilters); | 
|  | } | 
|  | } | 
|  | } |