/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.car.systembar; import static com.android.systemui.car.systembar.CarSystemBar.DEBUG; import android.annotation.IntDef; import android.annotation.Nullable; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.car.dockutil.Flags; import com.android.systemui.R; import com.android.systemui.car.hvac.HvacPanelOverlayViewController; import com.android.systemui.car.hvac.HvacView; import com.android.systemui.car.hvac.TemperatureControlView; import com.android.systemui.car.notification.NotificationPanelViewController; import com.android.systemui.car.systembar.CarSystemBarController.HvacPanelController; import com.android.systemui.car.systembar.CarSystemBarController.NotificationsShadeController; import com.android.systemui.settings.UserTracker; import java.lang.annotation.ElementType; import java.lang.annotation.Target; import java.util.Set; /** * A custom system bar for the automotive use case. *

* The system bar in the automotive use case is more like a list of shortcuts, rendered * in a linear layout. */ public class CarSystemBarView extends LinearLayout { @IntDef(value = {BUTTON_TYPE_NAVIGATION, BUTTON_TYPE_KEYGUARD, BUTTON_TYPE_OCCLUSION}) @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) private @interface ButtonsType { } private static final String TAG = CarSystemBarView.class.getSimpleName(); public static final int BUTTON_TYPE_NAVIGATION = 0; public static final int BUTTON_TYPE_KEYGUARD = 1; public static final int BUTTON_TYPE_OCCLUSION = 2; private final boolean mConsumeTouchWhenPanelOpen; private final boolean mButtonsDraggable; private CarSystemBarButton mHomeButton; private CarSystemBarButton mPassengerHomeButton; private View mNavButtons; private CarSystemBarButton mNotificationsButton; private CarSystemBarButton mHvacButton; private HvacView mDriverHvacView; private HvacView mPassengerHvacView; private NotificationsShadeController mNotificationsShadeController; private HvacPanelController mHvacPanelController; private View mLockScreenButtons; private View mOcclusionButtons; // used to wire in open/close gestures for overlay panels private Set mStatusBarWindowTouchListeners; private HvacPanelOverlayViewController mHvacPanelOverlayViewController; private NotificationPanelViewController mNotificationPanelViewController; private CarSystemBarButton mControlCenterButton; public CarSystemBarView(Context context, AttributeSet attrs) { super(context, attrs); mConsumeTouchWhenPanelOpen = getResources().getBoolean( R.bool.config_consumeSystemBarTouchWhenNotificationPanelOpen); mButtonsDraggable = getResources().getBoolean(R.bool.config_systemBarButtonsDraggable); } @Override public void onFinishInflate() { mHomeButton = findViewById(R.id.home); mPassengerHomeButton = findViewById(R.id.passenger_home); mNavButtons = findViewById(R.id.nav_buttons); mLockScreenButtons = findViewById(R.id.lock_screen_nav_buttons); mOcclusionButtons = findViewById(R.id.occlusion_buttons); mNotificationsButton = findViewById(R.id.notifications); mHvacButton = findViewById(R.id.hvac); mDriverHvacView = findViewById(R.id.driver_hvac); mPassengerHvacView = findViewById(R.id.passenger_hvac); mControlCenterButton = findViewById(R.id.control_center_nav); if (mNotificationsButton != null) { mNotificationsButton.setOnClickListener(this::onNotificationsClick); } setupHvacButton(); // Needs to be clickable so that it will receive ACTION_MOVE events. setClickable(true); // Needs to not be focusable so rotary won't highlight the entire nav bar. setFocusable(false); } void updateHomeButtonVisibility(boolean isPassenger) { if (!isPassenger) { return; } if (mPassengerHomeButton != null) { if (mHomeButton != null) { mHomeButton.setVisibility(GONE); } mPassengerHomeButton.setVisibility(VISIBLE); } } void setupHvacButton() { if (mHvacButton != null) { mHvacButton.setOnClickListener(this::onHvacClick); } if (Flags.dockFeature()) { if (mDriverHvacView instanceof TemperatureControlView) { ((TemperatureControlView) mDriverHvacView).setTemperatureTextClickListener( this::onHvacClick); } if (mPassengerHvacView instanceof TemperatureControlView) { ((TemperatureControlView) mPassengerHvacView).setTemperatureTextClickListener( this::onHvacClick); } } } void setupSystemBarButtons(UserTracker userTracker) { setupSystemBarButtons(this, userTracker); } private void setupSystemBarButtons(View v, UserTracker userTracker) { if (v instanceof CarSystemBarButton) { ((CarSystemBarButton) v).setUserTracker(userTracker); } else if (v instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) v; for (int i = 0; i < viewGroup.getChildCount(); i++) { setupSystemBarButtons(viewGroup.getChildAt(i), userTracker); } } } void updateControlCenterButtonVisibility(boolean isMumd) { if (mControlCenterButton != null) { mControlCenterButton.setVisibility(isMumd ? VISIBLE : GONE); } } // Used to forward touch events even if the touch was initiated from a child component @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mStatusBarWindowTouchListeners != null && !mStatusBarWindowTouchListeners.isEmpty()) { if (!mButtonsDraggable) { return false; } boolean shouldConsumeEvent = mNotificationsShadeController == null ? false : mNotificationsShadeController.isNotificationPanelOpen(); // Forward touch events to the status bar window so it can drag // windows if required (ex. Notification shade) triggerAllTouchListeners(this, ev); if (mConsumeTouchWhenPanelOpen && shouldConsumeEvent) { return true; } } return super.onInterceptTouchEvent(ev); } /** Sets the notifications panel controller. */ public void setNotificationsPanelController(NotificationsShadeController controller) { mNotificationsShadeController = controller; } /** Sets the HVAC panel controller. */ public void setHvacPanelController(HvacPanelController controller) { mHvacPanelController = controller; } /** Gets the notifications panel controller. */ public NotificationsShadeController getNotificationsPanelController() { return mNotificationsShadeController; } /** Gets the HVAC panel controller. */ public HvacPanelController getHvacPanelController() { return mHvacPanelController; } /** * Sets the touch listeners that will be called from onInterceptTouchEvent and onTouchEvent * * @param statusBarWindowTouchListeners List of listeners to call from touch and intercept touch */ public void setStatusBarWindowTouchListeners( Set statusBarWindowTouchListeners) { mStatusBarWindowTouchListeners = statusBarWindowTouchListeners; } /** Gets the touch listeners that will be called from onInterceptTouchEvent and onTouchEvent. */ public Set getStatusBarWindowTouchListeners() { return mStatusBarWindowTouchListeners; } @Override public boolean onTouchEvent(MotionEvent event) { triggerAllTouchListeners(this, event); return super.onTouchEvent(event); } protected void onNotificationsClick(View v) { if (mNotificationsButton != null && mNotificationsButton.getDisabled()) { mNotificationsButton.runOnClickWhileDisabled(); return; } if (mNotificationsShadeController != null) { // If the notification shade is about to open, close the hvac panel if (!mNotificationsShadeController.isNotificationPanelOpen() && mHvacPanelController != null && mHvacPanelController.isHvacPanelOpen()) { mHvacPanelController.togglePanel(); } mNotificationsShadeController.togglePanel(); } } protected void onHvacClick(View v) { if (mHvacPanelController != null) { // If the hvac panel is about to open, close the notification shade if (!mHvacPanelController.isHvacPanelOpen() && mNotificationsShadeController != null && mNotificationsShadeController.isNotificationPanelOpen()) { mNotificationsShadeController.togglePanel(); } mHvacPanelController.togglePanel(); } } /** * Shows buttons of the specified {@link ButtonsType}. * * NOTE: Only one type of buttons can be shown at a time, so showing buttons of one type will * hide all buttons of other types. * * @param buttonsType */ public void showButtonsOfType(@ButtonsType int buttonsType) { switch(buttonsType) { case BUTTON_TYPE_NAVIGATION: setNavigationButtonsVisibility(View.VISIBLE); setKeyguardButtonsVisibility(View.GONE); setOcclusionButtonsVisibility(View.GONE); break; case BUTTON_TYPE_KEYGUARD: setNavigationButtonsVisibility(View.GONE); setKeyguardButtonsVisibility(View.VISIBLE); setOcclusionButtonsVisibility(View.GONE); break; case BUTTON_TYPE_OCCLUSION: setNavigationButtonsVisibility(View.GONE); setKeyguardButtonsVisibility(View.GONE); setOcclusionButtonsVisibility(View.VISIBLE); break; } } /** * Sets the system bar view's disabled state and runnable when disabled. */ public void setDisabledSystemBarButton(int viewId, boolean disabled, Runnable runnable, @Nullable String buttonName) { CarSystemBarButton button = findViewById(viewId); if (button != null) { if (DEBUG) { Log.d(TAG, "setDisabledSystemBarButton for: " + buttonName + " to: " + disabled); } button.setDisabled(disabled, runnable); } } /** * Sets the system bar specific View container's visibility. ViewName is used just for * debugging. */ public void setVisibilityByViewId(int viewId, @Nullable String viewName, @View.Visibility int visibility) { View v = findViewById(viewId); if (v != null) { if (DEBUG) Log.d(TAG, "setVisibilityByViewId for: " + viewName + " to: " + visibility); v.setVisibility(visibility); } } /** * Sets the HvacPanelOverlayViewController and adds HVAC button listeners */ public void registerHvacPanelOverlayViewController(HvacPanelOverlayViewController controller) { mHvacPanelOverlayViewController = controller; if (mHvacPanelOverlayViewController != null && mHvacButton != null) { mHvacPanelOverlayViewController.registerViewStateListener(mHvacButton); } } /** * Sets the NotificationPanelViewController and adds button listeners */ public void registerNotificationPanelViewController( NotificationPanelViewController controller) { mNotificationPanelViewController = controller; if (mNotificationPanelViewController != null && mNotificationsButton != null) { mNotificationPanelViewController.registerViewStateListener(mNotificationsButton); } } private void setNavigationButtonsVisibility(@View.Visibility int visibility) { if (mNavButtons != null) { mNavButtons.setVisibility(visibility); } } private void setKeyguardButtonsVisibility(@View.Visibility int visibility) { if (mLockScreenButtons != null) { mLockScreenButtons.setVisibility(visibility); } } private void setOcclusionButtonsVisibility(@View.Visibility int visibility) { if (mOcclusionButtons != null) { mOcclusionButtons.setVisibility(visibility); } } private void triggerAllTouchListeners(View view, MotionEvent event) { if (mStatusBarWindowTouchListeners == null) { return; } for (OnTouchListener listener : mStatusBarWindowTouchListeners) { listener.onTouch(view, event); } } /** * Toggles the notification unseen indicator on/off. * * @param hasUnseen true if the unseen notification count is great than 0. */ public void toggleNotificationUnseenIndicator(Boolean hasUnseen) { if (mNotificationsButton == null) return; mNotificationsButton.setUnseen(hasUnseen); } }