• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 com.android.systemui.car.qc;
18 
19 import android.content.Context;
20 import android.net.Uri;
21 import android.util.Log;
22 
23 import androidx.annotation.Nullable;
24 
25 import com.android.car.qc.controller.BaseQCController;
26 import com.android.car.qc.controller.LocalQCController;
27 import com.android.car.qc.controller.RemoteQCController;
28 import com.android.car.qc.provider.BaseLocalQCProvider;
29 import com.android.systemui.settings.UserTracker;
30 
31 import java.lang.reflect.Constructor;
32 import java.util.Map;
33 
34 import javax.inject.Inject;
35 import javax.inject.Provider;
36 
37 /**
38  * Class to control instances of {@link SystemUIQCView}. This controller is responsible for
39  * attaching either a remote controller for the current user or a local controller using a dagger
40  * injected constructor and fall back to a default {@link Context} constructor when not present.
41  */
42 public final class SystemUIQCViewController {
43     private static final String TAG = SystemUIQCViewController.class.getName();
44     private final Context mContext;
45     private final UserTracker mUserTracker;
46     private final Map<Class<?>, Provider<BaseLocalQCProvider>> mLocalQCProviderCreators;
47     private SystemUIQCView mView;
48     private BaseQCController mController;
49     private boolean mUserChangedCallbackRegistered;
50 
51     private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() {
52         @Override
53         public void onUserChanged(int newUser, Context userContext) {
54             rebindController();
55         }
56     };
57 
58     @Inject
SystemUIQCViewController(Context context, UserTracker userTracker, Map<Class<?>, Provider<BaseLocalQCProvider>> localQCProviderCreators)59     public SystemUIQCViewController(Context context, UserTracker userTracker,
60             Map<Class<?>, Provider<BaseLocalQCProvider>> localQCProviderCreators) {
61         mContext = context;
62         mUserTracker = userTracker;
63         mLocalQCProviderCreators = localQCProviderCreators;
64     }
65 
66     /**
67      * Attaches a {@link SystemUIQCView} to this controller.
68      */
attachView(SystemUIQCView view)69     public void attachView(SystemUIQCView view) {
70         mView = view;
71         if (mView.getRemoteUriString() != null) {
72             Uri uri = Uri.parse(mView.getRemoteUriString());
73             if (uri.getUserInfo() == null) {
74                 // To bind to the content provider as the current user rather than user 0 (which
75                 // SystemUI is running on), add the current user id followed by the '@' symbol
76                 // before the Uri's authority.
77                 uri = uri.buildUpon().authority(
78                         String.format("%s@%s", mUserTracker.getUserId(),
79                                 uri.getAuthority())).build();
80             }
81             bindRemoteQCView(uri);
82             if (!mUserChangedCallbackRegistered) {
83                 mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
84                 mUserChangedCallbackRegistered = true;
85             }
86         } else if (mView.getLocalClassString() != null) {
87             bindLocalQCView(mView.getLocalClassString());
88         }
89     }
90 
91     /**
92      * Toggles whether or not this view should listen to live updates.
93      */
listen(boolean shouldListen)94     public void listen(boolean shouldListen) {
95         if (mController != null) {
96             mController.listen(shouldListen);
97         }
98     }
99 
100     /**
101      * Destroys the current QCView and associated controller.
102      */
destroy()103     public void destroy() {
104         resetViewAndController();
105         if (mUserChangedCallbackRegistered) {
106             mUserTracker.removeCallback(mUserChangedCallback);
107             mUserChangedCallbackRegistered = false;
108         }
109     }
110 
rebindController()111     private void rebindController() {
112         if (mView == null) {
113             return;
114         }
115         resetViewAndController();
116         attachView(mView);
117     }
118 
resetViewAndController()119     private void resetViewAndController() {
120         if (mController != null) {
121             mController.destroy();
122             mController = null;
123         }
124         if (mView != null) {
125             mView.onChanged(/* qcItem= */ null);
126         }
127     }
128 
bindRemoteQCView(Uri uri)129     private void bindRemoteQCView(Uri uri) {
130         mController = new RemoteQCController(mContext, uri);
131         mController.addObserver(mView);
132         mController.bind();
133     }
134 
bindLocalQCView(String localClass)135     private void bindLocalQCView(String localClass) {
136         BaseLocalQCProvider localQCProvider = createLocalQCProviderInstance(localClass, mContext);
137         mController = new LocalQCController(mContext, localQCProvider);
138         mController.addObserver(mView);
139         mController.bind();
140     }
141 
createLocalQCProviderInstance(String controllerName, Context context)142     private BaseLocalQCProvider createLocalQCProviderInstance(String controllerName,
143             Context context) {
144         try {
145             BaseLocalQCProvider injectedProvider = resolveInjectedLocalQCProviderInstance(
146                     controllerName);
147             if (injectedProvider != null) {
148                 return injectedProvider;
149             }
150             Class<?> clazz = Class.forName(controllerName);
151             Constructor<?> providerConstructor = clazz.getConstructor(Context.class);
152             return (BaseLocalQCProvider) providerConstructor.newInstance(context);
153         } catch (ReflectiveOperationException e) {
154             throw new IllegalArgumentException("Invalid controller: " + controllerName, e);
155         }
156     }
157 
resolveInjectedLocalQCProviderInstance(@ullable String className)158     private BaseLocalQCProvider resolveInjectedLocalQCProviderInstance(@Nullable String className) {
159         try {
160             Class<?> clazz = Class.forName(className);
161             Provider<BaseLocalQCProvider> provider = mLocalQCProviderCreators.get(clazz);
162             return provider == null ? null : provider.get();
163         } catch (ClassNotFoundException e) {
164             Log.d(TAG, "Could not find class " + className);
165             return null;
166         }
167     }
168 }
169