1 /* 2 * Copyright (C) 2015 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.stackdivider; 18 19 import android.content.res.Configuration; 20 import android.os.RemoteException; 21 import android.view.IDockedStackListener; 22 import android.view.LayoutInflater; 23 import android.view.View; 24 25 import com.android.systemui.R; 26 import com.android.systemui.SystemUI; 27 import com.android.systemui.recents.Recents; 28 import com.android.systemui.recents.events.EventBus; 29 import com.android.systemui.recents.events.ui.RecentsDrawnEvent; 30 import com.android.systemui.recents.misc.SystemServicesProxy; 31 32 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 33 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 34 35 import java.io.FileDescriptor; 36 import java.io.PrintWriter; 37 38 /** 39 * Controls the docked stack divider. 40 */ 41 public class Divider extends SystemUI { 42 private DividerWindowManager mWindowManager; 43 private DividerView mView; 44 private final DividerState mDividerState = new DividerState(); 45 private DockDividerVisibilityListener mDockDividerVisibilityListener; 46 private boolean mVisible = false; 47 private boolean mMinimized = false; 48 private boolean mAdjustedForIme = false; 49 private boolean mHomeStackResizable = false; 50 private ForcedResizableInfoActivityController mForcedResizableController; 51 52 @Override start()53 public void start() { 54 mWindowManager = new DividerWindowManager(mContext); 55 update(mContext.getResources().getConfiguration()); 56 putComponent(Divider.class, this); 57 mDockDividerVisibilityListener = new DockDividerVisibilityListener(); 58 SystemServicesProxy ssp = Recents.getSystemServices(); 59 ssp.registerDockedStackListener(mDockDividerVisibilityListener); 60 mForcedResizableController = new ForcedResizableInfoActivityController(mContext); 61 EventBus.getDefault().register(this); 62 } 63 64 @Override onConfigurationChanged(Configuration newConfig)65 protected void onConfigurationChanged(Configuration newConfig) { 66 super.onConfigurationChanged(newConfig); 67 update(newConfig); 68 } 69 getView()70 public DividerView getView() { 71 return mView; 72 } 73 isMinimized()74 public boolean isMinimized() { 75 return mMinimized; 76 } 77 isHomeStackResizable()78 public boolean isHomeStackResizable() { 79 return mHomeStackResizable; 80 } 81 addDivider(Configuration configuration)82 private void addDivider(Configuration configuration) { 83 mView = (DividerView) 84 LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null); 85 mView.injectDependencies(mWindowManager, mDividerState); 86 mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE); 87 mView.setMinimizedDockStack(mMinimized, mHomeStackResizable); 88 final int size = mContext.getResources().getDimensionPixelSize( 89 com.android.internal.R.dimen.docked_stack_divider_thickness); 90 final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE; 91 final int width = landscape ? size : MATCH_PARENT; 92 final int height = landscape ? MATCH_PARENT : size; 93 mWindowManager.add(mView, width, height); 94 } 95 removeDivider()96 private void removeDivider() { 97 if (mView != null) { 98 mView.onDividerRemoved(); 99 } 100 mWindowManager.remove(); 101 } 102 update(Configuration configuration)103 private void update(Configuration configuration) { 104 removeDivider(); 105 addDivider(configuration); 106 if (mMinimized) { 107 mView.setMinimizedDockStack(true, mHomeStackResizable); 108 updateTouchable(); 109 } 110 } 111 updateVisibility(final boolean visible)112 private void updateVisibility(final boolean visible) { 113 mView.post(new Runnable() { 114 @Override 115 public void run() { 116 if (mVisible != visible) { 117 mVisible = visible; 118 mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 119 120 // Update state because animations won't finish. 121 mView.setMinimizedDockStack(mMinimized, mHomeStackResizable); 122 } 123 } 124 }); 125 } 126 updateMinimizedDockedStack(final boolean minimized, final long animDuration, final boolean isHomeStackResizable)127 private void updateMinimizedDockedStack(final boolean minimized, final long animDuration, 128 final boolean isHomeStackResizable) { 129 mView.post(new Runnable() { 130 @Override 131 public void run() { 132 mHomeStackResizable = isHomeStackResizable; 133 if (mMinimized != minimized) { 134 mMinimized = minimized; 135 updateTouchable(); 136 if (animDuration > 0) { 137 mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable); 138 } else { 139 mView.setMinimizedDockStack(minimized, isHomeStackResizable); 140 } 141 } 142 } 143 }); 144 } 145 notifyDockedStackExistsChanged(final boolean exists)146 private void notifyDockedStackExistsChanged(final boolean exists) { 147 mView.post(new Runnable() { 148 @Override 149 public void run() { 150 mForcedResizableController.notifyDockedStackExistsChanged(exists); 151 } 152 }); 153 } 154 updateTouchable()155 private void updateTouchable() { 156 mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme); 157 } 158 159 /** 160 * Workaround for b/62528361, at the time RecentsDrawnEvent is sent, it may happen before a 161 * configuration change to the Divider, and internally, the event will be posted to the 162 * subscriber, or DividerView, which has been removed and prevented from resizing. Instead, 163 * register the event handler here and proxy the event to the current DividerView. 164 */ onBusEvent(RecentsDrawnEvent drawnEvent)165 public final void onBusEvent(RecentsDrawnEvent drawnEvent) { 166 if (mView != null) { 167 mView.onRecentsDrawn(); 168 } 169 } 170 171 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)172 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 173 pw.print(" mVisible="); pw.println(mVisible); 174 pw.print(" mMinimized="); pw.println(mMinimized); 175 pw.print(" mAdjustedForIme="); pw.println(mAdjustedForIme); 176 } 177 178 class DockDividerVisibilityListener extends IDockedStackListener.Stub { 179 180 @Override onDividerVisibilityChanged(boolean visible)181 public void onDividerVisibilityChanged(boolean visible) throws RemoteException { 182 updateVisibility(visible); 183 } 184 185 @Override onDockedStackExistsChanged(boolean exists)186 public void onDockedStackExistsChanged(boolean exists) throws RemoteException { 187 notifyDockedStackExistsChanged(exists); 188 } 189 190 @Override onDockedStackMinimizedChanged(boolean minimized, long animDuration, boolean isHomeStackResizable)191 public void onDockedStackMinimizedChanged(boolean minimized, long animDuration, 192 boolean isHomeStackResizable) throws RemoteException { 193 mHomeStackResizable = isHomeStackResizable; 194 updateMinimizedDockedStack(minimized, animDuration, isHomeStackResizable); 195 } 196 197 @Override onAdjustedForImeChanged(boolean adjustedForIme, long animDuration)198 public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration) 199 throws RemoteException { 200 mView.post(() -> { 201 if (mAdjustedForIme != adjustedForIme) { 202 mAdjustedForIme = adjustedForIme; 203 updateTouchable(); 204 if (!mMinimized) { 205 if (animDuration > 0) { 206 mView.setAdjustedForIme(adjustedForIme, animDuration); 207 } else { 208 mView.setAdjustedForIme(adjustedForIme); 209 } 210 } 211 } 212 }); 213 } 214 215 @Override onDockSideChanged(final int newDockSide)216 public void onDockSideChanged(final int newDockSide) throws RemoteException { 217 mView.post(() -> mView.notifyDockSideChanged(newDockSide)); 218 } 219 } 220 } 221