1/*
2 * Copyright (C) 2017 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.launcher3.uioverrides;
18
19import static android.view.View.VISIBLE;
20import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
21import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
22import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
23import static com.android.launcher3.LauncherState.ALL_APPS;
24import static com.android.launcher3.LauncherState.NORMAL;
25import static com.android.launcher3.LauncherState.OVERVIEW;
26import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
27import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
28import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
29
30import android.animation.AnimatorSet;
31import android.animation.ValueAnimator;
32import android.app.Activity;
33import android.content.Context;
34import android.os.CancellationSignal;
35import android.util.Base64;
36
37import com.android.launcher3.AbstractFloatingView;
38import com.android.launcher3.DeviceProfile;
39import com.android.launcher3.Launcher;
40import com.android.launcher3.LauncherAppTransitionManagerImpl;
41import com.android.launcher3.LauncherState;
42import com.android.launcher3.LauncherStateManager;
43import com.android.launcher3.LauncherStateManager.StateHandler;
44import com.android.launcher3.Utilities;
45import com.android.launcher3.anim.AnimatorPlaybackController;
46import com.android.launcher3.util.TouchController;
47import com.android.quickstep.OverviewInteractionState;
48import com.android.quickstep.RecentsModel;
49import com.android.quickstep.util.RemoteAnimationTargetSet;
50import com.android.quickstep.util.RemoteFadeOutAnimationListener;
51import com.android.quickstep.views.RecentsView;
52import com.android.systemui.shared.system.ActivityCompat;
53import com.android.systemui.shared.system.WindowManagerWrapper;
54
55import java.io.ByteArrayOutputStream;
56import java.io.PrintWriter;
57import java.util.zip.Deflater;
58
59public class UiFactory {
60
61    public static TouchController[] createTouchControllers(Launcher launcher) {
62        boolean swipeUpEnabled = OverviewInteractionState.getInstance(launcher)
63                .isSwipeUpGestureEnabled();
64        if (!swipeUpEnabled) {
65            return new TouchController[] {
66                    launcher.getDragController(),
67                    new OverviewToAllAppsTouchController(launcher),
68                    new LauncherTaskViewController(launcher)};
69        }
70        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
71            return new TouchController[] {
72                    launcher.getDragController(),
73                    new OverviewToAllAppsTouchController(launcher),
74                    new LandscapeEdgeSwipeController(launcher),
75                    new LauncherTaskViewController(launcher)};
76        } else {
77            return new TouchController[] {
78                    launcher.getDragController(),
79                    new PortraitStatesTouchController(launcher),
80                    new LauncherTaskViewController(launcher)};
81        }
82    }
83
84    public static void setOnTouchControllersChangedListener(Context context, Runnable listener) {
85        OverviewInteractionState.getInstance(context).setOnSwipeUpSettingChangedListener(listener);
86    }
87
88    public static StateHandler[] getStateHandler(Launcher launcher) {
89        return new StateHandler[] {launcher.getAllAppsController(), launcher.getWorkspace(),
90                new RecentsViewStateController(launcher), new BackButtonAlphaHandler(launcher)};
91    }
92
93    /**
94     * Sets the back button visibility based on the current state/window focus.
95     */
96    public static void onLauncherStateOrFocusChanged(Launcher launcher) {
97        boolean shouldBackButtonBeHidden = launcher != null
98                && launcher.getStateManager().getState().hideBackButton
99                && launcher.hasWindowFocus();
100        if (shouldBackButtonBeHidden) {
101            // Show the back button if there is a floating view visible.
102            shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(launcher,
103                    TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
104        }
105        OverviewInteractionState.getInstance(launcher)
106                .setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */);
107    }
108
109    public static void resetOverview(Launcher launcher) {
110        RecentsView recents = launcher.getOverviewPanel();
111        recents.reset();
112    }
113
114    public static void onCreate(Launcher launcher) {
115        if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
116            launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
117                @Override
118                public void onStateSetImmediately(LauncherState state) {
119                }
120
121                @Override
122                public void onStateTransitionStart(LauncherState toState) {
123                }
124
125                @Override
126                public void onStateTransitionComplete(LauncherState finalState) {
127                    boolean swipeUpEnabled = OverviewInteractionState.getInstance(launcher)
128                            .isSwipeUpGestureEnabled();
129                    LauncherState prevState = launcher.getStateManager().getLastState();
130
131                    if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
132                            && finalState == ALL_APPS && prevState == NORMAL))) {
133                        launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
134                        launcher.getStateManager().removeStateListener(this);
135                    }
136                }
137            });
138        }
139
140        if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
141            launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
142                @Override
143                public void onStateSetImmediately(LauncherState state) {
144                }
145
146                @Override
147                public void onStateTransitionStart(LauncherState toState) {
148                }
149
150                @Override
151                public void onStateTransitionComplete(LauncherState finalState) {
152                    LauncherState prevState = launcher.getStateManager().getLastState();
153
154                    if (finalState == ALL_APPS && prevState == OVERVIEW) {
155                        launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
156                        launcher.getStateManager().removeStateListener(this);
157                    }
158                }
159            });
160        }
161    }
162
163    public static void onStart(Context context) {
164        RecentsModel model = RecentsModel.getInstance(context);
165        if (model != null) {
166            model.onStart();
167        }
168    }
169
170    public static void onLauncherStateOrResumeChanged(Launcher launcher) {
171        LauncherState state = launcher.getStateManager().getState();
172        DeviceProfile profile = launcher.getDeviceProfile();
173        WindowManagerWrapper.getInstance().setShelfHeight(
174                state != ALL_APPS && launcher.isUserActive() && !profile.isVerticalBarLayout(),
175                profile.hotseatBarSizePx);
176
177        if (state == NORMAL) {
178            launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
179        }
180    }
181
182    public static void onTrimMemory(Context context, int level) {
183        RecentsModel model = RecentsModel.getInstance(context);
184        if (model != null) {
185            model.onTrimMemory(level);
186        }
187    }
188
189    public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
190            CancellationSignal cancellationSignal) {
191        LauncherAppTransitionManagerImpl appTransitionManager =
192                (LauncherAppTransitionManagerImpl) launcher.getAppTransitionManager();
193        appTransitionManager.setRemoteAnimationProvider((targets) -> {
194
195            // On the first call clear the reference.
196            cancellationSignal.cancel();
197
198            ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
199            fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(targets));
200            AnimatorSet anim = new AnimatorSet();
201            anim.play(fadeAnimation);
202            return anim;
203        }, cancellationSignal);
204    }
205
206    public static boolean dumpActivity(Activity activity, PrintWriter writer) {
207        if (!Utilities.IS_DEBUG_DEVICE) {
208            return false;
209        }
210        ByteArrayOutputStream out = new ByteArrayOutputStream();
211        if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
212            return false;
213        }
214
215        Deflater deflater = new Deflater();
216        deflater.setInput(out.toByteArray());
217        deflater.finish();
218
219        out.reset();
220        byte[] buffer = new byte[1024];
221        while (!deflater.finished()) {
222            int count = deflater.deflate(buffer); // returns the generated code... index
223            out.write(buffer, 0, count);
224        }
225
226        writer.println("--encoded-view-dump-v0--");
227        writer.println(Base64.encodeToString(
228                out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
229        return true;
230    }
231
232    public static void prepareToShowOverview(Launcher launcher) {
233        RecentsView overview = launcher.getOverviewPanel();
234        if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
235            SCALE_PROPERTY.set(overview, 1.33f);
236        }
237    }
238
239    private static class LauncherTaskViewController extends TaskViewTouchController<Launcher> {
240
241        public LauncherTaskViewController(Launcher activity) {
242            super(activity);
243        }
244
245        @Override
246        protected boolean isRecentsInteractive() {
247            return mActivity.isInState(OVERVIEW);
248        }
249
250        @Override
251        protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
252            mActivity.getStateManager().setCurrentUserControlledAnimation(animController);
253        }
254    }
255}
256