1 package com.android.car.carlauncher; 2 3 import android.car.CarProjectionManager; 4 import android.car.projection.ProjectionStatus; 5 import android.content.Context; 6 import android.content.pm.ApplicationInfo; 7 import android.content.pm.PackageManager; 8 import android.util.Log; 9 10 import androidx.annotation.NonNull; 11 import androidx.annotation.Nullable; 12 import androidx.lifecycle.LiveData; 13 14 import java.util.List; 15 16 /** A {@link LiveData} of {@link ContextualInfo} on projection status. */ 17 class ProjectionContextualInfoLiveData extends LiveData<ContextualInfo> 18 implements CarProjectionManager.ProjectionStatusListener { 19 private static final String TAG = "ProjectionContext"; 20 21 private final Context mContext; 22 private final CarProjectionManager mCarProjectionManager; 23 ProjectionContextualInfoLiveData( Context context, CarProjectionManager carProjectionManager)24 ProjectionContextualInfoLiveData( 25 Context context, 26 CarProjectionManager carProjectionManager) { 27 mContext = context; 28 mCarProjectionManager = carProjectionManager; 29 } 30 31 @Override onActive()32 protected void onActive() { 33 super.onActive(); 34 mCarProjectionManager.registerProjectionStatusListener(this); 35 } 36 37 @Override onInactive()38 protected void onInactive() { 39 mCarProjectionManager.unregisterProjectionStatusListener(this); 40 super.onInactive(); 41 } 42 43 @Override onProjectionStatusChanged( int state, @Nullable String packageName, @NonNull List<ProjectionStatus> details)44 public void onProjectionStatusChanged( 45 int state, @Nullable String packageName, @NonNull List<ProjectionStatus> details) { 46 if (Log.isLoggable(TAG, Log.DEBUG)) { 47 Log.d(TAG, "onProjectionStatusChanged state=" + state + " package=" + packageName); 48 } 49 if (state == ProjectionStatus.PROJECTION_STATE_INACTIVE || packageName == null) { 50 setValue(null); 51 return; 52 } 53 54 PackageManager pm = mContext.getPackageManager(); 55 ApplicationInfo applicationInfo; 56 try { 57 applicationInfo = pm.getApplicationInfo(packageName, 0); 58 } catch (PackageManager.NameNotFoundException e) { 59 Log.e(TAG, "Could not load projection package information", e); 60 setValue(null); 61 return; 62 } 63 64 setValue( 65 new ContextualInfo( 66 applicationInfo.loadIcon(pm), 67 applicationInfo.loadLabel(pm), 68 getStatusMessage(packageName, details), 69 /* showClock= */ false, 70 pm.getLaunchIntentForPackage(packageName))); 71 } 72 73 @Nullable getStatusMessage( String packageName, List<ProjectionStatus> details)74 private String getStatusMessage( 75 String packageName, List<ProjectionStatus> details) { 76 for (ProjectionStatus status : details) { 77 if (packageName.equals(status.getPackageName())) { 78 return getStatusMessage(status); 79 } 80 } 81 82 return null; 83 } 84 85 @Nullable getStatusMessage(ProjectionStatus status)86 private String getStatusMessage(ProjectionStatus status) { 87 // The status message is as follows: 88 // - If there is an unambiguous "best" device, the name of that device. 89 // "Unambiguous" is defined as only one projecting device, or no projecting devices 90 // and only one non-projecting device. 91 // - If there are multiple projecting or non-projecting devices, "N devices", where N 92 // is the total number of projecting and non-projecting devices. 93 // - If there are no devices at all, no message. This should not happen if projection 94 // apps are behaving properly, but may happen in the event of a projection app bug. 95 String projectingDevice = null; 96 String nonProjectingDevice = null; 97 int projectingDeviceCount = 0; 98 int nonProjectingDeviceCount = 0; 99 for (ProjectionStatus.MobileDevice device : status.getConnectedMobileDevices()) { 100 if (device.isProjecting()) { 101 projectingDevice = device.getName(); 102 projectingDeviceCount++; 103 } else { 104 nonProjectingDevice = device.getName(); 105 nonProjectingDeviceCount++; 106 } 107 } 108 109 if (projectingDeviceCount == 1) { 110 return projectingDevice; 111 } else if (projectingDeviceCount == 0 && nonProjectingDeviceCount == 1) { 112 return nonProjectingDevice; 113 } 114 115 int totalDeviceCount = projectingDeviceCount + nonProjectingDeviceCount; 116 if (totalDeviceCount > 0) { 117 return mContext.getResources().getQuantityString( 118 R.plurals.projection_devices, totalDeviceCount, totalDeviceCount); 119 } else { 120 return null; 121 } 122 } 123 } 124