summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yuncheol Heo <ycheo@google.com> 2020-06-04 16:06:43 -0700
committer Yuncheol Heo <ycheo@google.com> 2020-10-15 15:06:27 -0700
commitb1369818a36feba921a25091c1c18c44ef9b6092 (patch)
tree1f02397b38d9454e5fdb9a30d9c6618ad0f34822
parentbc89f3ea7a1a8eb379f16f8b874727e928f4f669 (diff)
Add ReadViewCameraViewController/Mediator.
- Launch config_rearViewCameraActivity in the overlay Window when gear is reversed. - Add/Remove ActivityView dynamically to save the system resource while it is hidden. Bug: 157481389 Test: atest com.android.systemui.car.rvc Test: Check if RearViewCamera is launched when the gear is reversed and if it will be closed when the gear is changed to the other one. Change-Id: I768fe9090eed9cb82c4108ab8f6335802d36fa64
-rw-r--r--packages/CarSystemUI/AndroidManifest.xml2
-rw-r--r--packages/CarSystemUI/res/layout/rear_view_camera.xml31
-rw-r--r--packages/CarSystemUI/res/layout/sysui_overlay_window.xml8
-rw-r--r--packages/CarSystemUI/res/values/colors.xml2
-rw-r--r--packages/CarSystemUI/res/values/config.xml4
-rw-r--r--packages/CarSystemUI/res/values/dimens.xml4
-rw-r--r--packages/CarSystemUI/res/values/strings.xml2
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewController.java141
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewMediator.java121
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java8
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewControllerTest.java117
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewMediatorTest.java169
12 files changed, 608 insertions, 1 deletions
diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml
index c0482e1b3e04..f0cf2a9b7156 100644
--- a/packages/CarSystemUI/AndroidManifest.xml
+++ b/packages/CarSystemUI/AndroidManifest.xml
@@ -23,6 +23,8 @@
<uses-permission android:name="android.car.permission.CAR_POWER" />
<!-- This permission is required to get the trusted device list of a user. -->
<uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST"/>
+ <!-- This permission is required to monitor gear state. -->
+ <uses-permission android:name="android.car.permission.CAR_POWERTRAIN" />
<!-- This permission is required to get bluetooth broadcast. -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- These permissions are required to implement icons based on role holders. -->
diff --git a/packages/CarSystemUI/res/layout/rear_view_camera.xml b/packages/CarSystemUI/res/layout/rear_view_camera.xml
new file mode 100644
index 000000000000..9b9898cf3f45
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/rear_view_camera.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/rear_view_camera_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/transparent"
+ android:orientation="vertical">
+ <Button
+ android:id="@+id/close_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/rear_view_camera_button_background"
+ android:text="@string/rear_view_camera_close_button_text"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+</LinearLayout>
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 2c9788955bfa..980265e84e54 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -42,4 +42,10 @@
android:layout_height="match_parent"
android:layout="@layout/car_user_switching_dialog"/>
-</FrameLayout> \ No newline at end of file
+ <!-- Should be at bottom to get the highest Z-order. -->
+ <ViewStub android:id="@+id/rear_view_camera_stub"
+ android:layout_width="@dimen/rear_view_camera_width"
+ android:layout_height="@dimen/rear_view_camera_height"
+ android:layout_gravity="center"
+ android:layout="@layout/rear_view_camera"/>
+</FrameLayout>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index 0181b9a39aea..91d416623538 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -66,4 +66,6 @@
<color name="car_user_switching_dialog_background_color">@android:color/black</color>
<color name="car_user_switching_dialog_loading_text_color">@*android:color/car_body1</color>
+
+ <color name="rear_view_camera_button_background">@*android:color/car_card_dark</color>
</resources>
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 949a0fac9598..8667ba1cd1a5 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -108,6 +108,7 @@
<item>com.android.systemui.car.keyguard.CarKeyguardViewMediator</item>
<item>com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator</item>
<item>com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator</item>
+ <item>com.android.systemui.car.rvc.RearViewCameraViewMediator</item>
</string-array>
<!--
@@ -150,4 +151,7 @@
<!-- How many milliseconds to wait before force hiding the UserSwitchTransitionView -->
<integer name="config_userSwitchTransitionViewShownTimeoutMs" translatable="false">5000</integer>
+
+ <!-- The Activity name for the Rear View Camera, if empty, the feature will be disabled. -->
+ <string name="config_rearViewCameraActivity" translatable="false"></string>
</resources>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index 86bfa751d085..a7d8ab5f2a4c 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -227,4 +227,8 @@
<dimen name="car_fullscreen_user_pod_image_avatar_height">96dp</dimen>
<dimen name="car_user_switching_dialog_loading_text_margin_top">@*android:dimen/car_padding_4</dimen>
<dimen name="car_user_switching_dialog_loading_text_font_size">@*android:dimen/car_body1_size</dimen>
+
+ <!-- dimensions for rear view camera -->
+ <dimen name="rear_view_camera_width">600dp</dimen>
+ <dimen name="rear_view_camera_height">500dp</dimen>
</resources>
diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml
index 06ae7cfd6d1b..264456588c80 100644
--- a/packages/CarSystemUI/res/values/strings.xml
+++ b/packages/CarSystemUI/res/values/strings.xml
@@ -42,4 +42,6 @@
<string name="car_loading_profile">Loading</string>
<!-- Message to inform user that the new user profile is loading with additional information on the previous and the next user. [CHAR LIMIT=100] -->
<string name="car_loading_profile_developer_message">Loading user (from <xliff:g id="from_user" example="10">%1$d</xliff:g> to <xliff:g id="to_user" example="12">%2$d</xliff:g>)</string>
+ <!-- Text for the close button in Rear View Camera [CHAR LIMIT=30] -->
+ <string name="rear_view_camera_close_button_text">Close</string>
</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewController.java
new file mode 100644
index 000000000000..d63463309ec5
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewController.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.rvc;
+
+import android.app.ActivityView;
+import android.app.ActivityView.StateCallback;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.util.Slog;
+import android.view.ViewGroup;
+import android.widget.LinearLayout.LayoutParams;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.car.window.OverlayViewController;
+import com.android.systemui.car.window.OverlayViewGlobalStateController;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import javax.inject.Inject;
+
+/** View controller for the rear view camera. */
+@SysUISingleton
+public class RearViewCameraViewController extends OverlayViewController {
+ private static final String TAG = "RearViewCameraView";
+ private static final boolean DBG = false;
+
+ private final ComponentName mRearViewCameraActivity;
+ private ViewGroup mRvcView;
+ private final LayoutParams mRvcViewLayoutParams = new LayoutParams(
+ LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, /* weight= */ 1.0f);
+ @VisibleForTesting
+ ActivityView mActivityView;
+ @VisibleForTesting
+ final StateCallback mActivityViewCallback = new StateCallback() {
+ @Override
+ public void onActivityViewReady(ActivityView view) {
+ Intent intent = new Intent(Intent.ACTION_MAIN)
+ .setComponent(mRearViewCameraActivity)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
+ .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ // TODO(b/170899079): Migrate this to FixedActivityService.
+ view.startActivity(intent);
+ }
+
+ @Override
+ public void onActivityViewDestroyed(ActivityView view) {}
+ };
+
+ @Inject
+ public RearViewCameraViewController(
+ @Main Resources resources,
+ OverlayViewGlobalStateController overlayViewGlobalStateController) {
+ super(R.id.rear_view_camera_stub, overlayViewGlobalStateController);
+ String rearViewCameraActivityName = resources.getString(
+ R.string.config_rearViewCameraActivity);
+ if (!rearViewCameraActivityName.isEmpty()) {
+ mRearViewCameraActivity = ComponentName.unflattenFromString(rearViewCameraActivityName);
+ if (DBG) Slog.d(TAG, "mRearViewCameraActivity=" + mRearViewCameraActivity);
+ } else {
+ mRearViewCameraActivity = null;
+ Slog.e(TAG, "RearViewCameraViewController is disabled, since no Activity is defined");
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mRvcView = (ViewGroup) getLayout().findViewById(R.id.rear_view_camera_container);
+ getLayout().findViewById(R.id.close_button).setOnClickListener(v -> {
+ stop();
+ });
+ }
+
+ @Override
+ protected void hideInternal() {
+ super.hideInternal();
+ if (DBG) Slog.d(TAG, "hideInternal: mActivityView=" + mActivityView);
+ if (mActivityView == null) return;
+ mRvcView.removeView(mActivityView);
+ // Release ActivityView since the Activity on ActivityView (with showWhenLocked flag) keeps
+ // running even if ActivityView is hidden.
+ mActivityView.release();
+ mActivityView = null;
+ }
+
+ @Override
+ protected void showInternal() {
+ super.showInternal();
+ if (DBG) Slog.d(TAG, "showInternal: mActivityView=" + mActivityView);
+ if (mActivityView != null) return;
+ mActivityView = new ActivityView(mRvcView.getContext());
+ mActivityView.setCallback(mActivityViewCallback);
+ mActivityView.setLayoutParams(mRvcViewLayoutParams);
+ mRvcView.addView(mActivityView, /* index= */ 0);
+ }
+
+ boolean isShown() {
+ return mActivityView != null;
+ }
+
+ boolean isEnabled() {
+ return mRearViewCameraActivity != null;
+ }
+
+ @Override
+ protected boolean shouldShowHUN() {
+ return false;
+ }
+
+ @Override
+ protected boolean shouldShowWhenOccluded() {
+ // Returns true to show it on top of Keylock.
+ return true;
+ }
+
+ @Override
+ protected boolean shouldShowNavigationBarInsets() {
+ return true;
+ }
+
+ @Override
+ protected boolean shouldShowStatusBarInsets() {
+ return true;
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewMediator.java
new file mode 100644
index 000000000000..c575c423b256
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewMediator.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.rvc;
+
+import android.car.Car;
+import android.car.VehicleGear;
+import android.car.VehiclePropertyIds;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyManager;
+import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.window.OverlayViewMediator;
+import com.android.systemui.dagger.SysUISingleton;
+
+import javax.inject.Inject;
+
+/**
+ * View mediator for the rear view camera (RVC), which monitors the gear changes and shows
+ * the RVC when the gear position is R and otherwise it hides the RVC.
+ */
+@SysUISingleton
+public class RearViewCameraViewMediator implements OverlayViewMediator {
+ private static final String TAG = "RearViewCameraView";
+ private static final boolean DBG = false;
+
+ private final RearViewCameraViewController mRearViewCameraViewController;
+ private final CarServiceProvider mCarServiceProvider;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+
+ private CarPropertyManager mCarPropertyManager;
+ // TODO(b/170792252): Replace the following with the callback from CarEvsManager if it's ready.
+ private final CarPropertyEventCallback mPropertyEventCallback = new CarPropertyEventCallback() {
+ @Override
+ public void onChangeEvent(CarPropertyValue value) {
+ if (DBG) Slog.d(TAG, "onChangeEvent value=" + value);
+ if (value.getPropertyId() != VehiclePropertyIds.GEAR_SELECTION) {
+ Slog.w(TAG, "Got the event for non-registered property: " + value.getPropertyId());
+ return;
+ }
+ if ((Integer) value.getValue() == VehicleGear.GEAR_REVERSE) {
+ mRearViewCameraViewController.start();
+ } else {
+ mRearViewCameraViewController.stop();
+ }
+ }
+ @Override
+ public void onErrorEvent(int propId, int zone) {
+ Slog.e(TAG, "onErrorEvent propId=" + propId + ", zone=" + zone);
+ }
+ };
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DBG) Slog.d(TAG, "onReceive: " + intent);
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())
+ && mRearViewCameraViewController.isShown()) {
+ mRearViewCameraViewController.stop();
+ }
+ }
+ };
+
+ @Inject
+ public RearViewCameraViewMediator(
+ RearViewCameraViewController rearViewCameraViewController,
+ CarServiceProvider carServiceProvider,
+ BroadcastDispatcher broadcastDispatcher) {
+ if (DBG) Slog.d(TAG, "RearViewCameraViewMediator:init");
+ mRearViewCameraViewController = rearViewCameraViewController;
+ mCarServiceProvider = carServiceProvider;
+ mBroadcastDispatcher = broadcastDispatcher;
+ }
+
+ @Override
+ public void registerListeners() {
+ if (DBG) Slog.d(TAG, "RearViewCameraViewMediator:registerListeners");
+ if (!mRearViewCameraViewController.isEnabled()) {
+ Slog.i(TAG, "RearViewCameraViewController isn't enabled");
+ return;
+ }
+
+ mCarServiceProvider.addListener(car -> {
+ mCarPropertyManager = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
+ if (mCarPropertyManager == null) {
+ Slog.e(TAG, "Unable to get CarPropertyManager");
+ return;
+ }
+ if (DBG) Slog.d(TAG, "Registering mPropertyEventCallback.");
+ mCarPropertyManager.registerCallback(mPropertyEventCallback,
+ VehiclePropertyIds.GEAR_SELECTION, CarPropertyManager.SENSOR_RATE_UI);
+ });
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), /* executor= */ null,
+ UserHandle.ALL);
+ }
+
+ @Override
+ public void setupOverlayContentViewControllers() {}
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
index 5a16efa3dd9b..fcbb0b807e74 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
@@ -20,6 +20,7 @@ import com.android.systemui.car.keyguard.CarKeyguardViewMediator;
import com.android.systemui.car.notification.BottomNotificationPanelViewMediator;
import com.android.systemui.car.notification.NotificationPanelViewMediator;
import com.android.systemui.car.notification.TopNotificationPanelViewMediator;
+import com.android.systemui.car.rvc.RearViewCameraViewMediator;
import com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator;
import com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator;
@@ -75,4 +76,11 @@ public abstract class OverlayWindowModule {
@ClassKey(UserSwitchTransitionViewMediator.class)
public abstract OverlayViewMediator bindUserSwitchTransitionViewMediator(
UserSwitchTransitionViewMediator userSwitchTransitionViewMediator);
+
+ /** Injects RearViewCameraViewMediator. */
+ @Binds
+ @IntoMap
+ @ClassKey(RearViewCameraViewMediator.class)
+ public abstract OverlayViewMediator bindRearViewCameraViewMediator(
+ RearViewCameraViewMediator overlayViewsMediator);
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewControllerTest.java
new file mode 100644
index 000000000000..a6160ecf1c62
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewControllerTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.rvc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityView;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarSystemUiTest;
+import com.android.systemui.car.window.OverlayViewGlobalStateController;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@CarSystemUiTest
+@RunWith(MockitoJUnitRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class RearViewCameraViewControllerTest extends SysuiTestCase {
+ private static final String TEST_ACTIVITY_NAME = "testPackage/testActivity";
+ private RearViewCameraViewController mRearViewCameraViewController;
+
+ @Mock
+ private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
+ @Mock
+ private ActivityView mMockActivityView;
+ @Captor
+ private ArgumentCaptor<Intent> mIntentCaptor;
+
+ private void setUpRearViewCameraViewController(String testActivityName) {
+ mContext.getOrCreateTestableResources().addOverride(
+ R.string.config_rearViewCameraActivity, testActivityName);
+ mRearViewCameraViewController = new RearViewCameraViewController(
+ mContext.getOrCreateTestableResources().getResources(),
+ mOverlayViewGlobalStateController);
+ mRearViewCameraViewController.inflate((ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.sysui_overlay_window, /* root= */ null));
+ }
+
+ @Test
+ public void testEmptyResourceDisablesController() {
+ setUpRearViewCameraViewController("");
+
+ assertThat(mRearViewCameraViewController.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void testNonEmptyResourceEnablesController() {
+ setUpRearViewCameraViewController(TEST_ACTIVITY_NAME);
+
+ assertThat(mRearViewCameraViewController.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void testShowInternal() {
+ setUpRearViewCameraViewController(TEST_ACTIVITY_NAME);
+ assertThat(mRearViewCameraViewController.isShown()).isFalse();
+ assertThat(mRearViewCameraViewController.mActivityView).isNull();
+
+ mRearViewCameraViewController.showInternal();
+
+ assertThat(mRearViewCameraViewController.isShown()).isTrue();
+ assertThat(mRearViewCameraViewController.mActivityView).isNotNull();
+ }
+
+ @Test
+ public void testHideInternal() {
+ setUpRearViewCameraViewController(TEST_ACTIVITY_NAME);
+ assertThat(mRearViewCameraViewController.isShown()).isFalse();
+ mRearViewCameraViewController.showInternal();
+ assertThat(mRearViewCameraViewController.isShown()).isTrue();
+
+ mRearViewCameraViewController.hideInternal();
+
+ assertThat(mRearViewCameraViewController.isShown()).isFalse();
+ assertThat(mRearViewCameraViewController.mActivityView).isNull();
+ }
+
+ @Test
+ public void testOnActivityViewReady_fireIntent() {
+ setUpRearViewCameraViewController(TEST_ACTIVITY_NAME);
+ mRearViewCameraViewController.mActivityViewCallback.onActivityViewReady(mMockActivityView);
+
+ verify(mMockActivityView).startActivity(mIntentCaptor.capture());
+ ComponentName expectedComponent = ComponentName.unflattenFromString(TEST_ACTIVITY_NAME);
+ assertThat(mIntentCaptor.getValue().getComponent()).isEqualTo(expectedComponent);
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewMediatorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewMediatorTest.java
new file mode 100644
index 000000000000..5be8f91ff430
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewMediatorTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.rvc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.car.Car;
+import android.car.VehicleAreaType;
+import android.car.VehicleGear;
+import android.car.VehiclePropertyIds;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyManager;
+import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.CarServiceProvider.CarServiceOnConnectedListener;
+import com.android.systemui.car.CarSystemUiTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@CarSystemUiTest
+@RunWith(MockitoJUnitRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class RearViewCameraViewMediatorTest extends SysuiTestCase {
+ private static final String TAG = RearViewCameraViewMediatorTest.class.getSimpleName();
+
+ private RearViewCameraViewMediator mRearViewCameraViewMediator;
+
+ @Mock
+ private CarServiceProvider mCarServiceProvider;
+ @Mock
+ private Car mCar;
+ @Mock
+ private CarPropertyManager mCarPropertyManager;
+ @Captor
+ private ArgumentCaptor<CarPropertyEventCallback> mCarPropertyEventCallbackCaptor;
+
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+ @Captor
+ private ArgumentCaptor<IntentFilter> mIntentFilterCaptor;
+
+ @Mock
+ private RearViewCameraViewController mRearViewCameraViewController;
+
+ @Before
+ public void setUp() throws Exception {
+ mRearViewCameraViewMediator = new RearViewCameraViewMediator(
+ mRearViewCameraViewController, mCarServiceProvider, mBroadcastDispatcher);
+ }
+
+ public void setUpListener() {
+ doAnswer(invocation -> {
+ CarServiceOnConnectedListener listener = invocation.getArgument(0);
+ listener.onConnected(mCar);
+ return null;
+ }).when(mCarServiceProvider).addListener(any(CarServiceOnConnectedListener.class));
+ when(mCar.getCarManager(Car.PROPERTY_SERVICE)).thenReturn(mCarPropertyManager);
+ when(mRearViewCameraViewController.isEnabled()).thenReturn(true);
+
+ mRearViewCameraViewMediator.registerListeners();
+
+ verify(mCarPropertyManager).registerCallback(mCarPropertyEventCallbackCaptor.capture(),
+ eq(VehiclePropertyIds.GEAR_SELECTION), anyFloat());
+ verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ mIntentFilterCaptor.capture(), any(), any());
+ assertThat(mIntentFilterCaptor.getValue().getAction(0)).isEqualTo(
+ Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ }
+
+ @Test
+ public void testDoesnNotRegisterListenersWhenRearViewCameraViewControllerIsDisabled() {
+ when(mRearViewCameraViewController.isEnabled()).thenReturn(false);
+
+ mRearViewCameraViewMediator.registerListeners();
+
+ verify(mCarPropertyManager, never()).registerCallback(any(), anyInt(), anyFloat());
+ verify(mBroadcastDispatcher, never()).registerReceiver(any(), any(), any());
+ }
+
+ @Test
+ public void testGearReverseStartsRearViewCamera() {
+ setUpListener();
+
+ CarPropertyValue<Integer> gearReverse = new CarPropertyValue(
+ VehiclePropertyIds.GEAR_SELECTION, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
+ VehicleGear.GEAR_REVERSE);
+ mCarPropertyEventCallbackCaptor.getValue().onChangeEvent(gearReverse);
+
+ verify(mRearViewCameraViewController, times(1)).start();
+ }
+
+ @Test
+ public void testGearNonReverseStopsRearViewCamera() {
+ setUpListener();
+
+ int[] nonReverseVehicleGears = new int[]{
+ VehicleGear.GEAR_NEUTRAL, VehicleGear.GEAR_PARK, VehicleGear.GEAR_DRIVE,
+ VehicleGear.GEAR_FIRST
+ };
+ for (int i = 0; i < nonReverseVehicleGears.length; ++i) {
+ Log.i(TAG, "testGearNonReverseStopsRearViewCamera: gear=" + nonReverseVehicleGears[i]);
+ CarPropertyValue<Integer> propertyGear = new CarPropertyValue(
+ VehiclePropertyIds.GEAR_SELECTION, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
+ nonReverseVehicleGears[i]);
+ mCarPropertyEventCallbackCaptor.getValue().onChangeEvent(propertyGear);
+
+ verify(mRearViewCameraViewController, times(i + 1)).stop();
+ }
+ }
+
+ @Test
+ public void testBroadcastIntentStopsRearViewCamera() {
+ setUpListener();
+ when(mRearViewCameraViewController.isShown()).thenReturn(true);
+
+ Intent randomIntent = new Intent(Intent.ACTION_MAIN);
+ mBroadcastReceiverCaptor.getValue().onReceive(mContext, randomIntent);
+
+ verify(mRearViewCameraViewController, never()).stop();
+
+ Intent actionCloseSystemDialogs = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ mBroadcastReceiverCaptor.getValue().onReceive(mContext, actionCloseSystemDialogs);
+
+ verify(mRearViewCameraViewController, times(1)).stop();
+ }
+}