1 /* 2 * Copyright (C) 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 package android.car.app; 17 18 import static android.Manifest.permission.INTERACT_ACROSS_USERS; 19 20 import android.annotation.FlaggedApi; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresPermission; 24 import android.annotation.SystemApi; 25 import android.annotation.UiThread; 26 import android.app.Activity; 27 import android.car.Car; 28 import android.car.builtin.util.Slogf; 29 import android.car.builtin.view.ViewHelper; 30 import android.car.feature.Flags; 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.pm.ActivityInfo; 35 import android.content.pm.PackageInfo; 36 import android.content.pm.PackageManager; 37 import android.graphics.Rect; 38 import android.os.Bundle; 39 import android.view.SurfaceView; 40 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.function.Consumer; 44 45 /** 46 * A container that's used by the display compat host. 47 * 48 * @hide 49 */ 50 //TODO: b/329677726 Allow setting width, height and density from CarDisplayCompatContainer 51 @FlaggedApi(Flags.FLAG_DISPLAY_COMPATIBILITY) 52 @SystemApi 53 public final class CarDisplayCompatContainer { 54 private static final String TAG = CarDisplayCompatContainer.class.getSimpleName(); 55 @NonNull 56 private final CarActivityManager mCarActivityManager; 57 @NonNull 58 private Activity mActivity; 59 @Nullable 60 private Consumer<SurfaceView> mSurfaceViewCallback; 61 @Nullable 62 private CarTaskViewController mCarTaskViewController; 63 @Nullable 64 private RemoteCarTaskView mRemoteCarTaskView; 65 @Nullable 66 private Intent mIntent; 67 68 @FlaggedApi(Flags.FLAG_DISPLAY_COMPATIBILITY) 69 public static final class Builder { 70 @NonNull 71 private Activity mActivity; 72 @Nullable 73 private Consumer<SurfaceView> mSurfaceViewCallBack; 74 @NonNull 75 private CarActivityManager mCarActivityManager; 76 Builder(@onNull Activity activity)77 public Builder(@NonNull Activity activity) { 78 mActivity = activity; 79 } 80 81 /** 82 * set density in dpi 83 */ 84 @NonNull setSurfaceViewCallback(@ullable Consumer<SurfaceView> callback)85 public Builder setSurfaceViewCallback(@Nullable Consumer<SurfaceView> callback) { 86 mSurfaceViewCallBack = callback; 87 return this; 88 } 89 90 /** 91 * set a car instance 92 */ 93 @NonNull setCarActivityManager(@onNull CarActivityManager carActivityManager)94 Builder setCarActivityManager(@NonNull CarActivityManager carActivityManager) { 95 mCarActivityManager = carActivityManager; 96 return this; 97 } 98 99 /** 100 * Returns a new instance of {@link CarDisplayCompatContainer} 101 */ 102 @NonNull build()103 CarDisplayCompatContainer build() { 104 return new CarDisplayCompatContainer( 105 mActivity, mSurfaceViewCallBack, mCarActivityManager); 106 } 107 } 108 109 /** 110 * @hide 111 */ CarDisplayCompatContainer(@onNull Activity activity, @Nullable Consumer<SurfaceView> callback, @NonNull CarActivityManager carActivityManager)112 CarDisplayCompatContainer(@NonNull Activity activity, 113 @Nullable Consumer<SurfaceView> callback, 114 @NonNull CarActivityManager carActivityManager) { 115 mActivity = activity; 116 mSurfaceViewCallback = callback; 117 mCarActivityManager = carActivityManager; 118 } 119 120 /** 121 * Set bounds of the display compat container 122 * 123 * @hide 124 */ 125 @UiThread 126 @SystemApi 127 @RequiresPermission(Car.PERMISSION_MANAGE_DISPLAY_COMPATIBILITY) 128 @NonNull setWindowBounds(@onNull Rect windowBounds)129 public Rect setWindowBounds(@NonNull Rect windowBounds) { 130 if (mRemoteCarTaskView != null) { 131 mRemoteCarTaskView.setWindowBounds(windowBounds); 132 } 133 Rect actualWindowBounds = new Rect(); 134 ViewHelper.getBoundsOnScreen(mRemoteCarTaskView, actualWindowBounds); 135 return actualWindowBounds; 136 } 137 138 /** 139 * Set the visibility of the display compat container 140 * see {@link android.view.View#setVisibility(int)} 141 * 142 * @hide 143 */ 144 @UiThread 145 @SystemApi 146 @RequiresPermission(Car.PERMISSION_MANAGE_DISPLAY_COMPATIBILITY) setVisibility(int visibility)147 public void setVisibility(int visibility) { 148 if (mRemoteCarTaskView != null) { 149 mRemoteCarTaskView.setVisibility(visibility); 150 } 151 } 152 153 /** 154 * Launch an activity on the display compat container 155 * 156 * @hide 157 */ 158 @UiThread 159 @SystemApi 160 @RequiresPermission(allOf = {Car.PERMISSION_MANAGE_DISPLAY_COMPATIBILITY, 161 Car.PERMISSION_MANAGE_CAR_SYSTEM_UI, 162 INTERACT_ACROSS_USERS}) startActivity(@onNull Intent intent, @Nullable Bundle bundle)163 public void startActivity(@NonNull Intent intent, @Nullable Bundle bundle) { 164 mIntent = intent; 165 Context context = mActivity.getApplicationContext(); 166 RemoteCarRootTaskViewCallback remoteCarTaskViewCallback = 167 new RemoteCarRootTaskViewCallbackImpl(mRemoteCarTaskView, 168 intent, context, mSurfaceViewCallback); 169 CarTaskViewControllerCallback carTaskViewControllerCallback = 170 new CarTaskViewControllerCallbackImpl(remoteCarTaskViewCallback); 171 172 mCarActivityManager.getCarTaskViewController(mActivity, mActivity.getMainExecutor(), 173 carTaskViewControllerCallback); 174 } 175 176 /** 177 * Called when the user clicks the back button 178 * 179 * @hide 180 */ 181 @UiThread 182 @SystemApi 183 @RequiresPermission(Car.PERMISSION_MANAGE_DISPLAY_COMPATIBILITY) notifyBackPressed()184 public void notifyBackPressed() { 185 } 186 187 private static final class RemoteCarRootTaskViewCallbackImpl implements 188 RemoteCarRootTaskViewCallback { 189 private RemoteCarTaskView mRemoteCarTaskView; 190 private final Intent mIntent; 191 private final Context mContext; 192 private Consumer<SurfaceView> mSurfaceViewCallback; RemoteCarRootTaskViewCallbackImpl( RemoteCarTaskView remoteCarTaskView, Intent intent, Context context, Consumer<SurfaceView> callback)193 private RemoteCarRootTaskViewCallbackImpl( 194 RemoteCarTaskView remoteCarTaskView, 195 Intent intent, 196 Context context, 197 Consumer<SurfaceView> callback) { 198 mRemoteCarTaskView = remoteCarTaskView; 199 mIntent = intent; 200 mContext = context; 201 mSurfaceViewCallback = callback; 202 } 203 204 @Override onTaskViewCreated(@onNull RemoteCarRootTaskView taskView)205 public void onTaskViewCreated(@NonNull RemoteCarRootTaskView taskView) { 206 mRemoteCarTaskView = taskView; 207 if (mSurfaceViewCallback != null) { 208 mSurfaceViewCallback.accept(mRemoteCarTaskView); 209 } 210 if (mIntent != null) { 211 mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 212 mContext.startActivity(mIntent); 213 } 214 } 215 } 216 217 private final class CarTaskViewControllerCallbackImpl implements CarTaskViewControllerCallback { 218 private final RemoteCarRootTaskViewCallback mRemoteCarRootTaskViewCallback; 219 CarTaskViewControllerCallbackImpl( @ullable RemoteCarRootTaskViewCallback remoteCarRootTaskViewCallback)220 private CarTaskViewControllerCallbackImpl( 221 @Nullable RemoteCarRootTaskViewCallback remoteCarRootTaskViewCallback) { 222 mRemoteCarRootTaskViewCallback = remoteCarRootTaskViewCallback; 223 } 224 225 @RequiresPermission("android.car.permission.CONTROL_CAR_APP_LAUNCH") 226 @Override onConnected(@onNull CarTaskViewController carTaskViewController)227 public void onConnected(@NonNull CarTaskViewController carTaskViewController) { 228 mCarTaskViewController = carTaskViewController; 229 if (mIntent != null) { 230 String packageName = mIntent.getComponent().getPackageName(); 231 if (packageName != null) { 232 List<ComponentName> componentNames = queryActivitiesFromPackage(mActivity, 233 packageName); 234 mCarTaskViewController.createRemoteCarRootTaskView( 235 new RemoteCarRootTaskViewConfig.Builder() 236 .setAllowListedActivities(componentNames) 237 .build(), 238 mActivity.getMainExecutor(), 239 mRemoteCarRootTaskViewCallback); 240 } 241 } 242 } 243 244 @Override onDisconnected(@onNull CarTaskViewController carTaskViewController)245 public void onDisconnected(@NonNull CarTaskViewController carTaskViewController) { 246 mRemoteCarTaskView = null; 247 } 248 } 249 queryActivitiesFromPackage( Activity activity, String packageName)250 private static List<ComponentName> queryActivitiesFromPackage( 251 Activity activity, String packageName) { 252 List<ComponentName> componentNames = new ArrayList<>(); 253 PackageInfo packageInfo; 254 try { 255 packageInfo = activity.getPackageManager().getPackageInfo( 256 packageName, 257 PackageManager.GET_ACTIVITIES); 258 for (ActivityInfo info: packageInfo.activities) { 259 ComponentName componentName = new ComponentName(packageName, info.name); 260 componentNames.add(componentName); 261 } 262 } catch (PackageManager.NameNotFoundException e) { 263 Slogf.e(TAG, "Package '%s' not found : %s", packageName , e); 264 } 265 return componentNames; 266 } 267 } 268