1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.companion;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.os.Parcel;
22import android.os.Parcelable;
23import android.provider.OneTimeUseBuilder;
24
25import com.android.internal.util.ArrayUtils;
26import com.android.internal.util.CollectionUtils;
27
28import java.util.ArrayList;
29import java.util.List;
30import java.util.Objects;
31
32/**
33 * A request for the user to select a companion device to associate with.
34 *
35 * You can optionally set {@link Builder#addDeviceFilter filters} for which devices to show to the
36 * user to select from.
37 * The exact type and fields of the filter you can set depend on the
38 * medium type. See {@link Builder}'s static factory methods for specific protocols that are
39 * supported.
40 *
41 * You can also set {@link Builder#setSingleDevice single device} to request a popup with single
42 * device to be shown instead of a list to choose from
43 */
44public final class AssociationRequest implements Parcelable {
45
46    private final boolean mSingleDevice;
47    private final List<DeviceFilter<?>> mDeviceFilters;
48
49    private AssociationRequest(
50            boolean singleDevice, @Nullable List<DeviceFilter<?>> deviceFilters) {
51        this.mSingleDevice = singleDevice;
52        this.mDeviceFilters = CollectionUtils.emptyIfNull(deviceFilters);
53    }
54
55    private AssociationRequest(Parcel in) {
56        this(
57            in.readByte() != 0,
58            in.readParcelableList(new ArrayList<>(), AssociationRequest.class.getClassLoader()));
59    }
60
61    /** @hide */
62    public boolean isSingleDevice() {
63        return mSingleDevice;
64    }
65
66    /** @hide */
67    @NonNull
68    public List<DeviceFilter<?>> getDeviceFilters() {
69        return mDeviceFilters;
70    }
71
72    @Override
73    public boolean equals(Object o) {
74        if (this == o) return true;
75        if (o == null || getClass() != o.getClass()) return false;
76        AssociationRequest that = (AssociationRequest) o;
77        return mSingleDevice == that.mSingleDevice &&
78                Objects.equals(mDeviceFilters, that.mDeviceFilters);
79    }
80
81    @Override
82    public int hashCode() {
83        return Objects.hash(mSingleDevice, mDeviceFilters);
84    }
85
86    @Override
87    public String toString() {
88        return "AssociationRequest{" +
89                "mSingleDevice=" + mSingleDevice +
90                ", mDeviceFilters=" + mDeviceFilters +
91                '}';
92    }
93
94    @Override
95    public void writeToParcel(Parcel dest, int flags) {
96        dest.writeByte((byte) (mSingleDevice ? 1 : 0));
97        dest.writeParcelableList(mDeviceFilters, flags);
98    }
99
100    @Override
101    public int describeContents() {
102        return 0;
103    }
104
105    public static final Creator<AssociationRequest> CREATOR = new Creator<AssociationRequest>() {
106        @Override
107        public AssociationRequest createFromParcel(Parcel in) {
108            return new AssociationRequest(in);
109        }
110
111        @Override
112        public AssociationRequest[] newArray(int size) {
113            return new AssociationRequest[size];
114        }
115    };
116
117    /**
118     * A builder for {@link AssociationRequest}
119     */
120    public static final class Builder extends OneTimeUseBuilder<AssociationRequest> {
121        private boolean mSingleDevice = false;
122        @Nullable private ArrayList<DeviceFilter<?>> mDeviceFilters = null;
123
124        public Builder() {}
125
126        /**
127         * @param singleDevice if true, scanning for a device will stop as soon as at least one
128         *                     fitting device is found
129         */
130        @NonNull
131        public Builder setSingleDevice(boolean singleDevice) {
132            checkNotUsed();
133            this.mSingleDevice = singleDevice;
134            return this;
135        }
136
137        /**
138         * @param deviceFilter if set, only devices matching the given filter will be shown to the
139         *                     user
140         */
141        @NonNull
142        public Builder addDeviceFilter(@Nullable DeviceFilter<?> deviceFilter) {
143            checkNotUsed();
144            if (deviceFilter != null) {
145                mDeviceFilters = ArrayUtils.add(mDeviceFilters, deviceFilter);
146            }
147            return this;
148        }
149
150        /** @inheritDoc */
151        @NonNull
152        @Override
153        public AssociationRequest build() {
154            markUsed();
155            return new AssociationRequest(mSingleDevice, mDeviceFilters);
156        }
157    }
158}
159