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