From 271e936e8160afd0885d84ab40ede64076248505 Mon Sep 17 00:00:00 2001 From: Robert Engels Date: Thu, 3 Mar 2022 19:09:42 +0000 Subject: add required permission android.permission.MEDIA_CONTENT_CONTROL Bug: 222497391 Test: manual build and test on emulator Change-Id: Iff8c31e98f2682f94d225bff71d600d703d088a0 --- DirectRenderingCluster/AndroidManifest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DirectRenderingCluster/AndroidManifest.xml b/DirectRenderingCluster/AndroidManifest.xml index 98c8c69..a73de73 100644 --- a/DirectRenderingCluster/AndroidManifest.xml +++ b/DirectRenderingCluster/AndroidManifest.xml @@ -54,6 +54,8 @@ + + Date: Sat, 4 Jun 2022 00:15:46 +0000 Subject: Update MetadataController constructor parameters Due to api change in car-media-common library. Bug: 233950160 Test: mma Change-Id: I7bcc252f8edda63d8e132835580daf244eef0014 Merged-In: I7bcc252f8edda63d8e132835580daf244eef0014 (cherry picked from commit 2aa1baf586edccf0e7fcc31d8fa2d1d05e0b937e) --- DirectRenderingCluster/src/android/car/cluster/MusicFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/DirectRenderingCluster/src/android/car/cluster/MusicFragment.java b/DirectRenderingCluster/src/android/car/cluster/MusicFragment.java index 540db4d..873fef3 100644 --- a/DirectRenderingCluster/src/android/car/cluster/MusicFragment.java +++ b/DirectRenderingCluster/src/android/car/cluster/MusicFragment.java @@ -94,6 +94,7 @@ public class MusicFragment extends Fragment { trackLength, seekBar, albumIcon, + null, new Size(artSize, artSize) ); -- cgit v1.2.3 From 08699560aac02953e7527a8b74eacbd0cbd48acb Mon Sep 17 00:00:00 2001 From: Yuncheol Heo Date: Mon, 13 Jun 2022 07:34:11 -0700 Subject: Support 3P navigation in ClusterHome. - Populates the UI availabiilty based on the package availability. - Makes cluster display static not to restart ClusterHomeActviity even if ClusterOsDoubleActivity is restarted. Bug: 230051910 Bug: 183115088 Bug: 202019001 Test: do the request focus in KitchesSync and check the Activity is shown in the cluster. Change-Id: Ifcc1c62e059f73aba4f2ee8da39e12fe0892bfc0 --- ClusterHomeSample/AndroidManifest.xml | 2 + .../car/cluster/home/ClusterHomeApplication.java | 159 +++++++++++++++++---- .../cluster/osdouble/ClusterOsDoubleActivity.java | 49 ++++--- 3 files changed, 157 insertions(+), 53 deletions(-) diff --git a/ClusterHomeSample/AndroidManifest.xml b/ClusterHomeSample/AndroidManifest.xml index 1dc7eb5..b2ca15f 100644 --- a/ClusterHomeSample/AndroidManifest.xml +++ b/ClusterHomeSample/AndroidManifest.xml @@ -21,6 +21,8 @@ + + diff --git a/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java b/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java index c85fa41..c0001da 100644 --- a/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java +++ b/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java @@ -35,6 +35,8 @@ import android.app.IActivityTaskManager; import android.app.TaskInfo; import android.app.TaskStackListener; import android.car.Car; +import android.car.CarAppFocusManager; +import android.car.CarAppFocusManager.OnAppFocusChangedListener; import android.car.CarOccupantZoneManager; import android.car.cluster.ClusterActivityState; import android.car.cluster.ClusterHomeManager; @@ -46,40 +48,47 @@ import android.car.user.CarUserManager.UserLifecycleListener; import android.car.user.UserLifecycleEventFilter; import android.content.ComponentName; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.ResolveInfoFlags; +import android.content.pm.ResolveInfo; import android.graphics.Rect; import android.hardware.input.InputManager; import android.os.RemoteException; import android.os.UserHandle; +import android.util.ArraySet; import android.util.Log; import android.view.Display; import android.view.KeyEvent; +import java.util.ArrayList; import java.util.List; public final class ClusterHomeApplication extends Application { public static final String TAG = "ClusterHome"; - private static final boolean DBG = false; + private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final int UI_TYPE_HOME = UI_TYPE_CLUSTER_HOME; private static final int UI_TYPE_MAPS = UI_TYPE_HOME + 1; private static final int UI_TYPE_MUSIC = UI_TYPE_HOME + 2; private static final int UI_TYPE_PHONE = UI_TYPE_HOME + 3; private static final int UI_TYPE_START = UI_TYPE_MAPS; - private static final byte HOME_AVAILABILITY = 1; - private static final byte MAPS_AVAILABILITY = 1; - private static final byte PHONE_AVAILABILITY = 1; - private static final byte MUSIC_AVAILABILITY = 1; + private static final byte UI_UNAVAILABLE = 0; + private static final byte UI_AVAILABLE = 1; + private PackageManager mPackageManager; private IActivityTaskManager mAtm; private InputManager mInputManager; private ClusterHomeManager mHomeManager; private CarUserManager mUserManager; private CarInputManager mCarInputManager; + private CarAppFocusManager mAppFocusManager; private ClusterState mClusterState; private byte mUiAvailability[]; private int mUserLifeCycleEvent = USER_LIFECYCLE_EVENT_TYPE_STARTING; - private ComponentName[] mClusterActivities; + private ArrayList mClusterActivities = new ArrayList<>(); + private int mDefaultClusterActivitySize = 0; private int mLastLaunchedUiType = UI_TYPE_CLUSTER_NONE; private int mLastReportedUiType = UI_TYPE_CLUSTER_NONE; @@ -87,15 +96,16 @@ public final class ClusterHomeApplication extends Application { @Override public void onCreate() { super.onCreate(); - mClusterActivities = new ComponentName[] { - new ComponentName(getApplicationContext(), ClusterHomeActivity.class), - ComponentName.unflattenFromString( - getString(R.string.config_clusterMapActivity)), - ComponentName.unflattenFromString( - getString(R.string.config_clusterMusicActivity)), - ComponentName.unflattenFromString( - getString(R.string.config_clusterPhoneActivity)), - }; + mClusterActivities.add(UI_TYPE_HOME, + new ComponentName(getApplicationContext(), ClusterHomeActivity.class)); + mClusterActivities.add(UI_TYPE_MAPS, + ComponentName.unflattenFromString(getString(R.string.config_clusterMapActivity))); + mClusterActivities.add(UI_TYPE_MUSIC, + ComponentName.unflattenFromString(getString(R.string.config_clusterMusicActivity))); + mClusterActivities.add(UI_TYPE_PHONE, + ComponentName.unflattenFromString(getString(R.string.config_clusterPhoneActivity))); + mDefaultClusterActivitySize = mClusterActivities.size(); + mPackageManager = getApplicationContext().getPackageManager(); mAtm = ActivityTaskManager.getService(); try { mAtm.registerTaskStackListener(mTaskStackListener); @@ -111,6 +121,8 @@ public final class ClusterHomeApplication extends Application { mHomeManager = (ClusterHomeManager) car.getCarManager(Car.CLUSTER_HOME_SERVICE); mUserManager = (CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE); mCarInputManager = (CarInputManager) car.getCarManager(Car.CAR_INPUT_SERVICE); + mAppFocusManager = (CarAppFocusManager) car.getCarManager( + Car.APP_FOCUS_SERVICE); initClusterHome(); }); } @@ -126,7 +138,7 @@ public final class ClusterHomeApplication extends Application { if (!mClusterState.on) { mHomeManager.requestDisplay(UI_TYPE_HOME); } - mUiAvailability = buildUiAvailability(); + mUiAvailability = buildUiAvailability(ActivityManager.getCurrentUser()); mHomeManager.reportState(mClusterState.uiType, UI_TYPE_CLUSTER_NONE, mUiAvailability); mHomeManager.registerClusterStateListener(getMainExecutor(), mClusterHomeCalback); @@ -137,6 +149,9 @@ public final class ClusterHomeApplication extends Application { .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED).build(); mUserManager.addListener(getMainExecutor(), filter, mUserLifecycleListener); + mAppFocusManager.addFocusListener(mAppFocusChangedListener, + CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION); + int r = mCarInputManager.requestInputEventCapture( DISPLAY_TYPE_INSTRUMENT_CLUSTER, new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS}, @@ -175,7 +190,7 @@ public final class ClusterHomeApplication extends Application { return; } mLastLaunchedUiType = uiType; - ComponentName activity = mClusterActivities[uiType]; + ComponentName activity = mClusterActivities.get(uiType); Intent intent = new Intent(ACTION_MAIN).setComponent(activity); if (mClusterState.bounds != null && mClusterState.insets != null) { @@ -195,11 +210,53 @@ public final class ClusterHomeApplication extends Application { mHomeManager.startFixedActivityModeAsUser(intent, options.toBundle(), userId); } - private byte[] buildUiAvailability() { - // TODO(b/183115088): populate uiAvailability based on the package availability - return new byte[] { - HOME_AVAILABILITY, MAPS_AVAILABILITY, PHONE_AVAILABILITY, MUSIC_AVAILABILITY - }; + private void add3PNavigationActivities(int currentUser) { + // Clean up the 3P Navigations from the previous user. + mClusterActivities.subList(mDefaultClusterActivitySize, mClusterActivities.size()).clear(); + + ArraySet clusterPackages = new ArraySet<>(); + for (int i = mDefaultClusterActivitySize - 1; i >= 0; --i) { + clusterPackages.add(mClusterActivities.get(i).getPackageName()); + } + Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Car.CAR_CATEGORY_NAVIGATION); + List resolveList = mPackageManager.queryIntentActivitiesAsUser( + intent, ResolveInfoFlags.of(PackageManager.GET_RESOLVED_FILTER), + UserHandle.of(currentUser)); + for (int i = resolveList.size() - 1; i >= 0; --i) { + ActivityInfo activityInfo = resolveList.get(i).activityInfo; + if (DBG) Log.d(TAG, "Found: " + activityInfo.packageName + "/" + activityInfo.name); + // Some package can have multiple navigation Activities, we choose the default one only. + if (clusterPackages.contains(activityInfo.packageName)) { + if (DBG) { + Log.d(TAG, "Skip this, because another Activity in the package is registered."); + }; + continue; + } + mClusterActivities.add(new ComponentName(activityInfo.packageName, activityInfo.name)); + } + mUiAvailability = buildUiAvailability(currentUser); + } + + private byte[] buildUiAvailability(int currentUser) { + byte[] availability = new byte[mClusterActivities.size()]; + Intent intent = new Intent(ACTION_MAIN); + for (int i = mClusterActivities.size() - 1; i >= 0; --i) { + ComponentName clusterActivity = mClusterActivities.get(i); + if (clusterActivity.getPackageName().equals(getPackageName())) { + // Assume that all Activities in ClusterHome are available. + availability[i] = UI_AVAILABLE; + continue; + } + intent.setComponent(clusterActivity); + ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser( + intent, PackageManager.MATCH_DEFAULT_ONLY, currentUser); + availability[i] = resolveInfo == null ? UI_UNAVAILABLE : UI_AVAILABLE; + if (DBG) { + Log.d(TAG, "availability=" + availability[i] + ", activity=" + clusterActivity + + ", userId=" + currentUser); + } + } + return availability; } private final ClusterStateListener mClusterHomeCalback = new ClusterStateListener() { @@ -255,8 +312,8 @@ public final class ClusterHomeApplication extends Application { } private int identifyTopTask(TaskInfo taskInfo) { - for (int i = mClusterActivities.length - 1; i >=0; --i) { - if (mClusterActivities[i].equals(taskInfo.topActivity)) { + for (int i = mClusterActivities.size() - 1; i >=0; --i) { + if (mClusterActivities.get(i).equals(taskInfo.topActivity)) { return i; } } @@ -269,9 +326,11 @@ public final class ClusterHomeApplication extends Application { mUserLifeCycleEvent = event.getEventType(); if (mUserLifeCycleEvent == USER_LIFECYCLE_EVENT_TYPE_STARTING) { startClusterActivity(UI_TYPE_HOME); - } else if (UI_TYPE_HOME != UI_TYPE_START - && mUserLifeCycleEvent == USER_LIFECYCLE_EVENT_TYPE_UNLOCKED) { - startClusterActivity(UI_TYPE_START); + } else if (mUserLifeCycleEvent == USER_LIFECYCLE_EVENT_TYPE_UNLOCKED) { + add3PNavigationActivities(event.getUserId()); + if (UI_TYPE_START != UI_TYPE_HOME) { + startClusterActivity(UI_TYPE_START); + } } }; @@ -287,11 +346,55 @@ public final class ClusterHomeApplication extends Application { if (DBG) Log.d(TAG, "onKeyEvent: " + keyEvent); if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MENU) { if (keyEvent.getAction() != KeyEvent.ACTION_DOWN) return; - int nextUiType = (mLastLaunchedUiType + 1) % mUiAvailability.length; + int nextUiType; + do { + nextUiType = (mLastLaunchedUiType + 1) % mUiAvailability.length; + } while (mUiAvailability[nextUiType] == UI_UNAVAILABLE); startClusterActivity(nextUiType); return; } // Use Android InputManager to forward KeyEvent. mInputManager.injectInputEvent(keyEvent, INJECT_INPUT_EVENT_MODE_ASYNC); } + + private OnAppFocusChangedListener mAppFocusChangedListener = new OnAppFocusChangedListener() { + @Override + public void onAppFocusChanged(int appType, boolean active) { + if (!active || appType != CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION) { + return; + } + int navigationUi = getFocusedNavigationUi(); + if (navigationUi != UI_TYPE_CLUSTER_NONE) { + startClusterActivity(navigationUi); + } + } + }; + + private int getFocusedNavigationUi() { + List focusOwnerPackageNames = mAppFocusManager.getAppTypeOwner( + CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION); + if (focusOwnerPackageNames == null || focusOwnerPackageNames.isEmpty()) { + Log.e(TAG, "Can't find the navigation owner"); + return UI_TYPE_CLUSTER_NONE; + } + for (int i = 0; i < focusOwnerPackageNames.size(); ++i) { + String focusOwnerPackage = focusOwnerPackageNames.get(i); + for (int j = mClusterActivities.size() - 1; j >= 0; --j) { + if (mUiAvailability[j] == UI_UNAVAILABLE) { + continue; + } + if (mClusterActivities.get(j).getPackageName().equals(focusOwnerPackage)) { + if (DBG) { + Log.d(TAG, "Found focused NavigationUI: " + j + + ", package=" + focusOwnerPackage); + } + return j; + } + } + } + Log.e(TAG, "Can't find the navigation UI for " + + String.join(", ", focusOwnerPackageNames) + "."); + return UI_TYPE_CLUSTER_NONE; + } + } diff --git a/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java b/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java index 8ba7eb3..3671b5d 100644 --- a/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java +++ b/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java @@ -20,13 +20,12 @@ import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; import static android.car.cluster.ClusterHomeManager.UI_TYPE_CLUSTER_HOME; import static android.car.cluster.ClusterHomeManager.UI_TYPE_CLUSTER_NONE; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; -import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; import static com.android.car.cluster.osdouble.ClusterOsDoubleApplication.TAG; import android.car.Car; -import android.car.cluster.navigation.NavigationState.NavigationStateProto; import android.car.VehiclePropertyIds; +import android.car.cluster.navigation.NavigationState.NavigationStateProto; import android.car.hardware.CarPropertyValue; import android.car.hardware.property.CarPropertyManager; import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback; @@ -36,6 +35,7 @@ import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.os.Bundle; import android.util.ArrayMap; +import android.util.IntArray; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; @@ -83,19 +83,23 @@ public class ClusterOsDoubleActivity extends ComponentActivity { private static final int VENDOR_CLUSTER_DISPLAY_STATE = toVendorId( VehiclePropertyIds.CLUSTER_DISPLAY_STATE); + // For the detail, please refer to vehicle/2.0/types.hal. + private static final int REPORT_STATE_MAIN_UI_INDEX = 9; + private static final int REPORT_STATE_UI_AVAILABILITY_INDEX = 11; + private DisplayManager mDisplayManager; private CarPropertyManager mPropertyManager; private SurfaceView mSurfaceView; private Rect mBounds; private Insets mInsets; - private VirtualDisplay mVirtualDisplay; + private static VirtualDisplay sVirtualDisplay; private ClusterViewModel mClusterViewModel; private final ArrayMap mGearsToIcon = new ArrayMap<>(); private final ArrayList mUiToButton = new ArrayList<>(); int mCurrentUi = UI_TYPE_CLUSTER_HOME; - int mTotalUiSize; + private final IntArray mUiAvailability = new IntArray(); private NavStateController mNavStateController; @@ -143,15 +147,6 @@ public class ClusterOsDoubleActivity extends ComponentActivity { findViewById(R.id.navigation_state), imageResolver); } - @Override - protected void onDestroy() { - if (mVirtualDisplay != null) { - mVirtualDisplay.release(); - mVirtualDisplay = null; - } - super.onDestroy(); - } - private final SurfaceHolder.Callback mSurfaceViewCallback = new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { @@ -173,10 +168,10 @@ public class ClusterOsDoubleActivity extends ComponentActivity { // Adds some empty space in the boundary of the display to verify if mBounds works. mBounds.inset(/* dx= */ 12, /* dy= */ 12); mInsets = Insets.of(obscuredWidth, obscuredHeight, obscuredWidth, obscuredHeight); - if (mVirtualDisplay == null) { - mVirtualDisplay = createVirtualDisplay(holder.getSurface(), width, height); + if (sVirtualDisplay == null) { + sVirtualDisplay = createVirtualDisplay(holder.getSurface(), width, height); } else { - mVirtualDisplay.setSurface(holder.getSurface()); + sVirtualDisplay.setSurface(holder.getSurface()); } } @@ -185,7 +180,7 @@ public class ClusterOsDoubleActivity extends ComponentActivity { Log.i(TAG, "surfaceDestroyed, holder: " + holder + ", detaching surface from" + " display, surface: " + holder.getSurface()); // detaching surface is similar to turning off the display - mVirtualDisplay.setSurface(null); + sVirtualDisplay.setSurface(null); } }; @@ -234,15 +229,17 @@ public class ClusterOsDoubleActivity extends ComponentActivity { if (values.length < 11) { throw new IllegalArgumentException("Insufficient size of CLUSTER_REPORT_STATE"); } - // mainUI is the 10th element, refer to vehicle/2.0/types.hal. - int mainUi = (Integer) values[9]; - if (mainUi >= 0 && mainUi < mTotalUiSize) { - selectUiButton(mainUi); + int mainUi = (Integer) values[REPORT_STATE_MAIN_UI_INDEX]; + int totalUiSize = values.length - REPORT_STATE_UI_AVAILABILITY_INDEX; + mUiAvailability.resize(totalUiSize); + for (int i = 0; i < totalUiSize; ++i) { + mUiAvailability.set(i, (Byte) values[i + REPORT_STATE_UI_AVAILABILITY_INDEX]); } + selectUiButton(mainUi); } private void selectUiButton(int mainUi) { - for (int i = 0; i < mTotalUiSize; ++i) { + for (int i = mUiToButton.size() - 1; i >= 0; --i) { View button = mUiToButton.get(i); button.setSelected(i == mainUi); } @@ -265,7 +262,6 @@ public class ClusterOsDoubleActivity extends ComponentActivity { sendDisplayState(); } - private static int toVendorId(int propId) { return (propId & ~MASK) | VENDOR; } @@ -300,7 +296,6 @@ public class ClusterOsDoubleActivity extends ComponentActivity { private void registerUi(View view) { int currentUi = mUiToButton.size(); mUiToButton.add(view); - mTotalUiSize = mUiToButton.size(); view.setOnTouchListener((v, event) -> { if (event.getAction() == MotionEvent.ACTION_DOWN) { Log.d(TAG, "onTouch: " + currentUi); @@ -329,7 +324,11 @@ public class ClusterOsDoubleActivity extends ComponentActivity { public boolean onKeyDown(int keyCode, KeyEvent event) { Log.d(TAG, "onKeyDown: " + keyCode); if (keyCode == KeyEvent.KEYCODE_MENU) { - switchUi((mCurrentUi + 1) % mTotalUiSize); + int nextUi = mCurrentUi; + do { + nextUi = (nextUi + 1) % mUiAvailability.size(); + } while (mUiAvailability.get(nextUi) == 0); + switchUi(nextUi); return true; } return super.onKeyDown(keyCode, event); -- cgit v1.2.3 From 363e8534aa2dcb5d592ec8279e20c9fa78343cb8 Mon Sep 17 00:00:00 2001 From: Robert Engels Date: Thu, 3 Mar 2022 19:09:42 +0000 Subject: add required permission android.permission.MEDIA_CONTENT_CONTROL Bug: 222497391 Test: manual build and test on emulator Change-Id: Iff8c31e98f2682f94d225bff71d600d703d088a0 Merged-In: Iff8c31e98f2682f94d225bff71d600d703d088a0 --- DirectRenderingCluster/AndroidManifest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DirectRenderingCluster/AndroidManifest.xml b/DirectRenderingCluster/AndroidManifest.xml index 6f1ce2b..1907ce9 100644 --- a/DirectRenderingCluster/AndroidManifest.xml +++ b/DirectRenderingCluster/AndroidManifest.xml @@ -54,6 +54,8 @@ + + Date: Fri, 19 Aug 2022 10:36:48 -0700 Subject: Drop the minSdkVersion from DirectRenderingCluster. - minSdkVersion is useless, because Cluster app is an embedded app. Bug: 242060238 Test: Check the dialog is showing. Change-Id: Ibd42f0cf20b04d8f082d4e51d2d9737d9703f28e --- DirectRenderingCluster/AndroidManifest.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/DirectRenderingCluster/AndroidManifest.xml b/DirectRenderingCluster/AndroidManifest.xml index 1907ce9..9808f77 100644 --- a/DirectRenderingCluster/AndroidManifest.xml +++ b/DirectRenderingCluster/AndroidManifest.xml @@ -20,8 +20,6 @@ android:process="android.car.cluster" android:sharedUserId="android.uid.cluster"> - - + + -- cgit v1.2.3 From ff50b60cc9dd1139b46dcf7cf9bb68b136a358b0 Mon Sep 17 00:00:00 2001 From: Yuncheol Heo Date: Tue, 20 Sep 2022 11:51:05 -0700 Subject: Support the private cluster display in ClusterOsDouble. Bug: 237787920 Test: Check if ClusterOsDobule works with private display. Change-Id: I25561728e9e2c7655786728f2968eab4275cb003 --- ClusterOsDouble/AndroidManifest.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ClusterOsDouble/AndroidManifest.xml b/ClusterOsDouble/AndroidManifest.xml index 5e8d334..e57b260 100644 --- a/ClusterOsDouble/AndroidManifest.xml +++ b/ClusterOsDouble/AndroidManifest.xml @@ -13,9 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. --> - + package="com.android.car.cluster.osdouble" + android:sharedUserId="android.uid.system"> + -- cgit v1.2.3 From b73d96c6d917b578a83d4b170c991d70d9a2388d Mon Sep 17 00:00:00 2001 From: Yuncheol Heo Date: Tue, 20 Sep 2022 12:59:21 -0700 Subject: Support the private display in DirectRenderingCluster. Bug: 237787920 Test: Check if DirectRenderingCluster works with the private display Change-Id: I00a2c78dbf942f6ba9bc82eb4c2dad6ec2f649aa --- DirectRenderingCluster/AndroidManifest.xml | 3 ++ .../car/cluster/ClusterDisplayProvider.java | 54 ++++++++-------------- 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/DirectRenderingCluster/AndroidManifest.xml b/DirectRenderingCluster/AndroidManifest.xml index d34a888..c4eaa87 100644 --- a/DirectRenderingCluster/AndroidManifest.xml +++ b/DirectRenderingCluster/AndroidManifest.xml @@ -42,8 +42,11 @@ + + + diff --git a/DirectRenderingCluster/src/android/car/cluster/ClusterDisplayProvider.java b/DirectRenderingCluster/src/android/car/cluster/ClusterDisplayProvider.java index 4050099..5949a50 100644 --- a/DirectRenderingCluster/src/android/car/cluster/ClusterDisplayProvider.java +++ b/DirectRenderingCluster/src/android/car/cluster/ClusterDisplayProvider.java @@ -18,7 +18,6 @@ package android.car.cluster; import android.car.Car; import android.car.CarOccupantZoneManager; -import android.car.CarOccupantZoneManager.OccupantZoneInfo; import android.content.Context; import android.hardware.display.DisplayManager.DisplayListener; import android.util.Log; @@ -26,8 +25,6 @@ import android.view.Display; import com.android.internal.util.Preconditions; -import java.util.List; - /** * This class provides a display for instrument cluster renderer. *

@@ -37,7 +34,7 @@ import java.util.List; */ public class ClusterDisplayProvider { private static final String TAG = "Cluster.DisplayProvider"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final DisplayListener mListener; private final Car mCar; @@ -66,35 +63,30 @@ public class ClusterDisplayProvider { Preconditions.checkArgument( occupantZoneManager != null,"Can't get CarOccupantZoneManager"); mOccupantZoneManager = occupantZoneManager; - checkClusterDisplayAdded(); + checkClusterDisplayChanged(); mOccupantZoneManager.registerOccupantZoneConfigChangeListener( new ClusterDisplayChangeListener()); } - private void checkClusterDisplayAdded() { - Display clusterDisplay = getClusterDisplay(); - if (clusterDisplay != null) { - Log.i(TAG, String.format("Found display: %s (id: %d, owner: %s)", - clusterDisplay.getName(), clusterDisplay.getDisplayId(), - clusterDisplay.getOwnerPackageName())); - mClusterDisplayId = clusterDisplay.getDisplayId(); - mListener.onDisplayAdded(clusterDisplay.getDisplayId()); + private void checkClusterDisplayChanged() { + int clusterDisplayId = getClusterDisplayId(); + if (clusterDisplayId == mClusterDisplayId) { + return; + } + if (mClusterDisplayId != Display.INVALID_DISPLAY) { + Log.i(TAG, "Cluster display is removed"); + mListener.onDisplayRemoved(mClusterDisplayId); + } + mClusterDisplayId = clusterDisplayId; + if (clusterDisplayId != Display.INVALID_DISPLAY) { + Log.i(TAG, "Found cluster displayId=" + clusterDisplayId); + mListener.onDisplayAdded(clusterDisplayId); } } - private Display getClusterDisplay() { - List zones = mOccupantZoneManager.getAllOccupantZones(); - int zones_size = zones.size(); - for (int i = 0; i < zones_size; ++i) { - OccupantZoneInfo zone = zones.get(i); - // Assumes that a Car has only one driver. - if (zone.occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { - return mOccupantZoneManager.getDisplayForOccupant( - zone, CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER); - } - } - Log.e(TAG, "Can't find the OccupantZoneInfo for driver"); - return null; + private int getClusterDisplayId() { + return mOccupantZoneManager.getDisplayIdForDriver( + CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER); } private final class ClusterDisplayChangeListener implements @@ -105,15 +97,7 @@ public class ClusterDisplayProvider { if ((changeFlags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY) == 0) { return; } - if (mClusterDisplayId == Display.INVALID_DISPLAY) { - checkClusterDisplayAdded(); - } else { - Display clusterDisplay = getClusterDisplay(); - if (clusterDisplay == null) { - mListener.onDisplayRemoved(mClusterDisplayId); - mClusterDisplayId = Display.INVALID_DISPLAY; - } - } + checkClusterDisplayChanged(); } } -- cgit v1.2.3 From 906df4920c4974fd18ae10b1080150571686165a Mon Sep 17 00:00:00 2001 From: Yuncheol Heo Date: Tue, 20 Sep 2022 11:51:05 -0700 Subject: Support the private cluster display in ClusterOsDouble. Bug: 237787920 Test: Check if ClusterOsDobule works with private display. Change-Id: I25561728e9e2c7655786728f2968eab4275cb003 (cherry picked from commit ff50b60cc9dd1139b46dcf7cf9bb68b136a358b0) Merged-In: I25561728e9e2c7655786728f2968eab4275cb003 --- ClusterOsDouble/AndroidManifest.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ClusterOsDouble/AndroidManifest.xml b/ClusterOsDouble/AndroidManifest.xml index 5e8d334..e57b260 100644 --- a/ClusterOsDouble/AndroidManifest.xml +++ b/ClusterOsDouble/AndroidManifest.xml @@ -13,9 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. --> - + package="com.android.car.cluster.osdouble" + android:sharedUserId="android.uid.system"> + -- cgit v1.2.3 From f8819ed7e14299e98c9a92db413827b9be3ee473 Mon Sep 17 00:00:00 2001 From: Yuncheol Heo Date: Tue, 20 Sep 2022 12:59:21 -0700 Subject: Support the private display in DirectRenderingCluster. Bug: 237787920 Test: Check if DirectRenderingCluster works with the private display Change-Id: I00a2c78dbf942f6ba9bc82eb4c2dad6ec2f649aa (cherry picked from commit b73d96c6d917b578a83d4b170c991d70d9a2388d) Merged-In: I00a2c78dbf942f6ba9bc82eb4c2dad6ec2f649aa --- DirectRenderingCluster/AndroidManifest.xml | 3 ++ .../car/cluster/ClusterDisplayProvider.java | 54 ++++++++-------------- 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/DirectRenderingCluster/AndroidManifest.xml b/DirectRenderingCluster/AndroidManifest.xml index 809fa3f..f972e97 100644 --- a/DirectRenderingCluster/AndroidManifest.xml +++ b/DirectRenderingCluster/AndroidManifest.xml @@ -42,8 +42,11 @@ + + + diff --git a/DirectRenderingCluster/src/android/car/cluster/ClusterDisplayProvider.java b/DirectRenderingCluster/src/android/car/cluster/ClusterDisplayProvider.java index 4050099..5949a50 100644 --- a/DirectRenderingCluster/src/android/car/cluster/ClusterDisplayProvider.java +++ b/DirectRenderingCluster/src/android/car/cluster/ClusterDisplayProvider.java @@ -18,7 +18,6 @@ package android.car.cluster; import android.car.Car; import android.car.CarOccupantZoneManager; -import android.car.CarOccupantZoneManager.OccupantZoneInfo; import android.content.Context; import android.hardware.display.DisplayManager.DisplayListener; import android.util.Log; @@ -26,8 +25,6 @@ import android.view.Display; import com.android.internal.util.Preconditions; -import java.util.List; - /** * This class provides a display for instrument cluster renderer. *

@@ -37,7 +34,7 @@ import java.util.List; */ public class ClusterDisplayProvider { private static final String TAG = "Cluster.DisplayProvider"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final DisplayListener mListener; private final Car mCar; @@ -66,35 +63,30 @@ public class ClusterDisplayProvider { Preconditions.checkArgument( occupantZoneManager != null,"Can't get CarOccupantZoneManager"); mOccupantZoneManager = occupantZoneManager; - checkClusterDisplayAdded(); + checkClusterDisplayChanged(); mOccupantZoneManager.registerOccupantZoneConfigChangeListener( new ClusterDisplayChangeListener()); } - private void checkClusterDisplayAdded() { - Display clusterDisplay = getClusterDisplay(); - if (clusterDisplay != null) { - Log.i(TAG, String.format("Found display: %s (id: %d, owner: %s)", - clusterDisplay.getName(), clusterDisplay.getDisplayId(), - clusterDisplay.getOwnerPackageName())); - mClusterDisplayId = clusterDisplay.getDisplayId(); - mListener.onDisplayAdded(clusterDisplay.getDisplayId()); + private void checkClusterDisplayChanged() { + int clusterDisplayId = getClusterDisplayId(); + if (clusterDisplayId == mClusterDisplayId) { + return; + } + if (mClusterDisplayId != Display.INVALID_DISPLAY) { + Log.i(TAG, "Cluster display is removed"); + mListener.onDisplayRemoved(mClusterDisplayId); + } + mClusterDisplayId = clusterDisplayId; + if (clusterDisplayId != Display.INVALID_DISPLAY) { + Log.i(TAG, "Found cluster displayId=" + clusterDisplayId); + mListener.onDisplayAdded(clusterDisplayId); } } - private Display getClusterDisplay() { - List zones = mOccupantZoneManager.getAllOccupantZones(); - int zones_size = zones.size(); - for (int i = 0; i < zones_size; ++i) { - OccupantZoneInfo zone = zones.get(i); - // Assumes that a Car has only one driver. - if (zone.occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { - return mOccupantZoneManager.getDisplayForOccupant( - zone, CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER); - } - } - Log.e(TAG, "Can't find the OccupantZoneInfo for driver"); - return null; + private int getClusterDisplayId() { + return mOccupantZoneManager.getDisplayIdForDriver( + CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER); } private final class ClusterDisplayChangeListener implements @@ -105,15 +97,7 @@ public class ClusterDisplayProvider { if ((changeFlags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY) == 0) { return; } - if (mClusterDisplayId == Display.INVALID_DISPLAY) { - checkClusterDisplayAdded(); - } else { - Display clusterDisplay = getClusterDisplay(); - if (clusterDisplay == null) { - mListener.onDisplayRemoved(mClusterDisplayId); - mClusterDisplayId = Display.INVALID_DISPLAY; - } - } + checkClusterDisplayChanged(); } } -- cgit v1.2.3 From 1281326ec5a054049dc003f91e4d0ac2aaa09f42 Mon Sep 17 00:00:00 2001 From: Marcell Kovacs Date: Mon, 17 Oct 2022 15:38:18 -0700 Subject: Add config to specify the display's unique id This change adds an overlayable string that can specify the unque id of the cluster display, when the port is set to -1. Test: m -j && emulator Bug: 239965306 Change-Id: Ie4511ab72b15c7b82bb7dfd237ee645f1dd52635 --- ClusterOsDouble/res/values/config.xml | 5 +- ClusterOsDouble/res/values/overlayable.xml | 1 + .../osdouble/ClusterOsDoubleApplication.java | 137 +++++++++++++++++---- 3 files changed, 116 insertions(+), 27 deletions(-) diff --git a/ClusterOsDouble/res/values/config.xml b/ClusterOsDouble/res/values/config.xml index 7f1cb52..e85c206 100644 --- a/ClusterOsDouble/res/values/config.xml +++ b/ClusterOsDouble/res/values/config.xml @@ -18,5 +18,8 @@ - 1 + -1 + + + diff --git a/ClusterOsDouble/res/values/overlayable.xml b/ClusterOsDouble/res/values/overlayable.xml index 465c39f..329ffee 100644 --- a/ClusterOsDouble/res/values/overlayable.xml +++ b/ClusterOsDouble/res/values/overlayable.xml @@ -19,6 +19,7 @@ + diff --git a/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleApplication.java b/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleApplication.java index dd102c7..4b63a9a 100644 --- a/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleApplication.java +++ b/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleApplication.java @@ -18,12 +18,16 @@ package com.android.car.cluster.osdouble; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import android.annotation.NonNull; import android.app.ActivityOptions; import android.app.Application; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.hardware.display.DisplayManager; +import android.os.Handler; +import android.os.Looper; +import android.text.TextUtils; import android.util.Log; import android.view.Display; import android.view.DisplayAddress; @@ -34,45 +38,126 @@ import android.view.DisplayAddress; public class ClusterOsDoubleApplication extends Application { public static final String TAG = "ClusterOsDouble"; - private DisplayManager mDisplayManager; - @Override public void onCreate() { super.onCreate(); Context context = getApplicationContext(); - mDisplayManager = context.getSystemService(DisplayManager.class); int displayPort = context.getResources().getInteger(R.integer.config_clusterDisplayPort); - if (displayPort == 0) { - Log.e(TAG, "Invalid resource: config_clusterDisplayPort"); - // Won't throw the exception, if so, it'll restart the application continuously, - // because this is the persistent application. + String displayUniqueId = context.getResources().getString( + R.string.config_clusterDisplayUniqueId); + + if (displayPort <= 0 && TextUtils.isEmpty(displayUniqueId)) { + Log.e(TAG, "Cluster display isn't configured."); return; } - int displayId = findDisplay(displayPort); - if (displayId == Display.INVALID_DISPLAY) { - Log.e(TAG, "Can't find the display with portId: " + displayPort); - return; + + DisplayManager displayManager = context.getSystemService(DisplayManager.class); + ClusterDisplayMonitor clusterDisplayMonitor = new ClusterDisplayMonitor(context, + displayManager, displayPort, displayUniqueId); + clusterDisplayMonitor.start(new Handler(Looper.myLooper())); + } + + /** + * Monitors displays and starts the cluster activity when the correct display becomes available. + */ + private static class ClusterDisplayMonitor { + private final Context mContext; + private final DisplayManager mDisplayManager; + private final int mDisplayPort; + private final String mDisplayUniqueId; + + private final DisplayManager.DisplayListener mDisplayListener = + new DisplayManager.DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { + int clusterDisplayId = findClusterDisplayId(); + if (clusterDisplayId == displayId) { + Log.d(TAG, "Display " + displayId + " was added. Starting cluster."); + onDisplayReadyForCluster(displayId); + } + } + + @Override + public void onDisplayRemoved(int displayId) { + // No-op + } + + @Override + public void onDisplayChanged(int displayId) { + // No-op + } + }; + + public ClusterDisplayMonitor(Context context, DisplayManager displayManager, + int displayPort, String displayUniqueId) { + mContext = context; + mDisplayManager = displayManager; + mDisplayPort = displayPort; + mDisplayUniqueId = displayUniqueId; + } + + public void start(Handler handler) { + int clusterDisplayId = findClusterDisplayId(); + if (clusterDisplayId != Display.INVALID_DISPLAY) { + onDisplayReadyForCluster(clusterDisplayId); + } + // This listener will never get unregistered. This is only ok as long as this is a + // persistent app that is not expected to stop. + mDisplayManager.registerDisplayListener(mDisplayListener, handler); } - Intent intent = Intent.makeMainActivity( - ComponentName.createRelative(context, ClusterOsDoubleActivity.class.getName())); - intent.addFlags(FLAG_ACTIVITY_NEW_TASK); + private void onDisplayReadyForCluster(int displayId) { + Intent intent = Intent.makeMainActivity( + ComponentName.createRelative(mContext, + ClusterOsDoubleActivity.class.getName())); + intent.addFlags(FLAG_ACTIVITY_NEW_TASK); - ActivityOptions options = ActivityOptions.makeBasic().setLaunchDisplayId(displayId); - context.startActivity(intent, options.toBundle()); - } + ActivityOptions options = ActivityOptions.makeBasic().setLaunchDisplayId(displayId); + mContext.startActivity(intent, options.toBundle()); + } - private int findDisplay(int displayPort) { - for (Display display : mDisplayManager.getDisplays()) { - DisplayAddress address = display.getAddress(); - if (!(address instanceof DisplayAddress.Physical)) { - continue; + private int findClusterDisplayId() { + int displayId = Display.INVALID_DISPLAY; + if (mDisplayPort > 0) { + displayId = findDisplayByPort(mDisplayPort); + if (displayId == Display.INVALID_DISPLAY) { + Log.e(TAG, "Can't find the display with portId: " + mDisplayPort); + } + } else if (!TextUtils.isEmpty(mDisplayUniqueId)) { + displayId = findDisplayIdByUniqueId(mDisplayUniqueId); + if (displayId == Display.INVALID_DISPLAY) { + Log.e(TAG, "Can't find the display with uniqueId: " + mDisplayUniqueId); + } + } else { + // This should not ever happen. + Log.wtf(TAG, "No valid cluster display configs found."); } - DisplayAddress.Physical physical = (DisplayAddress.Physical) address; - if (physical.getPort() == displayPort) { - return display.getDisplayId(); + + return displayId; + } + + private int findDisplayIdByUniqueId(@NonNull String displayUniqueId) { + for (Display display : mDisplayManager.getDisplays()) { + if (displayUniqueId.equals(display.getUniqueId())) { + return display.getDisplayId(); + } } + return Display.INVALID_DISPLAY; + } + + private int findDisplayByPort(int displayPort) { + for (Display display : mDisplayManager.getDisplays()) { + DisplayAddress address = display.getAddress(); + if (!(address instanceof DisplayAddress.Physical)) { + continue; + } + DisplayAddress.Physical physical = (DisplayAddress.Physical) address; + if (physical.getPort() == displayPort) { + return display.getDisplayId(); + } + } + return Display.INVALID_DISPLAY; } - return Display.INVALID_DISPLAY; } + } -- cgit v1.2.3 From a3ccc46544df2b630b6c339d6b37cfb9d3f7fd16 Mon Sep 17 00:00:00 2001 From: Marcell Kovacs Date: Mon, 17 Oct 2022 22:24:32 -0700 Subject: Create new cluster display when dimensions change Replace existing cluster display when the display size changes (mostly on emulators). Bug: 239965306 Test: lunch gcar_emu_x86_64-userdebug && m -j; resize display on the emulator extended controls UI. Change-Id: I3f0c1cdf1b8ebb8fe91e8a8b2e40bb88c5782c9e --- .../car/cluster/osdouble/ClusterOsDoubleActivity.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java b/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java index 3671b5d..c0b5fd2 100644 --- a/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java +++ b/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java @@ -29,14 +29,17 @@ import android.car.cluster.navigation.NavigationState.NavigationStateProto; import android.car.hardware.CarPropertyValue; import android.car.hardware.property.CarPropertyManager; import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback; +import android.content.res.CompatibilityInfo; import android.graphics.Insets; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.os.Bundle; import android.util.ArrayMap; +import android.util.DisplayMetrics; import android.util.IntArray; import android.util.Log; +import android.view.DisplayInfo; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; @@ -171,7 +174,21 @@ public class ClusterOsDoubleActivity extends ComponentActivity { if (sVirtualDisplay == null) { sVirtualDisplay = createVirtualDisplay(holder.getSurface(), width, height); } else { - sVirtualDisplay.setSurface(holder.getSurface()); + DisplayInfo displayInfo = new DisplayInfo(); + DisplayMetrics boundsMetrics = new DisplayMetrics(); + boolean isDisplayValid = sVirtualDisplay.getDisplay().getDisplayInfo(displayInfo); + displayInfo.getMaxBoundsMetrics(boundsMetrics, + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, + getResources().getConfiguration()); + if (isDisplayValid && boundsMetrics.widthPixels == width + && boundsMetrics.heightPixels == height) { + sVirtualDisplay.setSurface(holder.getSurface()); + } else { + // Display was resized, delete existing and create new display. + // TODO(b/254931119): Resize the display instead of replacing it. + sVirtualDisplay.release(); + sVirtualDisplay = createVirtualDisplay(holder.getSurface(), width, height); + } } } -- cgit v1.2.3 From 96270dd6e21c3dd85dd0fc0dd956e89ed0495b13 Mon Sep 17 00:00:00 2001 From: Changyeon Jo Date: Tue, 8 Nov 2022 16:22:39 -0800 Subject: Correct the property area ID This change replaces Sensors.GLOBAL_AREA_ID, which is defined with incorrect global area ID value, with VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL. Fix: 258288148 Test: Manually confirm reported fatal error messages do not exist in logcat messages Change-Id: I1172c6921d46ca43e493c9ecc8d31fe160ef036c --- .../src/android/car/cluster/ClusterViewModel.java | 4 +++- .../src/android/car/cluster/sensors/Sensors.java | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/DirectRenderingCluster/src/android/car/cluster/ClusterViewModel.java b/DirectRenderingCluster/src/android/car/cluster/ClusterViewModel.java index 7412ac6..0427b80 100644 --- a/DirectRenderingCluster/src/android/car/cluster/ClusterViewModel.java +++ b/DirectRenderingCluster/src/android/car/cluster/ClusterViewModel.java @@ -15,6 +15,8 @@ */ package android.car.cluster; +import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; + import android.annotation.Nullable; import android.app.Application; import android.car.Car; @@ -131,7 +133,7 @@ public class ClusterViewModel extends AndroidViewModel { } for (Sensor sensorId : Sensors.getInstance() .getSensorsForPropertyId(value.getPropertyId())) { - if (sensorId.mAreaId == Sensors.GLOBAL_AREA_ID + if (sensorId.mAreaId == VEHICLE_AREA_TYPE_GLOBAL || (sensorId.mAreaId & value.getAreaId()) != 0) { setSensorValue(sensorId, value); } diff --git a/DirectRenderingCluster/src/android/car/cluster/sensors/Sensors.java b/DirectRenderingCluster/src/android/car/cluster/sensors/Sensors.java index 25f6572..b0831a1 100644 --- a/DirectRenderingCluster/src/android/car/cluster/sensors/Sensors.java +++ b/DirectRenderingCluster/src/android/car/cluster/sensors/Sensors.java @@ -15,6 +15,8 @@ */ package android.car.cluster.sensors; +import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; + import android.car.VehiclePropertyIds; import android.car.VehiclePropertyType; import android.car.hardware.CarPropertyValue; @@ -31,9 +33,6 @@ import java.util.function.Function; * The collection of all sensors supported by this application. */ public class Sensors { - /** Area identifier used for sensors corresponding to global VHAL properties */ - public static final int GLOBAL_AREA_ID = -1; - private static Sensors sInstance; private static List> sSensors = new ArrayList<>(); private Map>> mSensorsByPropertyId = new HashMap<>(); @@ -48,31 +47,33 @@ public class Sensors { /** Fuel of the car, measured in millimeters */ public static final Sensor SENSOR_FUEL = registerSensor( - "Fuel", VehiclePropertyIds.FUEL_LEVEL, GLOBAL_AREA_ID, VehiclePropertyType.FLOAT, + "Fuel", VehiclePropertyIds.FUEL_LEVEL, VEHICLE_AREA_TYPE_GLOBAL, + VehiclePropertyType.FLOAT, value -> (Float) value.getValue()); /** Fuel capacity of the car, measured in millimeters */ public static final Sensor SENSOR_FUEL_CAPACITY = registerSensor( - "Fuel Capacity", VehiclePropertyIds.INFO_FUEL_CAPACITY, GLOBAL_AREA_ID, + "Fuel Capacity", VehiclePropertyIds.INFO_FUEL_CAPACITY, VEHICLE_AREA_TYPE_GLOBAL, VehiclePropertyType.FLOAT, value -> (Float) value.getValue()); /** RPMs */ public static final Sensor SENSOR_RPM = registerSensor( - "RPM", VehiclePropertyIds.ENGINE_RPM, GLOBAL_AREA_ID, + "RPM", VehiclePropertyIds.ENGINE_RPM, VEHICLE_AREA_TYPE_GLOBAL, VehiclePropertyType.FLOAT, value -> (Float) value.getValue()); /** Fuel range in meters */ public static final Sensor SENSOR_FUEL_RANGE = registerSensor( - "Fuel Range", VehiclePropertyIds.RANGE_REMAINING, GLOBAL_AREA_ID, + "Fuel Range", VehiclePropertyIds.RANGE_REMAINING, VEHICLE_AREA_TYPE_GLOBAL, VehiclePropertyType.FLOAT, value -> (Float) value.getValue()); /** Speed in meters per second */ public static final Sensor SENSOR_SPEED = registerSensor( - "Speed", VehiclePropertyIds.PERF_VEHICLE_SPEED, GLOBAL_AREA_ID, + "Speed", VehiclePropertyIds.PERF_VEHICLE_SPEED, VEHICLE_AREA_TYPE_GLOBAL, VehiclePropertyType.FLOAT, value -> (Float) value.getValue()); /** Current gear of the car */ public static final Sensor SENSOR_GEAR = registerSensor( - "Gear", VehiclePropertyIds.GEAR_SELECTION, GLOBAL_AREA_ID, VehiclePropertyType.INT32, + "Gear", VehiclePropertyIds.GEAR_SELECTION, VEHICLE_AREA_TYPE_GLOBAL, + VehiclePropertyType.INT32, value -> { if (value == null) { return null; -- cgit v1.2.3 From 7cd7be35492072474119ddae07294fd337cfecc7 Mon Sep 17 00:00:00 2001 From: Yuncheol Heo Date: Tue, 15 Nov 2022 22:43:40 -0800 Subject: Browse the preinstalled ClusterActivities only with a MENU key. - Some Cluster Activities like Phantascope show nothing until Phone is connected. It's safe to show the preinstalled ClusterActivities only with a MENU key. Bug: 256062472 Test: Send a MENU key to Cluster repeatedly and check if ClusterActivities rotate to the initial Activity. Test: adb shell input -d 2 keyevent KEYCODE_MENU Test: adb shell cmd car_service inject-key -d 1 82 Change-Id: Ib6081cf45ecf2a6d4b68434666a2e4da9c1d90d4 --- .../src/com/android/car/cluster/home/ClusterHomeApplication.java | 4 +++- .../src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java b/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java index c0001da..2171e8a 100644 --- a/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java +++ b/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java @@ -348,7 +348,9 @@ public final class ClusterHomeApplication extends Application { if (keyEvent.getAction() != KeyEvent.ACTION_DOWN) return; int nextUiType; do { - nextUiType = (mLastLaunchedUiType + 1) % mUiAvailability.length; + // Select the Cluster Activity within the preinstalled ones. + nextUiType = mLastLaunchedUiType + 1; + if (nextUiType >= mDefaultClusterActivitySize) nextUiType = 0; } while (mUiAvailability[nextUiType] == UI_UNAVAILABLE); startClusterActivity(nextUiType); return; diff --git a/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java b/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java index 3671b5d..785f9d8 100644 --- a/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java +++ b/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java @@ -326,7 +326,8 @@ public class ClusterOsDoubleActivity extends ComponentActivity { if (keyCode == KeyEvent.KEYCODE_MENU) { int nextUi = mCurrentUi; do { - nextUi = (nextUi + 1) % mUiAvailability.size(); + nextUi = nextUi + 1; + if (nextUi >= mUiToButton.size()) nextUi = 0; } while (mUiAvailability.get(nextUi) == 0); switchUi(nextUi); return true; -- cgit v1.2.3 From 935dd1894aeb214636268b974bf4125dc751a594 Mon Sep 17 00:00:00 2001 From: Yuncheol Heo Date: Mon, 28 Nov 2022 17:07:12 -0800 Subject: Fix the recreating of the Cluster display. DisplayInfo.getMaxBoundsMetrics() with the configuration will return the max bounds of the configuration. To get the correct bounds of the display, use DI.getLogicalMetrics() with null configuration. Bug: 260272318 Test: check the logcat of gcar_md and verify if recreating the cluster display. Change-Id: I74706835d3237e51b16d707dc9269981821e910f --- .../com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java b/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java index 9b5cce0..f86b001 100644 --- a/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java +++ b/ClusterOsDouble/src/com/android/car/cluster/osdouble/ClusterOsDoubleActivity.java @@ -177,9 +177,8 @@ public class ClusterOsDoubleActivity extends ComponentActivity { DisplayInfo displayInfo = new DisplayInfo(); DisplayMetrics boundsMetrics = new DisplayMetrics(); boolean isDisplayValid = sVirtualDisplay.getDisplay().getDisplayInfo(displayInfo); - displayInfo.getMaxBoundsMetrics(boundsMetrics, - CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, - getResources().getConfiguration()); + displayInfo.getLogicalMetrics(boundsMetrics, + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, /* configuration= */ null); if (isDisplayValid && boundsMetrics.widthPixels == width && boundsMetrics.heightPixels == height) { sVirtualDisplay.setSurface(holder.getSurface()); -- cgit v1.2.3 From b6c6359ae8ad0bd3b6db388238d4e40c84e0eee7 Mon Sep 17 00:00:00 2001 From: Tyler Trephan Date: Fri, 23 Dec 2022 00:57:16 +0000 Subject: Fixed null CarPropertyManager/CarPropertyValue handling. Test: Built. Fix: 263513869 Change-Id: Iec5fb29d99bc3b17f883bf5001d7d747f4c4b984 --- .../src/android/car/cluster/ClusterViewModel.java | 19 ++++++++++++------- .../src/android/car/cluster/sensors/Sensors.java | 3 --- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/DirectRenderingCluster/src/android/car/cluster/ClusterViewModel.java b/DirectRenderingCluster/src/android/car/cluster/ClusterViewModel.java index 0427b80..d02bab3 100644 --- a/DirectRenderingCluster/src/android/car/cluster/ClusterViewModel.java +++ b/DirectRenderingCluster/src/android/car/cluster/ClusterViewModel.java @@ -23,6 +23,7 @@ import android.car.Car; import android.car.CarAppFocusManager; import android.car.CarNotConnectedException; import android.car.VehicleAreaType; +import android.car.VehiclePropertyIds; import android.car.cluster.sensors.Sensor; import android.car.cluster.sensors.Sensors; import android.car.hardware.CarPropertyValue; @@ -219,19 +220,23 @@ public class ClusterViewModel extends AndroidViewModel { * Returns the current value of the sensor, directly from the VHAL. * * @param sensor sensor to read - * @param VHAL data type * @param data type of such sensor */ @Nullable public T getSensorValue(@NonNull Sensor sensor) { - try { - CarPropertyValue value = mCarPropertyManager - .getProperty(sensor.mPropertyId, sensor.mAreaId); - return sensor.mAdapter.apply(value); - } catch (CarNotConnectedException ex) { - Log.e(TAG, "We got disconnected from Car Service", ex); + if (mCarPropertyManager == null) { + Log.e(TAG, "CarPropertyManager reference is null, car service is disconnected."); return null; } + CarPropertyValue carPropertyValue = mCarPropertyManager.getProperty(sensor.mPropertyId, + sensor.mAreaId); + if (carPropertyValue == null) { + Log.w(TAG, "Property ID: " + VehiclePropertyIds.toString(sensor.mPropertyId) + + " Area ID: 0x" + Integer.toHexString(sensor.mAreaId) + + " returned null from CarPropertyManager#getProperty()"); + return null; + } + return sensor.mAdapter.apply(carPropertyValue); } /** diff --git a/DirectRenderingCluster/src/android/car/cluster/sensors/Sensors.java b/DirectRenderingCluster/src/android/car/cluster/sensors/Sensors.java index b0831a1..65dea7e 100644 --- a/DirectRenderingCluster/src/android/car/cluster/sensors/Sensors.java +++ b/DirectRenderingCluster/src/android/car/cluster/sensors/Sensors.java @@ -75,9 +75,6 @@ public class Sensors { "Gear", VehiclePropertyIds.GEAR_SELECTION, VEHICLE_AREA_TYPE_GLOBAL, VehiclePropertyType.INT32, value -> { - if (value == null) { - return null; - } Integer gear = (Integer) value.getValue(); if ((gear & CarSensorEvent.GEAR_REVERSE) != 0) { return Gear.REVERSE; -- cgit v1.2.3 From b28649973683d8993a4b9a99533704a2f58a8fe4 Mon Sep 17 00:00:00 2001 From: Arnaud Berry Date: Mon, 13 Feb 2023 17:46:29 -0800 Subject: Update call to MetadataController constructor Follow up with the changes made in ag/19956154 Fixes: 269178793 Test: compiles Change-Id: I6bf08338c64df9bc7abfa467e62c28fbb16945e9 --- DirectRenderingCluster/src/android/car/cluster/MusicFragment.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DirectRenderingCluster/src/android/car/cluster/MusicFragment.java b/DirectRenderingCluster/src/android/car/cluster/MusicFragment.java index 873fef3..ac17371 100644 --- a/DirectRenderingCluster/src/android/car/cluster/MusicFragment.java +++ b/DirectRenderingCluster/src/android/car/cluster/MusicFragment.java @@ -85,6 +85,7 @@ public class MusicFragment extends Fragment { new MetadataController( getViewLifecycleOwner(), playbackViewModel, + null, title, subtitle, null, @@ -95,7 +96,8 @@ public class MusicFragment extends Fragment { seekBar, albumIcon, null, - new Size(artSize, artSize) + new Size(artSize, artSize), + null ); return view; -- cgit v1.2.3 From 98c913e157735161ebbcbc396fa0f333f217a238 Mon Sep 17 00:00:00 2001 From: Yuncheol Heo Date: Mon, 1 May 2023 13:37:56 -0700 Subject: Add a log onClusterStateChanged Bug: 267340172 Test: follow the steps in b/267340172 Change-Id: I91c830f5fdbff6e15cf830be74dcc10621e55b71 --- .../car/cluster/home/ClusterHomeApplication.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java b/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java index 2171e8a..2e656a2 100644 --- a/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java +++ b/ClusterHomeSample/src/com/android/car/cluster/home/ClusterHomeApplication.java @@ -263,6 +263,10 @@ public final class ClusterHomeApplication extends Application { @Override public void onClusterStateChanged( ClusterState state, @ClusterHomeManager.Config int changes) { + if (DBG) { + Log.d(TAG, "onClusterStateChanged: changes=" + Integer.toHexString(changes) + + ", state=" + clusterStateToString(state)); + } mClusterState = state; // We'll restart Activity when the display bounds or insets are changed, to let Activity // redraw itself to fit the changed attributes. @@ -399,4 +403,21 @@ public final class ClusterHomeApplication extends Application { return UI_TYPE_CLUSTER_NONE; } + private static String clusterStateToString(ClusterState state) { + StringBuilder sb = new StringBuilder("ClusterState["); + sb.append("on="); sb.append(state.on); + if (state.bounds != null) { + sb.append(", bounds="); sb.append(state.bounds); + } + if (state.insets != null) { + sb.append(", insets="); sb.append(state.insets); + } + if (state.insets != null) { + sb.append(", insets="); sb.append(state.insets); + } + sb.append(", uiType="); sb.append(state.uiType); + sb.append(", displayId="); sb.append(state.displayId); + sb.append(']'); + return sb.toString(); + } } -- cgit v1.2.3