Home | History | Annotate | Download | only in statusbar
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.systemui.statusbar;
     18 
     19 import static android.view.Display.DEFAULT_DISPLAY;
     20 
     21 import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
     22 import static com.android.systemui.SysUiServiceProvider.getComponent;
     23 
     24 import android.content.Context;
     25 import android.hardware.display.DisplayManager;
     26 import android.os.Handler;
     27 import android.os.RemoteException;
     28 import android.util.Log;
     29 import android.util.SparseArray;
     30 import android.view.Display;
     31 import android.view.IWindowManager;
     32 import android.view.View;
     33 import android.view.WindowManagerGlobal;
     34 
     35 import androidx.annotation.Nullable;
     36 
     37 import com.android.internal.annotations.VisibleForTesting;
     38 import com.android.internal.statusbar.RegisterStatusBarResult;
     39 import com.android.systemui.Dependency;
     40 import com.android.systemui.plugins.DarkIconDispatcher;
     41 import com.android.systemui.statusbar.CommandQueue.Callbacks;
     42 import com.android.systemui.statusbar.phone.AutoHideController;
     43 import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
     44 import com.android.systemui.statusbar.phone.LightBarController;
     45 import com.android.systemui.statusbar.phone.NavigationBarFragment;
     46 import com.android.systemui.statusbar.phone.NavigationBarView;
     47 import com.android.systemui.statusbar.policy.BatteryController;
     48 
     49 import javax.inject.Inject;
     50 import javax.inject.Named;
     51 import javax.inject.Singleton;
     52 
     53 
     54 /** A controller to handle navigation bars. */
     55 @Singleton
     56 public class NavigationBarController implements Callbacks {
     57 
     58     private static final String TAG = NavigationBarController.class.getSimpleName();
     59 
     60     private final Context mContext;
     61     private final Handler mHandler;
     62     private final DisplayManager mDisplayManager;
     63 
     64     /** A displayId - nav bar maps. */
     65     @VisibleForTesting
     66     SparseArray<NavigationBarFragment> mNavigationBars = new SparseArray<>();
     67 
     68     @Inject
     69     public NavigationBarController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
     70         mContext = context;
     71         mHandler = handler;
     72         mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
     73         CommandQueue commandQueue = getComponent(mContext, CommandQueue.class);
     74         if (commandQueue != null) {
     75             commandQueue.addCallback(this);
     76         }
     77     }
     78 
     79     @Override
     80     public void onDisplayRemoved(int displayId) {
     81         removeNavigationBar(displayId);
     82     }
     83 
     84     @Override
     85     public void onDisplayReady(int displayId) {
     86         Display display = mDisplayManager.getDisplay(displayId);
     87         createNavigationBar(display, null);
     88     }
     89 
     90     // TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to
     91     // CarStatusBar because they have their own nav bar. Think about a better way for it.
     92     /**
     93      * Creates navigation bars when car/status bar initializes.
     94      *
     95      * @param includeDefaultDisplay {@code true} to create navigation bar on default display.
     96      */
     97     public void createNavigationBars(final boolean includeDefaultDisplay,
     98             RegisterStatusBarResult result) {
     99         Display[] displays = mDisplayManager.getDisplays();
    100         for (Display display : displays) {
    101             if (includeDefaultDisplay || display.getDisplayId() != DEFAULT_DISPLAY) {
    102                 createNavigationBar(display, result);
    103             }
    104         }
    105     }
    106 
    107     /**
    108      * Adds a navigation bar on default display or an external display if the display supports
    109      * system decorations.
    110      *
    111      * @param display the display to add navigation bar on.
    112      */
    113     @VisibleForTesting
    114     void createNavigationBar(Display display, RegisterStatusBarResult result) {
    115         if (display == null) {
    116             return;
    117         }
    118 
    119         final int displayId = display.getDisplayId();
    120         final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY;
    121         final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
    122 
    123         try {
    124             if (!wms.hasNavigationBar(displayId)) {
    125                 return;
    126             }
    127         } catch (RemoteException e) {
    128             // Cannot get wms, just return with warning message.
    129             Log.w(TAG, "Cannot get WindowManager.");
    130             return;
    131         }
    132         final Context context = isOnDefaultDisplay
    133                 ? mContext
    134                 : mContext.createDisplayContext(display);
    135         NavigationBarFragment.create(context, (tag, fragment) -> {
    136             NavigationBarFragment navBar = (NavigationBarFragment) fragment;
    137 
    138             // Unfortunately, we still need it because status bar needs LightBarController
    139             // before notifications creation. We cannot directly use getLightBarController()
    140             // from NavigationBarFragment directly.
    141             LightBarController lightBarController = isOnDefaultDisplay
    142                     ? Dependency.get(LightBarController.class)
    143                     : new LightBarController(context,
    144                             Dependency.get(DarkIconDispatcher.class),
    145                             Dependency.get(BatteryController.class));
    146             navBar.setLightBarController(lightBarController);
    147 
    148             // TODO(b/118592525): to support multi-display, we start to add something which is
    149             //                    per-display, while others may be global. I think it's time to add
    150             //                    a new class maybe named DisplayDependency to solve per-display
    151             //                    Dependency problem.
    152             AutoHideController autoHideController = isOnDefaultDisplay
    153                     ? Dependency.get(AutoHideController.class)
    154                     : new AutoHideController(context, mHandler);
    155             navBar.setAutoHideController(autoHideController);
    156             navBar.restoreSystemUiVisibilityState();
    157             mNavigationBars.append(displayId, navBar);
    158 
    159             if (result != null) {
    160                 navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
    161                         result.mImeWindowVis, result.mImeBackDisposition,
    162                         result.mShowImeSwitcher);
    163             }
    164         });
    165     }
    166 
    167     private void removeNavigationBar(int displayId) {
    168         NavigationBarFragment navBar = mNavigationBars.get(displayId);
    169         if (navBar != null) {
    170             View navigationWindow = navBar.getView().getRootView();
    171             WindowManagerGlobal.getInstance()
    172                     .removeView(navigationWindow, true /* immediate */);
    173             mNavigationBars.remove(displayId);
    174         }
    175     }
    176 
    177     /** @see NavigationBarFragment#checkNavBarModes() */
    178     public void checkNavBarModes(int displayId) {
    179         NavigationBarFragment navBar = mNavigationBars.get(displayId);
    180         if (navBar != null) {
    181             navBar.checkNavBarModes();
    182         }
    183     }
    184 
    185     /** @see NavigationBarFragment#finishBarAnimations() */
    186     public void finishBarAnimations(int displayId) {
    187         NavigationBarFragment navBar = mNavigationBars.get(displayId);
    188         if (navBar != null) {
    189             navBar.finishBarAnimations();
    190         }
    191     }
    192 
    193     /** @see NavigationBarFragment#touchAutoDim() */
    194     public void touchAutoDim(int displayId) {
    195         NavigationBarFragment navBar = mNavigationBars.get(displayId);
    196         if (navBar != null) {
    197             navBar.touchAutoDim();
    198         }
    199     }
    200 
    201     /** @see NavigationBarFragment#transitionTo(int, boolean) */
    202     public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) {
    203         NavigationBarFragment navBar = mNavigationBars.get(displayId);
    204         if (navBar != null) {
    205             navBar.transitionTo(barMode, animate);
    206         }
    207     }
    208 
    209     /** @see NavigationBarFragment#disableAnimationsDuringHide(long) */
    210     public void disableAnimationsDuringHide(int displayId, long delay) {
    211         NavigationBarFragment navBar = mNavigationBars.get(displayId);
    212         if (navBar != null) {
    213             navBar.disableAnimationsDuringHide(delay);
    214         }
    215     }
    216 
    217     /** @return {@link NavigationBarView} on the default display. */
    218     public @Nullable NavigationBarView getDefaultNavigationBarView() {
    219         return getNavigationBarView(DEFAULT_DISPLAY);
    220     }
    221 
    222     /**
    223      * @param displayId the ID of display which Navigation bar is on
    224      * @return {@link NavigationBarView} on the display with {@code displayId}.
    225      *         {@code null} if no navigation bar on that display.
    226      */
    227     public @Nullable NavigationBarView getNavigationBarView(int displayId) {
    228         NavigationBarFragment navBar = mNavigationBars.get(displayId);
    229         return (navBar == null) ? null : (NavigationBarView) navBar.getView();
    230     }
    231 
    232     /** @return {@link NavigationBarFragment} on the default display. */
    233     public NavigationBarFragment getDefaultNavigationBarFragment() {
    234         return mNavigationBars.get(DEFAULT_DISPLAY);
    235     }
    236 }
    237