1 /*
2  * Copyright 2020 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.car.app.testing;
18 
19 import static java.util.Objects.requireNonNull;
20 
21 import android.content.Intent;
22 
23 import androidx.car.app.Session;
24 import androidx.lifecycle.DefaultLifecycleObserver;
25 import androidx.lifecycle.Lifecycle;
26 import androidx.lifecycle.Lifecycle.Event;
27 import androidx.lifecycle.LifecycleOwner;
28 import androidx.lifecycle.LifecycleRegistry;
29 
30 import org.jspecify.annotations.NonNull;
31 
32 /**
33  * {@link SessionController} provides API that allows testing of a {@link Session}.
34  *
35  * <p>This controller allows:
36  *
37  * <ul>
38  *   <li>Injecting a {@link TestCarContext} into the {@link Session} instance, which provides access
39  *   to the test managers and other testing functionalities.
40  * </ul>
41  */
42 public class SessionController {
43     final Session mSession;
44     final TestCarContext mTestCarContext;
45     final Intent mIntent;
46     private final TestLifecycleOwner mLifecycleOwner;
47 
48     /**
49      * Creates a {@link SessionController} to control the provided {@link Session}.
50      *
51      * @param session the {@link Session} to control
52      * @param context the {@link TestCarContext} that the {@code session} should use.
53      * @param intent  the {@link Intent} that the {@code session} should start with during the
54      *                {@link androidx.lifecycle.Lifecycle.State#CREATED} state.
55      * @throws NullPointerException if {@code session} or {@code context} is {@code null}
56      */
SessionController(@onNull Session session, @NonNull TestCarContext context, @NonNull Intent intent)57     public SessionController(@NonNull Session session, @NonNull TestCarContext context,
58             @NonNull Intent intent) {
59         mSession = requireNonNull(session);
60         mTestCarContext = requireNonNull(context);
61         mIntent = requireNonNull(intent);
62 
63         mLifecycleOwner = new TestLifecycleOwner();
64         mLifecycleOwner.getRegistry().addObserver(new SessionLifecycleObserver());
65 
66         mSession.setCarContextInternal(mTestCarContext);
67         mSession.setLifecycleRegistryInternal(mTestCarContext.getLifecycleOwner().getRegistry());
68     }
69 
70     /** Returns the {@link Session} that is being controlled. */
getSession()71     public @NonNull Session getSession() {
72         return mSession;
73     }
74 
75     /**
76      * Moves the {@link Session} being controlled to the input {@code state}.
77      *
78      * <p>Note that {@link Lifecycle.State#DESTROYED} is a terminal state, and you cannot move to
79      * any other state after the {@link Session} reaches that state.</p>
80      *
81      * @see Session#getLifecycle
82      */
moveToState(Lifecycle.@onNull State state)83     public @NonNull SessionController moveToState(Lifecycle.@NonNull State state) {
84         mLifecycleOwner.getRegistry().setCurrentState(state);
85         return this;
86     }
87 
88     /**
89      * A helper class to forward the lifecycle events from this controller to the session.
90      */
91     class SessionLifecycleObserver implements DefaultLifecycleObserver {
92         @Override
onCreate(@onNull LifecycleOwner owner)93         public void onCreate(@NonNull LifecycleOwner owner) {
94             LifecycleRegistry registry = (LifecycleRegistry) mSession.getLifecycle();
95             TestScreenManager screenManager = mTestCarContext.getCarService(
96                     TestScreenManager.class);
97             registry.handleLifecycleEvent(Event.ON_CREATE);
98             screenManager.push(mSession.onCreateScreen(mIntent));
99         }
100 
101         @Override
onStart(@onNull LifecycleOwner owner)102         public void onStart(@NonNull LifecycleOwner owner) {
103             LifecycleRegistry registry = (LifecycleRegistry) mSession.getLifecycle();
104             registry.handleLifecycleEvent(Event.ON_START);
105         }
106 
107         @Override
onResume(@onNull LifecycleOwner owner)108         public void onResume(@NonNull LifecycleOwner owner) {
109             LifecycleRegistry registry = (LifecycleRegistry) mSession.getLifecycle();
110             registry.handleLifecycleEvent(Event.ON_RESUME);
111         }
112 
113         @Override
onPause(@onNull LifecycleOwner owner)114         public void onPause(@NonNull LifecycleOwner owner) {
115             LifecycleRegistry registry = (LifecycleRegistry) mSession.getLifecycle();
116             registry.handleLifecycleEvent(Event.ON_PAUSE);
117         }
118 
119         @Override
onStop(@onNull LifecycleOwner owner)120         public void onStop(@NonNull LifecycleOwner owner) {
121             LifecycleRegistry registry = (LifecycleRegistry) mSession.getLifecycle();
122             registry.handleLifecycleEvent(Event.ON_STOP);
123         }
124 
125         @Override
onDestroy(@onNull LifecycleOwner owner)126         public void onDestroy(@NonNull LifecycleOwner owner) {
127             LifecycleRegistry registry = (LifecycleRegistry) mSession.getLifecycle();
128             registry.handleLifecycleEvent(Event.ON_DESTROY);
129         }
130     }
131 }
132