1/*
2 * Copyright (C) 2016 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
17package com.android.systemui.stackdivider;
18
19import android.app.ActivityOptions;
20import android.content.Context;
21import android.content.Intent;
22import android.os.Handler;
23import android.util.ArraySet;
24import android.widget.Toast;
25
26import com.android.systemui.R;
27import com.android.systemui.recents.events.EventBus;
28import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
29import com.android.systemui.recents.misc.SystemServicesProxy;
30import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
31import com.android.systemui.stackdivider.events.StartedDragingEvent;
32import com.android.systemui.stackdivider.events.StoppedDragingEvent;
33
34/**
35 * Controller that decides when to show the {@link ForcedResizableInfoActivity}.
36 */
37public class ForcedResizableInfoActivityController {
38
39    private static final String SELF_PACKAGE_NAME = "com.android.systemui";
40
41    private static final int TIMEOUT = 1000;
42    private final Context mContext;
43    private final Handler mHandler = new Handler();
44    private final ArraySet<Integer> mPendingTaskIds = new ArraySet<>();
45    private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
46    private boolean mDividerDraging;
47
48    private final Runnable mTimeoutRunnable = new Runnable() {
49        @Override
50        public void run() {
51            showPending();
52        }
53    };
54
55    public ForcedResizableInfoActivityController(Context context) {
56        mContext = context;
57        EventBus.getDefault().register(this);
58        SystemServicesProxy.getInstance(context).registerTaskStackListener(
59                new TaskStackListener() {
60                    @Override
61                    public void onActivityForcedResizable(String packageName, int taskId) {
62                        activityForcedResizable(packageName, taskId);
63                    }
64
65                    @Override
66                    public void onActivityDismissingDockedStack() {
67                        activityDismissingDockedStack();
68                    }
69                });
70    }
71
72    public void notifyDockedStackExistsChanged(boolean exists) {
73        if (!exists) {
74            mPackagesShownInSession.clear();
75        }
76    }
77
78    public final void onBusEvent(AppTransitionFinishedEvent event) {
79        if (!mDividerDraging) {
80            showPending();
81        }
82    }
83
84    public final void onBusEvent(StartedDragingEvent event) {
85        mDividerDraging = true;
86        mHandler.removeCallbacks(mTimeoutRunnable);
87    }
88
89    public final void onBusEvent(StoppedDragingEvent event) {
90        mDividerDraging = false;
91        showPending();
92    }
93
94    private void activityForcedResizable(String packageName, int taskId) {
95        if (debounce(packageName)) {
96            return;
97        }
98        mPendingTaskIds.add(taskId);
99        postTimeout();
100    }
101
102    private void activityDismissingDockedStack() {
103        Toast toast = Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
104                Toast.LENGTH_SHORT);
105        toast.show();
106    }
107
108    private void showPending() {
109        mHandler.removeCallbacks(mTimeoutRunnable);
110        for (int i = mPendingTaskIds.size() - 1; i >= 0; i--) {
111            Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class);
112            ActivityOptions options = ActivityOptions.makeBasic();
113            options.setLaunchTaskId(mPendingTaskIds.valueAt(i));
114            options.setTaskOverlay(true);
115            mContext.startActivity(intent, options.toBundle());
116        }
117        mPendingTaskIds.clear();
118    }
119
120    private void postTimeout() {
121        mHandler.removeCallbacks(mTimeoutRunnable);
122        mHandler.postDelayed(mTimeoutRunnable, TIMEOUT);
123    }
124
125    private boolean debounce(String packageName) {
126        if (packageName == null) {
127            return false;
128        }
129
130        // We launch ForcedResizableInfoActivity into a task that was forced resizable, so that
131        // triggers another notification. So ignore our own activity.
132        if (SELF_PACKAGE_NAME.equals(packageName)) {
133            return true;
134        }
135        boolean debounce = mPackagesShownInSession.contains(packageName);
136        mPackagesShownInSession.add(packageName);
137        return debounce;
138    }
139}
140