1 /*
2  * Copyright 2023 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.testing.impl.fakes;
18 
19 import androidx.annotation.RestrictTo;
20 import androidx.camera.core.ImageCapture;
21 import androidx.camera.core.UseCase;
22 import androidx.camera.core.impl.CameraCaptureResult;
23 import androidx.camera.core.impl.CameraInfoInternal;
24 import androidx.camera.core.impl.Config;
25 import androidx.camera.core.impl.SessionConfig;
26 import androidx.camera.core.impl.StreamSpec;
27 import androidx.camera.core.impl.UseCaseConfig;
28 import androidx.camera.core.impl.UseCaseConfigFactory;
29 import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType;
30 import androidx.core.util.Supplier;
31 
32 import org.jspecify.annotations.NonNull;
33 import org.jspecify.annotations.Nullable;
34 
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.Set;
38 import java.util.concurrent.atomic.AtomicInteger;
39 
40 /**
41  * A fake {@link UseCase}.
42  */
43 public class FakeUseCase extends UseCase {
44 
45     private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 0;
46 
47     private volatile boolean mIsDetached = false;
48     private final AtomicInteger mStateAttachedCount = new AtomicInteger(0);
49     private final CaptureType mCaptureType;
50     private boolean mMergedConfigRetrieved = false;
51     private int mPipelineCreationCount = 0;
52     private Supplier<SessionConfig> mSessionConfigSupplier;
53     private Set<Integer> mEffectTargets = Collections.emptySet();
54 
55     /**
56      * Creates a new instance of a {@link FakeUseCase} with a given configuration and capture type.
57      */
FakeUseCase(@onNull FakeUseCaseConfig config, @NonNull CaptureType captureType)58     public FakeUseCase(@NonNull FakeUseCaseConfig config, @NonNull CaptureType captureType) {
59         super(config);
60         mCaptureType = captureType;
61     }
62 
63     /**
64      * Creates a new instance of a {@link FakeUseCase} with a given configuration.
65      */
FakeUseCase(@onNull FakeUseCaseConfig config)66     public FakeUseCase(@NonNull FakeUseCaseConfig config) {
67         this(config, CaptureType.PREVIEW);
68     }
69 
70     /**
71      * Creates a new instance of a {@link FakeUseCase} with a default configuration.
72      */
FakeUseCase()73     public FakeUseCase() {
74         this(new FakeUseCaseConfig.Builder()
75                 .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
76                 .setCaptureType(CaptureType.PREVIEW)
77                 .getUseCaseConfig());
78     }
79 
80     /**
81      * {@inheritDoc}
82      *
83      */
84     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
85     @Override
getUseCaseConfigBuilder(@onNull Config config)86     public UseCaseConfig.@NonNull Builder<?, ?, ?> getUseCaseConfigBuilder(@NonNull Config config) {
87         return new FakeUseCaseConfig.Builder(config)
88                 .setCaptureType(mCaptureType)
89                 .setSessionOptionUnpacker((resolution, useCaseConfig, sessionConfigBuilder) -> {
90                 });
91     }
92 
93     /**
94      * {@inheritDoc}
95      *
96      */
97     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
98     @Override
getDefaultConfig(boolean applyDefaultConfig, @NonNull UseCaseConfigFactory factory)99     public @Nullable UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
100             @NonNull UseCaseConfigFactory factory) {
101         Config config = factory.getConfig(
102                 mCaptureType,
103                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
104         return config == null ? null : getUseCaseConfigBuilder(config).getUseCaseConfig();
105     }
106 
107     @Override
onMergeConfig(@onNull CameraInfoInternal cameraInfo, UseCaseConfig.@NonNull Builder<?, ?, ?> builder)108     protected @NonNull UseCaseConfig<?> onMergeConfig(@NonNull CameraInfoInternal cameraInfo,
109             UseCaseConfig.@NonNull Builder<?, ?, ?> builder) {
110         mMergedConfigRetrieved = true;
111         return builder.getUseCaseConfig();
112     }
113 
114     @Override
onUnbind()115     public void onUnbind() {
116         super.onUnbind();
117         mIsDetached = true;
118     }
119 
120     @Override
onStateAttached()121     public void onStateAttached() {
122         super.onStateAttached();
123         mStateAttachedCount.incrementAndGet();
124     }
125 
126     @Override
onStateDetached()127     public void onStateDetached() {
128         super.onStateDetached();
129         mStateAttachedCount.decrementAndGet();
130     }
131 
132     @Override
onSuggestedStreamSpecUpdated( @onNull StreamSpec primaryStreamSpec, @Nullable StreamSpec secondaryStreamSpec)133     protected @NonNull StreamSpec onSuggestedStreamSpecUpdated(
134             @NonNull StreamSpec primaryStreamSpec,
135             @Nullable StreamSpec secondaryStreamSpec) {
136         SessionConfig sessionConfig = createPipeline();
137         if (sessionConfig != null) {
138             updateSessionConfig(List.of(sessionConfig));
139         }
140         return primaryStreamSpec;
141     }
142 
createPipeline()143     @Nullable SessionConfig createPipeline() {
144         mPipelineCreationCount++;
145         if (mSessionConfigSupplier != null) {
146             return mSessionConfigSupplier.get();
147         } else {
148             return null;
149         }
150     }
151 
152     /**
153      * Sets effect targets.
154      */
setSupportedEffectTargets(@onNull Set<Integer> effectTargets)155     public void setSupportedEffectTargets(@NonNull Set<Integer> effectTargets) {
156         mEffectTargets = effectTargets;
157     }
158 
159     /**
160      * @inheritDoc
161      */
162     @Override
getSupportedEffectTargets()163     public @NonNull Set<Integer> getSupportedEffectTargets() {
164         return mEffectTargets;
165     }
166 
167 
168     /**
169      * Returns true if {@link #onUnbind()} has been called previously.
170      */
isDetached()171     public boolean isDetached() {
172         return mIsDetached;
173     }
174 
175     /**
176      * Returns true if {@link #onStateAttached()} has been called previously.
177      */
getStateAttachedCount()178     public int getStateAttachedCount() {
179         return mStateAttachedCount.get();
180     }
181 
182     /**
183      * Returns true if {@link #mergeConfigs} have been invoked.
184      */
getMergedConfigRetrieved()185     public boolean getMergedConfigRetrieved() {
186         return mMergedConfigRetrieved;
187     }
188 
189     /**
190      * Returns how many times the pipeline has been created.
191      */
getPipelineCreationCount()192     public int getPipelineCreationCount() {
193         return mPipelineCreationCount;
194     }
195 
196     /**
197      * Returns {@link CameraCaptureResult} received by this use case.
198      */
setSessionConfigSupplier(@onNull Supplier<SessionConfig> sessionConfigSupplier)199     public void setSessionConfigSupplier(@NonNull Supplier<SessionConfig> sessionConfigSupplier) {
200         mSessionConfigSupplier = sessionConfigSupplier;
201     }
202 
203     /**
204      * Calls the protected method {@link UseCase#updateSessionConfig}.
205      */
updateSessionConfigForTesting(@onNull SessionConfig sessionConfig)206     public void updateSessionConfigForTesting(@NonNull SessionConfig sessionConfig) {
207         updateSessionConfig(List.of(sessionConfig));
208     }
209 
210     /**
211      * Calls the protected method {@link UseCase#notifyActive()}.
212      */
notifyActiveForTesting()213     public void notifyActiveForTesting() {
214         notifyActive();
215     }
216 
217     /**
218      * Calls the protected method {@link UseCase#notifyInactive()}.
219      */
notifyInactiveForTesting()220     public void notifyInactiveForTesting() {
221         notifyInactive();
222     }
223 
224     /**
225      * Calls the protected method {@link UseCase#notifyUpdated()}.
226      */
notifyUpdatedForTesting()227     public void notifyUpdatedForTesting() {
228         notifyUpdated();
229     }
230 
231     /**
232      * Calls the protected method {@link UseCase#notifyReset()}.
233      */
notifyResetForTesting()234     public void notifyResetForTesting() {
235         notifyReset();
236     }
237 }
238