Move car related code from SystemUI to CarSystemUI
Test: Emulator phone and Car Change-Id: Ia64a23c1d3643899118e578b82c665c034af1c8e
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
~ Copyright (C) 2017 The Android Open Source Project
|
||||
~ 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.
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
~ Copyright (C) 2015 The Android Open Source Project
|
||||
~ 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.
|
||||
@@ -14,15 +14,14 @@
|
||||
~ 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="32dp"
|
||||
android:height="37dp"
|
||||
android:viewportWidth="32.0"
|
||||
android:viewportHeight="37.0">
|
||||
<path
|
||||
android:pathData="M16.62,0.61L31.33,35.21C31.55,35.72 31.31,36.3 30.8,36.52C30.48,36.66 30.12,36.62 29.83,36.42L15.7,26.44L1.58,36.42C1.13,36.73 0.5,36.63 0.18,36.18C-0.02,35.89 -0.06,35.53 0.08,35.21L14.78,0.61C15,0.1 15.59,-0.14 16.1,0.08C16.33,0.18 16.52,0.37 16.62,0.61Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:fillType="evenOdd"
|
||||
android:fillColor="@color/car_grey_50"
|
||||
android:strokeWidth="1"/>
|
||||
</vector>
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="?android:attr/colorBackgroundFloating" />
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/car_radius_3"
|
||||
android:topLeftRadius="0dp"
|
||||
android:bottomRightRadius="@dimen/car_radius_3"
|
||||
android:topRightRadius="0dp"
|
||||
/>
|
||||
</shape>
|
||||
@@ -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="17dp"
|
||||
android:height="17dp"
|
||||
android:viewportWidth="18.0"
|
||||
android:viewportHeight="18.0">
|
||||
<group
|
||||
android:translateY="0.5"
|
||||
android:translateX="0.5" >
|
||||
<path
|
||||
android:pathData="M9.57,8.5l2.79,-2.78c0.3,-0.3 0.3,-0.8 0,-1.1L9.04,1.29L9.02,1.27C8.7,0.98 8.21,1 7.91,1.31C7.78,1.45 7.71,1.64 7.71,1.84v4.79L4.69,3.61c-0.3,-0.3 -0.79,-0.3 -1.09,0s-0.3,0.79 0,1.09L7.39,8.5L3.6,12.29c-0.3,0.3 -0.3,0.79 0,1.09s0.79,0.3 1.09,0l3.01,-3.01v4.8c0,0.42 0.35,0.77 0.77,0.77c0.19,0 0.39,-0.07 0.53,-0.21l0.04,-0.04l3.32,-3.32c0.3,-0.3 0.3,-0.8 0,-1.1L9.57,8.5zM9.19,6.77v-3.2l1.6,1.6L9.19,6.77zM9.19,13.42v-3.2l1.6,1.6L9.19,13.42zM4.03,9.29c-0.44,0.44 -1.15,0.44 -1.58,0C2.02,8.86 2.02,8.16 2.45,7.72l0.01,-0.01C2.89,7.27 3.59,7.27 4.02,7.7l0.01,0.01C4.47,8.15 4.47,8.85 4.03,9.29zM14.44,7.71c0.44,0.44 0.44,1.15 0,1.58c-0.44,0.44 -1.15,0.44 -1.58,0c-0.44,-0.43 -0.44,-1.13 -0.01,-1.57l0.01,-0.01C13.3,7.28 14,7.27 14.43,7.7C14.44,7.7 14.44,7.71 14.44,7.71z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -1,3 +1,19 @@
|
||||
<?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
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
**
|
||||
** Copyright 2017, The Android Open Source Project
|
||||
** Copyright 2018, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 The Android Open Source Project
|
||||
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.
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 The Android Open Source Project
|
||||
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.
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
**
|
||||
** Copyright 2016, The Android Open Source Project
|
||||
** Copyright 2018, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
**
|
||||
** Copyright 2016, The Android Open Source Project
|
||||
** Copyright 2018, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
**
|
||||
** Copyright 2016, The Android Open Source Project
|
||||
** Copyright 2018, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
@@ -58,7 +58,6 @@
|
||||
android:paddingTop="30dp"
|
||||
android:paddingBottom="30dp"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
@@ -79,7 +78,6 @@
|
||||
android:alpha="0.7"
|
||||
/>
|
||||
|
||||
|
||||
<com.android.systemui.statusbar.policy.Clock
|
||||
android:id="@+id/clock"
|
||||
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
* Copyright 2017, The Android Open Source Project
|
||||
* Copyright 2018, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
* Copyright (c) 2016, The Android Open Source Project
|
||||
* 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.
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) 2017, The Android Open Source Project
|
||||
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.
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/**
|
||||
* Copyright (c) 2016, The Android Open Source Project
|
||||
* 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.
|
||||
@@ -28,6 +28,8 @@ import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
|
||||
import com.android.systemui.statusbar.car.hvac.HvacController;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
import com.android.systemui.volume.CarVolumeDialogComponent;
|
||||
import com.android.systemui.volume.VolumeDialogComponent;
|
||||
|
||||
/**
|
||||
* Class factory to provide car specific SystemUI components.
|
||||
@@ -39,6 +41,10 @@ public class CarSystemUIFactory extends SystemUIFactory {
|
||||
return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
|
||||
}
|
||||
|
||||
public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
|
||||
return new CarVolumeDialogComponent(systemUi, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
|
||||
Context context) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -11,8 +11,9 @@
|
||||
* 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
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.car;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -1,16 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
* 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
|
||||
* 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.
|
||||
* 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.qs.car;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -1,16 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
* 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
|
||||
* 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.
|
||||
* 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.qs.car;
|
||||
|
||||
import android.animation.Animator;
|
||||
@@ -223,7 +226,7 @@ public class CarQSFragment extends Fragment implements QS {
|
||||
|
||||
private void animateHeightChange(boolean opening) {
|
||||
// Animation in progress; cancel it to avoid contention.
|
||||
if (mAnimatorSet != null){
|
||||
if (mAnimatorSet != null) {
|
||||
mAnimatorSet.cancel();
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
* 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
|
||||
* 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.
|
||||
* 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.qs.car;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -58,28 +58,31 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo
|
||||
private final Context mContext;
|
||||
|
||||
private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
private BluetoothHeadsetClient mBluetoothHeadsetClient;
|
||||
|
||||
private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
|
||||
private BluetoothHeadsetClient mBluetoothHeadsetClient;
|
||||
private final ServiceListener mHfpServiceListener = new ServiceListener() {
|
||||
@Override
|
||||
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||
if (profile == BluetoothProfile.HEADSET_CLIENT) {
|
||||
mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(int profile) {
|
||||
if (profile == BluetoothProfile.HEADSET_CLIENT) {
|
||||
mBluetoothHeadsetClient = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
private int mLevel;
|
||||
|
||||
/**
|
||||
* An interface indicating the container of a View that will display what the information
|
||||
* in the {@link CarBatteryController}.
|
||||
*/
|
||||
public interface BatteryViewHandler {
|
||||
void hideBatteryView();
|
||||
void showBatteryView();
|
||||
}
|
||||
|
||||
private BatteryViewHandler mBatteryViewHandler;
|
||||
|
||||
public CarBatteryController(Context context) {
|
||||
mContext = context;
|
||||
|
||||
if (mAdapter == null) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
|
||||
@@ -159,7 +162,7 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo
|
||||
|
||||
}
|
||||
BluetoothDevice device =
|
||||
(BluetoothDevice)intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
(BluetoothDevice) intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
updateBatteryIcon(device, newState);
|
||||
}
|
||||
}
|
||||
@@ -261,20 +264,14 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo
|
||||
}
|
||||
}
|
||||
|
||||
private final ServiceListener mHfpServiceListener = new ServiceListener() {
|
||||
@Override
|
||||
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||
if (profile == BluetoothProfile.HEADSET_CLIENT) {
|
||||
mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* An interface indicating the container of a View that will display what the information
|
||||
* in the {@link CarBatteryController}.
|
||||
*/
|
||||
public interface BatteryViewHandler {
|
||||
void hideBatteryView();
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(int profile) {
|
||||
if (profile == BluetoothProfile.HEADSET_CLIENT) {
|
||||
mBluetoothHeadsetClient = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
void showBatteryView();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.car;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
@@ -34,7 +50,6 @@ public class CarFacetButtonController {
|
||||
* Add facet button to this controller. The expected use is for the facet button
|
||||
* to get a reference to this controller via {@link com.android.systemui.Dependency}
|
||||
* and self add.
|
||||
* @param facetButton
|
||||
*/
|
||||
public void addFacetButton(CarFacetButton facetButton) {
|
||||
String[] categories = facetButton.getCategories();
|
||||
@@ -70,15 +85,16 @@ public class CarFacetButtonController {
|
||||
* They will then be compared with the supplied StackInfo list.
|
||||
* The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
|
||||
* for consideration if it has the same displayId as the CarFacetButtons.
|
||||
* @param taskInfo of the currently running application
|
||||
*
|
||||
* @param stackInfoList of the currently running application
|
||||
*/
|
||||
public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
|
||||
int displayId = getDisplayId();
|
||||
ActivityManager.StackInfo validStackInfo = null;
|
||||
for (ActivityManager.StackInfo stackInfo :stackInfoList) {
|
||||
for (ActivityManager.StackInfo stackInfo : stackInfoList) {
|
||||
// If the display id is unknown or it matches the stack, it's valid for use
|
||||
if ((displayId == -1 || displayId == stackInfo.displayId) &&
|
||||
stackInfo.topActivity != null) {
|
||||
if ((displayId == -1 || displayId == stackInfo.displayId)
|
||||
&& stackInfo.topActivity != null) {
|
||||
validStackInfo = stackInfo;
|
||||
break;
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.car;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -35,6 +35,8 @@ import com.android.systemui.R;
|
||||
import com.android.systemui.classifier.FalsingLog;
|
||||
import com.android.systemui.classifier.FalsingManager;
|
||||
import com.android.systemui.fragments.FragmentHostManager;
|
||||
import com.android.systemui.plugins.qs.QS;
|
||||
import com.android.systemui.qs.car.CarQSFragment;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
@@ -252,6 +254,11 @@ public class CarStatusBar extends StatusBar implements
|
||||
addTemperatureViewToController(mStatusBarWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QS createDefaultQSFragment() {
|
||||
return new CarQSFragment();
|
||||
}
|
||||
|
||||
private BatteryController createBatteryController() {
|
||||
mCarBatteryController = new CarBatteryController(mContext);
|
||||
mCarBatteryController.addBatteryViewHandler(this);
|
||||
@@ -549,7 +556,7 @@ public class CarStatusBar extends StatusBar implements
|
||||
*/
|
||||
public void dismissKeyguard() {
|
||||
executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
|
||||
true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
|
||||
true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.car;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -16,13 +32,13 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage
|
||||
ViewMediatorCallback callback,
|
||||
LockPatternUtils lockPatternUtils) {
|
||||
super(context, callback, lockPatternUtils);
|
||||
mShouldHideNavBar =context.getResources()
|
||||
mShouldHideNavBar = context.getResources()
|
||||
.getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateNavigationBarVisibility(boolean navBarVisible) {
|
||||
if(!mShouldHideNavBar) {
|
||||
if (!mShouldHideNavBar) {
|
||||
return;
|
||||
}
|
||||
CarStatusBar statusBar = (CarStatusBar) mStatusBar;
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.car;
|
||||
|
||||
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
|
||||
@@ -29,7 +45,7 @@ import com.android.systemui.statusbar.policy.BluetoothController;
|
||||
*/
|
||||
public class ConnectedDeviceSignalController extends BroadcastReceiver implements
|
||||
BluetoothController.Callback {
|
||||
private final static String TAG = "DeviceSignalCtlr";
|
||||
private static final String TAG = "DeviceSignalCtlr";
|
||||
|
||||
/**
|
||||
* The value that indicates if a network is unavailable. This value is according ot the
|
||||
@@ -70,6 +86,21 @@ public class ConnectedDeviceSignalController extends BroadcastReceiver implement
|
||||
private final SignalDrawable mSignalDrawable;
|
||||
|
||||
private BluetoothHeadsetClient mBluetoothHeadsetClient;
|
||||
private final ServiceListener mHfpServiceListener = new ServiceListener() {
|
||||
@Override
|
||||
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||
if (profile == BluetoothProfile.HEADSET_CLIENT) {
|
||||
mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(int profile) {
|
||||
if (profile == BluetoothProfile.HEADSET_CLIENT) {
|
||||
mBluetoothHeadsetClient = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public ConnectedDeviceSignalController(Context context, View signalsView) {
|
||||
mContext = context;
|
||||
@@ -87,7 +118,7 @@ public class ConnectedDeviceSignalController extends BroadcastReceiver implement
|
||||
new ScalingDrawableWrapper(mSignalDrawable, mIconScaleFactor));
|
||||
|
||||
if (mAdapter == null) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
|
||||
@@ -236,20 +267,4 @@ public class ConnectedDeviceSignalController extends BroadcastReceiver implement
|
||||
mSignalsView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private final ServiceListener mHfpServiceListener = new ServiceListener() {
|
||||
@Override
|
||||
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||
if (profile == BluetoothProfile.HEADSET_CLIENT) {
|
||||
mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(int profile) {
|
||||
if (profile == BluetoothProfile.HEADSET_CLIENT) {
|
||||
mBluetoothHeadsetClient = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -124,4 +124,4 @@ public class DrivingStateHelper {
|
||||
Log.d(TAG, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -11,7 +11,7 @@
|
||||
* 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
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.car;
|
||||
@@ -44,7 +44,7 @@ public class FullscreenUserSwitcher {
|
||||
// Initialize user grid.
|
||||
mUserGridView = container.findViewById(R.id.user_grid);
|
||||
GridLayoutManager layoutManager = new GridLayoutManager(context,
|
||||
context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
|
||||
context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
|
||||
mUserGridView.getRecyclerView().setLayoutManager(layoutManager);
|
||||
mUserGridView.buildAdapter();
|
||||
mUserGridView.setUserSelectionListener(this::onUserSelected);
|
||||
@@ -54,7 +54,7 @@ public class FullscreenUserSwitcher {
|
||||
hide();
|
||||
|
||||
mShortAnimDuration = container.getResources()
|
||||
.getInteger(android.R.integer.config_shortAnimTime);
|
||||
.getInteger(android.R.integer.config_shortAnimTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,4 +108,4 @@ public class FullscreenUserSwitcher {
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
* 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
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.car;
|
||||
@@ -210,7 +210,7 @@ public class UserGridRecyclerView extends PagedListView implements
|
||||
public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
|
||||
UserRecord userRecord = mUsers.get(position);
|
||||
RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes,
|
||||
getUserRecordIcon(userRecord));
|
||||
getUserRecordIcon(userRecord));
|
||||
circleIcon.setCircular(true);
|
||||
holder.mUserAvatarImageView.setImageDrawable(circleIcon);
|
||||
holder.mUserNameTextView.setText(userRecord.mInfo.name);
|
||||
@@ -254,13 +254,13 @@ public class UserGridRecyclerView extends PagedListView implements
|
||||
|
||||
private void showMaxUserLimitReachedDialog() {
|
||||
AlertDialog maxUsersDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
|
||||
.setTitle(R.string.user_limit_reached_title)
|
||||
.setMessage(getResources().getQuantityString(
|
||||
R.plurals.user_limit_reached_message,
|
||||
mCarUserManagerHelper.getMaxSupportedRealUsers(),
|
||||
mCarUserManagerHelper.getMaxSupportedRealUsers()))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.create();
|
||||
.setTitle(R.string.user_limit_reached_title)
|
||||
.setMessage(getResources().getQuantityString(
|
||||
R.plurals.user_limit_reached_message,
|
||||
mCarUserManagerHelper.getMaxSupportedRealUsers(),
|
||||
mCarUserManagerHelper.getMaxSupportedRealUsers()))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.create();
|
||||
// Sets window flags for the SysUI dialog
|
||||
SystemUIDialog.applyFlags(maxUsersDialog);
|
||||
maxUsersDialog.show();
|
||||
@@ -268,17 +268,17 @@ public class UserGridRecyclerView extends PagedListView implements
|
||||
|
||||
private void showConfirmAddUserDialog() {
|
||||
String message = mRes.getString(R.string.user_add_user_message_setup)
|
||||
.concat(System.getProperty("line.separator"))
|
||||
.concat(System.getProperty("line.separator"))
|
||||
.concat(mRes.getString(R.string.user_add_user_message_update));
|
||||
.concat(System.getProperty("line.separator"))
|
||||
.concat(System.getProperty("line.separator"))
|
||||
.concat(mRes.getString(R.string.user_add_user_message_update));
|
||||
|
||||
AlertDialog addUserDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
|
||||
.setTitle(R.string.user_add_user_title)
|
||||
.setMessage(message)
|
||||
.setNegativeButton(android.R.string.cancel, this)
|
||||
.setPositiveButton(android.R.string.ok, this)
|
||||
.setOnCancelListener(this)
|
||||
.create();
|
||||
.setTitle(R.string.user_add_user_title)
|
||||
.setMessage(message)
|
||||
.setNegativeButton(android.R.string.cancel, this)
|
||||
.setPositiveButton(android.R.string.ok, this)
|
||||
.setOnCancelListener(this)
|
||||
.create();
|
||||
// Sets window flags for the SysUI dialog
|
||||
SystemUIDialog.applyFlags(addUserDialog);
|
||||
addUserDialog.show();
|
||||
@@ -298,7 +298,7 @@ public class UserGridRecyclerView extends PagedListView implements
|
||||
|
||||
if (userRecord.mIsAddUser) {
|
||||
return UserIcons.convertToBitmap(mContext
|
||||
.getDrawable(R.drawable.car_add_circle_round));
|
||||
.getDrawable(R.drawable.car_add_circle_round));
|
||||
}
|
||||
|
||||
return mCarUserManagerHelper.getUserIcon(userRecord.mInfo);
|
||||
@@ -41,117 +41,13 @@ import java.util.Objects;
|
||||
public class HvacController {
|
||||
|
||||
public static final String TAG = "HvacController";
|
||||
public final static int BIND_TO_HVAC_RETRY_DELAY = 5000;
|
||||
public static final int BIND_TO_HVAC_RETRY_DELAY = 5000;
|
||||
|
||||
private Context mContext;
|
||||
private Handler mHandler;
|
||||
private Car mCar;
|
||||
private CarHvacManager mHvacManager;
|
||||
private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
|
||||
|
||||
public HvacController(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create connection to the Car service. Note: call backs from the Car service
|
||||
* ({@link CarHvacManager}) will happen on the same thread this method was called from.
|
||||
*/
|
||||
public void connectToCarService() {
|
||||
mHandler = new Handler();
|
||||
mCar = Car.createCar(mContext, mServiceConnection, mHandler);
|
||||
if (mCar != null) {
|
||||
// note: this connect call handles the retries
|
||||
mCar.connect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers callbacks and initializes components upon connection.
|
||||
*/
|
||||
private ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
try {
|
||||
service.linkToDeath(mRestart, 0);
|
||||
mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
|
||||
mHvacManager.registerCallback(mHardwareCallback);
|
||||
initComponents();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to correctly connect to HVAC", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
destroyHvacManager();
|
||||
}
|
||||
};
|
||||
|
||||
private void destroyHvacManager() {
|
||||
if (mHvacManager != null) {
|
||||
mHvacManager.unregisterCallback(mHardwareCallback);
|
||||
mHvacManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the connection to car service goes away then restart it.
|
||||
*/
|
||||
private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
|
||||
@Override
|
||||
public void binderDied() {
|
||||
Log.d(TAG, "Death of HVAC triggering a restart");
|
||||
if (mCar != null) {
|
||||
mCar.disconnect();
|
||||
}
|
||||
destroyHvacManager();
|
||||
mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add component to list and initialize it if the connection is up.
|
||||
* @param temperatureView
|
||||
*/
|
||||
public void addHvacTextView(TemperatureView temperatureView) {
|
||||
|
||||
HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
|
||||
if (!mTempComponents.containsKey(hvacKey)) {
|
||||
mTempComponents.put(hvacKey, new ArrayList<>());
|
||||
}
|
||||
mTempComponents.get(hvacKey).add(temperatureView);
|
||||
initComponent(temperatureView);
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
|
||||
mTempComponents.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
|
||||
List<TemperatureView> temperatureViews = next.getValue();
|
||||
for (TemperatureView view : temperatureViews) {
|
||||
initComponent(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void initComponent(TemperatureView view) {
|
||||
int id = view.getPropertyId();
|
||||
int zone = view.getAreaId();
|
||||
try {
|
||||
if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
|
||||
view.setTemp(Float.NaN);
|
||||
return;
|
||||
}
|
||||
view.setTemp(mHvacManager.getFloatProperty(id, zone));
|
||||
} catch (Exception e) {
|
||||
view.setTemp(Float.NaN);
|
||||
Log.e(TAG, "Failed to get value from hvac service", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
|
||||
* match.
|
||||
@@ -179,10 +75,109 @@ public class HvacController {
|
||||
|
||||
@Override
|
||||
public void onErrorEvent(final int propertyId, final int zone) {
|
||||
Log.d(TAG, "HVAC error event, propertyId: " + propertyId +
|
||||
" zone: " + zone);
|
||||
Log.d(TAG, "HVAC error event, propertyId: " + propertyId
|
||||
+ " zone: " + zone);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* If the connection to car service goes away then restart it.
|
||||
*/
|
||||
private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
|
||||
@Override
|
||||
public void binderDied() {
|
||||
Log.d(TAG, "Death of HVAC triggering a restart");
|
||||
if (mCar != null) {
|
||||
mCar.disconnect();
|
||||
}
|
||||
destroyHvacManager();
|
||||
mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Registers callbacks and initializes components upon connection.
|
||||
*/
|
||||
private ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
try {
|
||||
service.linkToDeath(mRestart, 0);
|
||||
mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
|
||||
mHvacManager.registerCallback(mHardwareCallback);
|
||||
initComponents();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to correctly connect to HVAC", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
destroyHvacManager();
|
||||
}
|
||||
};
|
||||
|
||||
public HvacController(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create connection to the Car service. Note: call backs from the Car service
|
||||
* ({@link CarHvacManager}) will happen on the same thread this method was called from.
|
||||
*/
|
||||
public void connectToCarService() {
|
||||
mHandler = new Handler();
|
||||
mCar = Car.createCar(mContext, mServiceConnection, mHandler);
|
||||
if (mCar != null) {
|
||||
// note: this connect call handles the retries
|
||||
mCar.connect();
|
||||
}
|
||||
}
|
||||
|
||||
private void destroyHvacManager() {
|
||||
if (mHvacManager != null) {
|
||||
mHvacManager.unregisterCallback(mHardwareCallback);
|
||||
mHvacManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add component to list and initialize it if the connection is up.
|
||||
*/
|
||||
public void addHvacTextView(TemperatureView temperatureView) {
|
||||
|
||||
HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
|
||||
if (!mTempComponents.containsKey(hvacKey)) {
|
||||
mTempComponents.put(hvacKey, new ArrayList<>());
|
||||
}
|
||||
mTempComponents.get(hvacKey).add(temperatureView);
|
||||
initComponent(temperatureView);
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
|
||||
mTempComponents.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
|
||||
List<TemperatureView> temperatureViews = next.getValue();
|
||||
for (TemperatureView view : temperatureViews) {
|
||||
initComponent(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initComponent(TemperatureView view) {
|
||||
int id = view.getPropertyId();
|
||||
int zone = view.getAreaId();
|
||||
try {
|
||||
if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
|
||||
view.setTemp(Float.NaN);
|
||||
return;
|
||||
}
|
||||
view.setTemp(mHvacManager.getFloatProperty(id, zone));
|
||||
} catch (Exception e) {
|
||||
view.setTemp(Float.NaN);
|
||||
Log.e(TAG, "Failed to get value from hvac service", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all registered components. This is useful if you need to rebuild the UI since
|
||||
@@ -200,7 +195,7 @@ public class HvacController {
|
||||
int mPropertyId;
|
||||
int mAreaId;
|
||||
|
||||
public HvacKey(int propertyId, int areaId) {
|
||||
private HvacKey(int propertyId, int areaId) {
|
||||
mPropertyId = propertyId;
|
||||
mAreaId = areaId;
|
||||
}
|
||||
@@ -210,8 +205,8 @@ public class HvacController {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
HvacKey hvacKey = (HvacKey) o;
|
||||
return mPropertyId == hvacKey.mPropertyId &&
|
||||
mAreaId == hvacKey.mAreaId;
|
||||
return mPropertyId == hvacKey.mPropertyId
|
||||
&& mAreaId == hvacKey.mAreaId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,7 +40,7 @@ public class TemperatureTextView extends TextView implements TemperatureView {
|
||||
public TemperatureTextView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView);
|
||||
mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId,-1);
|
||||
mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId, -1);
|
||||
mPropertyId = typedArray.getInt(R.styleable.TemperatureView_hvacPropertyId, -1);
|
||||
String format = typedArray.getString(R.styleable.TemperatureView_hvacTempFormat);
|
||||
mTempFormat = (format == null) ? "%.1f\u00B0" : format;
|
||||
@@ -48,6 +48,7 @@ public class TemperatureTextView extends TextView implements TemperatureView {
|
||||
|
||||
/**
|
||||
* Formats the float for display
|
||||
*
|
||||
* @param temp - The current temp or NaN
|
||||
*/
|
||||
@Override
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.systemui.volume;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.systemui.SystemUI;
|
||||
import com.android.systemui.plugins.VolumeDialog;
|
||||
|
||||
/**
|
||||
* Allows for adding car specific dialog when the volume dialog is created.
|
||||
*/
|
||||
public class CarVolumeDialogComponent extends VolumeDialogComponent {
|
||||
|
||||
public CarVolumeDialogComponent(SystemUI sysui, Context context) {
|
||||
super(sysui, context);
|
||||
}
|
||||
|
||||
protected VolumeDialog createDefault() {
|
||||
return new CarVolumeDialogImpl(mContext);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.systemui.volume;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorInflater;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.annotation.DrawableRes;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Dialog;
|
||||
import android.app.KeyguardManager;
|
||||
import android.car.Car;
|
||||
import android.car.CarNotConnectedException;
|
||||
import android.car.media.CarAudioManager;
|
||||
import android.car.media.ICarVolumeCallback;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Debug;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.util.Xml;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
|
||||
import androidx.car.widget.ListItem;
|
||||
import androidx.car.widget.ListItemAdapter;
|
||||
import androidx.car.widget.ListItemAdapter.BackgroundStyle;
|
||||
import androidx.car.widget.ListItemProvider.ListProvider;
|
||||
import androidx.car.widget.PagedListView;
|
||||
import androidx.car.widget.SeekbarListItem;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.VolumeDialog;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Car version of the volume dialog.
|
||||
*
|
||||
* Methods ending in "H" must be called on the (ui) handler.
|
||||
*/
|
||||
public class CarVolumeDialogImpl implements VolumeDialog {
|
||||
|
||||
private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
|
||||
|
||||
private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
|
||||
private static final String XML_TAG_VOLUME_ITEM = "item";
|
||||
private static final int HOVERING_TIMEOUT = 16000;
|
||||
private static final int NORMAL_TIMEOUT = 3000;
|
||||
private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
|
||||
private static final int DISMISS_DELAY_IN_MILLIS = 50;
|
||||
private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
|
||||
|
||||
private final Context mContext;
|
||||
private final H mHandler = new H();
|
||||
// All the volume items.
|
||||
private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>();
|
||||
// Available volume items in car audio manager.
|
||||
private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>();
|
||||
// Volume items in the PagedListView.
|
||||
private final List<ListItem> mVolumeLineItems = new ArrayList<>();
|
||||
private final KeyguardManager mKeyguard;
|
||||
private Window mWindow;
|
||||
private CustomDialog mDialog;
|
||||
private PagedListView mListView;
|
||||
private ListItemAdapter mPagedListAdapter;
|
||||
private Car mCar;
|
||||
private CarAudioManager mCarAudioManager;
|
||||
private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
|
||||
@Override
|
||||
public void onGroupVolumeChanged(int groupId, int flags) {
|
||||
VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
|
||||
int value = getSeekbarValue(mCarAudioManager, groupId);
|
||||
// Do not update the progress if it is the same as before. When car audio manager sets
|
||||
// its group volume caused by the seekbar progress changed, it also triggers this
|
||||
// callback. Updating the seekbar at the same time could block the continuous seeking.
|
||||
if (value != volumeItem.progress) {
|
||||
volumeItem.listItem.setProgress(value);
|
||||
volumeItem.progress = value;
|
||||
}
|
||||
if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
|
||||
mHandler.obtainMessage(H.SHOW, Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMasterMuteChanged(int flags) {
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
private boolean mHovering;
|
||||
private boolean mShowing;
|
||||
private boolean mExpanded;
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
try {
|
||||
mExpanded = false;
|
||||
mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
|
||||
int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
|
||||
// Populates volume slider items from volume groups to UI.
|
||||
for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
|
||||
VolumeItem volumeItem = getVolumeItemForUsages(
|
||||
mCarAudioManager.getUsagesForVolumeGroupId(groupId));
|
||||
mAvailableVolumeItems.add(volumeItem);
|
||||
// The first one is the default item.
|
||||
if (groupId == 0) {
|
||||
volumeItem.defaultItem = true;
|
||||
addSeekbarListItem(volumeItem, groupId,
|
||||
R.drawable.car_ic_keyboard_arrow_down,
|
||||
new ExpandIconListener());
|
||||
}
|
||||
}
|
||||
|
||||
// If list is already initiated, update its content.
|
||||
if (mPagedListAdapter != null) {
|
||||
mPagedListAdapter.notifyDataSetChanged();
|
||||
}
|
||||
mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder());
|
||||
} catch (CarNotConnectedException e) {
|
||||
Log.e(TAG, "Car is not connected!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This does not get called when service is properly disconnected.
|
||||
* So we need to also handle cleanups in destroy().
|
||||
*/
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
cleanupAudioManager();
|
||||
}
|
||||
};
|
||||
|
||||
public CarVolumeDialogImpl(Context context) {
|
||||
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
|
||||
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
|
||||
mCar = Car.createCar(mContext, mServiceConnection);
|
||||
}
|
||||
|
||||
private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
|
||||
try {
|
||||
return carAudioManager.getGroupVolume(volumeGroupId);
|
||||
} catch (CarNotConnectedException e) {
|
||||
Log.e(TAG, "Car is not connected!", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
|
||||
try {
|
||||
return carAudioManager.getGroupMaxVolume(volumeGroupId);
|
||||
} catch (CarNotConnectedException e) {
|
||||
Log.e(TAG, "Car is not connected!", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the volume window and connect to the CarService which registers with car audio
|
||||
* manager.
|
||||
*/
|
||||
@Override
|
||||
public void init(int windowType, Callback callback) {
|
||||
initDialog();
|
||||
|
||||
mCar.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
|
||||
cleanupAudioManager();
|
||||
// unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
|
||||
// audio manager beforehand.
|
||||
mCar.disconnect();
|
||||
}
|
||||
|
||||
private void initDialog() {
|
||||
loadAudioUsageItems();
|
||||
mVolumeLineItems.clear();
|
||||
mDialog = new CustomDialog(mContext);
|
||||
|
||||
mHovering = false;
|
||||
mShowing = false;
|
||||
mExpanded = false;
|
||||
mWindow = mDialog.getWindow();
|
||||
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
|
||||
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
|
||||
mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
|
||||
mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
|
||||
mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
|
||||
final WindowManager.LayoutParams lp = mWindow.getAttributes();
|
||||
lp.format = PixelFormat.TRANSLUCENT;
|
||||
lp.setTitle(VolumeDialogImpl.class.getSimpleName());
|
||||
lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
|
||||
lp.windowAnimations = -1;
|
||||
mWindow.setAttributes(lp);
|
||||
mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
mDialog.setCanceledOnTouchOutside(true);
|
||||
mDialog.setContentView(R.layout.car_volume_dialog);
|
||||
mDialog.setOnShowListener(dialog -> {
|
||||
mListView.setTranslationY(-mListView.getHeight());
|
||||
mListView.setAlpha(0);
|
||||
mListView.animate()
|
||||
.alpha(1)
|
||||
.translationY(0)
|
||||
.setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
|
||||
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
|
||||
.start();
|
||||
});
|
||||
mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
|
||||
mListView.setOnHoverListener((v, event) -> {
|
||||
int action = event.getActionMasked();
|
||||
mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
|
||||
|| (action == MotionEvent.ACTION_HOVER_MOVE);
|
||||
rescheduleTimeoutH();
|
||||
return true;
|
||||
});
|
||||
|
||||
mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
|
||||
BackgroundStyle.PANEL);
|
||||
mListView.setAdapter(mPagedListAdapter);
|
||||
mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
|
||||
}
|
||||
|
||||
|
||||
private void showH(int reason) {
|
||||
if (D.BUG) {
|
||||
Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
|
||||
}
|
||||
|
||||
mHandler.removeMessages(H.SHOW);
|
||||
mHandler.removeMessages(H.DISMISS);
|
||||
rescheduleTimeoutH();
|
||||
// Refresh the data set before showing.
|
||||
mPagedListAdapter.notifyDataSetChanged();
|
||||
if (mShowing) {
|
||||
return;
|
||||
}
|
||||
mShowing = true;
|
||||
|
||||
mDialog.show();
|
||||
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
|
||||
}
|
||||
|
||||
private void rescheduleTimeoutH() {
|
||||
mHandler.removeMessages(H.DISMISS);
|
||||
final int timeout = computeTimeoutH();
|
||||
mHandler.sendMessageDelayed(mHandler
|
||||
.obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
|
||||
|
||||
if (D.BUG) {
|
||||
Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
|
||||
}
|
||||
}
|
||||
|
||||
private int computeTimeoutH() {
|
||||
return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
|
||||
}
|
||||
|
||||
private void dismissH(int reason) {
|
||||
if (D.BUG) {
|
||||
Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
|
||||
}
|
||||
|
||||
mHandler.removeMessages(H.DISMISS);
|
||||
mHandler.removeMessages(H.SHOW);
|
||||
if (!mShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
mListView.animate().cancel();
|
||||
|
||||
mListView.setTranslationY(0);
|
||||
mListView.setAlpha(1);
|
||||
mListView.animate()
|
||||
.alpha(0)
|
||||
.translationY(-mListView.getHeight())
|
||||
.setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
|
||||
.setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
|
||||
.withEndAction(() -> mHandler.postDelayed(() -> {
|
||||
if (D.BUG) {
|
||||
Log.d(TAG, "mDialog.dismiss()");
|
||||
}
|
||||
mDialog.dismiss();
|
||||
mShowing = false;
|
||||
}, DISMISS_DELAY_IN_MILLIS))
|
||||
.start();
|
||||
|
||||
Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
|
||||
}
|
||||
|
||||
private void loadAudioUsageItems() {
|
||||
try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) {
|
||||
AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
int type;
|
||||
// Traverse to the first start tag
|
||||
while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT
|
||||
&& type != XmlResourceParser.START_TAG) {
|
||||
// Do Nothing (moving parser to start element)
|
||||
}
|
||||
|
||||
if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) {
|
||||
throw new RuntimeException("Meta-data does not start with carVolumeItems tag");
|
||||
}
|
||||
int outerDepth = parser.getDepth();
|
||||
int rank = 0;
|
||||
while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT
|
||||
&& (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlResourceParser.END_TAG) {
|
||||
continue;
|
||||
}
|
||||
if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
|
||||
TypedArray item = mContext.getResources().obtainAttributes(
|
||||
attrs, R.styleable.carVolumeItems_item);
|
||||
int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
|
||||
if (usage >= 0) {
|
||||
VolumeItem volumeItem = new VolumeItem();
|
||||
volumeItem.rank = rank;
|
||||
volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon,
|
||||
0);
|
||||
mVolumeItems.put(usage, volumeItem);
|
||||
rank++;
|
||||
}
|
||||
item.recycle();
|
||||
}
|
||||
}
|
||||
} catch (XmlPullParserException | IOException e) {
|
||||
Log.e(TAG, "Error parsing volume groups configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
private VolumeItem getVolumeItemForUsages(int[] usages) {
|
||||
int rank = Integer.MAX_VALUE;
|
||||
VolumeItem result = null;
|
||||
for (int usage : usages) {
|
||||
VolumeItem volumeItem = mVolumeItems.get(usage);
|
||||
if (volumeItem.rank < rank) {
|
||||
rank = volumeItem.rank;
|
||||
result = volumeItem;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem,
|
||||
int volumeGroupId,
|
||||
int supplementalIconId,
|
||||
@Nullable View.OnClickListener supplementalIconOnClickListener) {
|
||||
SeekbarListItem listItem = new SeekbarListItem(mContext);
|
||||
listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
|
||||
int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
|
||||
int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
|
||||
listItem.setProgress(progress);
|
||||
listItem.setOnSeekBarChangeListener(new CarVolumeDialogImpl
|
||||
.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
|
||||
Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
|
||||
primaryIcon.mutate().setTint(color);
|
||||
listItem.setPrimaryActionIcon(primaryIcon);
|
||||
if (supplementalIconId != 0) {
|
||||
Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
|
||||
supplementalIcon.mutate().setTint(color);
|
||||
listItem.setSupplementalIcon(supplementalIcon, true);
|
||||
listItem.setSupplementalIconListener(supplementalIconOnClickListener);
|
||||
} else {
|
||||
listItem.setSupplementalEmptyIcon(true);
|
||||
listItem.setSupplementalIconListener(null);
|
||||
}
|
||||
|
||||
mVolumeLineItems.add(listItem);
|
||||
volumeItem.listItem = listItem;
|
||||
volumeItem.progress = progress;
|
||||
return listItem;
|
||||
}
|
||||
|
||||
private VolumeItem findVolumeItem(SeekbarListItem targetItem) {
|
||||
for (int i = 0; i < mVolumeItems.size(); ++i) {
|
||||
VolumeItem volumeItem = mVolumeItems.valueAt(i);
|
||||
if (volumeItem.listItem == targetItem) {
|
||||
return volumeItem;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void cleanupAudioManager() {
|
||||
try {
|
||||
mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
|
||||
} catch (CarNotConnectedException e) {
|
||||
Log.e(TAG, "Car is not connected!", e);
|
||||
}
|
||||
mVolumeLineItems.clear();
|
||||
mCarAudioManager = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class which contains information of each volume group.
|
||||
*/
|
||||
private static class VolumeItem {
|
||||
|
||||
private int rank;
|
||||
private boolean defaultItem = false;
|
||||
private @DrawableRes int icon;
|
||||
private SeekbarListItem listItem;
|
||||
private int progress;
|
||||
}
|
||||
|
||||
private final class H extends Handler {
|
||||
|
||||
private static final int SHOW = 1;
|
||||
private static final int DISMISS = 2;
|
||||
|
||||
private H() {
|
||||
super(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case SHOW:
|
||||
showH(msg.arg1);
|
||||
break;
|
||||
case DISMISS:
|
||||
dismissH(msg.arg1);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class CustomDialog extends Dialog implements DialogInterface {
|
||||
|
||||
private CustomDialog(Context context) {
|
||||
super(context, com.android.systemui.R.style.qs_theme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
rescheduleTimeoutH();
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.setCanceledOnTouchOutside(true);
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (isShowing()) {
|
||||
if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
|
||||
mHandler.obtainMessage(
|
||||
H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private final class ExpandIconListener implements View.OnClickListener {
|
||||
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
mExpanded = !mExpanded;
|
||||
Animator inAnimator;
|
||||
if (mExpanded) {
|
||||
for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
|
||||
// Adding the items which are not coming from the default item.
|
||||
VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
|
||||
if (volumeItem.defaultItem) {
|
||||
// Set progress here due to the progress of seekbar may not be updated.
|
||||
volumeItem.listItem.setProgress(volumeItem.progress);
|
||||
} else {
|
||||
addSeekbarListItem(volumeItem, groupId, 0, null);
|
||||
}
|
||||
}
|
||||
inAnimator = AnimatorInflater.loadAnimator(
|
||||
mContext, R.anim.car_arrow_fade_in_rotate_up);
|
||||
} else {
|
||||
// Only keeping the default stream if it is not expended.
|
||||
Iterator itr = mVolumeLineItems.iterator();
|
||||
while (itr.hasNext()) {
|
||||
SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
|
||||
VolumeItem volumeItem = findVolumeItem(seekbarListItem);
|
||||
if (!volumeItem.defaultItem) {
|
||||
itr.remove();
|
||||
} else {
|
||||
// Set progress here due to the progress of seekbar may not be updated.
|
||||
seekbarListItem.setProgress(volumeItem.progress);
|
||||
}
|
||||
}
|
||||
inAnimator = AnimatorInflater.loadAnimator(
|
||||
mContext, R.anim.car_arrow_fade_in_rotate_down);
|
||||
}
|
||||
|
||||
Animator outAnimator = AnimatorInflater.loadAnimator(
|
||||
mContext, R.anim.car_arrow_fade_out);
|
||||
inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
|
||||
AnimatorSet animators = new AnimatorSet();
|
||||
animators.playTogether(outAnimator, inAnimator);
|
||||
animators.setTarget(v);
|
||||
animators.start();
|
||||
mPagedListAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
|
||||
|
||||
private final int mVolumeGroupId;
|
||||
private final CarAudioManager mCarAudioManager;
|
||||
|
||||
private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) {
|
||||
mVolumeGroupId = volumeGroupId;
|
||||
mCarAudioManager = carAudioManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (!fromUser) {
|
||||
// For instance, if this event is originated from AudioService,
|
||||
// we can ignore it as it has already been handled and doesn't need to be
|
||||
// sent back down again.
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (mCarAudioManager == null) {
|
||||
Log.w(TAG, "Ignoring volume change event because the car isn't connected");
|
||||
return;
|
||||
}
|
||||
mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
|
||||
mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
|
||||
} catch (CarNotConnectedException e) {
|
||||
Log.e(TAG, "Car is not connected!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<!--
|
||||
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:fillAlpha=".1"
|
||||
android:strokeAlpha=".1"
|
||||
android:pathData="M0 0h48v48H0z" />
|
||||
<path
|
||||
android:fillColor="@color/car_grey_50"
|
||||
android:pathData="M24 2C14.06 2 6 10.06 6 20v14c0 3.31 2.69 6 6 6h6V24h-8v-4c0-7.73 6.27-14
|
||||
14-14s14 6.27 14 14v4h-8v16h6c3.31 0 6-2.69 6-6V20c0-9.94-8.06-18-18-18z" />
|
||||
</vector>
|
||||
@@ -1,28 +0,0 @@
|
||||
<!--
|
||||
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>
|
||||
@@ -1,28 +0,0 @@
|
||||
<!--
|
||||
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>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="?android:attr/colorBackgroundFloating" />
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/car_radius_3"
|
||||
android:topLeftRadius="0dp"
|
||||
android:bottomRightRadius="@dimen/car_radius_3"
|
||||
android:topRightRadius="0dp"
|
||||
/>
|
||||
</shape>
|
||||
@@ -1,98 +0,0 @@
|
||||
<?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:background="@drawable/system_bar_background">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:id="@+id/nav_buttons"
|
||||
android:gravity="left"
|
||||
android:paddingLeft="30dp"
|
||||
android:layout_weight="1"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<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;launchFlags=0x14000000;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.TOGGLE_HVAC_CONTROLS;end"
|
||||
systemui:broadcast="true"
|
||||
android:src="@drawable/car_ic_hvac"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingLeft="30dp"
|
||||
android:paddingRight="30dp"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
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>
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
**
|
||||
** Copyright 2018, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
|
||||
<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:background="@drawable/system_bar_background">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:id="@+id/nav_buttons"
|
||||
android:gravity="left"
|
||||
android:paddingLeft="30dp"
|
||||
android:layout_weight="1"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<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;launchFlags=0x14000000;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.TOGGLE_HVAC_CONTROLS;end"
|
||||
systemui:broadcast="true"
|
||||
android:src="@drawable/car_ic_hvac"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingLeft="30dp"
|
||||
android:paddingRight="30dp"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</com.android.systemui.statusbar.car.CarNavigationBarView>
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 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.
|
||||
-->
|
||||
<!-- Extends LinearLayout -->
|
||||
<com.android.systemui.qs.car.CarStatusBarHeader
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:systemui="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/car_qs_header_system_icons_area_height"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp" >
|
||||
|
||||
<include layout="@layout/system_icons"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical|end"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
|
||||
<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|end"
|
||||
/>
|
||||
</com.android.systemui.qs.car.CarStatusBarHeader>
|
||||
@@ -1,38 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/**
|
||||
** Copyright 2018, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
|
||||
<com.android.systemui.statusbar.car.CarNavigationBarView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@drawable/system_bar_background">
|
||||
|
||||
<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"
|
||||
/>
|
||||
|
||||
</com.android.systemui.statusbar.car.CarNavigationBarView>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<androidx.car.widget.PagedListView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="@drawable/car_card_rounded_background"
|
||||
android:id="@+id/volume_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/car_margin"
|
||||
android:layout_marginEnd="@dimen/car_margin"
|
||||
android:minWidth="@dimen/volume_dialog_panel_width"
|
||||
android:theme="@style/Theme.Car.NoActionBar"
|
||||
app:dividerStartMargin="@dimen/car_keyline_1"
|
||||
app:dividerEndMargin="@dimen/car_keyline_1"
|
||||
app:gutter="none"
|
||||
app:showPagedListViewDivider="true"
|
||||
app:scrollBarEnabled="false" />
|
||||
@@ -56,11 +56,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
|
||||
android:layout="@layout/car_fullscreen_user_switcher"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<include layout="@layout/status_bar_expanded"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
**
|
||||
** Copyright 2015, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
<resources>
|
||||
<!-- These should be overriden in an overlay. The default implementation is empty.
|
||||
There needs to be correspondence per index between these arrays, which means that if there
|
||||
isn't a longpress action associated with a shortcut item, put in an empty item to make
|
||||
sure everything lines up.
|
||||
-->
|
||||
<array name="car_facet_icons" />
|
||||
<array name="car_facet_intent_uris" />
|
||||
<array name="car_facet_longpress_intent_uris" />
|
||||
<array name="car_facet_package_filters"/>
|
||||
<array name="car_facet_category_filters"/>
|
||||
</resources>
|
||||
@@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
*
|
||||
* Copyright 2018, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
-->
|
||||
|
||||
<!--
|
||||
Defines all possible items on car volume settings UI, keyed by usage.
|
||||
|
||||
This enables the CarSettings UI to associate VolumeGroups surfaced by
|
||||
CarAudioManager.getVolumeGroupCount with renderable assets (ie: title, icon)
|
||||
for presentation.
|
||||
|
||||
Order matters in this configuration. If one volume group contains multiple
|
||||
audio usages, the first one appears in this file would be picked to be
|
||||
presented on UI.
|
||||
|
||||
When overriding this configuration, please consult also the
|
||||
car_volume_groups.xml, which is read by car audio service.
|
||||
-->
|
||||
<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto">
|
||||
<item car:usage="unknown"
|
||||
car:icon="@drawable/car_ic_music"/>
|
||||
<item car:usage="media"
|
||||
car:icon="@drawable/car_ic_music"/>
|
||||
<item car:usage="voice_communication"
|
||||
car:icon="@*android:drawable/ic_audio_ring_notif"/>
|
||||
<item car:usage="voice_communication_signalling"
|
||||
car:icon="@*android:drawable/ic_audio_ring_notif"/>
|
||||
<item car:usage="alarm"
|
||||
car:icon="@*android:drawable/ic_audio_alarm"/>
|
||||
<item car:usage="notification"
|
||||
car:icon="@drawable/car_ic_notification"/>
|
||||
<item car:usage="notification_ringtone"
|
||||
car:icon="@*android:drawable/ic_audio_ring_notif"/>
|
||||
<item car:usage="notification_communication_request"
|
||||
car:icon="@drawable/car_ic_notification"/>
|
||||
<item car:usage="notification_communication_instant"
|
||||
car:icon="@drawable/car_ic_notification"/>
|
||||
<item car:usage="notification_communication_delayed"
|
||||
car:icon="@drawable/car_ic_notification"/>
|
||||
<item car:usage="notification_event"
|
||||
car:icon="@drawable/car_ic_notification"/>
|
||||
<item car:usage="assistance_accessibility"
|
||||
car:icon="@drawable/car_ic_notification"/>
|
||||
<item car:usage="assistance_navigation_guidance"
|
||||
car:icon="@drawable/car_ic_navigation"/>
|
||||
<item car:usage="assistance_sonification"
|
||||
car:icon="@drawable/car_ic_notification"/>
|
||||
<item car:usage="game"
|
||||
car:icon="@drawable/car_ic_music"/>
|
||||
<item car:usage="assistant"
|
||||
car:icon="@drawable/car_ic_music"/>
|
||||
</carVolumeItems>
|
||||
|
||||
@@ -62,6 +62,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
|
||||
import com.android.systemui.statusbar.policy.SmartReplyConstants;
|
||||
import com.android.systemui.volume.VolumeDialogComponent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -132,6 +133,10 @@ public class SystemUIFactory {
|
||||
return new QSTileHost(context, statusBar, iconController);
|
||||
}
|
||||
|
||||
public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
|
||||
return new VolumeDialogComponent(systemUi, context);
|
||||
}
|
||||
|
||||
public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
|
||||
Context context) {
|
||||
providers.put(StatusBarStateController.class, StatusBarStateController::new);
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package com.android.systemui.car;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.keyguard.ViewMediatorCallback;
|
||||
import com.android.systemui.Dependency.DependencyProvider;
|
||||
import com.android.systemui.SystemUIFactory;
|
||||
import com.android.systemui.statusbar.NotificationMediaManager;
|
||||
import com.android.systemui.statusbar.car.CarFacetButtonController;
|
||||
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
|
||||
import com.android.systemui.statusbar.car.hvac.HvacController;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
|
||||
/**
|
||||
* Class factory to provide car specific SystemUI components.
|
||||
*/
|
||||
public class CarSystemUIFactory extends SystemUIFactory {
|
||||
|
||||
public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
|
||||
ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
|
||||
return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
|
||||
Context context) {
|
||||
super.injectDependencies(providers, context);
|
||||
providers.put(NotificationEntryManager.class,
|
||||
() -> new CarNotificationEntryManager(context));
|
||||
providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
|
||||
providers.put(HvacController.class, () -> new HvacController(context));
|
||||
providers.put(NotificationMediaManager.class,
|
||||
() -> new CarNotificationMediaManager(context));
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
package com.android.systemui.statusbar.car;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.UserHandle;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.keyguard.AlphaOptimizedImageButton;
|
||||
import com.android.systemui.Dependency;
|
||||
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 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;
|
||||
private String[] mComponentNames;
|
||||
/** App categories that are to be used with this widget */
|
||||
private String[] mFacetCategories;
|
||||
/** App packages that are allowed to be used with this widget */
|
||||
private String[] mFacetPackages;
|
||||
private int mIconResourceId;
|
||||
/**
|
||||
* If defined in the xml this will be the icon that's rendered when the button is marked as
|
||||
* selected
|
||||
*/
|
||||
private int mSelectedIconResourceId;
|
||||
private boolean mUseMoreIcon = true;
|
||||
private float mSelectedAlpha = 1f;
|
||||
private float mUnselectedAlpha = 1f;
|
||||
|
||||
|
||||
public CarFacetButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
View.inflate(context, R.layout.car_facet_button, this);
|
||||
|
||||
// extract custom attributes
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
|
||||
setupIntents(typedArray);
|
||||
setupIcons(typedArray);
|
||||
CarFacetButtonController carFacetButtonController = Dependency.get(
|
||||
CarFacetButtonController.class);
|
||||
carFacetButtonController.addFacetButton(this);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
String componentNameString =
|
||||
typedArray.getString(R.styleable.CarFacetButton_componentNames);
|
||||
try {
|
||||
final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
|
||||
intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
|
||||
|
||||
if (packageString != null) {
|
||||
mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
|
||||
intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
|
||||
}
|
||||
if (categoryString != null) {
|
||||
mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
|
||||
intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
|
||||
}
|
||||
if (componentNameString != null) {
|
||||
mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER);
|
||||
}
|
||||
|
||||
setOnClickListener(v -> {
|
||||
intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
|
||||
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
|
||||
});
|
||||
|
||||
if (longPressIntentString != null) {
|
||||
final Intent longPressIntent = Intent.parseUri(longPressIntentString,
|
||||
Intent.URI_INTENT_SCHEME);
|
||||
setOnLongClickListener(v -> {
|
||||
mContext.startActivityAsUser(longPressIntent, UserHandle.CURRENT);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to attach intent", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setupIcons(TypedArray styledAttributes) {
|
||||
mSelectedAlpha = styledAttributes.getFloat(
|
||||
R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
|
||||
mUnselectedAlpha = styledAttributes.getFloat(
|
||||
R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha);
|
||||
mIcon = findViewById(R.id.car_nav_button_icon);
|
||||
mIcon.setScaleType(ImageView.ScaleType.CENTER);
|
||||
mIcon.setClickable(false);
|
||||
mIcon.setAlpha(mUnselectedAlpha);
|
||||
mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
|
||||
mIcon.setImageResource(mIconResourceId);
|
||||
mSelectedIconResourceId = styledAttributes.getResourceId(
|
||||
R.styleable.CarFacetButton_selectedIcon, mIconResourceId);
|
||||
|
||||
mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
|
||||
mMoreIcon.setClickable(false);
|
||||
mMoreIcon.setAlpha(mSelectedAlpha);
|
||||
mMoreIcon.setVisibility(GONE);
|
||||
mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The app categories the component represents
|
||||
*/
|
||||
public String[] getCategories() {
|
||||
if (mFacetCategories == null) {
|
||||
return new String[0];
|
||||
}
|
||||
return mFacetCategories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The valid packages that should be considered.
|
||||
*/
|
||||
public String[] getFacetPackages() {
|
||||
if (mFacetPackages == null) {
|
||||
return new String[0];
|
||||
}
|
||||
return mFacetPackages;
|
||||
}
|
||||
|
||||
public String[] getComponentName() {
|
||||
if (mComponentNames == null) {
|
||||
return new String[0];
|
||||
}
|
||||
return mComponentNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the alpha of the icons to "selected" and shows the "More icon"
|
||||
* @param selected true if the view must be selected, false otherwise
|
||||
*/
|
||||
public void setSelected(boolean selected) {
|
||||
super.setSelected(selected);
|
||||
setSelected(selected, selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the visual state to let the user know if it's been selected.
|
||||
* @param selected true if should update the alpha of the icon to selected, false otherwise
|
||||
* @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
|
||||
* is ignored if the attribute useMoreIcon is set to false
|
||||
*/
|
||||
public void setSelected(boolean selected, boolean showMoreIcon) {
|
||||
mSelected = selected;
|
||||
mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
|
||||
mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
|
||||
if (mUseMoreIcon) {
|
||||
mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,6 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.Snoo
|
||||
import com.android.systemui.qs.QSFragment;
|
||||
import com.android.systemui.qs.QSPanel;
|
||||
import com.android.systemui.qs.QSTileHost;
|
||||
import com.android.systemui.qs.car.CarQSFragment;
|
||||
import com.android.systemui.recents.Recents;
|
||||
import com.android.systemui.recents.ScreenPinningRequest;
|
||||
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
@@ -918,8 +917,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
Dependency.get(ExtensionController.class)
|
||||
.newExtension(QS.class)
|
||||
.withPlugin(QS.class)
|
||||
.withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new)
|
||||
.withDefault(QSFragment::new)
|
||||
.withDefault(this::createDefaultQSFragment)
|
||||
.build());
|
||||
final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
|
||||
mIconController);
|
||||
@@ -1007,6 +1005,10 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
|
||||
}
|
||||
|
||||
protected QS createDefaultQSFragment() {
|
||||
return new QSFragment();
|
||||
}
|
||||
|
||||
protected void setUpPresenter() {
|
||||
// Set up the initial notification state.
|
||||
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
|
||||
|
||||
@@ -1,605 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.systemui.volume;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorInflater;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.annotation.DrawableRes;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Dialog;
|
||||
import android.app.KeyguardManager;
|
||||
import android.car.Car;
|
||||
import android.car.CarNotConnectedException;
|
||||
import android.car.media.CarAudioManager;
|
||||
import android.car.media.ICarVolumeCallback;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Debug;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.util.Xml;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
|
||||
import androidx.car.widget.ListItem;
|
||||
import androidx.car.widget.ListItemAdapter;
|
||||
import androidx.car.widget.ListItemAdapter.BackgroundStyle;
|
||||
import androidx.car.widget.ListItemProvider.ListProvider;
|
||||
import androidx.car.widget.PagedListView;
|
||||
import androidx.car.widget.SeekbarListItem;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.VolumeDialog;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Car version of the volume dialog.
|
||||
*
|
||||
* Methods ending in "H" must be called on the (ui) handler.
|
||||
*/
|
||||
public class CarVolumeDialogImpl implements VolumeDialog {
|
||||
private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
|
||||
|
||||
private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
|
||||
private static final String XML_TAG_VOLUME_ITEM = "item";
|
||||
private static final int HOVERING_TIMEOUT = 16000;
|
||||
private static final int NORMAL_TIMEOUT = 3000;
|
||||
private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
|
||||
private static final int DISMISS_DELAY_IN_MILLIS = 50;
|
||||
private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
|
||||
|
||||
private final Context mContext;
|
||||
private final H mHandler = new H();
|
||||
|
||||
private Window mWindow;
|
||||
private CustomDialog mDialog;
|
||||
private PagedListView mListView;
|
||||
private ListItemAdapter mPagedListAdapter;
|
||||
// All the volume items.
|
||||
private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>();
|
||||
// Available volume items in car audio manager.
|
||||
private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>();
|
||||
// Volume items in the PagedListView.
|
||||
private final List<ListItem> mVolumeLineItems = new ArrayList<>();
|
||||
private final KeyguardManager mKeyguard;
|
||||
|
||||
private Car mCar;
|
||||
private CarAudioManager mCarAudioManager;
|
||||
|
||||
private boolean mHovering;
|
||||
private boolean mShowing;
|
||||
private boolean mExpanded;
|
||||
|
||||
public CarVolumeDialogImpl(Context context) {
|
||||
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
|
||||
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
|
||||
mCar = Car.createCar(mContext, mServiceConnection);
|
||||
}
|
||||
|
||||
public void init(int windowType, Callback callback) {
|
||||
initDialog();
|
||||
|
||||
mCar.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
|
||||
cleanupAudioManager();
|
||||
// unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
|
||||
// audio manager beforehand.
|
||||
mCar.disconnect();
|
||||
}
|
||||
|
||||
private void initDialog() {
|
||||
loadAudioUsageItems();
|
||||
mVolumeLineItems.clear();
|
||||
mDialog = new CustomDialog(mContext);
|
||||
|
||||
mHovering = false;
|
||||
mShowing = false;
|
||||
mExpanded = false;
|
||||
mWindow = mDialog.getWindow();
|
||||
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
|
||||
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
|
||||
mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
|
||||
mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
|
||||
mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
|
||||
final WindowManager.LayoutParams lp = mWindow.getAttributes();
|
||||
lp.format = PixelFormat.TRANSLUCENT;
|
||||
lp.setTitle(VolumeDialogImpl.class.getSimpleName());
|
||||
lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
|
||||
lp.windowAnimations = -1;
|
||||
mWindow.setAttributes(lp);
|
||||
mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
mDialog.setCanceledOnTouchOutside(true);
|
||||
mDialog.setContentView(R.layout.car_volume_dialog);
|
||||
mDialog.setOnShowListener(dialog -> {
|
||||
mListView.setTranslationY(-mListView.getHeight());
|
||||
mListView.setAlpha(0);
|
||||
mListView.animate()
|
||||
.alpha(1)
|
||||
.translationY(0)
|
||||
.setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
|
||||
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
|
||||
.start();
|
||||
});
|
||||
mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
|
||||
mListView.setOnHoverListener((v, event) -> {
|
||||
int action = event.getActionMasked();
|
||||
mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
|
||||
|| (action == MotionEvent.ACTION_HOVER_MOVE);
|
||||
rescheduleTimeoutH();
|
||||
return true;
|
||||
});
|
||||
|
||||
mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
|
||||
BackgroundStyle.PANEL);
|
||||
mListView.setAdapter(mPagedListAdapter);
|
||||
mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
|
||||
}
|
||||
|
||||
public void show(int reason) {
|
||||
mHandler.obtainMessage(H.SHOW, reason).sendToTarget();
|
||||
}
|
||||
|
||||
public void dismiss(int reason) {
|
||||
mHandler.obtainMessage(H.DISMISS, reason).sendToTarget();
|
||||
}
|
||||
|
||||
private void showH(int reason) {
|
||||
if (D.BUG) {
|
||||
Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
|
||||
}
|
||||
|
||||
mHandler.removeMessages(H.SHOW);
|
||||
mHandler.removeMessages(H.DISMISS);
|
||||
rescheduleTimeoutH();
|
||||
// Refresh the data set before showing.
|
||||
mPagedListAdapter.notifyDataSetChanged();
|
||||
if (mShowing) {
|
||||
return;
|
||||
}
|
||||
mShowing = true;
|
||||
|
||||
mDialog.show();
|
||||
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
|
||||
}
|
||||
|
||||
protected void rescheduleTimeoutH() {
|
||||
mHandler.removeMessages(H.DISMISS);
|
||||
final int timeout = computeTimeoutH();
|
||||
mHandler.sendMessageDelayed(mHandler
|
||||
.obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
|
||||
|
||||
if (D.BUG) {
|
||||
Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
|
||||
}
|
||||
}
|
||||
|
||||
private int computeTimeoutH() {
|
||||
return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
|
||||
}
|
||||
|
||||
protected void dismissH(int reason) {
|
||||
if (D.BUG) {
|
||||
Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
|
||||
}
|
||||
|
||||
mHandler.removeMessages(H.DISMISS);
|
||||
mHandler.removeMessages(H.SHOW);
|
||||
if (!mShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
mListView.animate().cancel();
|
||||
|
||||
mListView.setTranslationY(0);
|
||||
mListView.setAlpha(1);
|
||||
mListView.animate()
|
||||
.alpha(0)
|
||||
.translationY(-mListView.getHeight())
|
||||
.setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
|
||||
.setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
|
||||
.withEndAction(() -> mHandler.postDelayed(() -> {
|
||||
if (D.BUG) {
|
||||
Log.d(TAG, "mDialog.dismiss()");
|
||||
}
|
||||
mDialog.dismiss();
|
||||
mShowing = false;
|
||||
}, DISMISS_DELAY_IN_MILLIS))
|
||||
.start();
|
||||
|
||||
Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
|
||||
}
|
||||
|
||||
public void dump(PrintWriter writer) {
|
||||
writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
|
||||
writer.print(" mShowing: "); writer.println(mShowing);
|
||||
}
|
||||
|
||||
private void loadAudioUsageItems() {
|
||||
try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) {
|
||||
AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
int type;
|
||||
// Traverse to the first start tag
|
||||
while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
|
||||
&& type != XmlResourceParser.START_TAG) {
|
||||
}
|
||||
|
||||
if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) {
|
||||
throw new RuntimeException("Meta-data does not start with carVolumeItems tag");
|
||||
}
|
||||
int outerDepth = parser.getDepth();
|
||||
int rank = 0;
|
||||
while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
|
||||
&& (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlResourceParser.END_TAG) {
|
||||
continue;
|
||||
}
|
||||
if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
|
||||
TypedArray item = mContext.getResources().obtainAttributes(
|
||||
attrs, R.styleable.carVolumeItems_item);
|
||||
int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
|
||||
if (usage >= 0) {
|
||||
VolumeItem volumeItem = new VolumeItem();
|
||||
volumeItem.usage = usage;
|
||||
volumeItem.rank = rank;
|
||||
volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon, 0);
|
||||
mVolumeItems.put(usage, volumeItem);
|
||||
rank++;
|
||||
}
|
||||
item.recycle();
|
||||
}
|
||||
}
|
||||
} catch (XmlPullParserException | IOException e) {
|
||||
Log.e(TAG, "Error parsing volume groups configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
private VolumeItem getVolumeItemForUsages(int[] usages) {
|
||||
int rank = Integer.MAX_VALUE;
|
||||
VolumeItem result = null;
|
||||
for (int usage : usages) {
|
||||
VolumeItem volumeItem = mVolumeItems.get(usage);
|
||||
if (volumeItem.rank < rank) {
|
||||
rank = volumeItem.rank;
|
||||
result = volumeItem;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
|
||||
try {
|
||||
return carAudioManager.getGroupVolume(volumeGroupId);
|
||||
} catch (CarNotConnectedException e) {
|
||||
Log.e(TAG, "Car is not connected!", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
|
||||
try {
|
||||
return carAudioManager.getGroupMaxVolume(volumeGroupId);
|
||||
} catch (CarNotConnectedException e) {
|
||||
Log.e(TAG, "Car is not connected!", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem, int volumeGroupId,
|
||||
int supplementalIconId, @Nullable View.OnClickListener supplementalIconOnClickListener) {
|
||||
SeekbarListItem listItem = new SeekbarListItem(mContext);
|
||||
listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
|
||||
int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
|
||||
int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
|
||||
listItem.setProgress(progress);
|
||||
listItem.setOnSeekBarChangeListener(
|
||||
new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
|
||||
Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
|
||||
primaryIcon.mutate().setTint(color);
|
||||
listItem.setPrimaryActionIcon(primaryIcon);
|
||||
if (supplementalIconId != 0) {
|
||||
Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
|
||||
supplementalIcon.mutate().setTint(color);
|
||||
listItem.setSupplementalIcon(supplementalIcon, true);
|
||||
listItem.setSupplementalIconListener(supplementalIconOnClickListener);
|
||||
} else {
|
||||
listItem.setSupplementalEmptyIcon(true);
|
||||
listItem.setSupplementalIconListener(null);
|
||||
}
|
||||
|
||||
mVolumeLineItems.add(listItem);
|
||||
volumeItem.listItem = listItem;
|
||||
volumeItem.progress = progress;
|
||||
return listItem;
|
||||
}
|
||||
|
||||
private VolumeItem findVolumeItem(SeekbarListItem targetItem) {
|
||||
for (int i = 0; i < mVolumeItems.size(); ++i) {
|
||||
VolumeItem volumeItem = mVolumeItems.valueAt(i);
|
||||
if (volumeItem.listItem == targetItem) {
|
||||
return volumeItem;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void cleanupAudioManager() {
|
||||
try {
|
||||
mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
|
||||
} catch (CarNotConnectedException e) {
|
||||
Log.e(TAG, "Car is not connected!", e);
|
||||
}
|
||||
mVolumeLineItems.clear();
|
||||
mCarAudioManager = null;
|
||||
}
|
||||
|
||||
private final class H extends Handler {
|
||||
private static final int SHOW = 1;
|
||||
private static final int DISMISS = 2;
|
||||
|
||||
public H() {
|
||||
super(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case SHOW:
|
||||
showH(msg.arg1);
|
||||
break;
|
||||
case DISMISS:
|
||||
dismissH(msg.arg1);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class CustomDialog extends Dialog implements DialogInterface {
|
||||
public CustomDialog(Context context) {
|
||||
super(context, com.android.systemui.R.style.qs_theme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
rescheduleTimeoutH();
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.setCanceledOnTouchOutside(true);
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (isShowing()) {
|
||||
if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
|
||||
mHandler.obtainMessage(
|
||||
H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private final class ExpandIconListener implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
mExpanded = !mExpanded;
|
||||
Animator inAnimator;
|
||||
if (mExpanded) {
|
||||
for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
|
||||
// Adding the items which are not coming from the default item.
|
||||
VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
|
||||
if (volumeItem.defaultItem) {
|
||||
// Set progress here due to the progress of seekbar may not be updated.
|
||||
volumeItem.listItem.setProgress(volumeItem.progress);
|
||||
} else {
|
||||
addSeekbarListItem(volumeItem, groupId, 0, null);
|
||||
}
|
||||
}
|
||||
inAnimator = AnimatorInflater.loadAnimator(
|
||||
mContext, R.anim.car_arrow_fade_in_rotate_up);
|
||||
} else {
|
||||
// Only keeping the default stream if it is not expended.
|
||||
Iterator itr = mVolumeLineItems.iterator();
|
||||
while (itr.hasNext()) {
|
||||
SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
|
||||
VolumeItem volumeItem = findVolumeItem(seekbarListItem);
|
||||
if (!volumeItem.defaultItem) {
|
||||
itr.remove();
|
||||
} else {
|
||||
// Set progress here due to the progress of seekbar may not be updated.
|
||||
seekbarListItem.setProgress(volumeItem.progress);
|
||||
}
|
||||
}
|
||||
inAnimator = AnimatorInflater.loadAnimator(
|
||||
mContext, R.anim.car_arrow_fade_in_rotate_down);
|
||||
}
|
||||
|
||||
Animator outAnimator = AnimatorInflater.loadAnimator(
|
||||
mContext, R.anim.car_arrow_fade_out);
|
||||
inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
|
||||
AnimatorSet animators = new AnimatorSet();
|
||||
animators.playTogether(outAnimator, inAnimator);
|
||||
animators.setTarget(v);
|
||||
animators.start();
|
||||
mPagedListAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
|
||||
private final int mVolumeGroupId;
|
||||
private final CarAudioManager mCarAudioManager;
|
||||
|
||||
private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) {
|
||||
mVolumeGroupId = volumeGroupId;
|
||||
mCarAudioManager = carAudioManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (!fromUser) {
|
||||
// For instance, if this event is originated from AudioService,
|
||||
// we can ignore it as it has already been handled and doesn't need to be
|
||||
// sent back down again.
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (mCarAudioManager == null) {
|
||||
Log.w(TAG, "Ignoring volume change event because the car isn't connected");
|
||||
return;
|
||||
}
|
||||
mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
|
||||
mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
|
||||
} catch (CarNotConnectedException e) {
|
||||
Log.e(TAG, "Car is not connected!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {}
|
||||
}
|
||||
|
||||
private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
|
||||
@Override
|
||||
public void onGroupVolumeChanged(int groupId, int flags) {
|
||||
VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
|
||||
int value = getSeekbarValue(mCarAudioManager, groupId);
|
||||
// Do not update the progress if it is the same as before. When car audio manager sets its
|
||||
// group volume caused by the seekbar progress changed, it also triggers this callback.
|
||||
// Updating the seekbar at the same time could block the continuous seeking.
|
||||
if (value != volumeItem.progress) {
|
||||
volumeItem.listItem.setProgress(value);
|
||||
volumeItem.progress = value;
|
||||
}
|
||||
if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
|
||||
show(Events.SHOW_REASON_VOLUME_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMasterMuteChanged(int flags) {
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
try {
|
||||
mExpanded = false;
|
||||
mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
|
||||
int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
|
||||
// Populates volume slider items from volume groups to UI.
|
||||
for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
|
||||
VolumeItem volumeItem = getVolumeItemForUsages(
|
||||
mCarAudioManager.getUsagesForVolumeGroupId(groupId));
|
||||
mAvailableVolumeItems.add(volumeItem);
|
||||
// The first one is the default item.
|
||||
if (groupId == 0) {
|
||||
volumeItem.defaultItem = true;
|
||||
addSeekbarListItem(volumeItem, groupId, R.drawable.car_ic_keyboard_arrow_down,
|
||||
new ExpandIconListener());
|
||||
}
|
||||
}
|
||||
|
||||
// If list is already initiated, update its content.
|
||||
if (mPagedListAdapter != null) {
|
||||
mPagedListAdapter.notifyDataSetChanged();
|
||||
}
|
||||
mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder());
|
||||
} catch (CarNotConnectedException e) {
|
||||
Log.e(TAG, "Car is not connected!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This does not get called when service is properly disconnected.
|
||||
* So we need to also handle cleanups in destroy().
|
||||
*/
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
cleanupAudioManager();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper class which contains information of each volume group.
|
||||
*/
|
||||
private static class VolumeItem {
|
||||
private @AudioAttributes.AttributeUsage int usage;
|
||||
private int rank;
|
||||
private boolean defaultItem = false;
|
||||
private @DrawableRes int icon;
|
||||
private SeekbarListItem listItem;
|
||||
private int progress;
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,10 @@ package com.android.systemui.volume;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.media.AudioManager;
|
||||
import android.media.VolumePolicy;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
|
||||
import com.android.settingslib.applications.InterestingConfigChanges;
|
||||
@@ -57,7 +55,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
|
||||
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
|
||||
|
||||
private final SystemUI mSysui;
|
||||
private final Context mContext;
|
||||
protected final Context mContext;
|
||||
private final VolumeDialogControllerImpl mController;
|
||||
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
|
||||
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
|
||||
@@ -70,7 +68,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
|
||||
400 // vibrateToSilentDebounce
|
||||
);
|
||||
|
||||
public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler) {
|
||||
public VolumeDialogComponent(SystemUI sysui, Context context) {
|
||||
mSysui = sysui;
|
||||
mContext = context;
|
||||
mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class);
|
||||
@@ -81,7 +79,6 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
|
||||
Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
|
||||
.withPlugin(VolumeDialog.class)
|
||||
.withDefault(this::createDefault)
|
||||
.withFeature(PackageManager.FEATURE_AUTOMOTIVE, this::createCarDefault)
|
||||
.withCallback(dialog -> {
|
||||
if (mDialog != null) {
|
||||
mDialog.destroy();
|
||||
@@ -94,7 +91,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
|
||||
VOLUME_SILENT_DO_NOT_DISTURB);
|
||||
}
|
||||
|
||||
private VolumeDialog createDefault() {
|
||||
protected VolumeDialog createDefault() {
|
||||
VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
|
||||
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
|
||||
impl.setAutomute(true);
|
||||
@@ -102,10 +99,6 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
|
||||
return impl;
|
||||
}
|
||||
|
||||
private VolumeDialog createCarDefault() {
|
||||
return new CarVolumeDialogImpl(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTuningChanged(String key, String newValue) {
|
||||
if (VOLUME_DOWN_SILENT.equals(key)) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.util.Log;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SystemUI;
|
||||
import com.android.systemui.SystemUIFactory;
|
||||
import com.android.systemui.qs.tiles.DndTile;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -43,7 +44,9 @@ public class VolumeUI extends SystemUI {
|
||||
mContext.getResources().getBoolean(R.bool.enable_safety_warning);
|
||||
mEnabled = enableVolumeUi || enableSafetyWarning;
|
||||
if (!mEnabled) return;
|
||||
mVolumeComponent = new VolumeDialogComponent(this, mContext, null);
|
||||
|
||||
mVolumeComponent = SystemUIFactory.getInstance()
|
||||
.createVolumeDialogComponent(this, mContext);
|
||||
mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
|
||||
putComponent(VolumeComponent.class, getVolumeComponent());
|
||||
setDefaultVolumeController();
|
||||
|
||||
@@ -38,8 +38,6 @@ LOCAL_JAVA_LIBRARIES := \
|
||||
android.test.runner \
|
||||
telephony-common \
|
||||
android.test.base \
|
||||
android.car \
|
||||
android.car.userlib
|
||||
|
||||
LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard
|
||||
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
package com.android.systemui.qs.car;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.LayoutInflaterBuilder;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.keyguard.CarrierText;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.SysuiBaseFragmentTest;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
import com.android.systemui.statusbar.policy.Clock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Tests for {@link CarQSFragment}.
|
||||
*/
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
@Ignore
|
||||
public class CarQsFragmentTest extends SysuiBaseFragmentTest {
|
||||
public CarQsFragmentTest() {
|
||||
super(CarQSFragment.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void initDependencies() {
|
||||
mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
|
||||
new LayoutInflaterBuilder(mContext)
|
||||
.replace("com.android.systemui.statusbar.policy.SplitClockView",
|
||||
FrameLayout.class)
|
||||
.replace("TextClock", View.class)
|
||||
.replace(CarrierText.class, View.class)
|
||||
.replace(Clock.class, View.class)
|
||||
.build());
|
||||
mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
|
||||
mDependency.injectTestDependency(Dependency.BG_LOOPER,
|
||||
TestableLooper.get(this).getLooper());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Flaky")
|
||||
public void testLayoutInflation() {
|
||||
CarQSFragment fragment = (CarQSFragment) mFragment;
|
||||
mFragments.dispatchResume();
|
||||
|
||||
assertNotNull(fragment.getHeader());
|
||||
assertNotNull(fragment.getFooter());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Flaky")
|
||||
public void testListening() {
|
||||
CarQSFragment qs = (CarQSFragment) mFragment;
|
||||
mFragments.dispatchResume();
|
||||
processAllMessages();
|
||||
|
||||
qs.setListening(true);
|
||||
processAllMessages();
|
||||
|
||||
qs.setListening(false);
|
||||
processAllMessages();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user