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