Merge CarFacetButton and CarNavigationButton into a single class and add test coverage

Test: Manual - Manually verified that navigation button functionalities are working as expected.
      Unit Tests
Bug: 145827784
Bug: 145604656

Change-Id: Ib8f837ba34afbb8d66092a8b851b93c50fa20b26
This commit is contained in:
kwaky
2019-12-12 16:23:59 -08:00
parent d4fe2476b1
commit 77d81dc6ac
17 changed files with 936 additions and 553 deletions

View File

@@ -1,51 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2018, 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.
*/
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/car_facet_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:animateLayoutChanges="true"
android:orientation="vertical">
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/car_nav_button_icon"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:animateLayoutChanges="true"
android:background="@android:color/transparent"
android:scaleType="fitCenter">
</com.android.keyguard.AlphaOptimizedImageButton>
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/car_nav_button_more_icon"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:animateLayoutChanges="true"
android:src="@drawable/car_ic_arrow"
android:background="@android:color/transparent"
android:scaleType="fitCenter">
</com.android.keyguard.AlphaOptimizedImageButton>
</LinearLayout>
</merge>

View File

@@ -33,14 +33,14 @@
android:paddingEnd="20dp"
android:gravity="center">
<com.android.systemui.navigationbar.car.CarFacetButton
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/home"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
systemui:selectedIcon="@drawable/car_ic_overview_selected"
systemui:useMoreIcon="false"
systemui:highlightWhenSelected="true"
/>
<Space
@@ -48,14 +48,14 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
<com.android.systemui.navigationbar.car.CarFacetButton
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/maps_nav"
style="@style/NavigationBarButton"
systemui:categories="android.intent.category.APP_MAPS"
systemui:icon="@drawable/car_ic_navigation"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;launchFlags=0x14000000;end"
systemui:selectedIcon="@drawable/car_ic_navigation_selected"
systemui:useMoreIcon="false"
systemui:highlightWhenSelected="true"
/>
<Space
@@ -63,7 +63,7 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
<com.android.systemui.navigationbar.car.CarFacetButton
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/music_nav"
style="@style/NavigationBarButton"
systemui:categories="android.intent.category.APP_MUSIC"
@@ -71,7 +71,7 @@
systemui:intent="intent:#Intent;action=android.car.intent.action.MEDIA_TEMPLATE;launchFlags=0x10000000;end"
systemui:packages="com.android.car.media"
systemui:selectedIcon="@drawable/car_ic_music_selected"
systemui:useMoreIcon="false"
systemui:highlightWhenSelected="true"
/>
<Space
@@ -79,14 +79,14 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
<com.android.systemui.navigationbar.car.CarFacetButton
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/phone_nav"
style="@style/NavigationBarButton"
systemui:icon="@drawable/car_ic_phone"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
systemui:packages="com.android.car.dialer"
systemui:selectedIcon="@drawable/car_ic_phone_selected"
systemui:useMoreIcon="false"
systemui:highlightWhenSelected="true"
/>
<Space
@@ -94,14 +94,14 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
<com.android.systemui.navigationbar.car.CarFacetButton
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/grid_nav"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
systemui:icon="@drawable/car_ic_apps"
systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
systemui:selectedIcon="@drawable/car_ic_apps_selected"
systemui:useMoreIcon="false"
systemui:highlightWhenSelected="true"
/>
<Space
@@ -115,7 +115,6 @@
systemui:icon="@drawable/car_ic_notification"
systemui:longIntent="intent:#Intent;component=com.android.car.bugreport/.BugReportActivity;end"
systemui:selectedIcon="@drawable/car_ic_notification_selected"
systemui:useMoreIcon="false"
/>
<Space
@@ -127,7 +126,6 @@
android:id="@+id/assist"
style="@style/NavigationBarButton"
systemui:icon="@drawable/ic_mic_white"
systemui:useMoreIcon="false"
/>
</LinearLayout>

View File

@@ -31,7 +31,7 @@
android:paddingStart="@*android:dimen/car_padding_5"
android:paddingEnd="@*android:dimen/car_padding_5">
<com.android.systemui.navigationbar.car.CarFacetButton
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/home"
android:layout_width="@*android:dimen/car_touch_target_size"
android:layout_height="match_parent"
@@ -39,7 +39,8 @@
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
systemui:selectedIcon="@drawable/car_ic_overview_selected"
systemui:useMoreIcon="false"/>
systemui:highlightWhenSelected="true"
/>
</LinearLayout>
</com.android.systemui.navigationbar.car.CarNavigationBarView>

View File

@@ -18,12 +18,34 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<com.android.keyguard.AlphaOptimizedImageButton
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/car_nav_button_icon"
android:layout_height="wrap_content"
android:layout_width="@dimen/car_navigation_button_width"
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:animateLayoutChanges="true"
android:scaleType="fitCenter">
</com.android.keyguard.AlphaOptimizedImageButton>
android:orientation="vertical">
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/car_nav_button_icon_image"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:animateLayoutChanges="true"
android:background="@android:color/transparent"
android:scaleType="fitCenter">
</com.android.keyguard.AlphaOptimizedImageButton>
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/car_nav_button_more_icon"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:animateLayoutChanges="true"
android:src="@drawable/car_ic_arrow"
android:background="@android:color/transparent"
android:scaleType="fitCenter">
</com.android.keyguard.AlphaOptimizedImageButton>
</LinearLayout>
</merge>

View File

