9a90c2d521
This CL introduces a new controller to manage the visibility of the
launcher workspace when desktop mode is active and freeform tasks
are visible.
This controller will be notified when the sysui state flag related
to freeform task visibility is changed.
The controller will modify the workspace visibility as well as
the flags on the activity indicating if it's been paused / resumed
based on freeform tasks being visible or not.
Bug: 245569277
Test: manual - enable desktop mode and open some freeform tasks
=> observe that the contents of launcher is hidden and
taskbar shows
- remove all of the freeform tasks
=> observe that the contents of launcher reappears along
with the hotseat.
Change-Id: I378ab97b40cbb954a06f4e2426b195efddad905c
352 lines
12 KiB
Java
352 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.launcher3;
|
|
|
|
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
|
|
|
|
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
|
|
|
import android.app.Activity;
|
|
import android.content.Context;
|
|
import android.content.ContextWrapper;
|
|
import android.content.Intent;
|
|
import android.content.res.Configuration;
|
|
import android.os.Bundle;
|
|
import android.window.OnBackInvokedDispatcher;
|
|
|
|
import androidx.annotation.IntDef;
|
|
|
|
import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
|
|
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
|
|
import com.android.launcher3.logging.StatsLogManager;
|
|
import com.android.launcher3.testing.TestLogging;
|
|
import com.android.launcher3.testing.shared.TestProtocol;
|
|
import com.android.launcher3.util.SystemUiController;
|
|
import com.android.launcher3.util.ViewCache;
|
|
import com.android.launcher3.views.AppLauncher;
|
|
import com.android.launcher3.views.ScrimView;
|
|
|
|
import java.io.PrintWriter;
|
|
import java.lang.annotation.Retention;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Launcher BaseActivity
|
|
*/
|
|
public abstract class BaseActivity extends Activity implements AppLauncher,
|
|
DeviceProfileListenable {
|
|
|
|
private static final String TAG = "BaseActivity";
|
|
|
|
public static final int INVISIBLE_BY_STATE_HANDLER = 1 << 0;
|
|
public static final int INVISIBLE_BY_APP_TRANSITIONS = 1 << 1;
|
|
public static final int INVISIBLE_BY_PENDING_FLAGS = 1 << 2;
|
|
|
|
// This is not treated as invisibility flag, but adds as a hint for an incomplete transition.
|
|
// When the wallpaper animation runs, it replaces this flag with a proper invisibility
|
|
// flag, INVISIBLE_BY_PENDING_FLAGS only for the duration of that animation.
|
|
public static final int PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION = 1 << 3;
|
|
|
|
private static final int INVISIBLE_FLAGS =
|
|
INVISIBLE_BY_STATE_HANDLER | INVISIBLE_BY_APP_TRANSITIONS | INVISIBLE_BY_PENDING_FLAGS;
|
|
public static final int STATE_HANDLER_INVISIBILITY_FLAGS =
|
|
INVISIBLE_BY_STATE_HANDLER | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
|
|
public static final int INVISIBLE_ALL =
|
|
INVISIBLE_FLAGS | PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
|
|
|
|
@Retention(SOURCE)
|
|
@IntDef(
|
|
flag = true,
|
|
value = {INVISIBLE_BY_STATE_HANDLER, INVISIBLE_BY_APP_TRANSITIONS,
|
|
INVISIBLE_BY_PENDING_FLAGS, PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION})
|
|
public @interface InvisibilityFlags{}
|
|
|
|
private final ArrayList<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
|
|
private final ArrayList<MultiWindowModeChangedListener> mMultiWindowModeChangedListeners =
|
|
new ArrayList<>();
|
|
|
|
protected DeviceProfile mDeviceProfile;
|
|
protected SystemUiController mSystemUiController;
|
|
private StatsLogManager mStatsLogManager;
|
|
|
|
|
|
public static final int ACTIVITY_STATE_STARTED = 1 << 0;
|
|
public static final int ACTIVITY_STATE_RESUMED = 1 << 1;
|
|
|
|
/**
|
|
* State flags indicating that the activity has received one frame after resume, and was
|
|
* not immediately paused.
|
|
*/
|
|
public static final int ACTIVITY_STATE_DEFERRED_RESUMED = 1 << 2;
|
|
|
|
public static final int ACTIVITY_STATE_WINDOW_FOCUSED = 1 << 3;
|
|
|
|
/**
|
|
* State flag indicating if the user is active or the activity when to background as a result
|
|
* of user action.
|
|
* @see #isUserActive()
|
|
*/
|
|
public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4;
|
|
|
|
/**
|
|
* State flag indicating if the user will be active shortly.
|
|
*/
|
|
public static final int ACTIVITY_STATE_USER_WILL_BE_ACTIVE = 1 << 5;
|
|
|
|
/**
|
|
* State flag indicating that a state transition is in progress
|
|
*/
|
|
public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 6;
|
|
|
|
@Retention(SOURCE)
|
|
@IntDef(
|
|
flag = true,
|
|
value = {ACTIVITY_STATE_STARTED,
|
|
ACTIVITY_STATE_RESUMED,
|
|
ACTIVITY_STATE_DEFERRED_RESUMED,
|
|
ACTIVITY_STATE_WINDOW_FOCUSED,
|
|
ACTIVITY_STATE_USER_ACTIVE,
|
|
ACTIVITY_STATE_TRANSITION_ACTIVE})
|
|
public @interface ActivityFlags{}
|
|
|
|
@ActivityFlags
|
|
private int mActivityFlags;
|
|
|
|
// When the recents animation is running, the visibility of the Launcher is managed by the
|
|
// animation
|
|
@InvisibilityFlags private int mForceInvisible;
|
|
|
|
private final ViewCache mViewCache = new ViewCache();
|
|
|
|
@Override
|
|
public ViewCache getViewCache() {
|
|
return mViewCache;
|
|
}
|
|
|
|
@Override
|
|
public DeviceProfile getDeviceProfile() {
|
|
return mDeviceProfile;
|
|
}
|
|
|
|
@Override
|
|
public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
|
|
return mDPChangeListeners;
|
|
}
|
|
|
|
/**
|
|
* Returns {@link StatsLogManager} for user event logging.
|
|
*/
|
|
@Override
|
|
public StatsLogManager getStatsLogManager() {
|
|
if (mStatsLogManager == null) {
|
|
mStatsLogManager = StatsLogManager.newInstance(this);
|
|
}
|
|
return mStatsLogManager;
|
|
}
|
|
|
|
public SystemUiController getSystemUiController() {
|
|
if (mSystemUiController == null) {
|
|
mSystemUiController = new SystemUiController(getWindow());
|
|
}
|
|
return mSystemUiController;
|
|
}
|
|
|
|
public ScrimView getScrimView() {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
}
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
if (Utilities.ATLEAST_T) {
|
|
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
|
|
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
|
|
() -> {
|
|
onBackPressed();
|
|
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
|
|
});
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onStart() {
|
|
addActivityFlags(ACTIVITY_STATE_STARTED);
|
|
super.onStart();
|
|
}
|
|
|
|
@Override
|
|
protected void onResume() {
|
|
setResumed();
|
|
super.onResume();
|
|
}
|
|
|
|
@Override
|
|
protected void onUserLeaveHint() {
|
|
removeActivityFlags(ACTIVITY_STATE_USER_ACTIVE);
|
|
super.onUserLeaveHint();
|
|
}
|
|
|
|
@Override
|
|
public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
|
|
super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
|
|
for (int i = mMultiWindowModeChangedListeners.size() - 1; i >= 0; i--) {
|
|
mMultiWindowModeChangedListeners.get(i).onMultiWindowModeChanged(isInMultiWindowMode);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onStop() {
|
|
removeActivityFlags(ACTIVITY_STATE_STARTED | ACTIVITY_STATE_USER_ACTIVE);
|
|
mForceInvisible = 0;
|
|
super.onStop();
|
|
|
|
// Reset the overridden sysui flags used for the task-swipe launch animation, this is a
|
|
// catch all for if we do not get resumed (and therefore not paused below)
|
|
getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0);
|
|
}
|
|
|
|
@Override
|
|
protected void onPause() {
|
|
setPaused();
|
|
super.onPause();
|
|
|
|
// Reset the overridden sysui flags used for the task-swipe launch animation, we do this
|
|
// here instead of at the end of the animation because the start of the new activity does
|
|
// not happen immediately, which would cause us to reset to launcher's sysui flags and then
|
|
// back to the new app (causing a flash)
|
|
getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0);
|
|
}
|
|
|
|
@Override
|
|
public void onWindowFocusChanged(boolean hasFocus) {
|
|
super.onWindowFocusChanged(hasFocus);
|
|
if (hasFocus) {
|
|
addActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED);
|
|
} else {
|
|
removeActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED);
|
|
}
|
|
|
|
}
|
|
|
|
public boolean isStarted() {
|
|
return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
|
|
}
|
|
|
|
/**
|
|
* isResumed in already defined as a hidden final method in Activity.java
|
|
*/
|
|
public boolean hasBeenResumed() {
|
|
return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0;
|
|
}
|
|
|
|
/**
|
|
* Sets the activity to appear as paused.
|
|
*/
|
|
public void setPaused() {
|
|
removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
|
|
}
|
|
|
|
/**
|
|
* Sets the activity to appear as resumed.
|
|
*/
|
|
public void setResumed() {
|
|
addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
|
|
removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
|
|
}
|
|
|
|
public boolean isUserActive() {
|
|
return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0;
|
|
}
|
|
|
|
public int getActivityFlags() {
|
|
return mActivityFlags;
|
|
}
|
|
|
|
protected void addActivityFlags(int flags) {
|
|
mActivityFlags |= flags;
|
|
onActivityFlagsChanged(flags);
|
|
}
|
|
|
|
protected void removeActivityFlags(int flags) {
|
|
mActivityFlags &= ~flags;
|
|
onActivityFlagsChanged(flags);
|
|
}
|
|
|
|
protected void onActivityFlagsChanged(int changeBits) { }
|
|
|
|
public void addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) {
|
|
mMultiWindowModeChangedListeners.add(listener);
|
|
}
|
|
|
|
public void removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) {
|
|
mMultiWindowModeChangedListeners.remove(listener);
|
|
}
|
|
|
|
/**
|
|
* Used to set the override visibility state, used only to handle the transition home with the
|
|
* recents animation.
|
|
* @see QuickstepTransitionManager#createWallpaperOpenRunner
|
|
*/
|
|
public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
|
|
mForceInvisible |= flag;
|
|
}
|
|
|
|
public void clearForceInvisibleFlag(@InvisibilityFlags int flag) {
|
|
mForceInvisible &= ~flag;
|
|
}
|
|
|
|
/**
|
|
* @return Wether this activity should be considered invisible regardless of actual visibility.
|
|
*/
|
|
public boolean isForceInvisible() {
|
|
return hasSomeInvisibleFlag(INVISIBLE_FLAGS);
|
|
}
|
|
|
|
public boolean hasSomeInvisibleFlag(int mask) {
|
|
return (mForceInvisible & mask) != 0;
|
|
}
|
|
|
|
public interface MultiWindowModeChangedListener {
|
|
void onMultiWindowModeChanged(boolean isInMultiWindowMode);
|
|
}
|
|
|
|
protected void dumpMisc(String prefix, PrintWriter writer) {
|
|
writer.println(prefix + "deviceProfile isTransposed="
|
|
+ getDeviceProfile().isVerticalBarLayout());
|
|
writer.println(prefix + "orientation=" + getResources().getConfiguration().orientation);
|
|
writer.println(prefix + "mSystemUiController: " + mSystemUiController);
|
|
writer.println(prefix + "mActivityFlags: " + mActivityFlags);
|
|
writer.println(prefix + "mForceInvisible: " + mForceInvisible);
|
|
}
|
|
|
|
public static <T extends BaseActivity> T fromContext(Context context) {
|
|
if (context instanceof BaseActivity) {
|
|
return (T) context;
|
|
} else if (context instanceof ContextWrapper) {
|
|
return fromContext(((ContextWrapper) context).getBaseContext());
|
|
} else {
|
|
throw new IllegalArgumentException("Cannot find BaseActivity in parent tree");
|
|
}
|
|
}
|
|
}
|