Files
Lawnchair/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
T
Schneider Victor-tulias a571affc9d Fix unresponsive swipe up after launching a non-running app from overview
Flag: NONE
Fixes: 308982078
Test: quickly swipe to home after tapping a non-running task from overview
Change-Id: I1fbf669368586af4e5b9319603c6a928969cb494
2023-11-07 21:15:12 +00:00

222 lines
7.8 KiB
Java

/*
* Copyright (C) 2023 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.uioverrides.flags;
import static android.app.ActivityThread.currentApplication;
import static com.android.launcher3.BuildConfig.IS_DEBUG_DEVICE;
import static com.android.launcher3.config.FeatureFlags.FlagState.DISABLED;
import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.content.Context;
import android.content.SharedPreferences;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.launcher3.config.FeatureFlags.BooleanFlag;
import com.android.launcher3.config.FeatureFlags.FlagState;
import com.android.launcher3.config.FeatureFlags.IntFlag;
import com.android.launcher3.util.ScreenOnTracker;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Helper class to create various flags for system build
*/
public class FlagsFactory {
private static final String TAG = "FlagsFactory";
private static final FlagsFactory INSTANCE = new FlagsFactory();
private static final boolean FLAG_AUTO_APPLY_ENABLED = true;
private static final String FLAGS_PREF_NAME = "featureFlags";
public static final String NAMESPACE_LAUNCHER = "launcher";
private static final List<DebugFlag> sDebugFlags = new ArrayList<>();
private static final List<IntFlag> sIntFlags = new ArrayList<>();
private static SharedPreferences sSharedPreferences;
static final BooleanFlag TEAMFOOD_FLAG = getReleaseFlag(
0, "LAUNCHER_TEAMFOOD", DISABLED, "Enable this flag to opt-in all team food flags");
private final Set<String> mKeySet = new HashSet<>();
private boolean mRestartRequested = false;
private FlagsFactory() {
if (!FLAG_AUTO_APPLY_ENABLED) {
return;
}
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_LAUNCHER, UI_HELPER_EXECUTOR, this::onPropertiesChanged);
}
static boolean getEnabledValue(FlagState flagState) {
if (IS_DEBUG_DEVICE) {
switch (flagState) {
case ENABLED:
return true;
case TEAMFOOD:
return TEAMFOOD_FLAG.get();
default:
return false;
}
} else {
return flagState == ENABLED;
}
}
/**
* Creates a new debug flag. Debug flags always take their default value in release builds. On
* dogfood builds, they can be manually turned on using the flag toggle UI.
*/
public static BooleanFlag getDebugFlag(
int bugId, String key, FlagState flagState, String description) {
if (IS_DEBUG_DEVICE) {
boolean defaultValue = getEnabledValue(flagState);
boolean currentValue = getSharedPreferences().getBoolean(key, defaultValue);
DebugFlag flag = new DebugFlag(key, description, flagState, currentValue);
sDebugFlags.add(flag);
return flag;
} else {
return new BooleanFlag(getEnabledValue(flagState));
}
}
/**
* Creates a new release flag. Release flags can be rolled out using server configurations and
* also allow manual overrides on debug builds.
*/
public static BooleanFlag getReleaseFlag(
int bugId, String key, FlagState flagState, String description) {
INSTANCE.mKeySet.add(key);
boolean defaultValueInCode = getEnabledValue(flagState);
boolean defaultValue = DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, defaultValueInCode);
if (IS_DEBUG_DEVICE) {
boolean currentValue = getSharedPreferences().getBoolean(key, defaultValue);
DebugFlag flag = new DeviceFlag(key, description,
(defaultValue == defaultValueInCode) ? flagState
: defaultValue ? ENABLED : DISABLED, currentValue, defaultValueInCode);
sDebugFlags.add(flag);
return flag;
} else {
return new BooleanFlag(defaultValue);
}
}
/**
* Creates a new integer flag. Integer flags are always release flags
*/
public static IntFlag getIntFlag(
int bugId, String key, int defaultValueInCode, String description) {
INSTANCE.mKeySet.add(key);
int defaultValue = DeviceConfig.getInt(NAMESPACE_LAUNCHER, key, defaultValueInCode);
if (IS_DEBUG_DEVICE) {
IntDeviceFlag flag = new IntDeviceFlag(key, defaultValue, defaultValueInCode);
sIntFlags.add(flag);
return flag;
} else {
return new IntFlag(defaultValue);
}
}
static List<DebugFlag> getDebugFlags() {
if (!IS_DEBUG_DEVICE) {
return Collections.emptyList();
}
synchronized (sDebugFlags) {
return new ArrayList<>(sDebugFlags);
}
}
/** Returns the SharedPreferences instance backing Debug FeatureFlags. */
@NonNull
static SharedPreferences getSharedPreferences() {
if (sSharedPreferences == null) {
sSharedPreferences = currentApplication()
.createDeviceProtectedStorageContext()
.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE);
}
return sSharedPreferences;
}
/**
* Dumps the current flags state to the print writer
*/
public static void dump(PrintWriter pw) {
if (!IS_DEBUG_DEVICE) {
return;
}
pw.println("DeviceFlags:");
pw.println(" BooleanFlags:");
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
if (flag instanceof DeviceFlag) {
pw.println((flag.currentValueModified() ? " ->" : " ") + flag);
}
}
}
pw.println(" IntFlags:");
synchronized (sIntFlags) {
for (IntFlag flag : sIntFlags) {
pw.println(" " + flag);
}
}
pw.println(" DebugFlags:");
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
if (!(flag instanceof DeviceFlag)) {
pw.println((flag.currentValueModified() ? " ->" : " ") + flag);
}
}
}
}
private void onPropertiesChanged(Properties properties) {
if (!Collections.disjoint(properties.getKeyset(), mKeySet)) {
// Schedule a restart
if (mRestartRequested) {
return;
}
Log.e(TAG, "Flag changed, scheduling restart");
mRestartRequested = true;
ScreenOnTracker sot = ScreenOnTracker.INSTANCE.get(currentApplication());
if (sot.isScreenOn()) {
sot.addListener(this::onScreenOnChanged);
} else {
onScreenOnChanged(false);
}
}
}
private void onScreenOnChanged(boolean isOn) {
if (mRestartRequested && !isOn) {
Log.e(TAG, "Restart requested, killing process");
System.exit(0);
}
}
}