Merge changes from topic "aae_navbutton_refactor"
* changes: Move attrs_car from SysUI to attrs in CarSystemUI Merge CarFacetButton and CarNavigationButton into a single class and add test coverage
This commit is contained in:
committed by
Android (Google) Code Review
commit
c7ce0f2dde
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<attr name="icon" format="reference"/>
|
||||
<attr name="selectedIcon" format="reference"/>
|
||||
<attr name="intent" format="string"/>
|
||||
<attr name="longIntent" format="string"/>
|
||||
<attr name="selectedAlpha" format="float" />
|
||||
<attr name="unselectedAlpha" format="float" />
|
||||
|
||||
<!-- Custom attributes to configure hvac values -->
|
||||
<declare-styleable name="AnimatedTemperatureView">
|
||||
@@ -32,4 +38,67 @@
|
||||
<attr name="android:minEms"/>
|
||||
<attr name="android:textAppearance"/>
|
||||
</declare-styleable>
|
||||
|
||||
<!-- Allow for custom attribs to be added to a nav button -->
|
||||
<declare-styleable name="CarNavigationButton">
|
||||
<!-- intent to start when button is click -->
|
||||
<attr name="intent" />
|
||||
<!-- intent to start when a long press has happened -->
|
||||
<attr name="longIntent" />
|
||||
<!-- start the intent as a broad cast instead of an activity if true-->
|
||||
<attr name="broadcast" format="boolean"/>
|
||||
<!-- 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" />
|
||||
<!-- icon to be rendered when in selected state -->
|
||||
<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 -->
|
||||
<declare-styleable name="TemperatureView">
|
||||
<attr name="hvacAreaId" format="integer"/>
|
||||
<attr name="hvacPropertyId" format="integer"/>
|
||||
<attr name="hvacTempFormat" format="string"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="carVolumeItems"/>
|
||||
<declare-styleable name="carVolumeItems_item">
|
||||
<!-- Align with AudioAttributes.USAGE_* -->
|
||||
<attr name="usage">
|
||||
<enum name="unknown" value="0"/>
|
||||
<enum name="media" value="1"/>
|
||||
<enum name="voice_communication" value="2"/>
|
||||
<enum name="voice_communication_signalling" value="3"/>
|
||||
<enum name="alarm" value="4"/>
|
||||
<enum name="notification" value="5"/>
|
||||
<enum name="notification_ringtone" value="6"/>
|
||||
<enum name="notification_communication_request" value="7"/>
|
||||
<enum name="notification_communication_instant" value="8"/>
|
||||
<enum name="notification_communication_delayed" value="9"/>
|
||||
<enum name="notification_event" value="10"/>
|
||||
<enum name="assistance_accessibility" value="11"/>
|
||||
<enum name="assistance_navigation_guidance" value="12"/>
|
||||
<enum name="assistance_sonification" value="13"/>
|
||||
<enum name="game" value="14"/>
|
||||
<!-- hidden, do not use -->
|
||||
<!-- enum name="virtual_source" value="15"/ -->
|
||||
<enum name="assistant" value="16"/>
|
||||
</attr>
|
||||
|
||||
<!-- Icon resource ids to render on UI -->
|
||||
<attr name="icon" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<attr name="icon" format="reference"/>
|
||||
<attr name="selectedIcon" format="reference"/>
|
||||
<attr name="intent" format="string"/>
|
||||
<attr name="longIntent" format="string"/>
|
||||
<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 -->
|
||||
<attr name="intent" />
|
||||
<!-- intent to start when a long press has happened -->
|
||||
<attr name="longIntent" />
|
||||
<!-- start the intent as a broad cast instead of an activity if true-->
|
||||
<attr name="broadcast" format="boolean"/>
|
||||
<!-- 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" />
|
||||
<!-- icon to be rendered when in selected state -->
|
||||
<attr name="selectedIcon" />
|
||||
<!-- icon to be rendered (drawable) -->
|
||||
<attr name="icon"/>
|
||||
</declare-styleable>
|
||||
|
||||
<!-- Custom attributes to configure hvac values -->
|
||||
<declare-styleable name="TemperatureView">
|
||||
<attr name="hvacAreaId" format="integer"/>
|
||||
<attr name="hvacPropertyId" format="integer"/>
|
||||
<attr name="hvacTempFormat" format="string"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="carVolumeItems"/>
|
||||
<declare-styleable name="carVolumeItems_item">
|
||||
<!-- Align with AudioAttributes.USAGE_* -->
|
||||
<attr name="usage">
|
||||
<enum name="unknown" value="0"/>
|
||||
<enum name="media" value="1"/>
|
||||
<enum name="voice_communication" value="2"/>
|
||||
<enum name="voice_communication_signalling" value="3"/>
|
||||
<enum name="alarm" value="4"/>
|
||||
<enum name="notification" value="5"/>
|
||||
<enum name="notification_ringtone" value="6"/>
|
||||
<enum name="notification_communication_request" value="7"/>
|
||||
<enum name="notification_communication_instant" value="8"/>
|
||||
<enum name="notification_communication_delayed" value="9"/>
|
||||
<enum name="notification_event" value="10"/>
|
||||
<enum name="assistance_accessibility" value="11"/>
|
||||
<enum name="assistance_navigation_guidance" value="12"/>
|
||||
<enum name="assistance_sonification" value="13"/>
|
||||
<enum name="game" value="14"/>
|
||||
<!-- hidden, do not use -->
|
||||
<!-- enum name="virtual_source" value="15"/ -->
|
||||
<enum name="assistant" value="16"/>
|
||||
</attr>
|
||||
|
||||
<!-- Icon resource ids to render on UI -->
|
||||
<attr name="icon" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user