Move car related code from SystemUI to CarSystemUI

Test: Emulator phone and Car
Change-Id: Ia64a23c1d3643899118e578b82c665c034af1c8e
This commit is contained in:
Brad Stenning
2018-11-20 17:34:16 -08:00
parent f660c012b0
commit 8d1a51c4da
79 changed files with 1028 additions and 1673 deletions

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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>

View File

@@ -0,0 +1,28 @@
<!--
Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="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>

View File

@@ -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"

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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"

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}
}
};
}

View File

@@ -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 {
});
}
}
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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) {
}
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);

View File

@@ -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));
}
}

View File

@@ -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);
}
}
}

View File

@@ -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,

View File

@@ -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;
}
}

View File

@@ -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)) {

View File

@@ -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();

View File

@@ -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

View File

@@ -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();
}
}