1 /*
2  * Copyright (C) 2014 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.recents;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
22 
23 import android.annotation.Nullable;
24 import android.app.ActivityManager;
25 import android.app.trust.TrustManager;
26 import android.content.Context;
27 import android.graphics.Point;
28 import android.graphics.Rect;
29 import android.hardware.display.DisplayManager;
30 import android.os.Handler;
31 import android.os.RemoteException;
32 import android.util.Log;
33 import android.view.Display;
34 import android.widget.Toast;
35 
36 import com.android.systemui.Dependency;
37 import com.android.systemui.R;
38 import com.android.systemui.shared.recents.IOverviewProxy;
39 import com.android.systemui.shared.system.ActivityManagerWrapper;
40 import com.android.systemui.stackdivider.Divider;
41 import com.android.systemui.statusbar.phone.StatusBar;
42 
43 import java.util.Optional;
44 
45 import javax.inject.Inject;
46 import javax.inject.Singleton;
47 
48 import dagger.Lazy;
49 
50 /**
51  * An implementation of the Recents interface which proxies to the OverviewProxyService.
52  */
53 @Singleton
54 public class OverviewProxyRecentsImpl implements RecentsImplementation {
55 
56     private final static String TAG = "OverviewProxyRecentsImpl";
57     @Nullable
58     private final Lazy<StatusBar> mStatusBarLazy;
59     private final Optional<Divider> mDividerOptional;
60 
61     private Context mContext;
62     private Handler mHandler;
63     private TrustManager mTrustManager;
64     private OverviewProxyService mOverviewProxyService;
65 
66     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
67     @Inject
OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy, Optional<Divider> dividerOptional)68     public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy,
69             Optional<Divider> dividerOptional) {
70         mStatusBarLazy = statusBarLazy.orElse(null);
71         mDividerOptional = dividerOptional;
72     }
73 
74     @Override
onStart(Context context)75     public void onStart(Context context) {
76         mContext = context;
77         mHandler = new Handler();
78         mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
79         mOverviewProxyService = Dependency.get(OverviewProxyService.class);
80     }
81 
82     @Override
showRecentApps(boolean triggeredFromAltTab)83     public void showRecentApps(boolean triggeredFromAltTab) {
84         IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
85         if (overviewProxy != null) {
86             try {
87                 overviewProxy.onOverviewShown(triggeredFromAltTab);
88                 return;
89             } catch (RemoteException e) {
90                 Log.e(TAG, "Failed to send overview show event to launcher.", e);
91             }
92         } else {
93             // Do nothing
94         }
95     }
96 
97     @Override
hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey)98     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
99         IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
100         if (overviewProxy != null) {
101             try {
102                 overviewProxy.onOverviewHidden(triggeredFromAltTab, triggeredFromHomeKey);
103                 return;
104             } catch (RemoteException e) {
105                 Log.e(TAG, "Failed to send overview hide event to launcher.", e);
106             }
107         } else {
108             // Do nothing
109         }
110     }
111 
112     @Override
toggleRecentApps()113     public void toggleRecentApps() {
114         // If connected to launcher service, let it handle the toggle logic
115         IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
116         if (overviewProxy != null) {
117             final Runnable toggleRecents = () -> {
118                 try {
119                     if (mOverviewProxyService.getProxy() != null) {
120                         mOverviewProxyService.getProxy().onOverviewToggle();
121                         mOverviewProxyService.notifyToggleRecentApps();
122                     }
123                 } catch (RemoteException e) {
124                     Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
125                 }
126             };
127             // Preload only if device for current user is unlocked
128             if (mStatusBarLazy != null && mStatusBarLazy.get().isKeyguardShowing()) {
129                 mStatusBarLazy.get().executeRunnableDismissingKeyguard(() -> {
130                         // Flush trustmanager before checking device locked per user
131                         mTrustManager.reportKeyguardShowingChanged();
132                         mHandler.post(toggleRecents);
133                     }, null,  true /* dismissShade */, false /* afterKeyguardGone */,
134                     true /* deferred */);
135             } else {
136                 toggleRecents.run();
137             }
138             return;
139         } else {
140             // Do nothing
141         }
142     }
143 
144     @Override
splitPrimaryTask(int stackCreateMode, Rect initialBounds, int metricsDockAction)145     public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
146             int metricsDockAction) {
147         Point realSize = new Point();
148         if (initialBounds == null) {
149             mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
150                     .getRealSize(realSize);
151             initialBounds = new Rect(0, 0, realSize.x, realSize.y);
152         }
153 
154         ActivityManager.RunningTaskInfo runningTask =
155                 ActivityManagerWrapper.getInstance().getRunningTask();
156         final int activityType = runningTask != null
157                 ? runningTask.configuration.windowConfiguration.getActivityType()
158                 : ACTIVITY_TYPE_UNDEFINED;
159         boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
160         boolean isRunningTaskInHomeOrRecentsStack =
161                 activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
162         if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
163             if (runningTask.supportsSplitScreenMultiWindow) {
164                 if (ActivityManagerWrapper.getInstance().setTaskWindowingModeSplitScreenPrimary(
165                         runningTask.id, stackCreateMode, initialBounds)) {
166                     mDividerOptional.ifPresent(Divider::onDockedTopTask);
167 
168                     // The overview service is handling split screen, so just skip the wait for the
169                     // first draw and notify the divider to start animating now
170                     mDividerOptional.ifPresent(Divider::onRecentsDrawn);
171 
172                     return true;
173                 }
174             } else {
175                 Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
176                         Toast.LENGTH_SHORT).show();
177             }
178         }
179         return false;
180     }
181 }
182