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