System UI for auto.
This change adds in the beginnings of System UI for
the automotive use case. We extend the Phone status
and navigation bar and override the parts which are
customized for auto.
The navigation bar itself is built from a resource
array specified in car_arrays.xml to allow of ease
of customization of the shortcuts that are in the
navigation bar.
Bug: 26301185
Change-Id: I780a2ef9dd5ae2a4be9355b5874d08f521a86fa7
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 6a10c2c..bc18221 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -10,6 +10,7 @@
public void setGlowScale(float);
}
+-keep class com.android.systemui.statusbar.car.CarStatusBar
-keep class com.android.systemui.statusbar.phone.PhoneStatusBar
-keep class com.android.systemui.statusbar.tv.TvStatusBar
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
new file mode 100644
index 0000000..f7f673d
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_navigation_bar.xml
@@ -0,0 +1,51 @@
+<?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.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:background="@drawable/system_bar_background">
+
+ <!-- phone.NavigationBarView has rot0 and rot90 but we expect the car head unit to have a fixed
+ rotation so skip this level of the heirarchy.
+ -->
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:id="@+id/nav_buttons"
+ android:animateLayoutChanges="true">
+
+ <!-- Buttons get populated here from a car_arrays.xml. -->
+ </LinearLayout>
+
+ <!-- lights out layout to match exactly -->
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:id="@+id/lights_out"
+ android:visibility="gone">
+ <!-- Must match nav_buttons. -->
+ </LinearLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/values/arrays_car.xml b/packages/SystemUI/res/values/arrays_car.xml
new file mode 100644
index 0000000..230479d
--- /dev/null
+++ b/packages/SystemUI/res/values/arrays_car.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- These should be overriden in an overlay. The default implementation is empty.
+ There needs to be correspondence per index between these arrays, which means that if there
+ 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" />
+</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
new file mode 100644
index 0000000..5c0f38c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -0,0 +1,152 @@
+/*
+ * 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.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;
+
+/**
+ * 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.
+ */
+class CarNavigationBarView extends NavigationBarView {
+ private ActivityStarter mActivityStarter;
+
+ public CarNavigationBarView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @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);
+ }
+ }
+ }
+
+ 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;
+ }
+
+ @Override
+ public void setDisabledFlags(int disabledFlags, boolean force) {
+ // TODO: Populate.
+ }
+
+ @Override
+ public void reorient() {
+ // We expect the car head unit to always have a fixed rotation so we ignore this. The super
+ // class implentation expects mRotatedViews to be populated, so if you call into it, there
+ // is a possibility of a NullPointerException.
+ }
+
+ @Override
+ public View getCurrentView() {
+ return this;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
new file mode 100644
index 0000000..a72b5d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -0,0 +1,60 @@
+/*
+ * 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.PixelFormat;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+/**
+ * A status bar (and navigation bar) tailored for the automotive use case.
+ */
+public class CarStatusBar extends PhoneStatusBar {
+ @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,
+ PixelFormat.TRANSLUCENT);
+ lp.setTitle("CarNavigationBar");
+ lp.windowAnimations = 0;
+ mWindowManager.addView(mNavigationBarView, lp);
+ }
+
+ @Override
+ protected void createNavigationBarView(Context context) {
+ if (mNavigationBarView != null) {
+ return;
+ }
+
+ CarNavigationBarView carNavBar =
+ (CarNavigationBarView) View.inflate(context, R.layout.car_navigation_bar, null);
+ carNavBar.setActivityStarter(this);
+ mNavigationBarView = carNavBar;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index eade753..ca40357 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -157,8 +157,8 @@
final String how = "" + m.obj;
final int w = getWidth();
final int h = getHeight();
- final int vw = mCurrentView.getWidth();
- final int vh = mCurrentView.getHeight();
+ final int vw = getCurrentView().getWidth();
+ final int vh = getCurrentView().getHeight();
if (h != vh || w != vw) {
Log.w(TAG, String.format(
@@ -230,27 +230,27 @@
}
public KeyButtonView getRecentsButton() {
- return (KeyButtonView) mCurrentView.findViewById(R.id.recent_apps);
+ return (KeyButtonView) getCurrentView().findViewById(R.id.recent_apps);
}
public View getMenuButton() {
- return mCurrentView.findViewById(R.id.menu);
+ return getCurrentView().findViewById(R.id.menu);
}
public View getBackButton() {
- return mCurrentView.findViewById(R.id.back);
+ return getCurrentView().findViewById(R.id.back);
}
public KeyButtonView getHomeButton() {
- return (KeyButtonView) mCurrentView.findViewById(R.id.home);
+ return (KeyButtonView) getCurrentView().findViewById(R.id.home);
}
public View getImeSwitchButton() {
- return mCurrentView.findViewById(R.id.ime_switcher);
+ return getCurrentView().findViewById(R.id.ime_switcher);
}
public View getAppShelf() {
- return mCurrentView.findViewById(R.id.app_shelf);
+ return getCurrentView().findViewById(R.id.app_shelf);
}
private void getIcons(Resources res) {
@@ -326,7 +326,7 @@
setSlippery(disableHome && disableRecent && disableBack && disableSearch);
}
- ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
+ ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
if (navButtons != null) {
LayoutTransition lt = navButtons.getLayoutTransition();
if (lt != null) {
@@ -379,7 +379,7 @@
private void updateLayoutTransitionsEnabled() {
boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled;
- ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
+ ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
LayoutTransition lt = navButtons.getLayoutTransition();
if (lt != null) {
if (enabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 80fcba60e..aa9315b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -728,32 +728,7 @@
boolean showNav = mWindowManagerService.hasNavigationBar();
if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) {
- // Optionally show app shortcuts in the nav bar "shelf" area.
- if (shouldShowAppShelf()) {
- mNavigationBarView = (NavigationBarView) View.inflate(
- context, R.layout.navigation_bar_with_apps, null);
- } else {
- mNavigationBarView = (NavigationBarView) View.inflate(
- context, R.layout.navigation_bar, null);
- }
- mNavigationBarView.setDisabledFlags(mDisabled1);
- mNavigationBarView.setComponents(mRecents, getComponent(Divider.class));
- mNavigationBarView.setOnVerticalChangedListener(
- new NavigationBarView.OnVerticalChangedListener() {
- @Override
- public void onVerticalChanged(boolean isVertical) {
- if (mAssistManager != null) {
- mAssistManager.onConfigurationChanged();
- }
- mNotificationPanel.setQsScrimEnabled(!isVertical);
- }
- });
- mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- checkUserAutohide(v, event);
- return false;
- }});
+ createNavigationBarView(context);
}
} catch (RemoteException ex) {
// no window manager? good luck with that
@@ -979,6 +954,35 @@
return mStatusBarView;
}
+ protected void createNavigationBarView(Context context) {
+ // Optionally show app shortcuts in the nav bar "shelf" area.
+ if (shouldShowAppShelf()) {
+ mNavigationBarView = (NavigationBarView) View.inflate(
+ context, R.layout.navigation_bar_with_apps, null);
+ } else {
+ mNavigationBarView = (NavigationBarView) View.inflate(
+ context, R.layout.navigation_bar, null);
+ }
+ mNavigationBarView.setDisabledFlags(mDisabled1);
+ mNavigationBarView.setComponents(mRecents, getComponent(Divider.class));
+ mNavigationBarView.setOnVerticalChangedListener(
+ new NavigationBarView.OnVerticalChangedListener() {
+ @Override
+ public void onVerticalChanged(boolean isVertical) {
+ if (mAssistManager != null) {
+ mAssistManager.onConfigurationChanged();
+ }
+ mNotificationPanel.setQsScrimEnabled(!isVertical);
+ }
+ });
+ mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ checkUserAutohide(v, event);
+ return false;
+ }});
+ }
+
/** Returns true if the app shelf should be shown in the nav bar. */
private boolean shouldShowAppShelf() {
// Allow adb to override the default shelf behavior:
@@ -1191,7 +1195,7 @@
}
// For small-screen devices (read: phones) that lack hardware navigation buttons
- private void addNavigationBar() {
+ protected void addNavigationBar() {
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
if (mNavigationBarView == null) return;