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