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.car.carlauncher; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.PackageManager; 23 import android.util.Log; 24 25 import com.google.common.annotations.VisibleForTesting; 26 27 import java.net.URISyntaxException; 28 import java.util.Set; 29 30 /** 31 * Utils for CarLauncher package. 32 */ 33 public class CarLauncherUtils { 34 35 private static final String TAG = "CarLauncherUtils"; 36 private static final String ACTION_APP_GRID = "com.android.car.carlauncher.ACTION_APP_GRID"; 37 CarLauncherUtils()38 private CarLauncherUtils() { 39 } 40 getAppsGridIntent()41 public static Intent getAppsGridIntent() { 42 return new Intent(ACTION_APP_GRID); 43 } 44 45 /** Intent used to find/launch the maps activity to run in the relevant DisplayArea. */ getMapsIntent(Context context)46 public static Intent getMapsIntent(Context context) { 47 Intent defaultIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MAPS); 48 defaultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 49 PackageManager pm = context.getPackageManager(); 50 ComponentName defaultActivity = defaultIntent.resolveActivity(pm); 51 defaultIntent.setComponent(defaultActivity); 52 53 for (String intentUri : context.getResources().getStringArray( 54 R.array.config_homeCardPreferredMapActivities)) { 55 Intent preferredIntent; 56 try { 57 preferredIntent = Intent.parseUri(intentUri, Intent.URI_ANDROID_APP_SCHEME); 58 } catch (URISyntaxException se) { 59 Log.w(TAG, "Invalid intent URI in config_homeCardPreferredMapActivities", se); 60 continue; 61 } 62 63 if (defaultActivity != null && !defaultActivity.getPackageName().equals( 64 preferredIntent.getPackage())) { 65 continue; 66 } 67 68 if (preferredIntent.resolveActivityInfo(pm, /* flags= */ 0) != null) { 69 return maybeReplaceWithTosMapIntent(context, preferredIntent); 70 } 71 } 72 return maybeReplaceWithTosMapIntent(context, defaultIntent); 73 } 74 75 /** 76 * Returns {@code true} if a proper limited map intent is configured via 77 * {@code config_smallCanvasOptimizedMapIntent} string resource. 78 */ isSmallCanvasOptimizedMapIntentConfigured(Context context)79 public static boolean isSmallCanvasOptimizedMapIntentConfigured(Context context) { 80 String intentString = context.getString(R.string.config_smallCanvasOptimizedMapIntent); 81 if (intentString.isEmpty()) { 82 Log.d(TAG, "Empty intent URI in config_smallCanvasOptimizedMapIntent"); 83 return false; 84 } 85 86 try { 87 Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME); 88 return true; 89 } catch (URISyntaxException e) { 90 Log.w(TAG, "Invalid intent URI in config_smallCanvasOptimizedMapIntent: \"" 91 + intentString); 92 return false; 93 } 94 } 95 96 /** 97 * Returns an intent to trigger a map with a limited functionality (e.g., one to be used when 98 * there's not much screen real estate). 99 */ getSmallCanvasOptimizedMapIntent(Context context)100 public static Intent getSmallCanvasOptimizedMapIntent(Context context) { 101 String intentString = context.getString(R.string.config_smallCanvasOptimizedMapIntent); 102 try { 103 Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME); 104 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 105 return maybeReplaceWithTosMapIntent(context, intent); 106 } catch (URISyntaxException e) { 107 Log.w(TAG, "Invalid intent URI in config_smallCanvasOptimizedMapIntent: \"" 108 + intentString + "\". Falling back to fullscreen map."); 109 return maybeReplaceWithTosMapIntent(context, getMapsIntent(context)); 110 } 111 } 112 maybeReplaceWithTosMapIntent(Context context, Intent mapIntent)113 private static Intent maybeReplaceWithTosMapIntent(Context context, Intent mapIntent) { 114 String packageName = mapIntent.getComponent() != null 115 ? mapIntent.getComponent().getPackageName() 116 : null; 117 Set<String> tosDisabledPackages = AppLauncherUtils.getTosDisabledPackages(context); 118 119 // Launch tos map intent when the user has not accepted tos and when the 120 // default maps package is not available to package manager, or it's disabled by tos 121 if (!AppLauncherUtils.tosAccepted(context) 122 && (packageName == null || tosDisabledPackages.contains(packageName))) { 123 Log.i(TAG, "Replacing default maps intent with tos map intent"); 124 mapIntent = getTosMapIntent(context); 125 } 126 return mapIntent; 127 } 128 129 /** 130 * Return an intent used to launch the tos map activity 131 * @param context The application context 132 * @return Tos Intent, null if the config is incorrect 133 */ 134 @VisibleForTesting getTosMapIntent(Context context)135 public static Intent getTosMapIntent(Context context) { 136 String intentString = context.getString(R.string.config_tosMapIntent); 137 try { 138 return Intent.parseUri(intentString, Intent.URI_ANDROID_APP_SCHEME); 139 } catch (URISyntaxException se) { 140 Log.w(TAG, "Invalid intent URI in config_tosMapIntent", se); 141 return null; 142 } 143 } 144 145 } 146