1 /*
2  * Copyright 2024 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 
17 package androidx.camera.core.impl;
18 
19 import static java.util.Collections.emptySet;
20 import static java.util.Collections.unmodifiableSet;
21 
22 import org.jspecify.annotations.NonNull;
23 import org.jspecify.annotations.Nullable;
24 
25 import java.util.HashSet;
26 import java.util.Objects;
27 import java.util.Set;
28 
29 /**
30  * Manages device/camera-specific quirks (workarounds or customizations) for CameraX.
31  *
32  * <p>{@link Quirk}s are used to handle variations in device behavior or capabilities that may
33  * affect CameraX functionality. This class allows for fine-grained control over which quirks are
34  * enabled or disabled.
35  *
36  * <p>Key Features
37  * <ul>
38  *   <li>Default Behavior: Configure whether quirks should be enabled by default if the
39  *       device natively exhibits them.</li>
40  *   <li>Force Enable/Disable: Explicitly force specific quirks to be enabled or disabled,
41  *       overriding the default behavior.</li>
42  * </ul>
43  */
44 public class QuirkSettings {
45 
46     private final boolean mEnabledWhenDeviceHasQuirk;
47     private final Set<Class<? extends Quirk>> mForceEnabledQuirks;
48     private final Set<Class<? extends Quirk>> mForceDisabledQuirks;
49 
50     /**
51      * Private constructor for building `QuirkSettings` instances.
52      *
53      * @param enabledWhenDeviceHasQuirk Whether to enable quirks if the device natively exhibits
54      *                                  the quirk.
55      * @param forceEnabledQuirks        The set of quirks to be force-enabled.
56      * @param forceDisabledQuirks       The set of quirks to be force-disabled.
57      */
QuirkSettings(boolean enabledWhenDeviceHasQuirk, @Nullable Set<Class<? extends Quirk>> forceEnabledQuirks, @Nullable Set<Class<? extends Quirk>> forceDisabledQuirks)58     private QuirkSettings(boolean enabledWhenDeviceHasQuirk,
59             @Nullable Set<Class<? extends Quirk>> forceEnabledQuirks,
60             @Nullable Set<Class<? extends Quirk>> forceDisabledQuirks) {
61         mEnabledWhenDeviceHasQuirk = enabledWhenDeviceHasQuirk;
62         mForceEnabledQuirks = forceEnabledQuirks == null ? emptySet() : new HashSet<>(
63                 forceEnabledQuirks);
64         mForceDisabledQuirks = forceDisabledQuirks == null ? emptySet() : new HashSet<>(
65                 forceDisabledQuirks);
66     }
67 
68     /**
69      * Creates a QuirkSettings instance with default behavior, enabling all quirks if the device
70      * natively exhibits the quirk.
71      *
72      * @return A QuirkSettings instance with default behavior, enabling all quirks if the device
73      * natively exhibits the quirk.
74      */
withDefaultBehavior()75     public static @NonNull QuirkSettings withDefaultBehavior() {
76         return new QuirkSettings.Builder().setEnabledWhenDeviceHasQuirk(true).build();
77     }
78 
79     /**
80      * Creates a QuirkSettings instance with all quirks disabled.
81      *
82      * @return A QuirkSettings instance with all quirks disabled.
83      */
withAllQuirksDisabled()84     public static @NonNull QuirkSettings withAllQuirksDisabled() {
85         return new QuirkSettings.Builder().setEnabledWhenDeviceHasQuirk(false).build();
86     }
87 
88     /**
89      * Creates a QuirkSettings instance with specific quirks force-enabled.
90      *
91      * @param quirks The quirks to force-enable.
92      * @return A new QuirkSettings instance with the specified quirks force-enabled.
93      */
withQuirksForceEnabled( @onNull Set<Class<? extends Quirk>> quirks)94     public static @NonNull QuirkSettings withQuirksForceEnabled(
95             @NonNull Set<Class<? extends Quirk>> quirks) {
96         return new QuirkSettings.Builder().forceEnableQuirks(quirks).build();
97     }
98 
99     /**
100      * Creates a QuirkSettings instance with specific quirks force-disabled.
101      *
102      * @param quirks The quirks to force-disable.
103      * @return A new QuirkSettings instance with the specified quirks force-disabled.
104      */
withQuirksForceDisabled( @onNull Set<Class<? extends Quirk>> quirks)105     public static @NonNull QuirkSettings withQuirksForceDisabled(
106             @NonNull Set<Class<? extends Quirk>> quirks) {
107         return new QuirkSettings.Builder().forceDisableQuirks(quirks).build();
108     }
109 
110     /**
111      * Gets whether quirks should be enabled if the device natively exhibits the quirk.
112      *
113      * @return {@code true} if quirks should be enabled, {@code false} otherwise.
114      */
isEnabledWhenDeviceHasQuirk()115     public boolean isEnabledWhenDeviceHasQuirk() {
116         return mEnabledWhenDeviceHasQuirk;
117     }
118 
119     /**
120      * Gets the set of quirks that are force-enabled, regardless of device behavior.
121      *
122      * @return An unmodifiable set containing the names of force-enabled quirks.
123      */
getForceEnabledQuirks()124     public @NonNull Set<Class<? extends Quirk>> getForceEnabledQuirks() {
125         return unmodifiableSet(mForceEnabledQuirks);
126     }
127 
128     /**
129      * Gets the set of quirks that are force-disabled, regardless of device behavior.
130      *
131      * @return An unmodifiable set containing the names of force-disabled quirks.
132      */
getForceDisabledQuirks()133     public @NonNull Set<Class<? extends Quirk>> getForceDisabledQuirks() {
134         return unmodifiableSet(mForceDisabledQuirks);
135     }
136 
137     /**
138      * Determines whether a specific quirk should be enabled based on these settings and whether
139      * the device natively exhibits the quirk.
140      *
141      * <p>If a quirk is in both the force-enabled and force-disabled sets, it will be enabled.
142      *
143      * @param quirk          The quirk class to check.
144      * @param deviceHasQuirk Whether the device natively exhibits the quirk.
145      * @return true if the quirk should be enabled, false otherwise.
146      */
shouldEnableQuirk(@onNull Class<? extends Quirk> quirk, boolean deviceHasQuirk)147     public boolean shouldEnableQuirk(@NonNull Class<? extends Quirk> quirk,
148             boolean deviceHasQuirk) {
149         if (mForceEnabledQuirks.contains(quirk)) {
150             return true;
151         } else if (mForceDisabledQuirks.contains(quirk)) {
152             return false;
153         } else {
154             return mEnabledWhenDeviceHasQuirk && deviceHasQuirk;
155         }
156     }
157 
158     @Override
equals(@ullable Object obj)159     public boolean equals(@Nullable Object obj) {
160         if (!(obj instanceof QuirkSettings)) {
161             return false;
162         }
163         if (this == obj) {
164             return true;
165         }
166         QuirkSettings other = (QuirkSettings) obj;
167         return mEnabledWhenDeviceHasQuirk == other.mEnabledWhenDeviceHasQuirk
168                 && Objects.equals(mForceEnabledQuirks, other.mForceEnabledQuirks)
169                 && Objects.equals(mForceDisabledQuirks, other.mForceDisabledQuirks);
170     }
171 
172     @Override
hashCode()173     public int hashCode() {
174         return Objects.hash(mEnabledWhenDeviceHasQuirk, mForceEnabledQuirks, mForceDisabledQuirks);
175     }
176 
177     @Override
toString()178     public @NonNull String toString() {
179         return "QuirkSettings{"
180                 + "enabledWhenDeviceHasQuirk=" + mEnabledWhenDeviceHasQuirk
181                 + ", forceEnabledQuirks=" + mForceEnabledQuirks
182                 + ", forceDisabledQuirks=" + mForceDisabledQuirks
183                 + '}';
184     }
185 
186     /**
187      * Builder class for constructing {@link QuirkSettings} instances.
188      */
189     public static class Builder {
190         private boolean mEnabledWhenDeviceHasQuirk = true;
191         private Set<Class<? extends Quirk>> mForceEnabledQuirks;
192         private Set<Class<? extends Quirk>> mForceDisabledQuirks;
193 
194         /**
195          * Sets whether to enable quirks if the device natively exhibits the quirk.
196          */
setEnabledWhenDeviceHasQuirk(boolean enabled)197         public @NonNull Builder setEnabledWhenDeviceHasQuirk(boolean enabled) {
198             mEnabledWhenDeviceHasQuirk = enabled;
199             return this;
200         }
201 
202         /**
203          * Forces the specified quirks to be enabled, regardless of other settings.
204          */
forceEnableQuirks(@onNull Set<Class<? extends Quirk>> quirks)205         public @NonNull Builder forceEnableQuirks(@NonNull Set<Class<? extends Quirk>> quirks) {
206             mForceEnabledQuirks = new HashSet<>(quirks);
207             return this;
208         }
209 
210         /**
211          * Forces the specified quirks to be disabled, regardless of other settings.
212          */
forceDisableQuirks(@onNull Set<Class<? extends Quirk>> quirks)213         public @NonNull Builder forceDisableQuirks(@NonNull Set<Class<? extends Quirk>> quirks) {
214             mForceDisabledQuirks = new HashSet<>(quirks);
215             return this;
216         }
217 
218         /**
219          * Builds a new {@link QuirkSettings} instance with the configured options.
220          *
221          * @return A new `QuirkSettings` instance.
222          */
build()223         public @NonNull QuirkSettings build() {
224             return new QuirkSettings(mEnabledWhenDeviceHasQuirk, mForceEnabledQuirks,
225                     mForceDisabledQuirks);
226         }
227     }
228 }
229