@@ -30,9 +30,9 @@ import com.android.internal.app.IVoiceInteractionSessionShowCallback;
/**
* AssitantButton is a ui component that will trigger the Voice Interaction Service.
*/
public class AssitantButton extends CarFacetButton {
public class AssitantButton extends CarNavigationButton {
private static final String TAG = "CarFacetButton";
private static final String TAG = "AssistantButton";
private final AssistUtils mAssistUtils;
private IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@@ -62,7 +62,7 @@ public class AssitantButton extends CarFacetButton {
}
@Override
protected void setupIntents(TypedArray typedArray) {
protected void setUpIntents(TypedArray typedArray) {
// left blank because for the assistant button Intent will not be passed from the layout.
}
}

View File

@@ -28,7 +28,6 @@ import android.view.ViewGroup;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -36,151 +35,169 @@ import javax.inject.Inject;
import javax.inject.Singleton;
/**
* CarFacetButtons placed on the nav bar are designed to have visual indication that the active
* application on screen is associated with it. This is basically a similar concept to a radio
* button group.
* CarNavigationButtons can optionally have selection state that toggles certain visual indications
* based on whether the active application on screen is associated with it. This is basically a
* similar concept to a radio button group.
*
* This class controls the selection state of CarNavigationButtons that have opted in to have such
* selection state-dependent visual indications.
*/
@Singleton
public class CarFacetButtonController {
public class ButtonSelectionStateController {
private final Set<CarFacetButton> mRegisteredViews = new HashSet<>();
private final Set<CarNavigationButton> mRegisteredViews = new HashSet<>();
protected ButtonMap mButtonsByCategory = new ButtonMap();
protected ButtonMap mButtonsByPackage = new ButtonMap();
protected ButtonMap mButtonsByComponentName = new ButtonMap();
protected HashSet<CarFacetButton> mSelectedFacetButtons;
protected HashSet<CarNavigationButton> mSelectedButtons;
protected Context mContext;
@Inject
public CarFacetButtonController(Context context) {
public ButtonSelectionStateController(Context context) {
mContext = context;
mSelectedFacetButtons = new HashSet<>();
mSelectedButtons = new HashSet<>();
}
/**
* Add facet button to this controller. The expected use is for the facet button
* to get a reference to this controller via {@link com.android.systemui.Dependency}
* and self add.
*/
private void addFacetButton(CarFacetButton facetButton) {
if (mRegisteredViews.contains(facetButton)) {
return;
}
String[] categories = facetButton.getCategories();
for (int i = 0; i < categories.length; i++) {
mButtonsByCategory.add(categories[i], facetButton);
}
String[] facetPackages = facetButton.getFacetPackages();
for (int i = 0; i < facetPackages.length; i++) {
mButtonsByPackage.add(facetPackages[i], facetButton);
}
String[] componentNames = facetButton.getComponentName();
for (int i = 0; i < componentNames.length; i++) {
mButtonsByComponentName.add(componentNames[i], facetButton);
}
mRegisteredViews.add(facetButton);
}
/** Removes all buttons from the button maps. */
public void removeAll() {
mButtonsByCategory.clear();
mButtonsByPackage.clear();
mButtonsByComponentName.clear();
mSelectedFacetButtons.clear();
mRegisteredViews.clear();
}
/**
* Iterate through a view looking for CarFacetButtons and adding them to the controller if found
* Iterate through a view looking for CarNavigationButton and add it to the controller if it
* opted in to be highlighted when the active application is associated with it.
*
* @param v the View that may contain CarFacetButtons
*/
public void addAllFacetButtons(View v) {
if (v instanceof CarFacetButton) {
addFacetButton((CarFacetButton) v);
protected void addAllButtonsWithSelectionState(View v) {
if (v instanceof CarNavigationButton) {
if (((CarNavigationButton) v).hasSelectionState()) {
addButtonWithSelectionState((CarNavigationButton) v);
}
} else if (v instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) v;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
addAllFacetButtons(viewGroup.getChildAt(i));
addAllButtonsWithSelectionState(viewGroup.getChildAt(i));
}
}
}
/** Removes all buttons from the button maps. */
protected void removeAll() {
mButtonsByCategory.clear();
mButtonsByPackage.clear();
mButtonsByComponentName.clear();
mSelectedButtons.clear();
mRegisteredViews.clear();
}
/**
* This will unselect the currently selected CarFacetButton and determine which one should be
* selected next. It does this by reading the properties on the CarFacetButton and seeing if
* they are a match with the supplied StackInfo list.
* This will unselect the currently selected CarNavigationButton and determine which one should
* be selected next. It does this by reading the properties on the CarNavigationButton and
* seeing if they are a match with the supplied StackInfo list.
* The order of selection detection is ComponentName, PackageName then Category
* They will then be compared with the supplied StackInfo list.
* The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
* for consideration if it has the same displayId as the CarFacetButtons.
* for consideration if it has the same displayId as the CarNavigationButton.
*
* @param stackInfoList of the currently running application
* @param validDisplay index of the valid display
*/
public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
protected void taskChanged(List<ActivityManager.StackInfo> stackInfoList, int validDisplay) {
ActivityManager.StackInfo validStackInfo = null;
for (ActivityManager.StackInfo stackInfo : stackInfoList) {
// Find the first stack info with a topActivity in the primary display.
// TODO: We assume that CarFacetButton will launch an app only in the primary display.
// We need to extend the functionality to handle the mutliple display properly.
if (stackInfo.topActivity != null && stackInfo.displayId == Display.DEFAULT_DISPLAY) {
// We need to extend the functionality to handle the multiple display properly.
if (stackInfo.topActivity != null && stackInfo.displayId == validDisplay) {
validStackInfo = stackInfo;
break;
}
}
if (validStackInfo == null) {
// No stack was found that was on the same display as the facet buttons thus return
// No stack was found that was on the same display as the buttons thus return
return;
}
int displayId = validStackInfo.displayId;
if (mSelectedFacetButtons != null) {
Iterator<CarFacetButton> iterator = mSelectedFacetButtons.iterator();
while (iterator.hasNext()) {
CarFacetButton carFacetButton = iterator.next();
if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
carFacetButton.setSelected(false);
iterator.remove();
mSelectedButtons.forEach(carNavigationButton -> {
if (carNavigationButton.getDisplayId() == displayId) {
carNavigationButton.setSelected(false);
}
});
mSelectedButtons.clear();
HashSet<CarNavigationButton> selectedButtons = findSelectedButtons(validStackInfo);
if (selectedButtons != null) {
selectedButtons.forEach(carNavigationButton -> {
if (carNavigationButton.getDisplayId() == displayId) {
carNavigationButton.setSelected(true);
mSelectedButtons.add(carNavigationButton);
}
}
});
}
String packageName = validStackInfo.topActivity.getPackageName();
HashSet<CarFacetButton> facetButton =
findFacetButtonByComponentName(validStackInfo.topActivity);
if (facetButton == null) {
facetButton = mButtonsByPackage.get(packageName);
}
if (facetButton == null) {
String category = getPackageCategory(packageName);
if (category != null) {
facetButton = mButtonsByCategory.get(category);
}
}
if (facetButton != null) {
for (CarFacetButton carFacetButton : facetButton) {
if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
carFacetButton.setSelected(true);
mSelectedFacetButtons.add(carFacetButton);
}
}
}
}
private HashSet<CarFacetButton> findFacetButtonByComponentName(ComponentName componentName) {
HashSet<CarFacetButton> buttons =
/**
* Defaults to Display.DEFAULT_DISPLAY when no parameter is provided for the validDisplay.
*
* @param stackInfoList
*/
protected void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
taskChanged(stackInfoList, Display.DEFAULT_DISPLAY);
}
/**
* Add navigation button to this controller if it uses selection state.
*/
private void addButtonWithSelectionState(CarNavigationButton carNavigationButton) {
if (mRegisteredViews.contains(carNavigationButton)) {
return;
}
String[] categories = carNavigationButton.getCategories();
for (int i = 0; i < categories.length; i++) {
mButtonsByCategory.add(categories[i], carNavigationButton);
}
String[] packages = carNavigationButton.getPackages();
for (int i = 0; i < packages.length; i++) {
mButtonsByPackage.add(packages[i], carNavigationButton);
}
String[] componentNames = carNavigationButton.getComponentName();
for (int i = 0; i < componentNames.length; i++) {
mButtonsByComponentName.add(componentNames[i], carNavigationButton);
}
mRegisteredViews.add(carNavigationButton);
}
private HashSet<CarNavigationButton> findSelectedButtons(
ActivityManager.StackInfo validStackInfo) {
String packageName = validStackInfo.topActivity.getPackageName();
HashSet<CarNavigationButton> selectedButtons =
findButtonsByComponentName(validStackInfo.topActivity);
if (selectedButtons == null) {
selectedButtons = mButtonsByPackage.get(packageName);
}
if (selectedButtons == null) {
String category = getPackageCategory(packageName);
if (category != null) {
selectedButtons = mButtonsByCategory.get(category);
}
}
return selectedButtons;
}
private HashSet<CarNavigationButton> findButtonsByComponentName(
ComponentName componentName) {
HashSet<CarNavigationButton> buttons =
mButtonsByComponentName.get(componentName.flattenToShortString());
return (buttons != null) ? buttons :
mButtonsByComponentName.get(componentName.flattenToString());
}
protected String getPackageCategory(String packageName) {
private String getPackageCategory(String packageName) {
PackageManager pm = mContext.getPackageManager();
Set<String> supportedCategories = mButtonsByCategory.keySet();
for (String category : supportedCategories) {
@@ -190,7 +207,7 @@ public class CarFacetButtonController {
intent.addCategory(category);
List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
if (list.size() > 0) {
// Cache this package name into facetPackageMap, so we won't have to query
// Cache this package name into ButtonsByPackage map, so we won't have to query
// all categories next time this package name shows up.
mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
return category;
@@ -200,13 +217,13 @@ public class CarFacetButtonController {
}
// simple multi-map
private static class ButtonMap extends HashMap<String, HashSet<CarFacetButton>> {
private static class ButtonMap extends HashMap<String, HashSet<CarNavigationButton>> {
public boolean add(String key, CarFacetButton value) {
public boolean add(String key, CarNavigationButton value) {
if (containsKey(key)) {
return get(key).add(value);
}
HashSet<CarFacetButton> set = new HashSet<>();
HashSet<CarNavigationButton> set = new HashSet<>();
set.add(value);
put(key, set);
return true;

View File

@@ -24,28 +24,25 @@ import com.android.systemui.shared.system.TaskStackChangeListener;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Lazy;
/**
* An implementation of TaskStackChangeListener, that listens for changes in the system
* task stack and notifies the navigation bar.
*/
@Singleton
class FacetButtonTaskStackListener extends TaskStackChangeListener {
private static final String TAG = FacetButtonTaskStackListener.class.getSimpleName();
class ButtonSelectionStateListener extends TaskStackChangeListener {
private static final String TAG = ButtonSelectionStateListener.class.getSimpleName();
private final Lazy<CarFacetButtonController> mFacetButtonControllerLazy;
private final ButtonSelectionStateController mButtonSelectionStateController;
@Inject
FacetButtonTaskStackListener(
Lazy<CarFacetButtonController> carFacetButtonControllerLazy) {
mFacetButtonControllerLazy = carFacetButtonControllerLazy;
ButtonSelectionStateListener(ButtonSelectionStateController carNavigationButtonController) {
mButtonSelectionStateController = carNavigationButtonController;
}
@Override
public void onTaskStackChanged() {
try {
mFacetButtonControllerLazy.get().taskChanged(
mButtonSelectionStateController.taskChanged(
ActivityTaskManager.getService().getAllStackInfos());
} catch (Exception e) {
Log.e(TAG, "Getting StackInfo from activity manager failed", e);
@@ -55,7 +52,7 @@ class FacetButtonTaskStackListener extends TaskStackChangeListener {
@Override
public void onTaskDisplayChanged(int taskId, int newDisplayId) {
try {
mFacetButtonControllerLazy.get().taskChanged(
mButtonSelectionStateController.taskChanged(
ActivityTaskManager.getService().getAllStackInfos());
} catch (Exception e) {
Log.e(TAG, "Getting StackInfo from activity manager failed", e);

View File

@@ -1,226 +0,0 @@
/*
* Copyright (C) 2019 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.navigationbar.car;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.Display;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
/**
* CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
* category. It can also render a indicator implying that there are more options of apps to launch
* using this component. This is done with a "More icon" currently an arrow as defined in the layout
* file. The class is to serve as an example.
*
* New activity will be launched on the same display as the button is on.
* Usage example: A button that allows a user to select a music app and indicate that there are
* other music apps installed.
*/
public class CarFacetButton extends LinearLayout {
private static final String FACET_FILTER_DELIMITER = ";";
/**
* Extra information to be sent to a helper to make the decision of what app to launch when
* clicked.
*/
private static final String EXTRA_FACET_CATEGORIES = "categories";
private static final String EXTRA_FACET_PACKAGES = "packages";
private static final String EXTRA_FACET_ID = "filter_id";
private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
private static final String TAG = "CarFacetButton";
private Context mContext;
private AlphaOptimizedImageButton mIcon;
private AlphaOptimizedImageButton mMoreIcon;
private boolean mSelected = false;
private String[] mComponentNames;
/** App categories that are to be used with this widget */
private String[] mFacetCategories;
/** App packages that are allowed to be used with this widget */
private String[] mFacetPackages;
private int mIconResourceId;
/**
* If defined in the xml this will be the icon that's rendered when the button is marked as
* selected
*/
private int mSelectedIconResourceId;
private boolean mUseMoreIcon = true;
private float mSelectedAlpha = 1f;
private float mUnselectedAlpha = 1f;
public CarFacetButton(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
View.inflate(context, R.layout.car_facet_button, this);
// extract custom attributes
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
setupIntents(typedArray);
setupIcons(typedArray);
}
/**
* Reads the custom attributes to setup click handlers for this component.
*/
protected void setupIntents(TypedArray typedArray) {
String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
String packageString = typedArray.getString(R.styleable.CarFacetButton_packages);
String componentNameString =
typedArray.getString(R.styleable.CarFacetButton_componentNames);
try {
final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
if (packageString != null) {
mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
}
if (categoryString != null) {
mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
}
if (componentNameString != null) {
mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER);
}
setOnClickListener(v -> {
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(mContext.getDisplayId());
intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
mContext.sendBroadcastAsUser(
new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
});
if (longPressIntentString != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
final Intent longPressIntent = Intent.parseUri(longPressIntentString,
Intent.URI_INTENT_SCHEME);
setOnLongClickListener(v -> {
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(mContext.getDisplayId());
mContext.startActivityAsUser(longPressIntent, options.toBundle(),
UserHandle.CURRENT);
mContext.sendBroadcastAsUser(
new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
return true;
});
}
} catch (Exception e) {
throw new RuntimeException("Failed to attach intent", e);
}
}
private void setupIcons(TypedArray styledAttributes) {
mSelectedAlpha = styledAttributes.getFloat(
R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
mUnselectedAlpha = styledAttributes.getFloat(
R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha);
mIcon = findViewById(R.id.car_nav_button_icon);
mIcon.setScaleType(ImageView.ScaleType.CENTER);
mIcon.setClickable(false);
mIcon.setAlpha(mUnselectedAlpha);
mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
mIcon.setImageResource(mIconResourceId);
mSelectedIconResourceId = styledAttributes.getResourceId(
R.styleable.CarFacetButton_selectedIcon, mIconResourceId);
mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
mMoreIcon.setClickable(false);
mMoreIcon.setAlpha(mSelectedAlpha);
mMoreIcon.setVisibility(GONE);
mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true);
}
/**
* @return The app categories the component represents
*/
public String[] getCategories() {
if (mFacetCategories == null) {
return new String[0];
}
return mFacetCategories;
}
/**
* @return The valid packages that should be considered.
*/
public String[] getFacetPackages() {
if (mFacetPackages == null) {
return new String[0];
}
return mFacetPackages;
}
/**
* @return The list of component names.
*/
public String[] getComponentName() {
if (mComponentNames == null) {
return new String[0];
}
return mComponentNames;
}
/**
* Updates the alpha of the icons to "selected" and shows the "More icon"
*
* @param selected true if the view must be selected, false otherwise
*/
public void setSelected(boolean selected) {
super.setSelected(selected);
setSelected(selected, selected);
}
/**
* Updates the visual state to let the user know if it's been selected.
*
* @param selected true if should update the alpha of the icon to selected, false otherwise
* @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
* is ignored if the attribute useMoreIcon is set to false
*/
public void setSelected(boolean selected, boolean showMoreIcon) {
mSelected = selected;
mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
if (mUseMoreIcon) {
mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
}
}
/**
* @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on
* a display.
*/
public int getDisplayId() {
Display display = getDisplay();
if (display == null) {
return Display.INVALID_DISPLAY;
}
return display.getDisplayId();
}
}

View File

@@ -57,12 +57,12 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
private final WindowManager mWindowManager;
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final CommandQueue mCommandQueue;
private final Lazy<FacetButtonTaskStackListener> mFacetButtonTaskStackListenerLazy;
private final ButtonSelectionStateListener mButtonSelectionStateListener;
private final Handler mMainHandler;
private final Lazy<KeyguardStateController> mKeyguardStateControllerLazy;
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
private final Lazy<CarFacetButtonController> mCarFacetButtonControllerLazy;
private final ButtonSelectionStateController mButtonSelectionStateController;
private IStatusBarService mBarService;
private ActivityManagerWrapper mActivityManagerWrapper;
@@ -92,24 +92,24 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
WindowManager windowManager,
DeviceProvisionedController deviceProvisionedController,
CommandQueue commandQueue,
Lazy<FacetButtonTaskStackListener> facetButtonTaskStackListenerLazy,
ButtonSelectionStateListener buttonSelectionStateListener,
@Main Handler mainHandler,
Lazy<KeyguardStateController> keyguardStateControllerLazy,
Lazy<NavigationBarController> navigationBarControllerLazy,
SuperStatusBarViewFactory superStatusBarViewFactory,
Lazy<CarFacetButtonController> carFacetButtonControllerLazy) {
ButtonSelectionStateController buttonSelectionStateController) {
super(context);
mCarNavigationBarController = carNavigationBarController;
mWindowManager = windowManager;
mCarDeviceProvisionedController = (CarDeviceProvisionedController)
deviceProvisionedController;
mCommandQueue = commandQueue;
mFacetButtonTaskStackListenerLazy = facetButtonTaskStackListenerLazy;
mButtonSelectionStateListener = buttonSelectionStateListener;
mMainHandler = mainHandler;
mKeyguardStateControllerLazy = keyguardStateControllerLazy;
mNavigationBarControllerLazy = navigationBarControllerLazy;
mSuperStatusBarViewFactory = superStatusBarViewFactory;
mCarFacetButtonControllerLazy = carFacetButtonControllerLazy;
mButtonSelectionStateController = buttonSelectionStateController;
}
@Override
@@ -156,7 +156,7 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
createNavigationBar(result);
mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
mActivityManagerWrapper.registerTaskStackListener(mFacetButtonTaskStackListenerLazy.get());
mActivityManagerWrapper.registerTaskStackListener(mButtonSelectionStateListener);
mCarNavigationBarController.connectToHvac();
}
@@ -181,7 +181,7 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
// remove and reattach all hvac components such that we don't keep a reference to unused
// ui elements
mCarNavigationBarController.removeAllFromHvac();
mCarFacetButtonControllerLazy.get().removeAll();
mButtonSelectionStateController.removeAll();
if (mTopNavigationBarWindow != null) {
mTopNavigationBarWindow.removeAllViews();
@@ -343,7 +343,7 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print(" mTaskStackListener=");
pw.println(mFacetButtonTaskStackListenerLazy.get());
pw.println(mButtonSelectionStateListener);
pw.print(" mBottomNavigationBarView=");
pw.println(mBottomNavigationBarView);
}

View File

@@ -37,7 +37,7 @@ public class CarNavigationBarController {
private final Context mContext;
private final NavigationBarViewFactory mNavigationBarViewFactory;
private final Lazy<CarFacetButtonController> mCarFacetButtonControllerLazy;
private final ButtonSelectionStateController mButtonSelectionStateController;
private final Lazy<HvacController> mHvacControllerLazy;
private boolean mShowBottom;
@@ -58,11 +58,11 @@ public class CarNavigationBarController {
@Inject
public CarNavigationBarController(Context context,
NavigationBarViewFactory navigationBarViewFactory,
Lazy<CarFacetButtonController> carFacetButtonControllerLazy,
ButtonSelectionStateController buttonSelectionStateController,
Lazy<HvacController> hvacControllerLazy) {
mContext = context;
mNavigationBarViewFactory = navigationBarViewFactory;
mCarFacetButtonControllerLazy = carFacetButtonControllerLazy;
mButtonSelectionStateController = buttonSelectionStateController;
mHvacControllerLazy = hvacControllerLazy;
// Read configuration.
@@ -175,7 +175,7 @@ public class CarNavigationBarController {
NotificationsShadeController notifShadeController) {
view.setStatusBarWindowTouchListener(statusBarTouchListener);
view.setNotificationsPanelController(notifShadeController);
mCarFacetButtonControllerLazy.get().addAllFacetButtons(view);
mButtonSelectionStateController.addAllButtonsWithSelectionState(view);
mHvacControllerLazy.get().addTemperatureViewToController(view);
}

View File

@@ -24,8 +24,12 @@ import android.os.Build;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
import java.net.URISyntaxException;
@@ -35,110 +39,66 @@ import java.net.URISyntaxException;
* xml file level. This allows for more control via overlays instead of having to update
* code.
*/
public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImageButton {
private static final String TAG = "CarNavigationButton";
public class CarNavigationButton extends LinearLayout {
protected static final float DEFAULT_SELECTED_ALPHA = 1f;
protected static final float DEFAULT_UNSELECTED_ALPHA = 0.75f;
private static final String TAG = "CarNavigationButton";
private static final String BUTTON_FILTER_DELIMITER = ";";
private static final String EXTRA_BUTTON_CATEGORIES = "categories";
private static final String EXTRA_BUTTON_PACKAGES = "packages";
private static final int UNSEEN_ICON_RESOURCE_ID = R.drawable.car_ic_notification_unseen;
private static final int UNSEEN_SELECTED_ICON_RESOURCE_ID =
R.drawable.car_ic_notification_selected_unseen;
private Context mContext;
private AlphaOptimizedImageButton mIcon;
private AlphaOptimizedImageButton mMoreIcon;
private String mIntent;
private String mLongIntent;
private boolean mBroadcastIntent;
private boolean mHasUnseen = false;
private boolean mSelected = false;
private float mSelectedAlpha = 1f;
private float mUnselectedAlpha = 1f;
private float mSelectedAlpha;
private float mUnselectedAlpha;
private int mSelectedIconResourceId;
private int mIconResourceId;
private String[] mComponentNames;
/** App categories that are to be used with this widget */
private String[] mButtonCategories;
/** App packages that are allowed to be used with this widget */
private String[] mButtonPackages;
/** Whether to display more icon beneath the primary icon when the button is selected */
private boolean mShowMoreWhenSelected = false;
/** Whether to highlight the button if the active application is associated with it */
private boolean mHighlightWhenSelected = false;
public CarNavigationButton(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
View.inflate(mContext, R.layout.car_navigation_button, /* root= */ this);
// CarNavigationButton attrs
TypedArray typedArray = context.obtainStyledAttributes(
attrs, R.styleable.CarNavigationButton);
mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent);
mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent);
mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false);
mSelectedAlpha = typedArray.getFloat(
R.styleable.CarNavigationButton_selectedAlpha, mSelectedAlpha);
mUnselectedAlpha = typedArray.getFloat(
R.styleable.CarNavigationButton_unselectedAlpha, mUnselectedAlpha);
mSelectedIconResourceId = typedArray.getResourceId(
R.styleable.CarNavigationButton_selectedIcon, mIconResourceId);
mIconResourceId = typedArray.getResourceId(
R.styleable.CarNavigationButton_icon, 0);
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.CarNavigationButton);
setUpIntents(typedArray);
setUpIcons(typedArray);
typedArray.recycle();
}
/**
* After the standard inflate this then adds the xml defined intents to click and long click
* actions if defined.
*/
@Override
public void onFinishInflate() {
super.onFinishInflate();
setScaleType(ImageView.ScaleType.CENTER);
setAlpha(mUnselectedAlpha);
setImageResource(mIconResourceId);
try {
if (mIntent != null) {
final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
setOnClickListener(v -> {
try {
if (mBroadcastIntent) {
mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
mContext.sendBroadcastAsUser(
new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
UserHandle.CURRENT);
return;
}
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(mContext.getDisplayId());
mContext.startActivityAsUser(intent, options.toBundle(),
UserHandle.CURRENT);
} catch (Exception e) {
Log.e(TAG, "Failed to launch intent", e);
}
});
}
} catch (URISyntaxException e) {
throw new RuntimeException("Failed to attach intent", e);
}
try {
if (mLongIntent != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
setOnLongClickListener(v -> {
try {
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(mContext.getDisplayId());
mContext.startActivityAsUser(intent, options.toBundle(),
UserHandle.CURRENT);
} catch (Exception e) {
Log.e(TAG, "Failed to launch intent", e);
}
// consume event either way
return true;
});
}
} catch (URISyntaxException e) {
throw new RuntimeException("Failed to attach long press intent", e);
}
}
/**
* @param selected true if should indicate if this is a selected state, false otherwise
*/
public void setSelected(boolean selected) {
super.setSelected(selected);
mSelected = selected;
setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
if (mHighlightWhenSelected) {
setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
}
if (mShowMoreWhenSelected && mMoreIcon != null) {
mMoreIcon.setVisibility(selected ? VISIBLE : GONE);
}
updateImage();
}
@@ -155,12 +115,165 @@ public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImag
return mHasUnseen;
}
/**
* @return The app categories the component represents
*/
public String[] getCategories() {
if (mButtonCategories == null) {
return new String[0];
}
return mButtonCategories;
}
/**
* @return The valid packages that should be considered.
*/
public String[] getPackages() {
if (mButtonPackages == null) {
return new String[0];
}
return mButtonPackages;
}
/**
* @return The list of component names.
*/
public String[] getComponentName() {
if (mComponentNames == null) {
return new String[0];
}
return mComponentNames;
}
/**
* @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on
* a display.
*/
protected int getDisplayId() {
Display display = getDisplay();
if (display == null) {
return Display.INVALID_DISPLAY;
}
return display.getDisplayId();
}
protected boolean hasSelectionState() {
return mHighlightWhenSelected || mShowMoreWhenSelected;
}
/**
* Sets up intents for click, long touch, and broadcast.
*/
protected void setUpIntents(TypedArray typedArray) {
mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent);
mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent);
mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false);
String categoryString = typedArray.getString(R.styleable.CarNavigationButton_categories);
String packageString = typedArray.getString(R.styleable.CarNavigationButton_packages);
String componentNameString =
typedArray.getString(R.styleable.CarNavigationButton_componentNames);
try {
if (mIntent != null) {
final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
setOnClickListener(v -> {
try {
if (mBroadcastIntent) {
mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
mContext.sendBroadcastAsUser(
new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
UserHandle.CURRENT);
return;
}
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(mContext.getDisplayId());
mContext.startActivityAsUser(intent, options.toBundle(),
UserHandle.CURRENT);
mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
UserHandle.CURRENT);
} catch (Exception e) {
Log.e(TAG, "Failed to launch intent", e);
}
});
if (packageString != null) {
mButtonPackages = packageString.split(BUTTON_FILTER_DELIMITER);
intent.putExtra(EXTRA_BUTTON_PACKAGES, mButtonPackages);
}
if (categoryString != null) {
mButtonCategories = categoryString.split(BUTTON_FILTER_DELIMITER);
intent.putExtra(EXTRA_BUTTON_CATEGORIES, mButtonCategories);
}
if (componentNameString != null) {
mComponentNames = componentNameString.split(BUTTON_FILTER_DELIMITER);
}
}
} catch (URISyntaxException e) {
throw new RuntimeException("Failed to attach intent", e);
}
try {
if (mLongIntent != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
setOnLongClickListener(v -> {
try {
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(mContext.getDisplayId());
mContext.startActivityAsUser(intent, options.toBundle(),
UserHandle.CURRENT);
mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
UserHandle.CURRENT);
} catch (Exception e) {
Log.e(TAG, "Failed to launch intent", e);
}
// consume event either way
return true;
});
}
} catch (URISyntaxException e) {
throw new RuntimeException("Failed to attach long press intent", e);
}
}
/**
* Initializes view-related aspects of the button.
*/
private void setUpIcons(TypedArray typedArray) {
mSelectedAlpha = typedArray.getFloat(
R.styleable.CarNavigationButton_selectedAlpha, DEFAULT_SELECTED_ALPHA);
mUnselectedAlpha = typedArray.getFloat(
R.styleable.CarNavigationButton_unselectedAlpha, DEFAULT_UNSELECTED_ALPHA);
mHighlightWhenSelected = typedArray.getBoolean(
R.styleable.CarNavigationButton_highlightWhenSelected,
mHighlightWhenSelected);
mShowMoreWhenSelected = typedArray.getBoolean(
R.styleable.CarNavigationButton_showMoreWhenSelected,
mShowMoreWhenSelected);
mSelectedIconResourceId = typedArray.getResourceId(
R.styleable.CarNavigationButton_selectedIcon, mIconResourceId);
mIconResourceId = typedArray.getResourceId(
R.styleable.CarNavigationButton_icon, 0);
mIcon = findViewById(R.id.car_nav_button_icon_image);
mIcon.setScaleType(ImageView.ScaleType.CENTER);
mIcon.setClickable(false);
// Always apply selected alpha if the button does not toggle alpha based on selection state.
mIcon.setAlpha(mHighlightWhenSelected ? mUnselectedAlpha : mSelectedAlpha);
mIcon.setImageResource(mIconResourceId);
mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
mMoreIcon.setClickable(false);
mMoreIcon.setAlpha(mSelectedAlpha);
mMoreIcon.setVisibility(GONE);
}
private void updateImage() {
if (mHasUnseen) {
setImageResource(mSelected ? UNSEEN_SELECTED_ICON_RESOURCE_ID
mIcon.setImageResource(mSelected ? UNSEEN_SELECTED_ICON_RESOURCE_ID
: UNSEEN_ICON_RESOURCE_ID);
} else {
setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
}
}
}

View File

@@ -0,0 +1,55 @@
<!--
~ Copyright (C) 2019 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"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@id/nav_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:gravity="center">
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/detectable_by_component_name"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
systemui:highlightWhenSelected="true"
/>
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/detectable_by_category"
style="@style/NavigationBarButton"
systemui:categories="android.intent.category.APP_MAPS"
systemui:icon="@drawable/car_ic_navigation"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;launchFlags=0x14000000;end"
systemui:highlightWhenSelected="true"
/>
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/detectable_by_package"
style="@style/NavigationBarButton"
systemui:icon="@drawable/car_ic_phone"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
systemui:packages="com.android.car.dialer"
systemui:highlightWhenSelected="true"
/>
</LinearLayout>

View File

@@ -0,0 +1,115 @@
<!--
~ Copyright (C) 2019 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"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@id/nav_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:gravity="center">
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/default_no_selection_state"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
systemui:selectedIcon="@drawable/car_ic_overview_selected"
/>
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/app_grid_activity"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
systemui:icon="@drawable/car_ic_apps"
systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
systemui:selectedIcon="@drawable/car_ic_apps_selected"
systemui:highlightWhenSelected="true"
/>
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/long_click_app_grid_activity"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
systemui:icon="@drawable/car_ic_apps"
systemui:longIntent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
systemui:selectedIcon="@drawable/car_ic_apps_selected"
systemui:highlightWhenSelected="true"
/>
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/broadcast"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
systemui:broadcast="true"
systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
/>
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/selected_icon_undefined"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
/>
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/highlightable_no_more_button"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
systemui:selectedIcon="@drawable/car_ic_overview_selected"
systemui:highlightWhenSelected="true"
/>
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/not_highlightable_more_button"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
systemui:selectedIcon="@drawable/car_ic_overview_selected"
systemui:showMoreWhenSelected="true"
/>
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/highlightable_more_button"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
systemui:selectedIcon="@drawable/car_ic_overview_selected"
systemui:highlightWhenSelected="true"
systemui:showMoreWhenSelected="true"
/>
<com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/broadcast"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
systemui:selectedIcon="@drawable/car_ic_overview_selected"
systemui:broadcast="true"
/>
</LinearLayout>

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2019 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.navigationbar.car;
import static com.google.common.truth.Truth.assertThat;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class ButtonSelectionStateControllerTest extends SysuiTestCase {
private static final String TEST_COMPONENT_NAME_PACKAGE = "com.android.car.carlauncher";
private static final String TEST_COMPONENT_NAME_CLASS = ".CarLauncher";
private static final String TEST_CATEGORY = "com.google.android.apps.maps";
private static final String TEST_CATEGORY_CLASS = ".APP_MAPS";
private static final String TEST_PACKAGE = "com.android.car.dialer";
private static final String TEST_PACKAGE_CLASS = ".Dialer";
// LinearLayout with CarNavigationButtons with different configurations.
private LinearLayout mTestView;
private ButtonSelectionStateController mButtonSelectionStateController;
private ComponentName mComponentName;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTestView = (LinearLayout) LayoutInflater.from(mContext).inflate(
R.layout.car_button_selection_state_controller_test, /* root= */ null);
mButtonSelectionStateController = new ButtonSelectionStateController(mContext);
mButtonSelectionStateController.addAllButtonsWithSelectionState(mTestView);
}
@Test
public void onTaskChanged_buttonDetectableByComponentName_selectsAssociatedButton() {
CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_component_name);
mComponentName = new ComponentName(TEST_COMPONENT_NAME_PACKAGE, TEST_COMPONENT_NAME_CLASS);
List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
testButton.setSelected(false);
mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
assertbuttonSelected(testButton);
}
@Test
public void onTaskChanged_buttonDetectableByCategory_selectsAssociatedButton() {
CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_category);
mComponentName = new ComponentName(TEST_CATEGORY, TEST_CATEGORY_CLASS);
List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
testButton.setSelected(false);
mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
assertbuttonSelected(testButton);
}
@Test
public void onTaskChanged_buttonDetectableByPackage_selectsAssociatedButton() {
CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_package);
mComponentName = new ComponentName(TEST_PACKAGE, TEST_PACKAGE_CLASS);
List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
testButton.setSelected(false);
mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
assertbuttonSelected(testButton);
}
@Test
public void onTaskChanged_deselectsPreviouslySelectedButton() {
CarNavigationButton oldButton = mTestView.findViewById(R.id.detectable_by_component_name);
mComponentName = new ComponentName(TEST_COMPONENT_NAME_PACKAGE, TEST_COMPONENT_NAME_CLASS);
List<ActivityManager.StackInfo> oldStack = createTestStack(mComponentName);
oldButton.setSelected(false);
mButtonSelectionStateController.taskChanged(oldStack, /* validDisplay= */ -1);
mComponentName = new ComponentName(TEST_PACKAGE, TEST_PACKAGE_CLASS);
List<ActivityManager.StackInfo> newStack = createTestStack(mComponentName);
mButtonSelectionStateController.taskChanged(newStack, /* validDisplay= */ -1);
assertButtonUnselected(oldButton);
}
// Comparing alpha is a valid way to verify button selection state because all test buttons use
// highlightWhenSelected = true.
private void assertbuttonSelected(CarNavigationButton button) {
assertThat(button.getAlpha()).isEqualTo(CarNavigationButton.DEFAULT_SELECTED_ALPHA);
}
private void assertButtonUnselected(CarNavigationButton button) {
assertThat(button.getAlpha()).isEqualTo(CarNavigationButton.DEFAULT_UNSELECTED_ALPHA);
}
private List<ActivityManager.StackInfo> createTestStack(ComponentName componentName) {
ActivityManager.StackInfo validStackInfo = new ActivityManager.StackInfo();
validStackInfo.displayId = -1; // No display is assigned to this test view
validStackInfo.topActivity = componentName;
List<ActivityManager.StackInfo> testStack = new ArrayList<>();
testStack.add(validStackInfo);
return testStack;
}
}

View File

@@ -51,7 +51,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
private TestableResources mTestableResources;
@Mock
private CarFacetButtonController mCarFacetButtonController;
private ButtonSelectionStateController mButtonSelectionStateController;
@Mock
private HvacController mHvacController;
@@ -69,7 +69,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
@Test
public void testConnectToHvac_callsConnect() {
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
mCarNavigationBar.connectToHvac();
@@ -79,7 +79,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
@Test
public void testRemoveAllFromHvac_callsRemoveAll() {
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
mCarNavigationBar.removeAllFromHvac();
@@ -90,7 +90,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testGetBottomWindow_bottomDisabled_returnsNull() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, false);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getBottomWindow();
@@ -101,7 +101,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testGetBottomWindow_bottomEnabled_returnsWindow() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getBottomWindow();
@@ -112,7 +112,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testGetBottomWindow_bottomEnabled_calledTwice_returnsSameWindow() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window1 = mCarNavigationBar.getBottomWindow();
ViewGroup window2 = mCarNavigationBar.getBottomWindow();
@@ -124,7 +124,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testGetLeftWindow_leftDisabled_returnsNull() {
mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, false);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getLeftWindow();
assertThat(window).isNull();
}
@@ -133,7 +133,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testGetLeftWindow_leftEnabled_returnsWindow() {
mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getLeftWindow();
@@ -144,7 +144,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testGetLeftWindow_leftEnabled_calledTwice_returnsSameWindow() {
mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window1 = mCarNavigationBar.getLeftWindow();
ViewGroup window2 = mCarNavigationBar.getLeftWindow();
@@ -156,7 +156,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testGetRightWindow_rightDisabled_returnsNull() {
mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, false);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getRightWindow();
@@ -167,7 +167,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testGetRightWindow_rightEnabled_returnsWindow() {
mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getRightWindow();
@@ -178,7 +178,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testGetRightWindow_rightEnabled_calledTwice_returnsSameWindow() {
mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window1 = mCarNavigationBar.getRightWindow();
ViewGroup window2 = mCarNavigationBar.getRightWindow();
@@ -190,7 +190,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testSetBottomWindowVisibility_setTrue_isVisible() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getBottomWindow();
mCarNavigationBar.setBottomWindowVisibility(View.VISIBLE);
@@ -202,7 +202,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testSetBottomWindowVisibility_setFalse_isGone() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getBottomWindow();
mCarNavigationBar.setBottomWindowVisibility(View.GONE);
@@ -214,7 +214,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testSetLeftWindowVisibility_setTrue_isVisible() {
mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getLeftWindow();
mCarNavigationBar.setLeftWindowVisibility(View.VISIBLE);
@@ -226,7 +226,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testSetLeftWindowVisibility_setFalse_isGone() {
mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getLeftWindow();
mCarNavigationBar.setLeftWindowVisibility(View.GONE);
@@ -238,7 +238,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testSetRightWindowVisibility_setTrue_isVisible() {
mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getRightWindow();
mCarNavigationBar.setRightWindowVisibility(View.VISIBLE);
@@ -250,7 +250,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testSetRightWindowVisibility_setFalse_isGone() {
mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
ViewGroup window = mCarNavigationBar.getRightWindow();
mCarNavigationBar.setRightWindowVisibility(View.GONE);
@@ -262,7 +262,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testRegisterBottomBarTouchListener_createViewFirst_registrationSuccessful() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
View.OnTouchListener controller = bottomBar.getStatusBarWindowTouchListener();
@@ -277,7 +277,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testRegisterBottomBarTouchListener_registerFirst_registrationSuccessful() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
mCarNavigationBar.registerBottomBarTouchListener(mock(View.OnTouchListener.class));
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
@@ -290,7 +290,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testRegisterNotificationController_createViewFirst_registrationSuccessful() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
CarNavigationBarController.NotificationsShadeController controller =
@@ -307,7 +307,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testRegisterNotificationController_registerFirst_registrationSuccessful() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
mCarNavigationBar.registerNotificationController(
mock(CarNavigationBarController.NotificationsShadeController.class));
@@ -322,7 +322,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testShowAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsVisible() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons);
@@ -335,7 +335,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testShowAllKeyguardButtons_bottomEnabled_bottomNavButtonsGone() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
View bottomButtons = bottomBar.findViewById(R.id.nav_buttons);
@@ -348,7 +348,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testHideAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsGone() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons);
@@ -363,7 +363,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testHideAllKeyguardButtons_bottomEnabled_bottomNavButtonsVisible() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
View bottomButtons = bottomBar.findViewById(R.id.nav_buttons);
@@ -378,7 +378,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_hasUnseen_setCorrectly() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications);
@@ -393,7 +393,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase {
public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_noUnseen_setCorrectly() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
() -> mCarFacetButtonController, () -> mHvacController);
mButtonSelectionStateController, () -> mHvacController);
CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications);

View File

@@ -0,0 +1,222 @@
/*
* Copyright (C) 2019 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.navigationbar.car;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class CarNavigationButtonTest extends SysuiTestCase {
private static final String DEFAULT_BUTTON_ACTIVITY_NAME =
"com.android.car.carlauncher/.CarLauncher";
private static final String APP_GRID_BUTTON_ACTIVITY_NAME =
"com.android.car.carlauncher/.AppGridActivity";
private static final String BROADCAST_ACTION_NAME =
"android.car.intent.action.TOGGLE_HVAC_CONTROLS";
private ActivityManager mActivityManager;
// LinearLayout with CarNavigationButtons with different configurations.
private LinearLayout mTestView;
// Does not have any selection state which is the default configuration.
private CarNavigationButton mDefaultButton;
@Before
public void setUp() {
mContext = spy(mContext);
mTestView = (LinearLayout) LayoutInflater.from(mContext).inflate(
R.layout.car_navigation_button_test, /* root= */ null);
mDefaultButton = mTestView.findViewById(R.id.default_no_selection_state);
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
}
@Test
public void onCreate_iconIsVisible() {
AlphaOptimizedImageButton icon = mDefaultButton.findViewById(
R.id.car_nav_button_icon_image);
assertThat(icon.getDrawable()).isNotNull();
}
@Test
public void onSelected_selectedIconDefined_togglesIcon() {
mDefaultButton.setSelected(true);
Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
mDefaultButton.setSelected(false);
Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(selectedIconDrawable).isNotEqualTo(unselectedIconDrawable);
}
@Test
public void onSelected_selectedIconUndefined_displaysSameIcon() {
CarNavigationButton selectedIconUndefinedButton = mTestView.findViewById(
R.id.selected_icon_undefined);
selectedIconUndefinedButton.setSelected(true);
Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
selectedIconUndefinedButton.setSelected(false);
Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(selectedIconDrawable).isEqualTo(unselectedIconDrawable);
}
@Test
public void onUnselected_doesNotHighlightWhenSelected_applySelectedAlpha() {
mDefaultButton.setSelected(false);
assertThat(mDefaultButton.getAlpha()).isEqualTo(
CarNavigationButton.DEFAULT_SELECTED_ALPHA);
}
@Test
public void onSelected_doesNotHighlightWhenSelected_applySelectedAlpha() {
mDefaultButton.setSelected(true);
assertThat(mDefaultButton.getAlpha()).isEqualTo(
CarNavigationButton.DEFAULT_SELECTED_ALPHA);
}
@Test
public void onUnselected_highlightWhenSelected_applyDefaultUnselectedAlpha() {
CarNavigationButton highlightWhenSelectedButton = mTestView.findViewById(
R.id.highlightable_no_more_button);
highlightWhenSelectedButton.setSelected(false);
assertThat(highlightWhenSelectedButton.getAlpha()).isEqualTo(
CarNavigationButton.DEFAULT_UNSELECTED_ALPHA);
}
@Test
public void onSelected_highlightWhenSelected_applyDefaultSelectedAlpha() {
CarNavigationButton highlightWhenSelectedButton = mTestView.findViewById(
R.id.highlightable_no_more_button);
highlightWhenSelectedButton.setSelected(true);
assertThat(highlightWhenSelectedButton.getAlpha()).isEqualTo(
CarNavigationButton.DEFAULT_SELECTED_ALPHA);
}
@Test
public void onSelected_doesNotShowMoreWhenSelected_doesNotShowMoreIcon() {
mDefaultButton.setSelected(true);
AlphaOptimizedImageButton moreIcon = mDefaultButton.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void onSelected_showMoreWhenSelected_showsMoreIcon() {
CarNavigationButton showMoreWhenSelected = mTestView.findViewById(
R.id.not_highlightable_more_button);
showMoreWhenSelected.setSelected(true);
AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
public void onUnselected_showMoreWhenSelected_doesNotShowMoreIcon() {
CarNavigationButton showMoreWhenSelected = mTestView.findViewById(
R.id.highlightable_no_more_button);
showMoreWhenSelected.setSelected(true);
showMoreWhenSelected.setSelected(false);
AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void onClick_launchesIntentActivity() {
mDefaultButton.performClick();
assertThat(getCurrentActivityName()).isEqualTo(DEFAULT_BUTTON_ACTIVITY_NAME);
CarNavigationButton appGridButton = mTestView.findViewById(R.id.app_grid_activity);
appGridButton.performClick();
assertThat(getCurrentActivityName()).isEqualTo(APP_GRID_BUTTON_ACTIVITY_NAME);
}
@Test
public void onLongClick_longIntentDefined_launchesLongIntentActivity() {
mDefaultButton.performClick();
assertThat(getCurrentActivityName()).isEqualTo(DEFAULT_BUTTON_ACTIVITY_NAME);
CarNavigationButton appGridButton = mTestView.findViewById(
R.id.long_click_app_grid_activity);
appGridButton.performLongClick();
assertThat(getCurrentActivityName()).isEqualTo(APP_GRID_BUTTON_ACTIVITY_NAME);
}
@Test
public void onClick_useBroadcast_broadcastsIntent() {
CarNavigationButton appGridButton = mTestView.findViewById(R.id.broadcast);
appGridButton.performClick();
verify(mContext).sendBroadcastAsUser(argThat(new ArgumentMatcher<Intent>() {
@Override
public boolean matches(Intent argument) {
return argument.getAction().equals(BROADCAST_ACTION_NAME);
}
}), any());
}
private String getCurrentActivityName() {
return mActivityManager.getRunningTasks(1).get(0).topActivity.flattenToShortString();
}
}

View File

@@ -22,32 +22,6 @@
<attr name="selectedAlpha" format="float" />
<attr name="unselectedAlpha" format="float" />
<!-- Allow for custom attribs to be added to a facet button -->
<declare-styleable name="CarFacetButton">
<!-- icon to be rendered (drawable) -->
<attr name="icon"/>
<!-- icon to be rendered when in selected state -->
<attr name="selectedIcon"/>
<!-- intent to start when button is click -->
<attr name="intent"/>
<!-- intent to start when a long press has happened -->
<attr name="longIntent"/>
<!-- categories that will be added as extras to the fired intents -->
<attr name="categories" format="string"/>
<!-- package names that will be added as extras to the fired intents -->
<attr name="packages" format="string" />
<!-- componentName names that will be used for detecting selected state -->
<attr name="componentNames" format="string" />
<!-- Alpha value to used when in selected state. Defaults 1f -->
<attr name="selectedAlpha" />
<!-- Alpha value to used when in un-selected state. Defaults 0.7f -->
<attr name="unselectedAlpha" />
<!-- Render a "more" icon. Defaults true -->
<attr name="useMoreIcon" format="boolean" />
</declare-styleable>
<!-- Allow for custom attribs to be added to a nav button -->
<declare-styleable name="CarNavigationButton">
<!-- intent to start when button is click -->
@@ -64,6 +38,16 @@
<attr name="selectedIcon" />
<!-- icon to be rendered (drawable) -->
<attr name="icon"/>
<!-- categories that will be added as extras to the fired intents -->
<attr name="categories" format="string"/>
<!-- package names that will be added as extras to the fired intents -->
<attr name="packages" format="string" />
<!-- componentName names that will be used for detecting selected state -->
<attr name="componentNames" format="string" />
<!-- whether to highlight the button when selected. Defaults false -->
<attr name="showMoreWhenSelected" format="boolean" />
<!-- whether to highlight the button when selected. Defaults false -->
<attr name="highlightWhenSelected" format="boolean" />
</declare-styleable>
<!-- Custom attributes to configure hvac values -->