1 /* 2 * Copyright (C) 2021 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.app.ActivityManager; 20 import android.content.Context; 21 import android.content.res.TypedArray; 22 import android.net.Uri; 23 import android.util.AttributeSet; 24 25 import androidx.annotation.NonNull; 26 import androidx.annotation.Nullable; 27 28 import com.android.car.qc.controller.BaseQCController; 29 import com.android.car.qc.controller.LocalQCController; 30 import com.android.car.qc.controller.RemoteQCController; 31 import com.android.car.qc.provider.BaseLocalQCProvider; 32 import com.android.car.qc.view.QCView; 33 import com.android.systemui.R; 34 35 import java.lang.reflect.Constructor; 36 import java.lang.reflect.InvocationTargetException; 37 38 /** 39 * Quick Control View Element for CarSystemUI. 40 * 41 * This extended class allows for specifying a local or remote quick controls provider via xml 42 * attributes. When a remote provider is specified, it will bind to the provider as the current 43 * foreground user instead of user 0. 44 * 45 * @attr ref R.styleable#SystemUIQCView_remoteQCProvider 46 * @attr ref R.styleable#SystemUIQCView_localQCProvider 47 */ 48 public class SystemUIQCView extends QCView { 49 private BaseQCController mController; 50 private BaseLocalQCProvider mLocalQCProvider; 51 SystemUIQCView(Context context)52 public SystemUIQCView(Context context) { 53 super(context); 54 init(context, /* attrs= */ null); 55 } 56 SystemUIQCView(Context context, AttributeSet attrs)57 public SystemUIQCView(Context context, AttributeSet attrs) { 58 super(context, attrs); 59 init(context, attrs); 60 } 61 SystemUIQCView(Context context, AttributeSet attrs, int defStyleAttr)62 public SystemUIQCView(Context context, AttributeSet attrs, int defStyleAttr) { 63 super(context, attrs, defStyleAttr); 64 init(context, attrs); 65 } 66 SystemUIQCView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)67 public SystemUIQCView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 68 super(context, attrs, defStyleAttr, defStyleRes); 69 init(context, attrs); 70 } 71 72 /** 73 * Toggle whether or not this view should listen to live updates. 74 */ listen(boolean shouldListen)75 public void listen(boolean shouldListen) { 76 if (mController != null) { 77 mController.listen(shouldListen); 78 } 79 } 80 81 /** 82 * Destroy the current QCView and associated controller. 83 */ destroy()84 public void destroy() { 85 if (mController != null) { 86 mController.destroy(); 87 mController = null; 88 } 89 removeAllViews(); 90 } 91 92 /** 93 * @return {@link BaseLocalQCProvider} for this System UI Quick Controls View. 94 */ 95 @Nullable getLocalQCProvider()96 public BaseLocalQCProvider getLocalQCProvider() { 97 return mLocalQCProvider; 98 } 99 init(@onNull Context context, @Nullable AttributeSet attrs)100 private void init(@NonNull Context context, @Nullable AttributeSet attrs) { 101 setFocusable(false); 102 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SystemUIQCView); 103 String remoteUri = a.getString(R.styleable.SystemUIQCView_remoteQCProvider); 104 if (remoteUri != null) { 105 Uri uri = Uri.parse(remoteUri); 106 if (uri.getUserInfo() == null) { 107 // To bind to the content provider as the current user rather than user 0 (which 108 // SystemUI is running on), add the current user id followed by the '@' symbol 109 // before the Uri's authority. 110 uri = uri.buildUpon().authority( 111 String.format("%s@%s", ActivityManager.getCurrentUser(), 112 uri.getAuthority())).build(); 113 } 114 bindRemoteQCView(uri); 115 } else { 116 String localClass = a.getString(R.styleable.SystemUIQCView_localQCProvider); 117 if (localClass != null) { 118 bindLocalQCView(localClass); 119 } 120 } 121 a.recycle(); 122 } 123 bindRemoteQCView(Uri uri)124 private void bindRemoteQCView(Uri uri) { 125 mController = new RemoteQCController(mContext, uri); 126 mController.addObserver(this); 127 mController.listen(true); 128 } 129 bindLocalQCView(String localClass)130 private void bindLocalQCView(String localClass) { 131 mLocalQCProvider = createLocalQCProviderInstance(localClass, mContext); 132 mController = new LocalQCController(mContext, mLocalQCProvider); 133 mController.addObserver(this); 134 mController.listen(true); 135 } 136 createLocalQCProviderInstance(String controllerName, Context context)137 private BaseLocalQCProvider createLocalQCProviderInstance(String controllerName, 138 Context context) { 139 try { 140 Class<?> clazz = Class.forName(controllerName); 141 Constructor<?> providerConstructor = clazz.getConstructor(Context.class); 142 return (BaseLocalQCProvider) providerConstructor.newInstance(context); 143 } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException 144 | InvocationTargetException | IllegalAccessException e) { 145 throw new IllegalArgumentException("Invalid controller: " + controllerName, e); 146 } 147 } 148 } 149