Highlight car nav facet when recent task changes.
CarStatusBar will now register a ITaskStackListener to handle changes in task stack and highlight the appropriate facet for the current task. Currently using resource defined package names and category to the filter for a given facet. OEMs are expected to use categories definied in Intent.java Also refactored business logic from CarNavigationBarView into CarNavigationBar controller. Change-Id: I203917ea43f2f488a1167f27dab84f1c451b3e93
This commit is contained in:
27
packages/SystemUI/res/drawable/car_ic_arrow.xml
Normal file
27
packages/SystemUI/res/drawable/car_ic_arrow.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
~ Copyright (C) 2015 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
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48.0dp"
|
||||
android:height="48.0dp"
|
||||
android:viewportWidth="48.0"
|
||||
android:viewportHeight="48.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M14.0,20.0l10.0,10.0 10.0,-10.0z"/>
|
||||
<path
|
||||
android:pathData="M0 0h48v48H0z"
|
||||
android:fillColor="#00000000"/>
|
||||
</vector>
|
||||
42
packages/SystemUI/res/layout/car_navigation_button.xml
Normal file
42
packages/SystemUI/res/layout/car_navigation_button.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
**
|
||||
** Copyright 2016, 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.
|
||||
*/
|
||||
-->
|
||||
|
||||
<com.android.systemui.statusbar.car.CarNavigationButton
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
<com.android.keyguard.AlphaOptimizedImageButton
|
||||
android:id="@+id/car_nav_button_icon"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:animateLayoutChanges="true">
|
||||
</com.android.keyguard.AlphaOptimizedImageButton>
|
||||
|
||||
<com.android.keyguard.AlphaOptimizedImageButton
|
||||
android:id="@+id/car_nav_button_more_icon"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@+id/car_nav_button_icon"
|
||||
android:animateLayoutChanges="true">
|
||||
</com.android.keyguard.AlphaOptimizedImageButton>
|
||||
</com.android.systemui.statusbar.car.CarNavigationButton>
|
||||
@@ -22,7 +22,9 @@
|
||||
isn't a longpress action associated with a shortcut item, put in an empty item to make
|
||||
sure everything lines up.
|
||||
-->
|
||||
<array name="car_shortcut_icons" />
|
||||
<array name="car_shortcut_intent_uris" />
|
||||
<array name="car_shortcut_longpress_intent_uris" />
|
||||
<array name="car_facet_icons" />
|
||||
<array name="car_facet_intent_uris" />
|
||||
<array name="car_facet_longpress_intent_uris" />
|
||||
<array name="car_facet_package_filters"/>
|
||||
<array name="car_facet_category_filters"/>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.statusbar.car;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.util.SimpleArrayMap;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.phone.ActivityStarter;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A controller to populate data for CarNavigationBarView and handle user interactions.
|
||||
* <p/>
|
||||
* Each button inside the navigation bar is defined by data in arrays_car.xml. OEMs can customize
|
||||
* the navigation buttons by updating arrays_car.xml appropriately in an overlay.
|
||||
*/
|
||||
class CarNavigationBarController {
|
||||
|
||||
// Each facet of the navigation bar maps to a set of package names or categories defined in
|
||||
// arrays_car.xml. Package names for a given facet are delimited by ";"
|
||||
private static final String FACET_FILTER_DEMILITER = ";";
|
||||
|
||||
private Context mContext;
|
||||
private CarNavigationBarView mNavBar;
|
||||
private ActivityStarter mActivityStarter;
|
||||
|
||||
// Set of categories each facet will filter on.
|
||||
private List<String[]> mFacetCategories = new ArrayList<String[]>();
|
||||
// Set of package names each facet will filter on.
|
||||
private List<String[]> mFacetPackages = new ArrayList<String[]>();
|
||||
|
||||
private SimpleArrayMap<String, Integer> mFacetCategoryMap
|
||||
= new SimpleArrayMap<String, Integer>();
|
||||
private SimpleArrayMap<String, Integer> mFacetPackageMap
|
||||
= new SimpleArrayMap<String, Integer>();
|
||||
|
||||
private List<Intent> mIntents = new ArrayList<Intent>();
|
||||
private List<Intent> mLongPressIntents = new ArrayList<Intent>();
|
||||
|
||||
private List<CarNavigationButton> mNavButtons = new ArrayList<CarNavigationButton>();
|
||||
|
||||
private int mCurrentFacetIndex;
|
||||
|
||||
public CarNavigationBarController(Context context,
|
||||
CarNavigationBarView navBar,
|
||||
ActivityStarter activityStarter) {
|
||||
mContext = context;
|
||||
mNavBar = navBar;
|
||||
mActivityStarter = activityStarter;
|
||||
bind();
|
||||
}
|
||||
|
||||
public void taskChanged(String packageName) {
|
||||
// If the package name belongs to a filter, then highlight appropriate button in
|
||||
// the navigation bar.
|
||||
if (mFacetPackageMap.containsKey(packageName)) {
|
||||
setCurrentFacet(mFacetPackageMap.get(packageName));
|
||||
}
|
||||
|
||||
// Check if the package matches any of the categories for the facets
|
||||
String category = getPackageCategory(packageName);
|
||||
if (category != null) {
|
||||
setCurrentFacet(mFacetCategoryMap.get(category));
|
||||
}
|
||||
}
|
||||
|
||||
private void bind() {
|
||||
// Read up arrays_car.xml and populate the navigation bar here.
|
||||
Resources r = mContext.getResources();
|
||||
TypedArray icons = r.obtainTypedArray(R.array.car_facet_icons);
|
||||
TypedArray intents = r.obtainTypedArray(R.array.car_facet_intent_uris);
|
||||
TypedArray longpressIntents =
|
||||
r.obtainTypedArray(R.array.car_facet_longpress_intent_uris);
|
||||
TypedArray facetPackageNames = r.obtainTypedArray(R.array.car_facet_package_filters);
|
||||
|
||||
TypedArray facetCategories = r.obtainTypedArray(R.array.car_facet_category_filters);
|
||||
|
||||
if (icons.length() != intents.length()
|
||||
|| icons.length() != longpressIntents.length()
|
||||
|| icons.length() != facetPackageNames.length()
|
||||
|| icons.length() != facetCategories.length()) {
|
||||
throw new RuntimeException("car_facet array lengths do not match");
|
||||
}
|
||||
|
||||
for (int i = 0; i < icons.length(); i++) {
|
||||
Drawable icon = icons.getDrawable(i);
|
||||
try {
|
||||
mIntents.add(i,
|
||||
Intent.parseUri(intents.getString(i), Intent.URI_INTENT_SCHEME));
|
||||
|
||||
String longpressUri = longpressIntents.getString(i);
|
||||
boolean hasLongpress = !longpressUri.isEmpty();
|
||||
if (hasLongpress) {
|
||||
mLongPressIntents.add(i,
|
||||
Intent.parseUri(longpressUri, Intent.URI_INTENT_SCHEME));
|
||||
}
|
||||
|
||||
CarNavigationButton button = createNavButton(icon, i, hasLongpress);
|
||||
mNavButtons.add(button);
|
||||
mNavBar.addButton(button, createNavButton(icon, i, hasLongpress));
|
||||
|
||||
initFacetFilterMaps(i,
|
||||
facetPackageNames.getString(i).split(FACET_FILTER_DEMILITER),
|
||||
facetCategories.getString(i).split(FACET_FILTER_DEMILITER));
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException("Malformed intent uri", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initFacetFilterMaps(int id, String[] packageNames, String[] categories){
|
||||
mFacetCategories.add(categories);
|
||||
for (int i = 0; i < categories.length; i++) {
|
||||
mFacetCategoryMap.put(categories[i], id);
|
||||
}
|
||||
|
||||
mFacetPackages.add(packageNames);
|
||||
for (int i = 0; i < packageNames.length; i++) {
|
||||
mFacetPackageMap.put(packageNames[i], id);
|
||||
}
|
||||
}
|
||||
|
||||
private String getPackageCategory(String packageName) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
int size = mFacetCategories.size();
|
||||
// For each facet, check if the given package name matches one of its categories
|
||||
for (int i = 0; i < size; i++) {
|
||||
String[] categories = mFacetCategories.get(i);
|
||||
for (int j = 0; j < categories.length; j++) {
|
||||
String category = categories[j];
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(packageName);
|
||||
intent.setAction(Intent.ACTION_MAIN);
|
||||
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
|
||||
// all categories next time this package name shows up.
|
||||
mFacetPackageMap.put(packageName, mFacetCategoryMap.get(category));
|
||||
return category;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to check if a given facet has multiple packages associated with it.
|
||||
* This can be resource defined package names or package names filtered by facet category.
|
||||
*/
|
||||
private boolean facetHasMultiplePackages(int index) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
|
||||
// Check if the packages defined for the filter actually exists on the device
|
||||
String[] packages = mFacetPackages.get(index);
|
||||
if (packages.length > 1) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < packages.length; i++) {
|
||||
count += pm.getLaunchIntentForPackage(packages[i]) != null ? 1 : 0;
|
||||
if (count > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there weren't multiple packages defined for the facet, check the categories
|
||||
// and see if they resolve to multiple package names
|
||||
String categories[] = mFacetCategories.get(index);
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; i < categories.length; i++) {
|
||||
String category = categories[i];
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_MAIN);
|
||||
intent.addCategory(category);
|
||||
count += pm.queryIntentActivities(intent, 0).size();
|
||||
if (count > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setCurrentFacet(int index) {
|
||||
if (index == mCurrentFacetIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mNavButtons.get(mCurrentFacetIndex) != null) {
|
||||
mNavButtons.get(mCurrentFacetIndex)
|
||||
.setSelected(false /* selected */, false /* showMoreIcon */);
|
||||
}
|
||||
|
||||
if (mNavButtons.get(index) != null) {
|
||||
mNavButtons.get(index).setSelected(true /* selected */,
|
||||
facetHasMultiplePackages(index) /* showMoreIcon */);
|
||||
}
|
||||
mCurrentFacetIndex = index;
|
||||
}
|
||||
|
||||
private CarNavigationButton createNavButton(Drawable icon, final int id,
|
||||
boolean longClickEnabled) {
|
||||
CarNavigationButton button = (CarNavigationButton) View.inflate(mContext,
|
||||
R.layout.car_navigation_button, null);
|
||||
button.setResources(icon);
|
||||
LinearLayout.LayoutParams lp =
|
||||
new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
|
||||
button.setLayoutParams(lp);
|
||||
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setCurrentFacet(id);
|
||||
onFacetClicked(id);
|
||||
}
|
||||
});
|
||||
|
||||
if (longClickEnabled) {
|
||||
button.setLongClickable(true);
|
||||
button.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
onFacetLongClicked(id);
|
||||
setCurrentFacet(id);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
button.setLongClickable(false);
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
private void startActivity(Intent intent) {
|
||||
if (mActivityStarter != null && intent != null) {
|
||||
mActivityStarter.startActivity(intent, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void onFacetClicked(int index) {
|
||||
// TODO: determine what data to pass to the trampoline, so it can start
|
||||
// the default app or the lens picker.
|
||||
startActivity(mIntents.get(index));
|
||||
}
|
||||
|
||||
private void onFacetLongClicked(int index) {
|
||||
// TODO: determine what data to pass to the trampoline, so it can start
|
||||
// the default app or the lens picker.
|
||||
startActivity(mLongPressIntents.get(index));
|
||||
}
|
||||
}
|
||||
@@ -17,36 +17,29 @@
|
||||
package com.android.systemui.statusbar.car;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.R.color;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView.ScaleType;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.phone.ActivityStarter;
|
||||
import com.android.systemui.statusbar.phone.NavigationBarView;
|
||||
import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
|
||||
import com.android.systemui.statusbar.policy.KeyButtonView;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A custom navigation bar for the automotive use case.
|
||||
* <p>
|
||||
* The navigation bar in the automotive use case is more like a list of shortcuts, which we
|
||||
* expect to be customizable by the car OEMs. This implementation populates the nav_buttons layout
|
||||
* from resources rather than the layout file so customization would then mean updating
|
||||
* arrays_car.xml appropriately in an overlay.
|
||||
* The navigation bar in the automotive use case is more like a list of shortcuts, rendered
|
||||
* in a linear layout.
|
||||
*/
|
||||
class CarNavigationBarView extends NavigationBarView {
|
||||
private ActivityStarter mActivityStarter;
|
||||
private LinearLayout mNavButtons;
|
||||
private LinearLayout mLightsOutButtons;
|
||||
|
||||
public CarNavigationBarView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -54,83 +47,13 @@ class CarNavigationBarView extends NavigationBarView {
|
||||
|
||||
@Override
|
||||
public void onFinishInflate() {
|
||||
// Read up arrays_car.xml and populate the navigation bar here.
|
||||
Context context = getContext();
|
||||
Resources r = getContext().getResources();
|
||||
TypedArray icons = r.obtainTypedArray(R.array.car_shortcut_icons);
|
||||
TypedArray intents = r.obtainTypedArray(R.array.car_shortcut_intent_uris);
|
||||
TypedArray longpressIntents =
|
||||
r.obtainTypedArray(R.array.car_shortcut_longpress_intent_uris);
|
||||
|
||||
if (icons.length() != intents.length()) {
|
||||
throw new RuntimeException("car_shortcut_icons and car_shortcut_intents do not match");
|
||||
}
|
||||
|
||||
LinearLayout navButtons = (LinearLayout) findViewById(R.id.nav_buttons);
|
||||
LinearLayout lightsOut = (LinearLayout) findViewById(R.id.lights_out);
|
||||
|
||||
for (int i = 0; i < icons.length(); i++) {
|
||||
Drawable icon = icons.getDrawable(i);
|
||||
|
||||
try {
|
||||
Intent intent = Intent.parseUri(intents.getString(i), Intent.URI_INTENT_SCHEME);
|
||||
Intent longpress = null;
|
||||
String longpressUri = longpressIntents.getString(i);
|
||||
if (!longpressUri.isEmpty()) {
|
||||
longpress = Intent.parseUri(longpressUri, Intent.URI_INTENT_SCHEME);
|
||||
}
|
||||
|
||||
// nav_buttons and lights_out should match exactly.
|
||||
navButtons.addView(makeButton(context, icon, intent, longpress));
|
||||
lightsOut.addView(makeButton(context, icon, intent, longpress));
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException("Malformed intent uri", e);
|
||||
}
|
||||
}
|
||||
mNavButtons = (LinearLayout) findViewById(R.id.nav_buttons);
|
||||
mLightsOutButtons = (LinearLayout) findViewById(R.id.lights_out);
|
||||
}
|
||||
|
||||
private ImageButton makeButton(Context context, Drawable icon,
|
||||
final Intent intent, final Intent longpress) {
|
||||
ImageButton button = new ImageButton(context);
|
||||
|
||||
button.setImageDrawable(icon);
|
||||
button.setScaleType(ScaleType.CENTER);
|
||||
button.setBackgroundColor(color.transparent);
|
||||
LinearLayout.LayoutParams lp =
|
||||
new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
|
||||
button.setLayoutParams(lp);
|
||||
|
||||
button.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mActivityStarter != null) {
|
||||
mActivityStarter.startActivity(intent, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Long click handlers are optional.
|
||||
if (longpress != null) {
|
||||
button.setLongClickable(true);
|
||||
button.setOnLongClickListener(new OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
if (mActivityStarter != null) {
|
||||
mActivityStarter.startActivity(longpress, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
button.setLongClickable(false);
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
public void setActivityStarter(ActivityStarter activityStarter) {
|
||||
mActivityStarter = activityStarter;
|
||||
public void addButton(CarNavigationButton button, CarNavigationButton lightsOutButton){
|
||||
mNavButtons.addView(button);
|
||||
mLightsOutButtons.addView(lightsOutButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.statusbar.car;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import com.android.keyguard.AlphaOptimizedImageButton;
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* A wrapper view for a car navigation facet, which includes a button icon and a drop down icon.
|
||||
*/
|
||||
public class CarNavigationButton extends RelativeLayout {
|
||||
private static final float SELECTED_ALPHA = 1;
|
||||
private static final float UNSELECTED_ALPHA = 0.7f;
|
||||
|
||||
private AlphaOptimizedImageButton mIcon;
|
||||
private AlphaOptimizedImageButton mMoreIcon;
|
||||
|
||||
public CarNavigationButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_icon);
|
||||
mIcon.setClickable(false);
|
||||
mIcon.setScaleType(ImageView.ScaleType.CENTER);
|
||||
mIcon.setBackgroundColor(android.R.color.transparent);
|
||||
mIcon.setAlpha(UNSELECTED_ALPHA);
|
||||
|
||||
mMoreIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_more_icon);
|
||||
mMoreIcon.setClickable(false);
|
||||
mMoreIcon.setScaleType(ImageView.ScaleType.CENTER);
|
||||
mMoreIcon.setBackgroundColor(android.R.color.transparent);
|
||||
mMoreIcon.setVisibility(INVISIBLE);
|
||||
mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
|
||||
mMoreIcon.setAlpha(UNSELECTED_ALPHA);
|
||||
}
|
||||
|
||||
public void setResources(Drawable icon) {
|
||||
mIcon.setImageDrawable(icon);
|
||||
}
|
||||
|
||||
public void setSelected(boolean selected, boolean showMoreIcon) {
|
||||
if (selected) {
|
||||
mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : INVISIBLE);
|
||||
mMoreIcon.setAlpha(SELECTED_ALPHA);
|
||||
mIcon.setAlpha(SELECTED_ALPHA);
|
||||
} else {
|
||||
mMoreIcon.setVisibility(INVISIBLE);
|
||||
mIcon.setAlpha(UNSELECTED_ALPHA);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,30 +16,52 @@
|
||||
|
||||
package com.android.systemui.statusbar.car;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ITaskStackListener;
|
||||
import android.content.Context;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.recents.Recents;
|
||||
import com.android.systemui.recents.misc.SystemServicesProxy;
|
||||
import com.android.systemui.statusbar.phone.PhoneStatusBar;
|
||||
|
||||
/**
|
||||
* A status bar (and navigation bar) tailored for the automotive use case.
|
||||
*/
|
||||
public class CarStatusBar extends PhoneStatusBar {
|
||||
private SystemServicesProxy mSystemServicesProxy;
|
||||
private TaskStackListenerImpl mTaskStackListener;
|
||||
private Handler mHandler;
|
||||
|
||||
private CarNavigationBarView mCarNavigationBar;
|
||||
private CarNavigationBarController mController;
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
mHandler = new Handler();
|
||||
mTaskStackListener = new TaskStackListenerImpl(mHandler);
|
||||
mSystemServicesProxy = new SystemServicesProxy(mContext);
|
||||
mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addNavigationBar() {
|
||||
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
|
||||
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
|
||||
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
lp.setTitle("CarNavigationBar");
|
||||
lp.windowAnimations = 0;
|
||||
@@ -51,11 +73,11 @@ public class CarStatusBar extends PhoneStatusBar {
|
||||
if (mNavigationBarView != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CarNavigationBarView carNavBar =
|
||||
mCarNavigationBar =
|
||||
(CarNavigationBarView) View.inflate(context, R.layout.car_navigation_bar, null);
|
||||
carNavBar.setActivityStarter(this);
|
||||
mNavigationBarView = carNavBar;
|
||||
mController = new CarNavigationBarController(context, mCarNavigationBar,
|
||||
this /* ActivityStarter*/);
|
||||
mNavigationBarView = mCarNavigationBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,4 +85,40 @@ public class CarStatusBar extends PhoneStatusBar {
|
||||
// The navigation bar for a vehicle will not need to be repositioned, as it is always
|
||||
// set at the bottom.
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of ITaskStackListener, that listens for changes in the system task
|
||||
* stack and notifies the navigation bar.
|
||||
*/
|
||||
private class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
|
||||
private Handler mHandler;
|
||||
|
||||
public TaskStackListenerImpl(Handler handler) {
|
||||
this.mHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPinned() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskStackChanged() {
|
||||
mHandler.removeCallbacks(this);
|
||||
mHandler.post(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ensureMainThread();
|
||||
SystemServicesProxy ssp = Recents.getSystemServices();
|
||||
ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
|
||||
mController.taskChanged(runningTaskInfo.baseActivity.getPackageName());
|
||||
}
|
||||
|
||||
private void ensureMainThread() {
|
||||
if (!Looper.getMainLooper().isCurrentThread()) {
|
||||
throw new RuntimeException("Must be called on the UI thread");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user