Merge "Controls UI - Basic impl"
This commit is contained in:
7
core/java/android/service/controls/TokenProvider.aidl
Normal file
7
core/java/android/service/controls/TokenProvider.aidl
Normal file
@@ -0,0 +1,7 @@
|
||||
package android.service.controls;
|
||||
|
||||
/** @hide */
|
||||
interface TokenProvider {
|
||||
void setAuthToken(String token);
|
||||
String getAccountName();
|
||||
}
|
||||
6
packages/SystemUI/res/color/light_background.xml
Normal file
6
packages/SystemUI/res/color/light_background.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false"
|
||||
android:color="@color/control_default_background" />
|
||||
<item android:color="@color/GM2_yellow_50" />
|
||||
</selector>
|
||||
6
packages/SystemUI/res/color/light_foreground.xml
Normal file
6
packages/SystemUI/res/color/light_foreground.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false"
|
||||
android:color="@color/control_default_foreground" />
|
||||
<item android:color="@color/GM2_orange_900" />
|
||||
</selector>
|
||||
6
packages/SystemUI/res/color/lock_background.xml
Normal file
6
packages/SystemUI/res/color/lock_background.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false"
|
||||
android:color="@color/control_default_background" />
|
||||
<item android:color="@color/GM2_blue_50" />
|
||||
</selector>
|
||||
6
packages/SystemUI/res/color/lock_foreground.xml
Normal file
6
packages/SystemUI/res/color/lock_foreground.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false"
|
||||
android:color="@color/control_default_foreground" />
|
||||
<item android:color="@color/GM2_blue_700" />
|
||||
</selector>
|
||||
6
packages/SystemUI/res/color/unknown_foreground.xml
Normal file
6
packages/SystemUI/res/color/unknown_foreground.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false"
|
||||
android:color="@color/control_default_foreground" />
|
||||
<item android:color="@color/GM2_blue_700" />
|
||||
</selector>
|
||||
32
packages/SystemUI/res/drawable/control_background.xml
Normal file
32
packages/SystemUI/res/drawable/control_background.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
* Copyright 2020, 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.
|
||||
*/
|
||||
-->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="?android:attr/colorBackgroundFloating"/>
|
||||
<corners android:radius="@dimen/control_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:id="@+id/clip_layer">
|
||||
<clip
|
||||
android:clipOrientation="horizontal"
|
||||
android:drawable="@drawable/control_layer"/>
|
||||
</item>
|
||||
</layer-list>
|
||||
22
packages/SystemUI/res/drawable/control_layer.xml
Normal file
22
packages/SystemUI/res/drawable/control_layer.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
* Copyright 2020, 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.
|
||||
*/
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
<corners android:radius="@dimen/control_corner_radius" />
|
||||
</shape>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
* Copyright 2020, 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.
|
||||
*/
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<stroke android:width="1dp" android:color="?android:attr/colorBackgroundFloating"/>
|
||||
<corners android:radius="@dimen/control_corner_radius" />
|
||||
</shape>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18,9h-5v2h5m3,-6h-8v2h8m-9,11.97c0.62,-0.83 1,-1.85 1,-2.97 0,-1.63 -0.79,-3.09 -2,-4V6c0,-1.66 -1.34,-3 -3,-3S5,4.34 5,6v6c-1.21,0.91 -2,2.37 -2,4 0,1.12 0.38,2.14 1,2.97V19h0.02c0.91,1.21 2.35,2 3.98,2s3.06,-0.79 3.98,-2H12v-0.03zM6.2,13.6L7,13V6c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v7l0.8,0.6c0.75,0.57 1.2,1.46 1.2,2.4H5c0,-0.94 0.45,-1.84 1.2,-2.4z"/>
|
||||
</vector>
|
||||
19
packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml
Normal file
19
packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M9,21v-1h6v1c0,0.55 -0.45,1 -1,1h-4c-0.55,0 -1,-0.45 -1,-1z"/>
|
||||
<group>
|
||||
<clip-path android:pathData="M0,0h24v24H0z M 0,0"/>
|
||||
</group>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,2c-1.89,0 -3.6,0.75 -4.86,1.97l1.41,1.41C9.45,4.53 10.67,4 12,4c2.76,0 5,2.24 5,5 0,1.28 -0.5,2.5 -1.36,3.42l-0.02,0.02 1.41,1.41C18.25,12.6 19,10.89 19,9c0,-3.86 -3.14,-7 -7,-7z"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M2.92,2.29L1.65,3.57l3.59,3.59C5.09,7.75 5,8.36 5,9c0,2.38 1.19,4.47 3,5.74V17c0,0.55 0.45,1 1,1h6c0.3,0 0.57,-0.13 0.75,-0.34L20.09,22l1.27,-1.27L2.92,2.29zM10,16v-2.3l-0.85,-0.6C7.8,12.16 7,10.63 7,9v-0.08L14.09,16H10z"/>
|
||||
</vector>
|
||||
9
packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml
Normal file
9
packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L9,8L9,6zM18,20L6,20L6,10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
|
||||
</vector>
|
||||
12
packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml
Normal file
12
packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h2c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,15m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
|
||||
</vector>
|
||||
24
packages/SystemUI/res/drawable/ic_more_vert.xml
Normal file
24
packages/SystemUI/res/drawable/ic_more_vert.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
Copyright (C) 2020 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="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
|
||||
</vector>
|
||||
9
packages/SystemUI/res/drawable/ic_power_gm2_24px.xml
Normal file
9
packages/SystemUI/res/drawable/ic_power_gm2_24px.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M16,9v4.66l-3.5,3.51V19h-1v-1.83L8,13.65V9h8m0,-6h-2v4h-4V3H8v4h-0.01C6.9,6.99 6,7.89 6,8.98v5.52L9.5,18v3h5v-3l3.5,-3.51V9c0,-1.1 -0.9,-2 -2,-2V3z"/>
|
||||
</vector>
|
||||
9
packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml
Normal file
9
packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M21.19,21.19L2.81,2.81 1.39,4.22l4.63,4.63L6,14.5 9.5,18v3h5v-3l0.34,-0.34 4.94,4.94 1.41,-1.41zM12.5,17.17L12.5,19h-1v-1.83L8,13.65v-2.83l5.42,5.42 -0.92,0.93zM11.83,9L8,5.17L8,3h2v4h4L14,3h2v4c1.1,0 2,0.9 2,2v5.49l-0.34,0.34L16,13.17L16,9h-4.17z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M11,2h2v10h-2zM18.37,5.64l-1.41,1.41c2.73,2.73 2.72,7.16 -0.01,9.89 -2.73,2.73 -7.17,2.73 -9.89,0.01 -2.73,-2.73 -2.74,-7.18 -0.01,-9.91l-1.41,-1.4c-3.51,3.51 -3.51,9.21 0.01,12.73 3.51,3.51 9.21,3.51 12.72,-0.01 3.51,-3.51 3.51,-9.2 0,-12.72z"/>
|
||||
</vector>
|
||||
9
packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml
Normal file
9
packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,9h-8.02C10.06,7.79 8.63,7 7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5c1.63,0 3.06,-0.79 3.98,-2H19c1.66,0 3,-1.34 3,-3S20.66,9 19,9zM19,13h-7.1c0.07,-0.32 0.1,-0.66 0.1,-1s-0.04,-0.68 -0.1,-1H19c0.55,0 1,0.45 1,1S19.55,13 19,13z"/>
|
||||
</vector>
|
||||
9
packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml
Normal file
9
packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M4,16c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zM4,20c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM23,20v2h-7v-2h2.49L12.01,4.59C11.6,3.63 10.66,3 9.61,3 8.17,3 7,4.17 7,5.61L7,9h2c2.21,0 4,1.79 4,4v9L7.99,22c0.44,-0.58 0.76,-1.26 0.91,-2L11,20v-7c0,-1.1 -0.9,-2 -2,-2L4,11v3c-0.71,0 -1.39,0.15 -2,0.42L2,9h3L5,5.61C5,3.07 7.07,1 9.61,1c1.86,0 3.53,1.11 4.25,2.82L20.66,20L23,20z"/>
|
||||
</vector>
|
||||
9
packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml
Normal file
9
packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18,10.48L18,6c0,-1.1 -0.9,-2 -2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-4.48l4,3.98v-11l-4,3.98zM16,9.69L16,18L4,18L4,6h12v3.69z"/>
|
||||
</vector>
|
||||
79
packages/SystemUI/res/layout/controls_base_item.xml
Normal file
79
packages/SystemUI/res/layout/controls_base_item.xml
Normal file
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 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.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="@dimen/control_height"
|
||||
android:padding="@dimen/control_padding"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:background="@drawable/control_background">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/control_status_normal"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:fontFamily="@*android:string/config_bodyFontFamily"
|
||||
android:paddingLeft="3dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/icon"
|
||||
app:layout_constraintStart_toEndOf="@+id/icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_extra"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/control_status_normal"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:fontFamily="@*android:string/config_bodyFontFamily"
|
||||
android:paddingLeft="3dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/icon"
|
||||
app:layout_constraintStart_toEndOf="@+id/status" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="18sp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamily"
|
||||
app:layout_constraintBottom_toTopOf="@+id/subtitle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamily"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
18
packages/SystemUI/res/layout/controls_no_favorites.xml
Normal file
18
packages/SystemUI/res/layout/controls_no_favorites.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<TextView
|
||||
android:id="@+id/controls_title"
|
||||
android:text="@string/quick_controls_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:gravity="center"
|
||||
android:textSize="25dp"
|
||||
android:paddingTop="40dp"
|
||||
android:paddingBottom="40dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamily"
|
||||
android:background="@drawable/control_no_favorites_background"/>
|
||||
</merge>
|
||||
22
packages/SystemUI/res/layout/controls_row.xml
Normal file
22
packages/SystemUI/res/layout/controls_row.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2020 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/control_spacing" />
|
||||
35
packages/SystemUI/res/layout/controls_with_favorites.xml
Normal file
35
packages/SystemUI/res/layout/controls_with_favorites.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:text="@string/quick_controls_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:gravity="center"
|
||||
android:textSize="25dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamily"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"/>
|
||||
<ImageView
|
||||
android:id="@+id/controls_more"
|
||||
android:src="@drawable/ic_more_vert"
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/global_actions_controls_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
</merge>
|
||||
@@ -111,20 +111,7 @@
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/global_actions_panel">
|
||||
<TextView
|
||||
android:text="Home"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:gravity="center"
|
||||
android:textSize="25dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamily" />
|
||||
<LinearLayout
|
||||
android:id="@+id/global_actions_controls_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
|
||||
@@ -200,18 +200,28 @@
|
||||
<color name="GM2_grey_800">#3C4043</color>
|
||||
<color name="GM2_grey_900">#202124</color>
|
||||
|
||||
<color name="GM2_red_50">#FCE8E6</color>
|
||||
<color name="GM2_red_300">#F28B82</color>
|
||||
<color name="GM2_red_500">#B71C1C</color>
|
||||
<color name="GM2_red_700">#C5221F</color>
|
||||
|
||||
<color name="GM2_blue_50">#E8F0FE</color>
|
||||
<color name="GM2_blue_200">#AECBFA</color>
|
||||
<color name="GM2_blue_300">#8AB4F8</color>
|
||||
<color name="GM2_blue_500">#FF4285F4</color>
|
||||
<color name="GM2_blue_600">#1A73E8</color>
|
||||
<color name="GM2_blue_700">#1967D2</color>
|
||||
|
||||
<color name="GM2_yellow_50">#FEF7E0</color>
|
||||
<color name="GM2_yellow_500">#FFFBBC04</color>
|
||||
|
||||
<color name="GM2_green_500">#FF34A853</color>
|
||||
<color name="GM2_blue_500">#FF4285F4</color>
|
||||
|
||||
<color name="GM2_orange_900">#B06000</color>
|
||||
|
||||
<color name="magnification_border_color">#FF9900</color>
|
||||
|
||||
<!-- controls -->
|
||||
<color name="control_default_foreground">?android:attr/textColorPrimary</color>
|
||||
<color name="control_default_background">?android:attr/colorBackgroundFloating</color>
|
||||
</resources>
|
||||
|
||||
@@ -1181,5 +1181,12 @@
|
||||
<dimen name="magnifier_up_down_controls_width">45dp</dimen>
|
||||
<dimen name="magnifier_up_down_controls_height">40dp</dimen>
|
||||
|
||||
<!-- Home Controls -->
|
||||
<dimen name="control_spacing">5dp</dimen>
|
||||
<dimen name="control_corner_radius">15dp</dimen>
|
||||
<dimen name="control_height">100dp</dimen>
|
||||
<dimen name="control_padding">15dp</dimen>
|
||||
<dimen name="control_status_normal">12dp</dimen>
|
||||
<dimen name="control_status_expanded">18dp</dimen>
|
||||
<dimen name="app_icon_size">32dp</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -2546,4 +2546,7 @@
|
||||
<string name="magnification_window_title">Magnification Window</string>
|
||||
<!-- Title for Magnification Controls Window [CHAR LIMIT=NONE] -->
|
||||
<string name="magnification_controls_title">Magnification Window Controls</string>
|
||||
|
||||
<!-- Quick Controls strings [CHAR LIMIT=30] -->
|
||||
<string name="quick_controls_title">Quick Controls</string>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.controls.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.ClipDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.DeviceTypes
|
||||
import android.service.controls.actions.BooleanAction
|
||||
import android.service.controls.actions.ControlAction
|
||||
import android.service.controls.actions.FloatAction
|
||||
import android.service.controls.templates.ControlTemplate
|
||||
import android.service.controls.templates.RangeTemplate
|
||||
import android.service.controls.templates.ToggleRangeTemplate
|
||||
import android.service.controls.templates.ToggleTemplate
|
||||
import android.util.TypedValue
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
|
||||
import com.android.systemui.controls.controller.ControlsController
|
||||
import com.android.systemui.R
|
||||
|
||||
private const val MIN_LEVEL = 0
|
||||
private const val MAX_LEVEL = 10000
|
||||
|
||||
class ControlViewHolder(
|
||||
val layout: ViewGroup,
|
||||
val controlsController: ControlsController
|
||||
) {
|
||||
val icon: ImageView = layout.requireViewById(R.id.icon)
|
||||
val status: TextView = layout.requireViewById(R.id.status)
|
||||
val statusExtra: TextView = layout.requireViewById(R.id.status_extra)
|
||||
val title: TextView = layout.requireViewById(R.id.title)
|
||||
val subtitle: TextView = layout.requireViewById(R.id.subtitle)
|
||||
val context: Context = layout.getContext()
|
||||
val clipLayer: ClipDrawable
|
||||
val gd: GradientDrawable
|
||||
lateinit var cws: ControlWithState
|
||||
|
||||
init {
|
||||
val ld = layout.getBackground() as LayerDrawable
|
||||
ld.mutate()
|
||||
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
|
||||
gd = clipLayer.getDrawable() as GradientDrawable
|
||||
}
|
||||
|
||||
fun bindData(cws: ControlWithState) {
|
||||
this.cws = cws
|
||||
|
||||
val (status, template) = cws.control?.let {
|
||||
title.setText(it.getTitle())
|
||||
subtitle.setText(it.getSubtitle())
|
||||
Pair(it.getStatus(), it.getControlTemplate())
|
||||
} ?: run {
|
||||
title.setText(cws.ci.controlTitle)
|
||||
subtitle.setText("")
|
||||
Pair(Control.STATUS_UNKNOWN, ControlTemplate.NO_TEMPLATE)
|
||||
}
|
||||
|
||||
findBehavior(status, template).apply(this, cws)
|
||||
}
|
||||
|
||||
fun action(action: ControlAction) {
|
||||
controlsController.action(cws.ci, action)
|
||||
}
|
||||
|
||||
private fun findBehavior(status: Int, template: ControlTemplate): Behavior {
|
||||
return when {
|
||||
status == Control.STATUS_UNKNOWN -> UnknownBehavior()
|
||||
template is ToggleTemplate -> ToggleTemplateBehavior()
|
||||
template is ToggleRangeTemplate -> ToggleRangeTemplateBehavior()
|
||||
else -> {
|
||||
object : Behavior {
|
||||
override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
|
||||
cvh.status.setText(cws.control?.getStatusText())
|
||||
cvh.applyRenderInfo(findRenderInfo(cws.ci.deviceType, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun applyRenderInfo(ri: RenderInfo) {
|
||||
val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
|
||||
val bg = context.getResources().getColorStateList(ri.background, context.getTheme())
|
||||
status.setTextColor(fg)
|
||||
statusExtra.setTextColor(fg)
|
||||
|
||||
icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
|
||||
icon.setImageTintList(fg)
|
||||
|
||||
gd.setColor(bg)
|
||||
}
|
||||
|
||||
fun setEnabled(enabled: Boolean) {
|
||||
status.setEnabled(enabled)
|
||||
icon.setEnabled(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
private interface Behavior {
|
||||
fun apply(cvh: ControlViewHolder, cws: ControlWithState)
|
||||
|
||||
fun findRenderInfo(deviceType: Int, isActive: Boolean): RenderInfo =
|
||||
deviceRenderMap.getOrDefault(deviceType, unknownDeviceMap).getValue(isActive)
|
||||
}
|
||||
|
||||
private class UnknownBehavior : Behavior {
|
||||
override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
|
||||
cvh.status.setText("Loading...")
|
||||
cvh.applyRenderInfo(findRenderInfo(cws.ci.deviceType, false))
|
||||
}
|
||||
}
|
||||
|
||||
private class ToggleRangeTemplateBehavior : Behavior {
|
||||
lateinit var clipLayer: Drawable
|
||||
lateinit var template: ToggleRangeTemplate
|
||||
lateinit var control: Control
|
||||
lateinit var cvh: ControlViewHolder
|
||||
lateinit var rangeTemplate: RangeTemplate
|
||||
lateinit var statusExtra: TextView
|
||||
lateinit var status: TextView
|
||||
lateinit var context: Context
|
||||
|
||||
override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
|
||||
this.control = cws.control!!
|
||||
this.cvh = cvh
|
||||
|
||||
statusExtra = cvh.statusExtra
|
||||
status = cvh.status
|
||||
|
||||
status.setText(control.getStatusText())
|
||||
|
||||
context = status.getContext()
|
||||
|
||||
cvh.layout.setOnTouchListener(ToggleRangeTouchListener())
|
||||
|
||||
val ld = cvh.layout.getBackground() as LayerDrawable
|
||||
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
|
||||
|
||||
template = control.getControlTemplate() as ToggleRangeTemplate
|
||||
rangeTemplate = template.getRange()
|
||||
|
||||
val checked = template.isChecked()
|
||||
val deviceType = control.getDeviceType()
|
||||
|
||||
updateRange((rangeTemplate.getCurrentValue() / 100.0f), checked)
|
||||
|
||||
cvh.setEnabled(checked)
|
||||
cvh.applyRenderInfo(findRenderInfo(deviceType, checked))
|
||||
}
|
||||
|
||||
fun toggle() {
|
||||
cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
|
||||
|
||||
val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
|
||||
clipLayer.setLevel(nextLevel)
|
||||
}
|
||||
|
||||
fun beginUpdateRange() {
|
||||
status.setVisibility(View.GONE)
|
||||
statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
|
||||
.getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
|
||||
}
|
||||
|
||||
fun updateRange(f: Float, checked: Boolean) {
|
||||
clipLayer.setLevel(if (checked) (MAX_LEVEL * f).toInt() else MIN_LEVEL)
|
||||
|
||||
if (checked && f < 100.0f && f > 0.0f) {
|
||||
statusExtra.setText("" + (f * 100.0).toInt() + "%")
|
||||
statusExtra.setVisibility(View.VISIBLE)
|
||||
} else {
|
||||
statusExtra.setText("")
|
||||
statusExtra.setVisibility(View.GONE)
|
||||
}
|
||||
}
|
||||
|
||||
fun endUpdateRange(f: Float) {
|
||||
statusExtra.setText(" - " + (f * 100.0).toInt() + "%")
|
||||
|
||||
val newValue = rangeTemplate.getMinValue() +
|
||||
(f * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()))
|
||||
|
||||
statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
|
||||
.getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
|
||||
status.setVisibility(View.VISIBLE)
|
||||
|
||||
cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(newValue)))
|
||||
}
|
||||
|
||||
fun findNearestStep(value: Float): Float {
|
||||
var minDiff = 1000f
|
||||
|
||||
var f = rangeTemplate.getMinValue()
|
||||
while (f <= rangeTemplate.getMaxValue()) {
|
||||
val currentDiff = Math.abs(value - f)
|
||||
if (currentDiff < minDiff) {
|
||||
minDiff = currentDiff
|
||||
} else {
|
||||
return f - rangeTemplate.getStepValue()
|
||||
}
|
||||
|
||||
f += rangeTemplate.getStepValue()
|
||||
}
|
||||
|
||||
return rangeTemplate.getMaxValue()
|
||||
}
|
||||
|
||||
inner class ToggleRangeTouchListener() : View.OnTouchListener {
|
||||
private var initialTouchX: Float = 0.0f
|
||||
private var initialTouchY: Float = 0.0f
|
||||
private var isDragging: Boolean = false
|
||||
private val minDragDiff = 20
|
||||
|
||||
override fun onTouch(v: View, e: MotionEvent): Boolean {
|
||||
when (e.getActionMasked()) {
|
||||
MotionEvent.ACTION_DOWN -> setupTouch(e)
|
||||
MotionEvent.ACTION_MOVE -> detectDrag(v, e)
|
||||
MotionEvent.ACTION_UP -> endTouch(v, e)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setupTouch(e: MotionEvent) {
|
||||
initialTouchX = e.getX()
|
||||
initialTouchY = e.getY()
|
||||
}
|
||||
|
||||
private fun detectDrag(v: View, e: MotionEvent) {
|
||||
val xDiff = Math.abs(e.getX() - initialTouchX)
|
||||
val yDiff = Math.abs(e.getY() - initialTouchY)
|
||||
|
||||
if (xDiff < minDragDiff) {
|
||||
isDragging = false
|
||||
} else {
|
||||
if (!isDragging) {
|
||||
this@ToggleRangeTemplateBehavior.beginUpdateRange()
|
||||
}
|
||||
v.getParent().requestDisallowInterceptTouchEvent(true)
|
||||
isDragging = true
|
||||
if (yDiff > xDiff) {
|
||||
endTouch(v, e)
|
||||
} else {
|
||||
val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
|
||||
this@ToggleRangeTemplateBehavior.updateRange(percent, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun endTouch(v: View, e: MotionEvent) {
|
||||
if (!isDragging) {
|
||||
this@ToggleRangeTemplateBehavior.toggle()
|
||||
} else {
|
||||
val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
|
||||
this@ToggleRangeTemplateBehavior.endUpdateRange(percent)
|
||||
}
|
||||
|
||||
initialTouchX = 0.0f
|
||||
initialTouchY = 0.0f
|
||||
isDragging = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ToggleTemplateBehavior : Behavior {
|
||||
lateinit var clipLayer: Drawable
|
||||
lateinit var template: ToggleTemplate
|
||||
lateinit var control: Control
|
||||
lateinit var cvh: ControlViewHolder
|
||||
lateinit var context: Context
|
||||
lateinit var status: TextView
|
||||
|
||||
override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
|
||||
this.control = cws.control!!
|
||||
this.cvh = cvh
|
||||
status = cvh.status
|
||||
|
||||
status.setText(control.getStatusText())
|
||||
|
||||
cvh.layout.setOnClickListener(View.OnClickListener() { toggle() })
|
||||
|
||||
val ld = cvh.layout.getBackground() as LayerDrawable
|
||||
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
|
||||
|
||||
template = control.getControlTemplate() as ToggleTemplate
|
||||
|
||||
val checked = template.isChecked()
|
||||
val deviceType = control.getDeviceType()
|
||||
|
||||
clipLayer.setLevel(if (checked) MAX_LEVEL else MIN_LEVEL)
|
||||
cvh.setEnabled(checked)
|
||||
cvh.applyRenderInfo(findRenderInfo(deviceType, checked))
|
||||
}
|
||||
|
||||
fun toggle() {
|
||||
cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
|
||||
|
||||
val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
|
||||
clipLayer.setLevel(nextLevel)
|
||||
}
|
||||
}
|
||||
|
||||
internal data class RenderInfo(val iconResourceId: Int, val foreground: Int, val background: Int)
|
||||
|
||||
private val unknownDeviceMap = mapOf(
|
||||
false to RenderInfo(
|
||||
R.drawable.ic_light_off_gm2_24px,
|
||||
R.color.unknown_foreground,
|
||||
R.color.unknown_foreground),
|
||||
true to RenderInfo(
|
||||
R.drawable.ic_lightbulb_outline_gm2_24px,
|
||||
R.color.unknown_foreground,
|
||||
R.color.unknown_foreground)
|
||||
)
|
||||
|
||||
private val deviceRenderMap = mapOf<Int, Map<Boolean, RenderInfo>>(
|
||||
DeviceTypes.TYPE_UNKNOWN to unknownDeviceMap,
|
||||
DeviceTypes.TYPE_LIGHT to mapOf(
|
||||
false to RenderInfo(
|
||||
R.drawable.ic_light_off_gm2_24px,
|
||||
R.color.light_foreground,
|
||||
R.color.light_background),
|
||||
true to RenderInfo(
|
||||
R.drawable.ic_lightbulb_outline_gm2_24px,
|
||||
R.color.light_foreground,
|
||||
R.color.light_background)
|
||||
),
|
||||
DeviceTypes.TYPE_THERMOSTAT to mapOf(
|
||||
false to RenderInfo(
|
||||
R.drawable.ic_device_thermostat_gm2_24px,
|
||||
R.color.light_foreground,
|
||||
R.color.light_background),
|
||||
true to RenderInfo(
|
||||
R.drawable.ic_device_thermostat_gm2_24px,
|
||||
R.color.light_foreground,
|
||||
R.color.light_background)
|
||||
),
|
||||
DeviceTypes.TYPE_CAMERA to mapOf(
|
||||
false to RenderInfo(
|
||||
R.drawable.ic_videocam_gm2_24px,
|
||||
R.color.light_foreground,
|
||||
R.color.light_background),
|
||||
true to RenderInfo(
|
||||
R.drawable.ic_videocam_gm2_24px,
|
||||
R.color.light_foreground,
|
||||
R.color.light_background)
|
||||
),
|
||||
DeviceTypes.TYPE_LOCK to mapOf(
|
||||
false to RenderInfo(
|
||||
R.drawable.ic_lock_open_gm2_24px,
|
||||
R.color.lock_foreground,
|
||||
R.color.lock_background),
|
||||
true to RenderInfo(
|
||||
R.drawable.ic_lock_gm2_24px,
|
||||
R.color.lock_foreground,
|
||||
R.color.lock_background)
|
||||
),
|
||||
DeviceTypes.TYPE_SWITCH to mapOf(
|
||||
false to RenderInfo(
|
||||
R.drawable.ic_switches_gm2_24px,
|
||||
R.color.lock_foreground,
|
||||
R.color.lock_background),
|
||||
true to RenderInfo(
|
||||
R.drawable.ic_switches_gm2_24px,
|
||||
R.color.lock_foreground,
|
||||
R.color.lock_background)
|
||||
),
|
||||
DeviceTypes.TYPE_OUTLET to mapOf(
|
||||
false to RenderInfo(
|
||||
R.drawable.ic_power_off_gm2_24px,
|
||||
R.color.lock_foreground,
|
||||
R.color.lock_background),
|
||||
true to RenderInfo(
|
||||
R.drawable.ic_power_gm2_24px,
|
||||
R.color.lock_foreground,
|
||||
R.color.lock_background)
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.controls.ui
|
||||
|
||||
import android.service.controls.Control
|
||||
|
||||
import com.android.systemui.controls.controller.ControlInfo
|
||||
|
||||
/**
|
||||
* A container for:
|
||||
* <ul>
|
||||
* <li>ControlInfo - Basic cached info about a Control
|
||||
* <li>Control - Actual Control parcelable received directly from
|
||||
* the participating application
|
||||
* </ul>
|
||||
*/
|
||||
data class ControlWithState(val ci: ControlInfo, val control: Control?)
|
||||
@@ -19,12 +19,15 @@ package com.android.systemui.controls.ui
|
||||
import android.content.ComponentName
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.actions.ControlAction
|
||||
import android.view.ViewGroup
|
||||
|
||||
interface ControlsUiController {
|
||||
fun show(parent: ViewGroup)
|
||||
fun hide()
|
||||
fun onRefreshState(componentName: ComponentName, controls: List<Control>)
|
||||
fun onActionResponse(
|
||||
componentName: ComponentName,
|
||||
controlId: String,
|
||||
@ControlAction.ResponseResult response: Int
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,204 @@
|
||||
|
||||
package com.android.systemui.controls.ui
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.TokenProvider
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
|
||||
import com.android.systemui.controls.controller.ControlsController
|
||||
import com.android.systemui.controls.controller.ControlInfo
|
||||
import com.android.systemui.controls.management.ControlsProviderSelectorActivity
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.R
|
||||
|
||||
import dagger.Lazy
|
||||
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
private const val TAG = "ControlsUi"
|
||||
|
||||
// TEMP CODE for MOCK
|
||||
private const val TOKEN = "https://www.googleapis.com/auth/assistant"
|
||||
private const val SCOPE = "oauth2:" + TOKEN
|
||||
private var tokenProviderConnection: TokenProviderConnection? = null
|
||||
class TokenProviderConnection(val cc: ControlsController, val context: Context)
|
||||
: ServiceConnection {
|
||||
private var mTokenProvider: TokenProvider? = null
|
||||
|
||||
override fun onServiceConnected(cName: ComponentName, binder: IBinder) {
|
||||
Thread({
|
||||
Log.i(TAG, "TokenProviderConnection connected")
|
||||
mTokenProvider = TokenProvider.Stub.asInterface(binder)
|
||||
|
||||
val mLastAccountName = mTokenProvider?.getAccountName()
|
||||
|
||||
if (mLastAccountName == null || mLastAccountName.isEmpty()) {
|
||||
Log.e(TAG, "NO ACCOUNT IS SET. Open HomeMock app")
|
||||
} else {
|
||||
mTokenProvider?.setAuthToken(getAuthToken(mLastAccountName))
|
||||
cc.subscribeToFavorites()
|
||||
}
|
||||
}, "TokenProviderThread").start()
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(cName: ComponentName) {
|
||||
mTokenProvider = null
|
||||
}
|
||||
|
||||
fun getAuthToken(accountName: String): String? {
|
||||
val am = AccountManager.get(context)
|
||||
val accounts = am.getAccountsByType("com.google")
|
||||
if (accounts == null || accounts.size == 0) {
|
||||
Log.w(TAG, "No com.google accounts found")
|
||||
return null
|
||||
}
|
||||
|
||||
var account: Account? = null
|
||||
for (a in accounts) {
|
||||
if (a.name.equals(accountName)) {
|
||||
account = a
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (account == null) {
|
||||
account = accounts[0]
|
||||
}
|
||||
|
||||
try {
|
||||
return am.blockingGetAuthToken(account!!, SCOPE, true)
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "Error getting auth token", e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class ControlsUiControllerImpl @Inject constructor() : ControlsUiController {
|
||||
class ControlsUiControllerImpl @Inject constructor (
|
||||
val controlsController: Lazy<ControlsController>,
|
||||
val context: Context,
|
||||
@Main val uiExecutor: Executor
|
||||
) : ControlsUiController {
|
||||
|
||||
private lateinit var controlInfos: List<ControlInfo>
|
||||
private val controlsById = mutableMapOf<Pair<ComponentName, String>, ControlWithState>()
|
||||
private val controlViewsById = mutableMapOf<String, ControlViewHolder>()
|
||||
private lateinit var parent: ViewGroup
|
||||
|
||||
override fun show(parent: ViewGroup) {
|
||||
Log.d(TAG, "show()")
|
||||
|
||||
this.parent = parent
|
||||
|
||||
controlInfos = controlsController.get().getFavoriteControls()
|
||||
|
||||
controlInfos.map {
|
||||
ControlWithState(it, null)
|
||||
}.associateByTo(controlsById) { Pair(it.ci.component, it.ci.controlId) }
|
||||
|
||||
if (controlInfos.isEmpty()) {
|
||||
showInitialSetupView()
|
||||
} else {
|
||||
showControlsView()
|
||||
}
|
||||
|
||||
// Temp code to pass auth
|
||||
tokenProviderConnection = TokenProviderConnection(controlsController.get(), context)
|
||||
val serviceIntent = Intent()
|
||||
serviceIntent.setComponent(ComponentName("com.android.systemui.home.mock",
|
||||
"com.android.systemui.home.mock.AuthService"))
|
||||
context.bindService(serviceIntent, tokenProviderConnection!!, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
|
||||
private fun showInitialSetupView() {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
inflater.inflate(R.layout.controls_no_favorites, parent, true)
|
||||
|
||||
val textView = parent.requireViewById(R.id.controls_title) as TextView
|
||||
textView.setOnClickListener {
|
||||
val i = Intent()
|
||||
i.setComponent(ComponentName(context, ControlsProviderSelectorActivity::class.java))
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
context.startActivity(i)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showControlsView() {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
inflater.inflate(R.layout.controls_with_favorites, parent, true)
|
||||
|
||||
val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
|
||||
var lastRow: ViewGroup = createRow(inflater, listView)
|
||||
controlInfos.forEach {
|
||||
Log.d(TAG, "favorited control id: " + it.controlId)
|
||||
if (lastRow.getChildCount() == 2) {
|
||||
lastRow = createRow(inflater, listView)
|
||||
}
|
||||
val item = inflater.inflate(
|
||||
R.layout.controls_base_item, lastRow, false) as ViewGroup
|
||||
lastRow.addView(item)
|
||||
val cvh = ControlViewHolder(item, controlsController.get())
|
||||
cvh.bindData(controlsById.get(Pair(it.component, it.controlId))!!)
|
||||
controlViewsById.put(it.controlId, cvh)
|
||||
}
|
||||
|
||||
val moreImageView = parent.requireViewById(R.id.controls_more) as View
|
||||
moreImageView.setOnClickListener {
|
||||
val i = Intent()
|
||||
i.setComponent(ComponentName(context, ControlsProviderSelectorActivity::class.java))
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
context.startActivity(i)
|
||||
}
|
||||
}
|
||||
|
||||
override fun hide() {
|
||||
Log.d(TAG, "hide()")
|
||||
controlsController.get().unsubscribe()
|
||||
context.unbindService(tokenProviderConnection)
|
||||
tokenProviderConnection = null
|
||||
|
||||
parent.removeAllViews()
|
||||
controlsById.clear()
|
||||
controlViewsById.clear()
|
||||
}
|
||||
|
||||
override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
|
||||
TODO("not implemented")
|
||||
Log.d(TAG, "onRefreshState()")
|
||||
controls.forEach { c ->
|
||||
controlsById.get(Pair(componentName, c.getControlId()))?.let {
|
||||
Log.d(TAG, "onRefreshState() for id: " + c.getControlId())
|
||||
val cws = ControlWithState(it.ci, c)
|
||||
controlsById.put(Pair(componentName, c.getControlId()), cws)
|
||||
|
||||
uiExecutor.execute {
|
||||
controlViewsById.get(c.getControlId())?.bindData(cws)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
|
||||
Log.d(TAG, "onActionResponse()")
|
||||
TODO("not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createRow(inflater: LayoutInflater, parent: ViewGroup): ViewGroup {
|
||||
val row = inflater.inflate(R.layout.controls_row, parent, false) as ViewGroup
|
||||
parent.addView(row)
|
||||
return row
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +93,13 @@ import com.android.systemui.MultiListLayout;
|
||||
import com.android.systemui.MultiListLayout.MultiListAdapter;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.controls.ui.ControlsUiController;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
|
||||
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
|
||||
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
|
||||
import com.android.systemui.statusbar.BlurUtils;
|
||||
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
|
||||
import com.android.systemui.statusbar.phone.ScrimController;
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
@@ -183,6 +184,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
private final IStatusBarService mStatusBarService;
|
||||
private final NotificationShadeWindowController mNotificationShadeWindowController;
|
||||
private GlobalActionsPanelPlugin mPanelPlugin;
|
||||
private ControlsUiController mControlsUiController;
|
||||
|
||||
/**
|
||||
* @param context everything needs a context :(
|
||||
@@ -200,7 +202,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
TelecomManager telecomManager, MetricsLogger metricsLogger,
|
||||
BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
|
||||
IStatusBarService statusBarService,
|
||||
NotificationShadeWindowController notificationShadeWindowController) {
|
||||
NotificationShadeWindowController notificationShadeWindowController,
|
||||
ControlsUiController controlsUiController) {
|
||||
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
|
||||
mWindowManagerFuncs = windowManagerFuncs;
|
||||
mAudioManager = audioManager;
|
||||
@@ -220,6 +223,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
mSysuiColorExtractor = colorExtractor;
|
||||
mStatusBarService = statusBarService;
|
||||
mNotificationShadeWindowController = notificationShadeWindowController;
|
||||
mControlsUiController = controlsUiController;
|
||||
|
||||
// receive broadcasts
|
||||
IntentFilter filter = new IntentFilter();
|
||||
@@ -455,9 +459,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
mKeyguardManager.isDeviceLocked())
|
||||
: null;
|
||||
|
||||
boolean showControls = !mKeyguardManager.isDeviceLocked() && isControlsEnabled(mContext);
|
||||
|
||||
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
|
||||
mBlurUtils, mSysuiColorExtractor, mStatusBarService,
|
||||
mNotificationShadeWindowController, isControlsEnabled(mContext));
|
||||
mNotificationShadeWindowController,
|
||||
showControls ? mControlsUiController : null);
|
||||
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
|
||||
dialog.setKeyguardShowing(mKeyguardShowing);
|
||||
|
||||
@@ -1543,13 +1550,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
private boolean mHadTopUi;
|
||||
private final NotificationShadeWindowController mNotificationShadeWindowController;
|
||||
private final BlurUtils mBlurUtils;
|
||||
private final boolean mControlsEnabled;
|
||||
|
||||
private ControlsUiController mControlsUiController;
|
||||
private ViewGroup mControlsView;
|
||||
|
||||
ActionsDialog(Context context, MyAdapter adapter,
|
||||
GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils,
|
||||
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
|
||||
NotificationShadeWindowController notificationShadeWindowController,
|
||||
boolean controlsEnabled) {
|
||||
ControlsUiController controlsUiController) {
|
||||
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
|
||||
mContext = context;
|
||||
mAdapter = adapter;
|
||||
@@ -1557,7 +1566,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
mColorExtractor = sysuiColorExtractor;
|
||||
mStatusBarService = statusBarService;
|
||||
mNotificationShadeWindowController = notificationShadeWindowController;
|
||||
mControlsEnabled = controlsEnabled;
|
||||
mControlsUiController = controlsUiController;
|
||||
|
||||
// Window initialization
|
||||
Window window = getWindow();
|
||||
@@ -1639,6 +1648,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
private void initializeLayout() {
|
||||
setContentView(getGlobalActionsLayoutId(mContext));
|
||||
fixNavBarClipping();
|
||||
mControlsView = findViewById(com.android.systemui.R.id.global_actions_controls);
|
||||
mGlobalActionsLayout = findViewById(com.android.systemui.R.id.global_actions_view);
|
||||
mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
|
||||
((View) mGlobalActionsLayout.getParent()).setOnClickListener(view -> dismiss());
|
||||
@@ -1674,7 +1684,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
}
|
||||
|
||||
private int getGlobalActionsLayoutId(Context context) {
|
||||
if (mControlsEnabled) {
|
||||
if (mControlsUiController != null) {
|
||||
return com.android.systemui.R.layout.global_actions_grid_v2;
|
||||
}
|
||||
|
||||
@@ -1758,6 +1768,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
mBlurUtils.radiusForRatio(animatedValue));
|
||||
})
|
||||
.start();
|
||||
if (mControlsUiController != null) {
|
||||
mControlsUiController.show(mControlsView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1766,6 +1779,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
return;
|
||||
}
|
||||
mShowing = false;
|
||||
if (mControlsUiController != null) mControlsUiController.hide();
|
||||
mGlobalActionsLayout.setTranslationX(0);
|
||||
mGlobalActionsLayout.setTranslationY(0);
|
||||
mGlobalActionsLayout.setAlpha(1);
|
||||
|
||||
Reference in New Issue
Block a user