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 androidx.annotation.VisibleForTesting;
20 import androidx.camera.core.Logger;
21 import androidx.core.util.Consumer;
22 
23 import org.jspecify.annotations.NonNull;
24 import org.jspecify.annotations.Nullable;
25 
26 import java.util.concurrent.ExecutionException;
27 import java.util.concurrent.Executor;
28 
29 /**
30  * Singleton holder for managing global {@link QuirkSettings} configuration.
31  *
32  * <p>This class provides thread-safe access to a single, shared instance of `QuirkSettings`,
33  * ensuring consistency across the application. It notify registered listeners when the settings
34  * change.
35  */
36 public final class QuirkSettingsHolder {
37 
38     /**
39      * The default {@link QuirkSettings} configuration.
40      *
41      * <p>This default configuration enables all quirks when the device natively exhibits them.
42      */
43     public static final QuirkSettings DEFAULT = QuirkSettings.withDefaultBehavior();
44     private static final QuirkSettingsHolder sInstance = new QuirkSettingsHolder();
45 
46     private final MutableStateObservable<QuirkSettings> mObservable =
47             MutableStateObservable.withInitialState(DEFAULT);
48 
49     /**
50      * Returns the singleton instance of {@link QuirkSettingsHolder}.
51      */
instance()52     public static @NonNull QuirkSettingsHolder instance() {
53         return sInstance;
54     }
55 
56     /**
57      * Retrieves the current global {@link QuirkSettings} instance.
58      *
59      * <p>This method is thread-safe and returns a snapshot of the current settings.
60      *
61      * @return The current global QuirkSettings instance.
62      */
get()63     public @NonNull QuirkSettings get() {
64         try {
65             return mObservable.fetchData().get();
66         } catch (ExecutionException | InterruptedException e) {
67             throw new AssertionError("Unexpected error in QuirkSettings StateObservable", e);
68         }
69     }
70 
71     /**
72      * Sets the global {@link QuirkSettings} instance, triggering notifications to listeners.
73      *
74      * @param settings The new QuirkSettings instance to be set globally.
75      */
set(@onNull QuirkSettings settings)76     public void set(@NonNull QuirkSettings settings) {
77         mObservable.setState(settings);
78     }
79 
80     /**
81      * Adds a listener to be notified when the global {@link QuirkSettings} change.
82      *
83      * <p>The listener will be invoked on the specified executor whenever the settings are updated.
84      *
85      * @param executor The executor on which the listener should be called.
86      * @param listener The listener to be notified of changes.
87      */
observe(@onNull Executor executor, @NonNull Consumer<QuirkSettings> listener)88     public void observe(@NonNull Executor executor, @NonNull Consumer<QuirkSettings> listener) {
89         mObservable.addObserver(executor, new ObserverToConsumerAdapter<>(listener));
90     }
91 
92     /**
93      * Resets the internal state of the {@link QuirkSettingsHolder}.
94      *
95      * <p>Clears observers and restores to {@link #DEFAULT}.
96      *
97      * <p>This method is intended for testing purposes and should not be used in production code.
98      */
99     @VisibleForTesting
reset()100     public void reset() {
101         mObservable.removeObservers();
102         mObservable.setState(DEFAULT);
103     }
104 
105     /**
106      * Adapts an {@link Observable.Observer} to work with a {@link Consumer}.
107      */
108     private static class ObserverToConsumerAdapter<T> implements Observable.Observer<T> {
109         private static final String TAG = "ObserverToConsumerAdapter";
110         private final Consumer<T> mDelegate;
111 
112         /**
113          * Creates a new ObserverToConsumerAdapter.
114          *
115          * @param delegate     The underlying consumer to receive filtered updates.
116          */
ObserverToConsumerAdapter(@onNull Consumer<T> delegate)117         ObserverToConsumerAdapter(@NonNull Consumer<T> delegate) {
118             mDelegate = delegate;
119         }
120 
121         @Override
onNewData(@ullable T newValue)122         public void onNewData(@Nullable T newValue) {
123             mDelegate.accept(newValue);
124         }
125 
126         @Override
onError(@onNull Throwable t)127         public void onError(@NonNull Throwable t) {
128             Logger.e(TAG, "Unexpected error in Observable", t);
129         }
130     }
131 }
132