Loading packages/SystemUI/AndroidManifest.xml +3 −0 Original line number Diff line number Diff line Loading @@ -209,6 +209,9 @@ <!-- to read and change hvac values in a car --> <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" /> <!-- to be able to detect the driving state in a car--> <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" /> <!-- Permission necessary to change car audio volume through CarAudioManager --> <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" /> Loading packages/SystemUI/res/values/integers_car.xml +5 −0 Original line number Diff line number Diff line Loading @@ -19,4 +19,9 @@ <integer name="car_user_switcher_anim_cascade_delay_ms">27</integer> <!-- Full screen user switcher column number TODO: move to support library--> <integer name="user_fullscreen_switcher_num_col">3</integer> <!-- Number of milliseconds user can spend driving with the keyguard up. After that, we switch to Guest. --> <!-- If the number is negative, the feature is disabled. If it's zero, we switch to guest immediately as we start driving. --> <integer name="driving_on_keyguard_timeout_ms">30000</integer> </resources> packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +27 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.car; import android.app.ActivityTaskManager; import android.car.drivingstate.CarDrivingStateEvent; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; import android.util.Log; Loading Loading @@ -82,6 +83,8 @@ public class CarStatusBar extends StatusBar implements private DeviceProvisionedController mDeviceProvisionedController; private boolean mDeviceIsProvisioned = true; private HvacController mHvacController; private DrivingStateHelper mDrivingStateHelper; private SwitchToGuestTimer mSwitchToGuestTimer; @Override public void start() { Loading Loading @@ -111,6 +114,12 @@ public class CarStatusBar extends StatusBar implements } }); } // Register a listener for driving state changes. mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged); mDrivingStateHelper.connectToCarService(); mSwitchToGuestTimer = new SwitchToGuestTimer(mContext); } /** Loading Loading @@ -205,6 +214,7 @@ public class CarStatusBar extends StatusBar implements mCarBatteryController.stopListening(); mConnectedDeviceSignalController.stopListening(); mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackListener); mDrivingStateHelper.disconnectFromCarService(); if (mNavigationBarWindow != null) { mWindowManager.removeViewImmediate(mNavigationBarWindow); Loading Loading @@ -476,6 +486,20 @@ public class CarStatusBar extends StatusBar implements } } private void onDrivingStateChanged(CarDrivingStateEvent notUsed) { // Check if we need to start the timer every time driving state changes. startSwitchToGuestTimerIfDrivingOnKeyguard(); } private void startSwitchToGuestTimerIfDrivingOnKeyguard() { if (mDrivingStateHelper.isCurrentlyDriving() && mState != StatusBarState.SHADE) { // We're driving while keyguard is up. mSwitchToGuestTimer.start(); } else { mSwitchToGuestTimer.cancel(); } } @Override protected void createUserSwitcher() { UserSwitcherController userSwitcherController = Loading @@ -491,6 +515,9 @@ public class CarStatusBar extends StatusBar implements @Override public void onStateChanged(int newState) { super.onStateChanged(newState); startSwitchToGuestTimerIfDrivingOnKeyguard(); if (mFullscreenUserSwitcher == null) { return; // Not using the full screen user switcher. } Loading packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * 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.car.Car; import android.car.CarNotConnectedException; import android.car.drivingstate.CarDrivingStateEvent; import android.car.drivingstate.CarDrivingStateManager; import android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener; import android.content.ComponentName; import android.content.Context; import android.content.ServiceConnection; import android.os.IBinder; import android.util.Log; import androidx.annotation.NonNull; /** * Helper class for connecting to the {@link CarDrivingStateManager} and listening for driving state * changes. */ public class DrivingStateHelper { public static final String TAG = "DrivingStateHelper"; private final Context mContext; private CarDrivingStateManager mDrivingStateManager; private Car mCar; private CarDrivingStateEventListener mDrivingStateHandler; public DrivingStateHelper(Context context, @NonNull CarDrivingStateEventListener drivingStateHandler) { mContext = context; mDrivingStateHandler = drivingStateHandler; } /** * Queries {@link CarDrivingStateManager} for current driving state. Returns {@code true} if car * is idling or moving, {@code false} otherwise. */ public boolean isCurrentlyDriving() { try { CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState(); if (currentState != null) { return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING; } } catch (CarNotConnectedException e) { Log.e(TAG, "Cannot determine current driving state. Car not connected", e); } return false; // Default to false. } /** * Establishes connection with the Car service. */ public void connectToCarService() { mCar = Car.createCar(mContext, mCarConnectionListener); if (mCar != null) { mCar.connect(); } } /** * Disconnects from Car service and cleans up listeners. */ public void disconnectFromCarService() { if (mCar != null) { mCar.disconnect(); } } private final ServiceConnection mCarConnectionListener = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { logD("Car Service connected"); try { mDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager( Car.CAR_DRIVING_STATE_SERVICE); if (mDrivingStateManager != null) { mDrivingStateManager.registerListener(mDrivingStateHandler); mDrivingStateHandler.onDrivingStateChanged( mDrivingStateManager.getCurrentCarDrivingState()); } else { Log.e(TAG, "CarDrivingStateService service not available"); } } catch (CarNotConnectedException e) { Log.e(TAG, "Car not connected", e); } } @Override public void onServiceDisconnected(ComponentName name) { destroyDrivingStateManager(); } }; private void destroyDrivingStateManager() { try { if (mDrivingStateManager != null) { mDrivingStateManager.unregisterListener(); } } catch (CarNotConnectedException e) { Log.e(TAG, "Error unregistering listeners", e); } } private void logD(String message) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, message); } } } No newline at end of file packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java 0 → 100644 +116 −0 Original line number Diff line number Diff line /* * 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.car.userlib.CarUserManagerHelper; import android.content.Context; import android.os.CountDownTimer; import android.util.Log; import com.android.systemui.R; import androidx.annotation.GuardedBy; /** * Wrapper for a countdown timer that switches to Guest if the user has been driving with * the keyguard up for configurable number of seconds. */ public class SwitchToGuestTimer { private static final String TAG = "SwitchToGuestTimer"; // After how many ms CountdownTimer.onTick gets triggered. private static final int COUNTDOWN_INTERVAL_MS = 1000; private final CarUserManagerHelper mCarUserManagerHelper; private final Object mTimerLock; private final String mGuestName; private final int mTimeoutMs; private final boolean mEnabled; @GuardedBy("mTimerLock") private CountDownTimer mSwitchToGuestTimer; public SwitchToGuestTimer(Context context) { mCarUserManagerHelper = new CarUserManagerHelper(context); mGuestName = context.getResources().getString(R.string.car_guest); mTimeoutMs = context.getResources().getInteger(R.integer.driving_on_keyguard_timeout_ms); // Lock prevents multiple timers being started. mTimerLock = new Object(); // If milliseconds to switch is a negative number, the feature is disabled. mEnabled = mTimeoutMs >= 0; } /** * Starts the timer if it's not already running. */ public void start() { if (!mEnabled) { logD("Switching to guest after driving on keyguard is disabled."); return; } synchronized (mTimerLock) { if (mSwitchToGuestTimer != null) { logD("Timer is already running."); return; } mSwitchToGuestTimer = new CountDownTimer(mTimeoutMs, COUNTDOWN_INTERVAL_MS) { @Override public void onTick(long msUntilFinished) { logD("Ms until switching to guest: " + Long.toString(msUntilFinished)); } @Override public void onFinish() { mCarUserManagerHelper.startNewGuestSession(mGuestName); cancel(); } }; logI("Starting timer"); mSwitchToGuestTimer.start(); } } /** * Cancels the running timer. */ public void cancel() { synchronized (mTimerLock) { if (mSwitchToGuestTimer != null) { logI("Cancelling timer"); mSwitchToGuestTimer.cancel(); mSwitchToGuestTimer = null; } } } private void logD(String message) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, message); } } private void logI(String message) { if (Log.isLoggable(TAG, Log.INFO)) { Log.i(TAG, message); } } } Loading
packages/SystemUI/AndroidManifest.xml +3 −0 Original line number Diff line number Diff line Loading @@ -209,6 +209,9 @@ <!-- to read and change hvac values in a car --> <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" /> <!-- to be able to detect the driving state in a car--> <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" /> <!-- Permission necessary to change car audio volume through CarAudioManager --> <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" /> Loading
packages/SystemUI/res/values/integers_car.xml +5 −0 Original line number Diff line number Diff line Loading @@ -19,4 +19,9 @@ <integer name="car_user_switcher_anim_cascade_delay_ms">27</integer> <!-- Full screen user switcher column number TODO: move to support library--> <integer name="user_fullscreen_switcher_num_col">3</integer> <!-- Number of milliseconds user can spend driving with the keyguard up. After that, we switch to Guest. --> <!-- If the number is negative, the feature is disabled. If it's zero, we switch to guest immediately as we start driving. --> <integer name="driving_on_keyguard_timeout_ms">30000</integer> </resources>
packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +27 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.car; import android.app.ActivityTaskManager; import android.car.drivingstate.CarDrivingStateEvent; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; import android.util.Log; Loading Loading @@ -82,6 +83,8 @@ public class CarStatusBar extends StatusBar implements private DeviceProvisionedController mDeviceProvisionedController; private boolean mDeviceIsProvisioned = true; private HvacController mHvacController; private DrivingStateHelper mDrivingStateHelper; private SwitchToGuestTimer mSwitchToGuestTimer; @Override public void start() { Loading Loading @@ -111,6 +114,12 @@ public class CarStatusBar extends StatusBar implements } }); } // Register a listener for driving state changes. mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged); mDrivingStateHelper.connectToCarService(); mSwitchToGuestTimer = new SwitchToGuestTimer(mContext); } /** Loading Loading @@ -205,6 +214,7 @@ public class CarStatusBar extends StatusBar implements mCarBatteryController.stopListening(); mConnectedDeviceSignalController.stopListening(); mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackListener); mDrivingStateHelper.disconnectFromCarService(); if (mNavigationBarWindow != null) { mWindowManager.removeViewImmediate(mNavigationBarWindow); Loading Loading @@ -476,6 +486,20 @@ public class CarStatusBar extends StatusBar implements } } private void onDrivingStateChanged(CarDrivingStateEvent notUsed) { // Check if we need to start the timer every time driving state changes. startSwitchToGuestTimerIfDrivingOnKeyguard(); } private void startSwitchToGuestTimerIfDrivingOnKeyguard() { if (mDrivingStateHelper.isCurrentlyDriving() && mState != StatusBarState.SHADE) { // We're driving while keyguard is up. mSwitchToGuestTimer.start(); } else { mSwitchToGuestTimer.cancel(); } } @Override protected void createUserSwitcher() { UserSwitcherController userSwitcherController = Loading @@ -491,6 +515,9 @@ public class CarStatusBar extends StatusBar implements @Override public void onStateChanged(int newState) { super.onStateChanged(newState); startSwitchToGuestTimerIfDrivingOnKeyguard(); if (mFullscreenUserSwitcher == null) { return; // Not using the full screen user switcher. } Loading
packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * 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.car.Car; import android.car.CarNotConnectedException; import android.car.drivingstate.CarDrivingStateEvent; import android.car.drivingstate.CarDrivingStateManager; import android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener; import android.content.ComponentName; import android.content.Context; import android.content.ServiceConnection; import android.os.IBinder; import android.util.Log; import androidx.annotation.NonNull; /** * Helper class for connecting to the {@link CarDrivingStateManager} and listening for driving state * changes. */ public class DrivingStateHelper { public static final String TAG = "DrivingStateHelper"; private final Context mContext; private CarDrivingStateManager mDrivingStateManager; private Car mCar; private CarDrivingStateEventListener mDrivingStateHandler; public DrivingStateHelper(Context context, @NonNull CarDrivingStateEventListener drivingStateHandler) { mContext = context; mDrivingStateHandler = drivingStateHandler; } /** * Queries {@link CarDrivingStateManager} for current driving state. Returns {@code true} if car * is idling or moving, {@code false} otherwise. */ public boolean isCurrentlyDriving() { try { CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState(); if (currentState != null) { return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING; } } catch (CarNotConnectedException e) { Log.e(TAG, "Cannot determine current driving state. Car not connected", e); } return false; // Default to false. } /** * Establishes connection with the Car service. */ public void connectToCarService() { mCar = Car.createCar(mContext, mCarConnectionListener); if (mCar != null) { mCar.connect(); } } /** * Disconnects from Car service and cleans up listeners. */ public void disconnectFromCarService() { if (mCar != null) { mCar.disconnect(); } } private final ServiceConnection mCarConnectionListener = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { logD("Car Service connected"); try { mDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager( Car.CAR_DRIVING_STATE_SERVICE); if (mDrivingStateManager != null) { mDrivingStateManager.registerListener(mDrivingStateHandler); mDrivingStateHandler.onDrivingStateChanged( mDrivingStateManager.getCurrentCarDrivingState()); } else { Log.e(TAG, "CarDrivingStateService service not available"); } } catch (CarNotConnectedException e) { Log.e(TAG, "Car not connected", e); } } @Override public void onServiceDisconnected(ComponentName name) { destroyDrivingStateManager(); } }; private void destroyDrivingStateManager() { try { if (mDrivingStateManager != null) { mDrivingStateManager.unregisterListener(); } } catch (CarNotConnectedException e) { Log.e(TAG, "Error unregistering listeners", e); } } private void logD(String message) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, message); } } } No newline at end of file
packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java 0 → 100644 +116 −0 Original line number Diff line number Diff line /* * 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.car.userlib.CarUserManagerHelper; import android.content.Context; import android.os.CountDownTimer; import android.util.Log; import com.android.systemui.R; import androidx.annotation.GuardedBy; /** * Wrapper for a countdown timer that switches to Guest if the user has been driving with * the keyguard up for configurable number of seconds. */ public class SwitchToGuestTimer { private static final String TAG = "SwitchToGuestTimer"; // After how many ms CountdownTimer.onTick gets triggered. private static final int COUNTDOWN_INTERVAL_MS = 1000; private final CarUserManagerHelper mCarUserManagerHelper; private final Object mTimerLock; private final String mGuestName; private final int mTimeoutMs; private final boolean mEnabled; @GuardedBy("mTimerLock") private CountDownTimer mSwitchToGuestTimer; public SwitchToGuestTimer(Context context) { mCarUserManagerHelper = new CarUserManagerHelper(context); mGuestName = context.getResources().getString(R.string.car_guest); mTimeoutMs = context.getResources().getInteger(R.integer.driving_on_keyguard_timeout_ms); // Lock prevents multiple timers being started. mTimerLock = new Object(); // If milliseconds to switch is a negative number, the feature is disabled. mEnabled = mTimeoutMs >= 0; } /** * Starts the timer if it's not already running. */ public void start() { if (!mEnabled) { logD("Switching to guest after driving on keyguard is disabled."); return; } synchronized (mTimerLock) { if (mSwitchToGuestTimer != null) { logD("Timer is already running."); return; } mSwitchToGuestTimer = new CountDownTimer(mTimeoutMs, COUNTDOWN_INTERVAL_MS) { @Override public void onTick(long msUntilFinished) { logD("Ms until switching to guest: " + Long.toString(msUntilFinished)); } @Override public void onFinish() { mCarUserManagerHelper.startNewGuestSession(mGuestName); cancel(); } }; logI("Starting timer"); mSwitchToGuestTimer.start(); } } /** * Cancels the running timer. */ public void cancel() { synchronized (mTimerLock) { if (mSwitchToGuestTimer != null) { logI("Cancelling timer"); mSwitchToGuestTimer.cancel(); mSwitchToGuestTimer = null; } } } private void logD(String message) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, message); } } private void logI(String message) { if (Log.isLoggable(TAG, Log.INFO)) { Log.i(TAG, message); } } }