Add tests for CarUi class and add FPV to shared lib

Fixes: 190782532
Fixes: 189866049
Test: ./gradlew createDebugCoverageReport
Change-Id: I38757254872456fe17fbc227d9ab9fb81b3f887a
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/AndroidManifest.xml b/car-ui-lib/car-ui-lib/src/androidTest/AndroidManifest.xml
index 675f5bc..21c8813 100644
--- a/car-ui-lib/car-ui-lib/src/androidTest/AndroidManifest.xml
+++ b/car-ui-lib/car-ui-lib/src/androidTest/AndroidManifest.xml
@@ -18,6 +18,8 @@
           package="com.android.car.ui.test">
     <application android:debuggable="true" android:theme="@style/Theme.CarUi.NoToolbar">
         <uses-library android:name="android.test.runner" />
+        <activity android:name="com.android.car.ui.TrulyEmptyActivity"
+            android:theme="@android:style/Theme.Material.NoActionBar"/>
         <activity android:name="com.android.car.ui.TestActivity" />
         <activity android:name="com.android.car.ui.recyclerview.CarUiRecyclerViewTestActivity" />
         <activity android:name="com.android.car.ui.imewidescreen.CarUiImeWideScreenTestActivity" />
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/TrulyEmptyActivity.java b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/TrulyEmptyActivity.java
new file mode 100644
index 0000000..6085f6c
--- /dev/null
+++ b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/TrulyEmptyActivity.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 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.car.ui;
+
+import android.app.Activity;
+
+/**
+ * An empty activity used for testing
+ */
+public class TrulyEmptyActivity extends Activity {
+}
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/core/CarUiTest.java b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/core/CarUiTest.java
new file mode 100644
index 0000000..34b082a
--- /dev/null
+++ b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/core/CarUiTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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.car.ui.core;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import com.android.car.ui.test.R;
+
+import org.junit.Test;
+
+/** A test for {@link com.android.car.ui.core.CarUi} */
+public class CarUiTest {
+
+    @Test
+    public void test_findCarUiComponentById_returnsNullWithNullInput() {
+        assertNull(CarUi.findCarUiComponentById(null, R.id.car_ui_recycler_view));
+    }
+
+    @Test
+    public void test_requireCarUiComponentById_throwsWithNullInput() {
+        assertThrows(NullPointerException.class,
+                () -> CarUi.requireCarUiComponentById(null, R.id.car_ui_recycler_view));
+    }
+}
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/EmptyToolbarController.java b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/EmptyToolbarController.java
new file mode 100644
index 0000000..a984e14
--- /dev/null
+++ b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/EmptyToolbarController.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2021 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.car.ui.toolbar;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.car.ui.imewidescreen.CarUiImeSearchListItem;
+
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * A class that implements ToolbarController but does nothing, just to easily get a non-null
+ * ToolbarController
+ */
+public class EmptyToolbarController implements ToolbarController {
+    @Override
+    public void setTitle(int title) {
+
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+
+    }
+
+    @Override
+    public CharSequence getTitle() {
+        return null;
+    }
+
+    @Override
+    public void setSubtitle(int title) {
+
+    }
+
+    @Override
+    public void setSubtitle(CharSequence title) {
+
+    }
+
+    @Override
+    public CharSequence getSubtitle() {
+        return null;
+    }
+
+    @Override
+    public void setTabs(@Nullable List<Tab> tabs) {
+
+    }
+
+    @Override
+    public void setTabs(@Nullable List<Tab> tabs,
+            int selectedTab) {
+
+    }
+
+    @Override
+    public List<Tab> getTabs() {
+        return null;
+    }
+
+    @Override
+    public int getTabCount() {
+        return 0;
+    }
+
+    @Override
+    public int getTabPosition(TabLayout.Tab tab) {
+        return 0;
+    }
+
+    @Override
+    public void addTab(TabLayout.Tab tab) {
+
+    }
+
+    @Override
+    public void clearAllTabs() {
+
+    }
+
+    @Override
+    public TabLayout.Tab getTab(int position) {
+        return null;
+    }
+
+    @Override
+    public void selectTab(int position) {
+
+    }
+
+    @Override
+    public int getSelectedTab() {
+        return 0;
+    }
+
+    @Override
+    public void setShowTabsInSubpage(boolean showTabs) {
+
+    }
+
+    @Override
+    public boolean getShowTabsInSubpage() {
+        return false;
+    }
+
+    @Override
+    public void setLogo(int resId) {
+
+    }
+
+    @Override
+    public void setLogo(Drawable drawable) {
+
+    }
+
+    @Override
+    public void setSearchHint(int resId) {
+
+    }
+
+    @Override
+    public void setSearchHint(CharSequence hint) {
+
+    }
+
+    @Override
+    public CharSequence getSearchHint() {
+        return null;
+    }
+
+    @Override
+    public void setSearchIcon(int resId) {
+
+    }
+
+    @Override
+    public void setSearchIcon(Drawable d) {
+
+    }
+
+    @Override
+    public void setSearchMode(SearchMode mode) {
+
+    }
+
+    @Override
+    public void setNavButtonMode(Toolbar.NavButtonMode style) {
+
+    }
+
+    @Override
+    public void setNavButtonMode(NavButtonMode mode) {
+
+    }
+
+    @Override
+    public Toolbar.NavButtonMode getNavButtonMode() {
+        return null;
+    }
+
+    @Override
+    public void setBackgroundShown(boolean shown) {
+
+    }
+
+    @Override
+    public boolean getBackgroundShown() {
+        return false;
+    }
+
+    @Override
+    public void setMenuItems(@Nullable List<MenuItem> items) {
+
+    }
+
+    @Override
+    public List<MenuItem> setMenuItems(int resId) {
+        return null;
+    }
+
+    @NonNull
+    @Override
+    public List<MenuItem> getMenuItems() {
+        return null;
+    }
+
+    @Nullable
+    @Override
+    public MenuItem findMenuItemById(int id) {
+        return null;
+    }
+
+    @NonNull
+    @Override
+    public MenuItem requireMenuItemById(int id) {
+        return null;
+    }
+
+    @Override
+    public void setShowMenuItemsWhileSearching(boolean showMenuItems) {
+
+    }
+
+    @Override
+    public boolean getShowMenuItemsWhileSearching() {
+        return false;
+    }
+
+    @Override
+    public void setSearchQuery(String query) {
+
+    }
+
+    @Override
+    public void setState(Toolbar.State state) {
+
+    }
+
+    @Override
+    public Toolbar.State getState() {
+        return null;
+    }
+
+    @Override
+    public boolean isStateSet() {
+        return false;
+    }
+
+    @Override
+    public void registerOnTabSelectedListener(Toolbar.OnTabSelectedListener listener) {
+
+    }
+
+    @Override
+    public boolean unregisterOnTabSelectedListener(Toolbar.OnTabSelectedListener listener) {
+        return false;
+    }
+
+    @Override
+    public void registerOnSearchListener(Toolbar.OnSearchListener listener) {
+
+    }
+
+    @Override
+    public boolean unregisterOnSearchListener(Toolbar.OnSearchListener listener) {
+        return false;
+    }
+
+    @Override
+    public void registerSearchListener(Consumer<String> listener) {
+
+    }
+
+    @Override
+    public boolean unregisterSearchListener(Consumer<String> listener) {
+        return false;
+    }
+
+    @Override
+    public void setSearchConfig(@Nullable SearchConfig searchConfig) {
+
+    }
+
+    @Override
+    public SearchCapabilities getSearchCapabilities() {
+        return null;
+    }
+
+    @Override
+    public boolean canShowSearchResultItems() {
+        return false;
+    }
+
+    @Override
+    public boolean canShowSearchResultsView() {
+        return false;
+    }
+
+    @Override
+    public void setSearchResultsView(@Nullable View view) {
+
+    }
+
+    @Override
+    public void setSearchResultsInputViewIcon(@NonNull Drawable drawable) {
+
+    }
+
+    @Override
+    public void setSearchResultItems(List<? extends CarUiImeSearchListItem> searchItems) {
+
+    }
+
+    @Override
+    public void registerOnSearchCompletedListener(Toolbar.OnSearchCompletedListener listener) {
+
+    }
+
+    @Override
+    public boolean unregisterOnSearchCompletedListener(Toolbar.OnSearchCompletedListener listener) {
+        return false;
+    }
+
+    @Override
+    public void registerSearchCompletedListener(Runnable listener) {
+
+    }
+
+    @Override
+    public boolean unregisterSearchCompletedListener(Runnable listener) {
+        return false;
+    }
+
+    @Override
+    public void registerOnBackListener(Toolbar.OnBackListener listener) {
+
+    }
+
+    @Override
+    public boolean unregisterOnBackListener(Toolbar.OnBackListener listener) {
+        return false;
+    }
+
+    @Override
+    public void registerBackListener(Supplier<Boolean> listener) {
+
+    }
+
+    @Override
+    public boolean unregisterBackListener(Supplier<Boolean> listener) {
+        return false;
+    }
+
+    @Override
+    public ProgressBarController getProgressBar() {
+        return null;
+    }
+}
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/InstallBaseLayoutAroundTest.java b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/InstallBaseLayoutAroundTest.java
new file mode 100644
index 0000000..a09357e
--- /dev/null
+++ b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/InstallBaseLayoutAroundTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 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.car.ui.toolbar;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import com.android.car.ui.FocusParkingView;
+import com.android.car.ui.TrulyEmptyActivity;
+import com.android.car.ui.baselayout.Insets;
+import com.android.car.ui.core.CarUi;
+import com.android.car.ui.sharedlibrarysupport.SharedLibraryFactorySingleton;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** A test of {@link com.android.car.ui.core.CarUi#installBaseLayoutAround} */
+@RunWith(Parameterized.class)
+public class InstallBaseLayoutAroundTest {
+
+    @Parameterized.Parameters
+    public static Object[] data() {
+        // It's important to do no shared library first, so that the shared library will
+        // still be enabled when this test finishes
+        return new Object[] { false, true };
+    }
+
+    private final boolean mSharedLibEnabled;
+
+    public InstallBaseLayoutAroundTest(boolean sharedLibEnabled) {
+        mSharedLibEnabled = sharedLibEnabled;
+        SharedLibraryFactorySingleton.setSharedLibEnabled(sharedLibEnabled);
+    }
+
+    @Rule
+    public final ActivityScenarioRule<TrulyEmptyActivity> mScenarioRule =
+            new ActivityScenarioRule<>(TrulyEmptyActivity.class);
+
+    @Test
+    public void test_installBaseLayoutAround_createsToolbar() {
+        onView(isAssignableFrom(FocusParkingView.class)).check(doesNotExist());
+
+        ToolbarController[] toolbar = new ToolbarController[] { null };
+        Insets[] insets = new Insets[] { null };
+        mScenarioRule.getScenario().onActivity(activity -> {
+            toolbar[0] = CarUi.installBaseLayoutAround(
+                    activity.requireViewById(android.R.id.content),
+                    i -> insets[0] = i,
+                    true);
+            if (toolbar[0] != null) {
+                toolbar[0].setTitle("Hello, world!");
+            }
+        });
+
+        assertNotNull(toolbar[0]);
+        onView(withText("Hello, world!")).check(matches(isDisplayed()));
+
+        assertNotNull(insets[0]);
+        // Technically this doesn't have to be true depending on the OEM's customizations
+        assertTrue(insets[0].getTop() > 0);
+        onView(isAssignableFrom(FocusParkingView.class)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void test_installBaseLayoutAround_doesntCreateToolbar() {
+        onView(isAssignableFrom(FocusParkingView.class)).check(doesNotExist());
+
+        ToolbarController[] toolbar = new ToolbarController[] { null };
+        mScenarioRule.getScenario().onActivity(activity ->
+                toolbar[0] = CarUi.installBaseLayoutAround(
+                        activity.requireViewById(android.R.id.content),
+                        i -> {},
+                        false));
+
+        assertNull(toolbar[0]);
+        onView(isAssignableFrom(FocusParkingView.class)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void test_emptyactivity_doesnthaveinsetsortoolbar() {
+        Insets[] insets = new Insets[] { new Insets() };
+        mScenarioRule.getScenario().onActivity(activity -> insets[0] = CarUi.getInsets(activity));
+        assertNull(insets[0]);
+
+        ToolbarController[] toolbar = new ToolbarController[] { new EmptyToolbarController() };
+        mScenarioRule.getScenario().onActivity(activity -> toolbar[0] = CarUi.getToolbar(activity));
+        assertNull(toolbar[0]);
+    }
+}
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/ToolbarTest.java b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/ToolbarTest.java
index abd5c15..0971722 100644
--- a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/ToolbarTest.java
+++ b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/ToolbarTest.java
@@ -28,6 +28,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.TestCase.assertEquals;
 
 import android.content.Context;
@@ -36,6 +37,7 @@
 import androidx.test.ext.junit.rules.ActivityScenarioRule;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.car.ui.baselayout.Insets;
 import com.android.car.ui.core.CarUi;
 import com.android.car.ui.sharedlibrarysupport.SharedLibraryFactorySingleton;
 import com.android.car.ui.test.R;
@@ -244,6 +246,13 @@
         }
     }
 
+    @Test
+    public void test_requireInsets_returnsInsets() {
+        Insets[] insets = new Insets[] { null };
+        mScenarioRule.getScenario().onActivity(activity ->
+                insets[0] = CarUi.requireInsets(activity));
+        assertNotNull(insets[0]);
+    }
 
     private void runWithToolbar(Consumer<ToolbarController> toRun) {
         mScenarioRule.getScenario().onActivity(activity -> {
diff --git a/car-ui-lib/referencedesign/sharedlibrary/src/main/java/com/chassis/car/ui/sharedlibrary/toolbar/BaseLayoutInstaller.java b/car-ui-lib/referencedesign/sharedlibrary/src/main/java/com/chassis/car/ui/sharedlibrary/toolbar/BaseLayoutInstaller.java
index 9a7c13c..602777a 100644
--- a/car-ui-lib/referencedesign/sharedlibrary/src/main/java/com/chassis/car/ui/sharedlibrary/toolbar/BaseLayoutInstaller.java
+++ b/car-ui-lib/referencedesign/sharedlibrary/src/main/java/com/chassis/car/ui/sharedlibrary/toolbar/BaseLayoutInstaller.java
@@ -23,6 +23,7 @@
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
+import androidx.annotation.LayoutRes;
 import androidx.annotation.Nullable;
 
 import com.android.car.ui.sharedlibrary.oemapis.FocusAreaOEMV1;
@@ -54,11 +55,6 @@
             @Nullable Function<Context, FocusParkingViewOEMV1> focusParkingViewFactory,
             @Nullable Function<Context, FocusAreaOEMV1> focusAreaFactory) {
 
-        if (!toolbarEnabled) {
-            // We don't need a toolbar-less base layout in this design, so we're done.
-            return null;
-        }
-
         Context activityContext = contentView.getContext();
 
         // Add the configuration from the activity context to the shared library context,
@@ -67,8 +63,11 @@
         sharedLibraryContext = sharedLibraryContext.createConfigurationContext(
                 activityContext.getResources().getConfiguration());
 
+        @LayoutRes int layout = toolbarEnabled
+                ? R.layout.base_layout_toolbar
+                : R.layout.base_layout;
         FrameLayout baseLayout = (FrameLayout) LayoutInflater.from(sharedLibraryContext).inflate(
-                R.layout.base_layout_toolbar, null, false);
+                layout, null, false);
 
         // Replace the app's content view with a base layout
         ViewGroup contentViewParent = (ViewGroup) contentView.getParent();
@@ -103,7 +102,7 @@
         // the FocusArea using the app's context, so that it can access it's resources,
         // but we want children of the FocusArea to use the shared library context, so we can
         // access shared library resources.
-        if (focusAreaFactory != null) {
+        if (focusAreaFactory != null && toolbarEnabled) {
             LinearLayout focusArea = focusAreaFactory.apply(activityContext).getView();
             if (focusArea != null) {
                 View toolbar = baseLayout.requireViewById(R.id.toolbar_background);
@@ -116,8 +115,11 @@
         }
 
 
-        ToolbarControllerOEMV1 toolbarController = new ToolbarControllerImpl(
-                baseLayout, sharedLibraryContext, activityContext);
+        ToolbarControllerOEMV1 toolbarController = null;
+        if (toolbarEnabled) {
+            toolbarController = new ToolbarControllerImpl(
+                    baseLayout, sharedLibraryContext, activityContext);
+        }
 
         InsetsUpdater updater = new InsetsUpdater(baseLayout, contentView);
         updater.replaceInsetsChangedListenerWith(insetsChangedListener);
diff --git a/car-ui-lib/referencedesign/sharedlibrary/src/main/res/layout/base_layout.xml b/car-ui-lib/referencedesign/sharedlibrary/src/main/res/layout/base_layout.xml
new file mode 100644
index 0000000..fc2b830
--- /dev/null
+++ b/car-ui-lib/referencedesign/sharedlibrary/src/main/res/layout/base_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <FrameLayout
+        android:id="@+id/base_layout_content_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</FrameLayout>