Adding new Style of decor windows for Car use case.
This allows for left, right or bottom navigation bars that can be configured with xml files Bug: 62364016 Test: Manual Change-Id: If0c27ce30fa126491caf715f0e949134f93cdeaf
This commit is contained in:
51
packages/SystemUI/res/drawable/car_ic_hvac.xml
Normal file
51
packages/SystemUI/res/drawable/car_ic_hvac.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="37dp"
|
||||
android:height="31dp"
|
||||
android:viewportWidth="37"
|
||||
android:viewportHeight="31">
|
||||
|
||||
<group
|
||||
android:translateX="-4.000000"
|
||||
android:translateY="-6.000000">
|
||||
<group
|
||||
android:translateX="5.000000"
|
||||
android:translateY="5.000000">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#FAFAFA"
|
||||
android:strokeWidth="3.5"
|
||||
android:pathData="M0.320769938,6.07518051 C6.46754647,1.46509811 12.4222362,1.46509811
|
||||
18.1848392,6.07518051 C23.9474422,10.6852629 29.3258717,10.4931761
|
||||
34.3201276,5.49892021" />
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#FAFAFA"
|
||||
android:strokeWidth="3.5"
|
||||
android:pathData="M0.320769938,17.0751805 C6.46754647,12.4650981 12.4222362,12.4650981
|
||||
18.1848392,17.0751805 C23.9474422,21.6852629 29.3258717,21.4931761
|
||||
34.3201276,16.4989202" />
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#FAFAFA"
|
||||
android:strokeWidth="3.5"
|
||||
android:pathData="M0.320769938,28.0751805 C6.46754647,23.4650981 12.4222362,23.4650981
|
||||
18.1848392,28.0751805 C23.9474422,32.6852629 29.3258717,32.4931761
|
||||
34.3201276,27.4989202" />
|
||||
</group>
|
||||
</group>
|
||||
</vector>
|
||||
28
packages/SystemUI/res/drawable/car_ic_notification.xml
Normal file
28
packages/SystemUI/res/drawable/car_ic_notification.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="56dp"
|
||||
android:height="56dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M24 44c2.21 0 4-1.79 4-4h-8c0 2.21 1.79 4 4
|
||||
4zm12-12V22c0-6.15-3.27-11.28-9-12.64V8c0-1.66-1.34-3-3-3s-3 1.34-3 3v1.36c-5.73
|
||||
1.36-9 6.49-9 12.64v10l-4 4v2h32v-2l-4-4zm-4 2H16V22c0-4.97 3.03-9 8-9s8 4.03 8
|
||||
9v12z" />
|
||||
</vector>
|
||||
28
packages/SystemUI/res/drawable/car_ic_overview.xml
Normal file
28
packages/SystemUI/res/drawable/car_ic_overview.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="56dp"
|
||||
android:height="56dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
|
||||
<path
|
||||
android:pathData="M0 0h48v48H0z" />
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82
|
||||
0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z" />
|
||||
</vector>
|
||||
50
packages/SystemUI/res/layout/car_facet_button.xml
Normal file
50
packages/SystemUI/res/layout/car_facet_button.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
**
|
||||
** Copyright 2017, 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:background="@android:color/transparent"
|
||||
android:scaleType="fitCenter">
|
||||
</com.android.keyguard.AlphaOptimizedImageButton>
|
||||
|
||||
</LinearLayout>
|
||||
</merge>
|
||||
99
packages/SystemUI/res/layout/car_left_navigation_bar.xml
Normal file
99
packages/SystemUI/res/layout/car_left_navigation_bar.xml
Normal file
@@ -0,0 +1,99 @@
|
||||
<?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"
|
||||
xmlns:systemui="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/system_bar_background">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/nav_buttons"
|
||||
android:orientation="vertical"
|
||||
android:gravity="top"
|
||||
android:paddingTop="30dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/system_bar_background"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<com.android.systemui.statusbar.car.CarNavigationButton
|
||||
android:id="@+id/home"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
|
||||
android:src="@drawable/car_ic_overview"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingTop="30dp"
|
||||
android:paddingBottom="30dp"
|
||||
/>
|
||||
|
||||
<com.android.systemui.statusbar.car.CarNavigationButton
|
||||
android:id="@+id/hvac"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
|
||||
systemui:broadcast="true"
|
||||
android:src="@drawable/car_ic_hvac"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingTop="30dp"
|
||||
android:paddingBottom="30dp"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="bottom"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.android.keyguard.AlphaOptimizedImageButton
|
||||
android:id="@+id/notifications"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:src="@drawable/car_ic_notification"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:alpha="0.7"
|
||||
/>
|
||||
|
||||
<com.android.systemui.statusbar.policy.Clock
|
||||
android:id="@+id/clock"
|
||||
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:singleLine="true"
|
||||
android:paddingStart="@dimen/status_bar_clock_starting_padding"
|
||||
android:paddingEnd="@dimen/status_bar_clock_end_padding"
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingBottom="20dp"
|
||||
/>
|
||||
|
||||
<Space
|
||||
android:layout_height="10dp"
|
||||
android:layout_width="match_parent"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.android.systemui.statusbar.car.CarNavigationBarView>
|
||||
@@ -19,34 +19,80 @@
|
||||
|
||||
<com.android.systemui.statusbar.car.CarNavigationBarView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:systemui="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:gravity="center"
|
||||
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="@dimen/car_navigation_bar_width"
|
||||
android:layout_width="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:id="@+id/nav_buttons"
|
||||
android:gravity="left"
|
||||
android:paddingLeft="30dp"
|
||||
android:layout_weight="1"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<!-- Buttons get populated here from a car_arrays.xml. -->
|
||||
<com.android.systemui.statusbar.car.CarNavigationButton
|
||||
android:id="@+id/home"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
|
||||
android:src="@drawable/car_ic_overview"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingLeft="30dp"
|
||||
android:paddingRight="30dp"
|
||||
/>
|
||||
|
||||
<com.android.systemui.statusbar.car.CarNavigationButton
|
||||
android:id="@+id/hvac"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
|
||||
systemui:broadcast="true"
|
||||
android:src="@drawable/car_ic_hvac"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingLeft="30dp"
|
||||
android:paddingRight="30dp"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- lights out layout to match exactly -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
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. -->
|
||||
android:layout_weight="1"
|
||||
android:gravity="right"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.android.keyguard.AlphaOptimizedImageButton
|
||||
android:id="@+id/notifications"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:src="@drawable/car_ic_notification"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:alpha="0.7"
|
||||
/>
|
||||
|
||||
<com.android.systemui.statusbar.policy.Clock
|
||||
android:id="@+id/clock"
|
||||
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:singleLine="true"
|
||||
android:paddingStart="@dimen/status_bar_clock_starting_padding"
|
||||
android:paddingEnd="@dimen/status_bar_clock_end_padding"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingRight="20dp"
|
||||
/>
|
||||
|
||||
<Space
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.android.systemui.statusbar.car.CarNavigationBarView>
|
||||
|
||||
|
||||
@@ -17,28 +17,13 @@
|
||||
*/
|
||||
-->
|
||||
|
||||
<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:background="?android:attr/selectableItemBackground">
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<com.android.keyguard.AlphaOptimizedImageButton
|
||||
android:id="@+id/car_nav_button_icon"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="@dimen/car_navigation_button_width"
|
||||
android:layout_centerInParent="true"
|
||||
android:animateLayoutChanges="true"
|
||||
android:scaleType="fitCenter">
|
||||
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:animateLayoutChanges="true"
|
||||
android:scaleType="fitCenter">
|
||||
</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"
|
||||
android:scaleType="fitCenter">
|
||||
</com.android.keyguard.AlphaOptimizedImageButton>
|
||||
</com.android.systemui.statusbar.car.CarNavigationButton>
|
||||
</merge>
|
||||
|
||||
101
packages/SystemUI/res/layout/car_right_navigation_bar.xml
Normal file
101
packages/SystemUI/res/layout/car_right_navigation_bar.xml
Normal file
@@ -0,0 +1,101 @@
|
||||
<?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"
|
||||
xmlns:systemui="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/system_bar_background">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/nav_buttons"
|
||||
android:orientation="vertical"
|
||||
android:gravity="top"
|
||||
android:paddingTop="30dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/system_bar_background"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<com.android.systemui.statusbar.car.CarNavigationButton
|
||||
android:id="@+id/home"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
|
||||
android:src="@drawable/car_ic_overview"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingTop="30dp"
|
||||
android:paddingBottom="30dp"
|
||||
/>
|
||||
|
||||
<com.android.systemui.statusbar.car.CarNavigationButton
|
||||
android:id="@+id/hvac"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
|
||||
systemui:broadcast="true"
|
||||
android:src="@drawable/car_ic_hvac"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingTop="30dp"
|
||||
android:paddingBottom="30dp"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="bottom"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.android.keyguard.AlphaOptimizedImageButton
|
||||
android:id="@+id/notifications"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:src="@drawable/car_ic_notification"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:alpha="0.7"
|
||||
/>
|
||||
|
||||
|
||||
<com.android.systemui.statusbar.policy.Clock
|
||||
android:id="@+id/clock"
|
||||
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:singleLine="true"
|
||||
android:paddingStart="@dimen/status_bar_clock_starting_padding"
|
||||
android:paddingEnd="@dimen/status_bar_clock_end_padding"
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingBottom="20dp"
|
||||
/>
|
||||
|
||||
<Space
|
||||
android:layout_height="10dp"
|
||||
android:layout_width="match_parent"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.android.systemui.statusbar.car.CarNavigationBarView>
|
||||
42
packages/SystemUI/res/values/attrs_car.xml
Normal file
42
packages/SystemUI/res/values/attrs_car.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?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>
|
||||
<!-- Allow for custom attribs to be added to a facet button -->
|
||||
<declare-styleable name="CarFacetButton">
|
||||
<!-- icon to be rendered (drawable) -->
|
||||
<attr name="icon" format="reference"/>
|
||||
<!-- intent to start when button is click -->
|
||||
<attr name="intent" format="string"/>
|
||||
<!-- intent to start when a long press has happened -->
|
||||
<attr name="longIntent" format="string"/>
|
||||
<!-- 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" />
|
||||
</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" format="string"/>
|
||||
<!-- intent to start when a long press has happened -->
|
||||
<attr name="longIntent" format="string"/>
|
||||
<!-- start the intent as a broad cast instead of an activity if true-->
|
||||
<attr name="broadcast" format="boolean"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -22,4 +22,9 @@
|
||||
uri that will be launched into the docked window. -->
|
||||
<bool name="config_enablePersistentDockedActivity">false</bool>
|
||||
<string name="config_persistentDockedActivityIntentUri" translatable="false"></string>
|
||||
|
||||
<!-- configure which system ui bars should be displayed -->
|
||||
<bool name="config_enableLeftNavigationBar">false</bool>
|
||||
<bool name="config_enableRightNavigationBar">false</bool>
|
||||
<bool name="config_enableBottomNavigationBar">true</bool>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.android.systemui.statusbar.car;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
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 impling 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.
|
||||
* 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 float SELECTED_ALPHA = 1f;
|
||||
private static final float UNSELECTED_ALPHA = 0.7f;
|
||||
|
||||
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 Context mContext;
|
||||
private AlphaOptimizedImageButton mIcon;
|
||||
private AlphaOptimizedImageButton mMoreIcon;
|
||||
private boolean mSelected = false;
|
||||
/** 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;
|
||||
|
||||
|
||||
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.
|
||||
*/
|
||||
private 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);
|
||||
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);
|
||||
}
|
||||
|
||||
setOnClickListener(v -> {
|
||||
intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
|
||||
mContext.startActivity(intent);
|
||||
});
|
||||
|
||||
if (longPressIntentString != null) {
|
||||
final Intent longPressIntent = Intent.parseUri(longPressIntentString,
|
||||
Intent.URI_INTENT_SCHEME);
|
||||
setOnLongClickListener(v -> {
|
||||
mContext.startActivity(longPressIntent);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to attach intent", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setupIcons(TypedArray styledAttributes) {
|
||||
mIcon = findViewById(R.id.car_nav_button_icon);
|
||||
mIcon.setScaleType(ImageView.ScaleType.CENTER);
|
||||
mIcon.setClickable(false);
|
||||
mIcon.setAlpha(UNSELECTED_ALPHA);
|
||||
int iconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
|
||||
if (iconResourceId == 0) {
|
||||
throw new RuntimeException("specified icon resource was not found and is required");
|
||||
}
|
||||
mIcon.setImageResource(iconResourceId);
|
||||
|
||||
mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
|
||||
mMoreIcon.setClickable(false);
|
||||
mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
|
||||
mMoreIcon.setAlpha(UNSELECTED_ALPHA);
|
||||
mMoreIcon.setVisibility(GONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public void setSelected(boolean selected, boolean showMoreIcon) {
|
||||
mSelected = selected;
|
||||
if (selected) {
|
||||
mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
|
||||
mMoreIcon.setAlpha(SELECTED_ALPHA);
|
||||
mIcon.setAlpha(SELECTED_ALPHA);
|
||||
} else {
|
||||
mMoreIcon.setVisibility(GONE);
|
||||
mIcon.setAlpha(UNSELECTED_ALPHA);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.android.systemui.statusbar.car;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public class CarFacetButtonController {
|
||||
|
||||
protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
|
||||
protected HashMap<String, CarFacetButton> mButtonsByPackage = new HashMap<>();
|
||||
protected CarFacetButton mSelectedFacetButton;
|
||||
protected Context mContext;
|
||||
|
||||
public CarFacetButtonController(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes through the supplied CarNavigationBarView and keeps track of all the CarFacetButtons
|
||||
* such that it can select and unselect them based on running task chages
|
||||
* @param bar that may contain CarFacetButtons
|
||||
*/
|
||||
public void addCarNavigationBar(CarNavigationBarView bar) {
|
||||
findFacets(bar);
|
||||
}
|
||||
|
||||
private void findFacets(ViewGroup root) {
|
||||
final int childCount = root.getChildCount();
|
||||
|
||||
for (int i = 0; i < childCount; ++i) {
|
||||
final View v = root.getChildAt(i);
|
||||
if (v instanceof CarFacetButton) {
|
||||
CarFacetButton facetButton = (CarFacetButton) v;
|
||||
String[] categories = facetButton.getCategories();
|
||||
for (int j = 0; j < categories.length; j++) {
|
||||
String category = categories[j];
|
||||
mButtonsByCategory.put(category, facetButton);
|
||||
}
|
||||
|
||||
String[] facetPackages = facetButton.getFacetPackages();
|
||||
for (int j = 0; j < facetPackages.length; j++) {
|
||||
String facetPackage = facetPackages[j];
|
||||
mButtonsByPackage.put(facetPackage, facetButton);
|
||||
}
|
||||
} else if (v instanceof ViewGroup) {
|
||||
findFacets((ViewGroup) v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 taskino.
|
||||
* @param taskInfo of the currently running application
|
||||
*/
|
||||
public void taskChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
if (taskInfo == null || taskInfo.baseActivity == null) {
|
||||
return;
|
||||
}
|
||||
String packageName = taskInfo.baseActivity.getPackageName();
|
||||
|
||||
// If the package name belongs to a filter, then highlight appropriate button in
|
||||
// the navigation bar.
|
||||
if (mSelectedFacetButton != null) {
|
||||
mSelectedFacetButton.setSelected(false);
|
||||
}
|
||||
CarFacetButton facetButton = mButtonsByPackage.get(packageName);
|
||||
if (facetButton != null) {
|
||||
facetButton.setSelected(true);
|
||||
mSelectedFacetButton = facetButton;
|
||||
} else {
|
||||
String category = getPackageCategory(packageName);
|
||||
if (category != null) {
|
||||
facetButton = mButtonsByCategory.get(category);
|
||||
facetButton.setSelected(true);
|
||||
mSelectedFacetButton = facetButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String getPackageCategory(String packageName) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
Set<String> supportedCategories = mButtonsByCategory.keySet();
|
||||
for (String category : supportedCategories) {
|
||||
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.
|
||||
mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
|
||||
return category;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
/*
|
||||
* 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 static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
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.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import com.android.systemui.R;
|
||||
|
||||
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 {
|
||||
private static final String TAG = "CarNavBarController";
|
||||
|
||||
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";
|
||||
|
||||
/**
|
||||
* 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_DELIMITER = ";";
|
||||
|
||||
private final Context mContext;
|
||||
private final CarNavigationBarView mNavBar;
|
||||
private final CarStatusBar mStatusBar;
|
||||
|
||||
/**
|
||||
* Set of categories each facet will filter on.
|
||||
*/
|
||||
private final List<String[]> mFacetCategories = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Set of package names each facet will filter on.
|
||||
*/
|
||||
private final List<String[]> mFacetPackages = new ArrayList<>();
|
||||
|
||||
private final SimpleArrayMap<String, Integer> mFacetCategoryMap = new SimpleArrayMap<>();
|
||||
private final SimpleArrayMap<String, Integer> mFacetPackageMap = new SimpleArrayMap<>();
|
||||
|
||||
private final List<CarNavigationButton> mNavButtons = new ArrayList<>();
|
||||
|
||||
private final SparseBooleanArray mFacetHasMultipleAppsCache = new SparseBooleanArray();
|
||||
|
||||
private int mCurrentFacetIndex;
|
||||
private Intent mPersistentTaskIntent;
|
||||
|
||||
public CarNavigationBarController(Context context, CarNavigationBarView navBar,
|
||||
CarStatusBar activityStarter) {
|
||||
mContext = context;
|
||||
mNavBar = navBar;
|
||||
mStatusBar = activityStarter;
|
||||
bind();
|
||||
|
||||
if (context.getResources().getBoolean(R.bool.config_enablePersistentDockedActivity)) {
|
||||
setupPersistentDockedTask();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupPersistentDockedTask() {
|
||||
try {
|
||||
mPersistentTaskIntent = Intent.parseUri(
|
||||
mContext.getString(R.string.config_persistentDockedActivityIntentUri),
|
||||
Intent.URI_INTENT_SCHEME);
|
||||
} catch (URISyntaxException e) {
|
||||
Log.e(TAG, "Malformed persistent task intent.");
|
||||
}
|
||||
}
|
||||
|
||||
public void taskChanged(String packageName, ActivityManager.RunningTaskInfo taskInfo) {
|
||||
// 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));
|
||||
}
|
||||
|
||||
// Set up the persistent docked task if needed.
|
||||
boolean isHomeTask =
|
||||
taskInfo.configuration.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME;
|
||||
if (mPersistentTaskIntent != null && !mStatusBar.hasDockedTask() && !isHomeTask) {
|
||||
mStatusBar.startActivityOnStack(mPersistentTaskIntent,
|
||||
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPackageChange(String packageName) {
|
||||
if (mFacetPackageMap.containsKey(packageName)) {
|
||||
int index = mFacetPackageMap.get(packageName);
|
||||
mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index));
|
||||
// No need to check categories because we've already refreshed the cache.
|
||||
return;
|
||||
}
|
||||
|
||||
String category = getPackageCategory(packageName);
|
||||
if (mFacetCategoryMap.containsKey(category)) {
|
||||
int index = mFacetCategoryMap.get(category);
|
||||
mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through the items in arrays_car.xml and sets up the facet bar buttons to
|
||||
* perform the task in that configuration file when clicked or long-pressed.
|
||||
*/
|
||||
private void bind() {
|
||||
Resources res = mContext.getResources();
|
||||
|
||||
TypedArray icons = res.obtainTypedArray(R.array.car_facet_icons);
|
||||
TypedArray intents = res.obtainTypedArray(R.array.car_facet_intent_uris);
|
||||
TypedArray longPressIntents = res.obtainTypedArray(R.array.car_facet_longpress_intent_uris);
|
||||
TypedArray facetPackageNames = res.obtainTypedArray(R.array.car_facet_package_filters);
|
||||
TypedArray facetCategories = res.obtainTypedArray(R.array.car_facet_category_filters);
|
||||
|
||||
try {
|
||||
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, size = icons.length(); i < size; i++) {
|
||||
Drawable icon = icons.getDrawable(i);
|
||||
CarNavigationButton button = createNavButton(icon);
|
||||
initClickListeners(button, i, intents.getString(i), longPressIntents.getString(i));
|
||||
|
||||
mNavButtons.add(button);
|
||||
mNavBar.addButton(button, createNavButton(icon) /* lightsOutButton */);
|
||||
|
||||
initFacetFilterMaps(i, facetPackageNames.getString(i).split(FACET_FILTER_DELIMITER),
|
||||
facetCategories.getString(i).split(FACET_FILTER_DELIMITER));
|
||||
mFacetHasMultipleAppsCache.put(i, facetHasMultiplePackages(i));
|
||||
}
|
||||
} finally {
|
||||
// Clean up all the TypedArrays.
|
||||
icons.recycle();
|
||||
intents.recycle();
|
||||
longPressIntents.recycle();
|
||||
facetPackageNames.recycle();
|
||||
facetCategories.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreates each of the buttons on a density or font scale change. This manual process is
|
||||
* necessary since this class is not part of an activity that automatically gets recreated.
|
||||
*/
|
||||
public void onDensityOrFontScaleChanged() {
|
||||
TypedArray icons = mContext.getResources().obtainTypedArray(R.array.car_facet_icons);
|
||||
|
||||
try {
|
||||
int length = icons.length();
|
||||
if (length != mNavButtons.size()) {
|
||||
// This should not happen since the mNavButtons list is created from the length
|
||||
// of the icons array in bind().
|
||||
throw new RuntimeException("car_facet array lengths do not match number of "
|
||||
+ "created buttons.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
Drawable icon = icons.getDrawable(i);
|
||||
|
||||
// Setting a new icon will trigger a requestLayout() call if necessary.
|
||||
mNavButtons.get(i).setResources(icon);
|
||||
}
|
||||
} finally {
|
||||
icons.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
private void initFacetFilterMaps(int id, String[] packageNames, String[] categories) {
|
||||
mFacetCategories.add(categories);
|
||||
for (String category : categories) {
|
||||
mFacetCategoryMap.put(category, id);
|
||||
}
|
||||
|
||||
mFacetPackages.add(packageNames);
|
||||
for (String packageName : packageNames) {
|
||||
mFacetPackageMap.put(packageName, 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.
|
||||
*
|
||||
* @return {@code true} if the facet at the given index has more than one package.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the facet at the given index to be the facet that is currently active. The button will
|
||||
* be highlighted appropriately.
|
||||
*/
|
||||
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 */,
|
||||
mFacetHasMultipleAppsCache.get(index) /* showMoreIcon */);
|
||||
}
|
||||
|
||||
mCurrentFacetIndex = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the View that is used for the buttons along the navigation bar.
|
||||
*
|
||||
* @param icon The icon to be used for the button.
|
||||
*/
|
||||
private CarNavigationButton createNavButton(Drawable icon) {
|
||||
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);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the click and long click listeners that correspond to the given command string.
|
||||
* The click listeners are attached to the given button.
|
||||
*/
|
||||
private void initClickListeners(View button, int index, String clickString,
|
||||
String longPressString) {
|
||||
// Each button at least have an action when pressed.
|
||||
if (TextUtils.isEmpty(clickString)) {
|
||||
throw new RuntimeException("Facet at index " + index + " does not have click action.");
|
||||
}
|
||||
|
||||
try {
|
||||
Intent intent = Intent.parseUri(clickString, Intent.URI_INTENT_SCHEME);
|
||||
button.setOnClickListener(v -> onFacetClicked(intent, index));
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException("Malformed intent uri", e);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(longPressString)) {
|
||||
button.setLongClickable(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Intent intent = Intent.parseUri(longPressString, Intent.URI_INTENT_SCHEME);
|
||||
button.setOnLongClickListener(v -> {
|
||||
onFacetLongClicked(intent, index);
|
||||
return true;
|
||||
});
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException("Malformed long-press intent uri", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a click on a facet. A click will trigger the given Intent.
|
||||
*
|
||||
* @param index The index of the facet that was clicked.
|
||||
*/
|
||||
private void onFacetClicked(Intent intent, int index) {
|
||||
String packageName = intent.getPackage();
|
||||
|
||||
if (packageName == null && !intent.getCategories().contains(Intent.CATEGORY_HOME)) {
|
||||
return;
|
||||
}
|
||||
|
||||
intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories.get(index));
|
||||
intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages.get(index));
|
||||
// The facet is identified by the index in which it was added to the nav bar.
|
||||
// This value can be used to determine which facet was selected
|
||||
intent.putExtra(EXTRA_FACET_ID, Integer.toString(index));
|
||||
|
||||
// If the current facet is clicked, we want to launch the picker by default
|
||||
// rather than the "preferred/last run" app.
|
||||
intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, index == mCurrentFacetIndex);
|
||||
|
||||
int windowingMode = WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
|
||||
int activityType = ACTIVITY_TYPE_UNDEFINED;
|
||||
if (intent.getCategories().contains(Intent.CATEGORY_HOME)) {
|
||||
windowingMode = WINDOWING_MODE_UNDEFINED;
|
||||
activityType = ACTIVITY_TYPE_HOME;
|
||||
}
|
||||
|
||||
setCurrentFacet(index);
|
||||
mStatusBar.startActivityOnStack(intent, windowingMode, activityType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a long-press on a facet. The long-press will trigger the given Intent.
|
||||
*
|
||||
* @param index The index of the facet that was clicked.
|
||||
*/
|
||||
private void onFacetLongClicked(Intent intent, int index) {
|
||||
setCurrentFacet(index);
|
||||
mStatusBar.startActivityOnStack(intent,
|
||||
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED);
|
||||
}
|
||||
}
|
||||
@@ -16,17 +16,15 @@
|
||||
|
||||
package com.android.systemui.statusbar.car;
|
||||
|
||||
import android.app.UiModeManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.keyguard.AlphaOptimizedImageButton;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.statusbar.phone.NavGesture;
|
||||
import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
|
||||
import com.android.systemui.statusbar.phone.NavigationBarView;
|
||||
|
||||
/**
|
||||
* A custom navigation bar for the automotive use case.
|
||||
@@ -34,9 +32,10 @@ import com.android.systemui.statusbar.phone.NavigationBarView;
|
||||
* The navigation bar in the automotive use case is more like a list of shortcuts, rendered
|
||||
* in a linear layout.
|
||||
*/
|
||||
class CarNavigationBarView extends NavigationBarView {
|
||||
class CarNavigationBarView extends LinearLayout {
|
||||
private LinearLayout mNavButtons;
|
||||
private LinearLayout mLightsOutButtons;
|
||||
private AlphaOptimizedImageButton mNotificationsButton;
|
||||
private CarStatusBar mCarStatusBar;
|
||||
|
||||
public CarNavigationBarView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -45,99 +44,16 @@ class CarNavigationBarView extends NavigationBarView {
|
||||
@Override
|
||||
public void onFinishInflate() {
|
||||
mNavButtons = findViewById(R.id.nav_buttons);
|
||||
mLightsOutButtons = findViewById(R.id.lights_out);
|
||||
|
||||
mNotificationsButton = findViewById(R.id.notifications);
|
||||
mNotificationsButton.setOnClickListener(this::onNotificationsClick);
|
||||
}
|
||||
|
||||
public void addButton(CarNavigationButton button, CarNavigationButton lightsOutButton){
|
||||
mNavButtons.addView(button);
|
||||
mLightsOutButtons.addView(lightsOutButton);
|
||||
void setStatusBar(CarStatusBar carStatusBar) {
|
||||
mCarStatusBar = carStatusBar;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationIconHints(int hints, boolean force) {
|
||||
// We do not need to set the navigation icon hints for a vehicle
|
||||
// Calling setNavigationIconHints in the base class will result in a NPE as the car
|
||||
// navigation bar does not have a back button.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginConnected(NavGesture plugin, Context context) {
|
||||
// set to null version of the plugin ignoring incoming arg.
|
||||
super.onPluginConnected(new NullNavGesture(), context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginDisconnected(NavGesture plugin) {
|
||||
// reinstall the null nav gesture plugin
|
||||
super.onPluginConnected(new NullNavGesture(), getContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Null object pattern to work around expectations of the base class.
|
||||
* This is a temporary solution to have the car system ui working.
|
||||
* Already underway is a refactor of they car sys ui as to not use this class
|
||||
* hierarchy.
|
||||
*/
|
||||
private static class NullNavGesture implements NavGesture {
|
||||
@Override
|
||||
public GestureHelper getGestureHelper() {
|
||||
return new GestureHelper() {
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBarState(boolean vertical, boolean isRtl) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDarkIntensityChange(float intensity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Context sysuiContext, Context pluginContext) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
}
|
||||
protected void onNotificationsClick(View v) {
|
||||
mCarStatusBar.togglePanel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,87 @@
|
||||
/*
|
||||
* 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.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import com.android.keyguard.AlphaOptimizedImageButton;
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
/**
|
||||
* A wrapper view for a car navigation facet, which includes a button icon and a drop down icon.
|
||||
* CarNavigationButton is an image button that allows for a bit more configuration at the
|
||||
* xml file level. This allows for more control via overlays instead of having to update
|
||||
* code.
|
||||
*/
|
||||
public class CarNavigationButton extends RelativeLayout {
|
||||
public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImageButton {
|
||||
|
||||
private static final float SELECTED_ALPHA = 1;
|
||||
private static final float UNSELECTED_ALPHA = 0.7f;
|
||||
|
||||
private AlphaOptimizedImageButton mIcon;
|
||||
private AlphaOptimizedImageButton mMoreIcon;
|
||||
private Context mContext;
|
||||
private String mIntent = null;
|
||||
private String mLongIntent = null;
|
||||
private boolean mBroadcastIntent = false;
|
||||
private boolean mSelected = false;
|
||||
|
||||
|
||||
public CarNavigationButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
mIcon = findViewById(R.id.car_nav_button_icon);
|
||||
mIcon.setScaleType(ImageView.ScaleType.CENTER);
|
||||
mIcon.setClickable(false);
|
||||
mIcon.setBackgroundColor(android.R.color.transparent);
|
||||
mIcon.setAlpha(UNSELECTED_ALPHA);
|
||||
setScaleType(ImageView.ScaleType.CENTER);
|
||||
setAlpha(UNSELECTED_ALPHA);
|
||||
try {
|
||||
if (mIntent != null) {
|
||||
final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
setOnClickListener(v -> {
|
||||
if (mBroadcastIntent) {
|
||||
mContext.sendBroadcast(intent);
|
||||
return;
|
||||
}
|
||||
mContext.startActivity(intent);
|
||||
});
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException("Failed to attach intent", e);
|
||||
}
|
||||
|
||||
mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
|
||||
mMoreIcon.setClickable(false);
|
||||
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);
|
||||
try {
|
||||
if (mLongIntent != null) {
|
||||
final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
setOnLongClickListener(v -> {
|
||||
mContext.startActivity(intent);
|
||||
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 ? SELECTED_ALPHA : UNSELECTED_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,14 @@ package com.android.systemui.statusbar.car;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
@@ -46,10 +43,7 @@ import com.android.systemui.classifier.FalsingManager;
|
||||
import com.android.systemui.fragments.FragmentHostManager;
|
||||
import com.android.systemui.recents.Recents;
|
||||
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
|
||||
import com.android.systemui.recents.misc.SystemServicesProxy;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.NotificationData;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
|
||||
import com.android.systemui.statusbar.phone.NavigationBarView;
|
||||
@@ -69,7 +63,6 @@ public class CarStatusBar extends StatusBar implements
|
||||
|
||||
private TaskStackListenerImpl mTaskStackListener;
|
||||
|
||||
private CarNavigationBarController mController;
|
||||
private FullscreenUserSwitcher mFullscreenUserSwitcher;
|
||||
|
||||
private CarBatteryController mCarBatteryController;
|
||||
@@ -78,15 +71,23 @@ public class CarStatusBar extends StatusBar implements
|
||||
|
||||
private ConnectedDeviceSignalController mConnectedDeviceSignalController;
|
||||
private ViewGroup mNavigationBarWindow;
|
||||
private ViewGroup mLeftNavigationBarWindow;
|
||||
private ViewGroup mRightNavigationBarWindow;
|
||||
private CarNavigationBarView mNavigationBarView;
|
||||
private CarNavigationBarView mLeftNavigationBarView;
|
||||
private CarNavigationBarView mRightNavigationBarView;
|
||||
|
||||
private final Object mQueueLock = new Object();
|
||||
private boolean mShowLeft;
|
||||
private boolean mShowRight;
|
||||
private boolean mShowBottom;
|
||||
private CarFacetButtonController mCarFacetButtonController;
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
mTaskStackListener = new TaskStackListenerImpl();
|
||||
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
|
||||
registerPackageChangeReceivers();
|
||||
|
||||
mStackScroller.setScrollingEnabled(true);
|
||||
|
||||
@@ -104,6 +105,16 @@ public class CarStatusBar extends StatusBar implements
|
||||
mNavigationBarView = null;
|
||||
}
|
||||
|
||||
if (mLeftNavigationBarWindow != null) {
|
||||
mWindowManager.removeViewImmediate(mLeftNavigationBarWindow);
|
||||
mLeftNavigationBarView = null;
|
||||
}
|
||||
|
||||
if (mRightNavigationBarWindow != null) {
|
||||
mWindowManager.removeViewImmediate(mRightNavigationBarWindow);
|
||||
mRightNavigationBarView = null;
|
||||
}
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@@ -153,10 +164,36 @@ public class CarStatusBar extends StatusBar implements
|
||||
|
||||
@Override
|
||||
protected void createNavigationBar() {
|
||||
mCarFacetButtonController = new CarFacetButtonController(mContext);
|
||||
if (mNavigationBarView != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
|
||||
if (mShowBottom) {
|
||||
buildBottomBar();
|
||||
}
|
||||
|
||||
int widthForSides = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.navigation_bar_height_car_mode);
|
||||
|
||||
|
||||
mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
|
||||
|
||||
if (mShowLeft) {
|
||||
buildLeft(widthForSides);
|
||||
}
|
||||
|
||||
mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
|
||||
|
||||
if (mShowRight) {
|
||||
buildRight(widthForSides);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void buildBottomBar() {
|
||||
// SystemUI requires that the navigation bar view have a parent. Since the regular
|
||||
// StatusBar inflates navigation_bar_window as this parent view, use the same view for the
|
||||
// CarNavigationBarView.
|
||||
@@ -171,17 +208,15 @@ public class CarStatusBar extends StatusBar implements
|
||||
mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
|
||||
if (mNavigationBarView == null) {
|
||||
Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
|
||||
throw new RuntimeException("Unable to build botom nav bar due to missing layout");
|
||||
}
|
||||
mNavigationBarView.setStatusBar(this);
|
||||
|
||||
|
||||
mController = new CarNavigationBarController(mContext, mNavigationBarView,
|
||||
this /* ActivityStarter*/);
|
||||
mNavigationBarView.getBarTransitions().setAlwaysOpaque(true);
|
||||
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_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
|
||||
@@ -189,9 +224,74 @@ public class CarStatusBar extends StatusBar implements
|
||||
lp.setTitle("CarNavigationBar");
|
||||
lp.windowAnimations = 0;
|
||||
|
||||
|
||||
mCarFacetButtonController.addCarNavigationBar(mNavigationBarView);
|
||||
mWindowManager.addView(mNavigationBarWindow, lp);
|
||||
}
|
||||
|
||||
private void buildLeft(int widthForSides) {
|
||||
mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
|
||||
R.layout.navigation_bar_window, null);
|
||||
if (mLeftNavigationBarWindow == null) {
|
||||
Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window");
|
||||
}
|
||||
|
||||
View.inflate(mContext, R.layout.car_left_navigation_bar, mLeftNavigationBarWindow);
|
||||
mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
|
||||
if (mLeftNavigationBarView == null) {
|
||||
Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
|
||||
throw new RuntimeException("Unable to build left nav bar due to missing layout");
|
||||
}
|
||||
mLeftNavigationBarView.setStatusBar(this);
|
||||
mCarFacetButtonController.addCarNavigationBar(mLeftNavigationBarView);
|
||||
|
||||
WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
|
||||
widthForSides, LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
leftlp.setTitle("LeftCarNavigationBar");
|
||||
leftlp.windowAnimations = 0;
|
||||
leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
|
||||
leftlp.gravity = Gravity.LEFT;
|
||||
mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
|
||||
}
|
||||
|
||||
|
||||
private void buildRight(int widthForSides) {
|
||||
mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
|
||||
R.layout.navigation_bar_window, null);
|
||||
if (mRightNavigationBarWindow == null) {
|
||||
Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window");
|
||||
}
|
||||
|
||||
View.inflate(mContext, R.layout.car_right_navigation_bar, mRightNavigationBarWindow);
|
||||
mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
|
||||
if (mRightNavigationBarView == null) {
|
||||
Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
|
||||
throw new RuntimeException("Unable to build right nav bar due to missing layout");
|
||||
}
|
||||
mRightNavigationBarView.setStatusBar(this);
|
||||
mCarFacetButtonController.addCarNavigationBar(mRightNavigationBarView);
|
||||
|
||||
WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
|
||||
widthForSides, LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
rightlp.setTitle("RightCarNavigationBar");
|
||||
rightlp.windowAnimations = 0;
|
||||
rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
|
||||
rightlp.gravity = Gravity.RIGHT;
|
||||
mWindowManager.addView(mRightNavigationBarWindow, rightlp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
//When executing dump() funciton simultaneously, we need to serialize them
|
||||
@@ -204,8 +304,8 @@ public class CarStatusBar extends StatusBar implements
|
||||
}
|
||||
|
||||
pw.print(" mTaskStackListener="); pw.println(mTaskStackListener);
|
||||
pw.print(" mController=");
|
||||
pw.println(mController);
|
||||
pw.print(" mCarFacetButtonController=");
|
||||
pw.println(mCarFacetButtonController);
|
||||
pw.print(" mFullscreenUserSwitcher="); pw.println(mFullscreenUserSwitcher);
|
||||
pw.print(" mCarBatteryController=");
|
||||
pw.println(mCarBatteryController);
|
||||
@@ -229,10 +329,6 @@ public class CarStatusBar extends StatusBar implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigationBarView getNavigationBarView() {
|
||||
return mNavigationBarView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getNavigationBarWindow() {
|
||||
@@ -269,24 +365,6 @@ public class CarStatusBar extends StatusBar implements
|
||||
}
|
||||
}
|
||||
|
||||
private BroadcastReceiver mPackageChangeReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getData() == null || mController == null) {
|
||||
return;
|
||||
}
|
||||
String packageName = intent.getData().getSchemeSpecificPart();
|
||||
mController.onPackageChange(packageName);
|
||||
}
|
||||
};
|
||||
|
||||
private void registerPackageChangeReceivers() {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addDataScheme("package");
|
||||
mContext.registerReceiver(mPackageChangeReceiver, filter);
|
||||
}
|
||||
|
||||
public boolean hasDockedTask() {
|
||||
return Recents.getSystemServices().hasDockedTask();
|
||||
@@ -301,10 +379,7 @@ public class CarStatusBar extends StatusBar implements
|
||||
public void onTaskStackChanged() {
|
||||
ActivityManager.RunningTaskInfo runningTaskInfo =
|
||||
ActivityManagerWrapper.getInstance().getRunningTask();
|
||||
if (runningTaskInfo != null && runningTaskInfo.baseActivity != null) {
|
||||
mController.taskChanged(runningTaskInfo.baseActivity.getPackageName(),
|
||||
runningTaskInfo);
|
||||
}
|
||||
mCarFacetButtonController.taskChanged(runningTaskInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,33 +421,6 @@ public class CarStatusBar extends StatusBar implements
|
||||
// Do nothing, we don't want to display media art in the lock screen for a car.
|
||||
}
|
||||
|
||||
private int startActivityWithOptions(Intent intent, Bundle options) {
|
||||
int result = ActivityManager.START_CANCELED;
|
||||
try {
|
||||
result = ActivityManager.getService().startActivityAsUser(null /* caller */,
|
||||
mContext.getBasePackageName(),
|
||||
intent,
|
||||
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
|
||||
null /* resultTo*/,
|
||||
null /* resultWho*/,
|
||||
0 /* requestCode*/,
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP,
|
||||
null /* profilerInfo*/,
|
||||
options,
|
||||
UserHandle.CURRENT.getIdentifier());
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Unable to start activity", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int startActivityOnStack(Intent intent, int windowingMode, int activityType) {
|
||||
final ActivityOptions options = ActivityOptions.makeBasic();
|
||||
options.setLaunchWindowingMode(windowingMode);
|
||||
options.setLaunchActivityType(activityType);
|
||||
return startActivityWithOptions(intent, options.toBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void animateExpandNotificationsPanel() {
|
||||
@@ -390,8 +438,6 @@ public class CarStatusBar extends StatusBar implements
|
||||
@Override
|
||||
public void onDensityOrFontScaleChanged() {
|
||||
super.onDensityOrFontScaleChanged();
|
||||
mController.onDensityOrFontScaleChanged();
|
||||
|
||||
// Need to update the background on density changed in case the change was due to night
|
||||
// mode.
|
||||
mNotificationPanelBackground = getDefaultWallpaper();
|
||||
|
||||
Reference in New Issue
Block a user