12adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi/*
22adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi * Copyright (C) 2016 The Android Open Source Project
32adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi *
42adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi * Licensed under the Apache License, Version 2.0 (the "License");
52adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi * you may not use this file except in compliance with the License.
62adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi * You may obtain a copy of the License at
72adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi *
82adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi *      http://www.apache.org/licenses/LICENSE-2.0
92adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi *
102adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi * Unless required by applicable law or agreed to in writing, software
112adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi * distributed under the License is distributed on an "AS IS" BASIS,
122adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi * See the License for the specific language governing permissions and
142adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi * limitations under the License
152adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi */
162adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
172adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggipackage com.android.systemui.stackdivider;
182adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
19036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulianimport static com.android.systemui.stackdivider.ForcedResizableInfoActivity
20036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian        .EXTRA_FORCED_RESIZEABLE_REASON;
21036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian
222adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggiimport android.app.ActivityOptions;
232adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggiimport android.content.Context;
242adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggiimport android.content.Intent;
252adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggiimport android.os.Handler;
26675c5d8e5a6fd98e8324798e1ef31df20cee5b0fWinsonimport android.os.UserHandle;
272adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggiimport android.util.ArraySet;
28d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggiimport android.widget.Toast;
292adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
30d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggiimport com.android.systemui.R;
312adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggiimport com.android.systemui.recents.events.EventBus;
322adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggiimport com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
33675c5d8e5a6fd98e8324798e1ef31df20cee5b0fWinsonimport com.android.systemui.recents.events.component.ShowUserToastEvent;
34d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggiimport com.android.systemui.recents.misc.SystemServicesProxy;
35d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggiimport com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
362adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggiimport com.android.systemui.stackdivider.events.StartedDragingEvent;
372adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggiimport com.android.systemui.stackdivider.events.StoppedDragingEvent;
382adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
392adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi/**
402adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi * Controller that decides when to show the {@link ForcedResizableInfoActivity}.
412adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi */
422adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggipublic class ForcedResizableInfoActivityController {
432adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
442adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    private static final String SELF_PACKAGE_NAME = "com.android.systemui";
452adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
462adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    private static final int TIMEOUT = 1000;
472adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    private final Context mContext;
482adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    private final Handler mHandler = new Handler();
49036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian    private final ArraySet<PendingTaskRecord> mPendingTasks = new ArraySet<>();
502adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
512adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    private boolean mDividerDraging;
522adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
532adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    private final Runnable mTimeoutRunnable = new Runnable() {
542adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        @Override
552adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        public void run() {
562adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi            showPending();
572adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        }
582adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    };
592adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
60036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian    /** Record of force resized task that's pending to be handled. */
61036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian    private class PendingTaskRecord {
62036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian        int taskId;
63036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian        /**
64036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian         * {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SPLIT_SCREEN} or
65036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian         * {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY}
66036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian         */
67036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian        int reason;
68036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian
69036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian        PendingTaskRecord(int taskId, int reason) {
70036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian            this.taskId = taskId;
71036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian            this.reason = reason;
72036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian        }
73036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian    }
74036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian
752adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    public ForcedResizableInfoActivityController(Context context) {
762adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        mContext = context;
772adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        EventBus.getDefault().register(this);
78d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi        SystemServicesProxy.getInstance(context).registerTaskStackListener(
79d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi                new TaskStackListener() {
80d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi                    @Override
81036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian                    public void onActivityForcedResizable(String packageName, int taskId,
82036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian                            int reason) {
83036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian                        activityForcedResizable(packageName, taskId, reason);
84d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi                    }
85d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi
86d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi                    @Override
87d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi                    public void onActivityDismissingDockedStack() {
88d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi                        activityDismissingDockedStack();
89d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi                    }
90036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian
91036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian                    @Override
92036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian                    public void onActivityLaunchOnSecondaryDisplayFailed() {
93036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian                        activityLaunchOnSecondaryDisplayFailed();
94036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian                    }
95d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi                });
962adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    }
972adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
982adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    public void notifyDockedStackExistsChanged(boolean exists) {
992adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        if (!exists) {
1002adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi            mPackagesShownInSession.clear();
1012adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        }
1022adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    }
1032adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
1042adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    public final void onBusEvent(AppTransitionFinishedEvent event) {
1052adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        if (!mDividerDraging) {
1062adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi            showPending();
1072adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        }
1082adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    }
1092adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
1102adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    public final void onBusEvent(StartedDragingEvent event) {
1112adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        mDividerDraging = true;
1122adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        mHandler.removeCallbacks(mTimeoutRunnable);
1132adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    }
1142adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
1152adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    public final void onBusEvent(StoppedDragingEvent event) {
1162adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        mDividerDraging = false;
1172adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        showPending();
1182adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    }
1192adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
120036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian    private void activityForcedResizable(String packageName, int taskId, int reason) {
121d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi        if (debounce(packageName)) {
122d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi            return;
123d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi        }
124036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian        mPendingTasks.add(new PendingTaskRecord(taskId, reason));
125d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi        postTimeout();
126d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi    }
127d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi
128d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi    private void activityDismissingDockedStack() {
129675c5d8e5a6fd98e8324798e1ef31df20cee5b0fWinson        EventBus.getDefault().send(new ShowUserToastEvent(
130675c5d8e5a6fd98e8324798e1ef31df20cee5b0fWinson                R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT));
131d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi    }
132d53f09254ed48365d3a5149d640437d76aed2e5dJorim Jaggi
133036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian    private void activityLaunchOnSecondaryDisplayFailed() {
134036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian        EventBus.getDefault().send(new ShowUserToastEvent(
135036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian                R.string.activity_launch_on_secondary_display_failed_text, Toast.LENGTH_SHORT));
136036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian    }
137036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian
1382adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    private void showPending() {
1392adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        mHandler.removeCallbacks(mTimeoutRunnable);
140036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian        for (int i = mPendingTasks.size() - 1; i >= 0; i--) {
141036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian            PendingTaskRecord pendingRecord = mPendingTasks.valueAt(i);
1422adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi            Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class);
1432adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi            ActivityOptions options = ActivityOptions.makeBasic();
144036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian            options.setLaunchTaskId(pendingRecord.taskId);
145d5020c15b9bee33ecc63054450616cad928eec6aAndrii Kulian            // Set as task overlay and allow to resume, so that when an app enters split-screen and
146d5020c15b9bee33ecc63054450616cad928eec6aAndrii Kulian            // becomes paused, the overlay will still be shown.
147d5020c15b9bee33ecc63054450616cad928eec6aAndrii Kulian            options.setTaskOverlay(true, true /* canResume */);
148036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian            intent.putExtra(EXTRA_FORCED_RESIZEABLE_REASON, pendingRecord.reason);
149675c5d8e5a6fd98e8324798e1ef31df20cee5b0fWinson            mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
1502adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        }
151036e3adce72b39ac5baefcf059891d6951206d81Andrii Kulian        mPendingTasks.clear();
1522adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    }
1532adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
1542adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    private void postTimeout() {
1552adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        mHandler.removeCallbacks(mTimeoutRunnable);
1562adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        mHandler.postDelayed(mTimeoutRunnable, TIMEOUT);
1572adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    }
1582adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
1592adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    private boolean debounce(String packageName) {
1602adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        if (packageName == null) {
1612adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi            return false;
1622adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        }
1632adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi
1642adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        // We launch ForcedResizableInfoActivity into a task that was forced resizable, so that
1652adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        // triggers another notification. So ignore our own activity.
1662adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        if (SELF_PACKAGE_NAME.equals(packageName)) {
1672adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi            return true;
1682adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        }
1692adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        boolean debounce = mPackagesShownInSession.contains(packageName);
1702adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        mPackagesShownInSession.add(packageName);
1712adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi        return debounce;
1722adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi    }
1732adba07d75419462873dfeef40d4c983d832ed99Jorim Jaggi}
174