feat: Add option to clear home screen in settings (#6125)

Signed-off-by: abhixv <abhi.sharma1@hotmail.com>
This commit is contained in:
Abhishek Sharma
2025-12-20 03:26:30 +05:30
committed by Pun Butrach
parent 9898749619
commit 5f3a03f4fb
1577 changed files with 112563 additions and 80248 deletions
@@ -16,7 +16,6 @@ package com.android.systemui.plugins;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
@@ -25,8 +24,6 @@ import android.view.View;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.plugins.annotations.ProvidesInterface;
import kotlinx.coroutines.CoroutineScope;
/**
* An interface to start activities. This is used as a callback from the views to
* {@link PhoneStatusBar} to allow custom handling for starting the activity, i.e. dismissing the
@@ -36,23 +33,6 @@ import kotlinx.coroutines.CoroutineScope;
public interface ActivityStarter {
int VERSION = 2;
/**
* Registers the given {@link ActivityTransitionAnimator.ControllerFactory} for launching and
* closing transitions matching the {@link ActivityTransitionAnimator.TransitionCookie} and the
* {@link ComponentName} that it contains, within the given {@link CoroutineScope}.
*/
void registerTransition(
ActivityTransitionAnimator.TransitionCookie cookie,
ActivityTransitionAnimator.ControllerFactory controllerFactory,
CoroutineScope scope);
/**
* Unregisters the {@link ActivityTransitionAnimator.ControllerFactory} previously registered
* containing the given {@link ActivityTransitionAnimator.TransitionCookie}. If no such
* registration exists, this is a no-op.
*/
void unregisterTransition(ActivityTransitionAnimator.TransitionCookie cookie);
void startPendingIntentDismissingKeyguard(PendingIntent intent);
/**
@@ -104,17 +84,14 @@ public interface ActivityStarter {
* Similar to {@link #startPendingIntentMaybeDismissingKeyguard(PendingIntent, Runnable,
* ActivityTransitionAnimator.Controller)}, but also specifies a fill-in intent and extra
* option that could be used to populate the pending intent and launch the activity. This also
* allows the caller to avoid dismissing the shade. An optional custom message can be set as
* the unlock reason in the alternate bouncer.
* allows the caller to avoid dismissing the shade.
*/
void startPendingIntentMaybeDismissingKeyguard(PendingIntent intent,
boolean dismissShade,
@Nullable Runnable intentSentUiThreadCallback,
@Nullable ActivityTransitionAnimator.Controller animationController,
@Nullable Intent fillInIntent,
@Nullable Bundle extraOptions,
@Nullable String customMessage
);
@Nullable Bundle extraOptions);
/**
* The intent flag can be specified in startActivity().
@@ -143,11 +120,6 @@ public interface ActivityStarter {
void postStartActivityDismissingKeyguard(Intent intent, int delay,
@Nullable ActivityTransitionAnimator.Controller animationController,
@Nullable String customMessage);
/** Posts a start activity intent that dismisses keyguard. */
void postStartActivityDismissingKeyguard(Intent intent, int delay,
@Nullable ActivityTransitionAnimator.Controller animationController,
@Nullable String customMessage,
@Nullable UserHandle userHandle);
void postStartActivityDismissingKeyguard(PendingIntent intent);
/**
@@ -162,20 +134,14 @@ public interface ActivityStarter {
void dismissKeyguardThenExecute(OnDismissAction action, @Nullable Runnable cancel,
boolean afterKeyguardGone);
/**
* Authenticates if needed and dismisses keyguard to execute an action.
*
* TODO(b/348431835) Display the custom message in the new alternate bouncer, when the
* device_entry_udfps_refactor flag is enabled.
*/
/** Authenticates if needed and dismisses keyguard to execute an action. */
void dismissKeyguardThenExecute(OnDismissAction action, @Nullable Runnable cancel,
boolean afterKeyguardGone, @Nullable String customMessage);
/** Starts an activity and dismisses keyguard. */
void startActivityDismissingKeyguard(Intent intent,
boolean onlyProvisioned,
boolean dismissShade,
@Nullable String customMessage);
boolean dismissShade);
/** Starts an activity and dismisses keyguard. */
void startActivityDismissingKeyguard(Intent intent,
@@ -36,9 +36,6 @@ import java.util.Collection;
public interface DarkIconDispatcher {
int VERSION = 2;
/** Called when work should stop and resources should be cleaned up. */
default void stop() {}
/**
* Sets the dark area so {@link #applyDark} only affects the icons in the specified area.
*
@@ -26,7 +26,6 @@ import com.android.systemui.plugins.annotations.ProvidesInterface;
@DependsOn(target = Callback.class)
public interface VolumeDialog extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_VOLUME";
String ACTION_VOLUME_UNDO = "com.android.systemui.volume.ACTION_VOLUME_UNDO";
int VERSION = 1;
void init(int windowType, Callback callback);
@@ -14,6 +14,7 @@
package com.android.systemui.plugins;
import android.annotation.IntegerRes;
import android.content.ComponentName;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -21,8 +22,6 @@ import android.os.Handler;
import android.os.VibrationEffect;
import android.util.SparseArray;
import androidx.annotation.StringRes;
import com.android.systemui.plugins.VolumeDialogController.Callbacks;
import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.plugins.VolumeDialogController.StreamState;
@@ -91,7 +90,7 @@ public interface VolumeDialogController {
public int levelMax;
public boolean muted;
public boolean muteSupported;
public @StringRes int name;
public @IntegerRes int name;
public String remoteLabel;
public boolean routedToBluetooth;
@@ -0,0 +1,6 @@
package com.android.systemui.plugins.clocks
data class AlarmData(
val nextAlarmMillis: Long?,
val descriptionId: String?,
)
@@ -0,0 +1,122 @@
/*
* Copyright (C) 2022 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.systemui.plugins.clocks
import com.android.internal.annotations.Keep
import org.json.JSONObject
/** Identifies a clock design */
typealias ClockId = String
data class AodClockBurnInModel(
val scale: Float,
val translationX: Float,
val translationY: Float,
)
/** Tick rates for clocks */
enum class ClockTickRate(val value: Int) {
PER_MINUTE(2), // Update the clock once per minute.
PER_SECOND(1), // Update the clock once per second.
PER_FRAME(0), // Update the clock every second.
}
/** Some data about a clock design */
data class ClockMetadata(
val clockId: ClockId,
)
/** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
data class ClockConfig(
val id: String,
/** Localized name of the clock */
val name: String,
/** Localized accessibility description for the clock */
val description: String,
/** Transition to AOD should move smartspace like large clock instead of small clock */
val useAlternateSmartspaceAODTransition: Boolean = false,
/** True if the clock will react to tone changes in the seed color. */
val isReactiveToTone: Boolean = true,
/** True if the clock is large frame clock, which will use weather in compose. */
val useCustomClockScene: Boolean = false,
)
/** Render configuration options for a clock face. Modifies the way SystemUI behaves. */
data class ClockFaceConfig(
/** Expected interval between calls to onTimeTick. Can always reduce to PER_MINUTE in AOD. */
val tickRate: ClockTickRate = ClockTickRate.PER_MINUTE,
/** Call to check whether the clock consumes weather data */
val hasCustomWeatherDataDisplay: Boolean = false,
/**
* Whether this clock has a custom position update animation. If true, the keyguard will call
* `onPositionUpdated` to notify the clock of a position update animation. If false, a default
* animation will be used (e.g. a simple translation).
*/
val hasCustomPositionUpdatedAnimation: Boolean = false,
/** True if the clock is large frame clock, which will use weatherBlueprint in compose. */
val useCustomClockScene: Boolean = false,
)
/** Structure for keeping clock-specific settings */
@Keep
data class ClockSettings(
val clockId: ClockId? = null,
val seedColor: Int? = null,
) {
// Exclude metadata from equality checks
var metadata: JSONObject = JSONObject()
companion object {
private val KEY_CLOCK_ID = "clockId"
private val KEY_SEED_COLOR = "seedColor"
private val KEY_METADATA = "metadata"
fun serialize(setting: ClockSettings?): String {
if (setting == null) {
return ""
}
return JSONObject()
.put(KEY_CLOCK_ID, setting.clockId)
.put(KEY_SEED_COLOR, setting.seedColor)
.put(KEY_METADATA, setting.metadata)
.toString()
}
fun deserialize(jsonStr: String?): ClockSettings? {
if (jsonStr.isNullOrEmpty()) {
return null
}
val json = JSONObject(jsonStr)
val result =
ClockSettings(
if (!json.isNull(KEY_CLOCK_ID)) json.getString(KEY_CLOCK_ID) else null,
if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null
)
if (!json.isNull(KEY_METADATA)) {
result.metadata = json.getJSONObject(KEY_METADATA)
}
return result
}
}
}
@@ -0,0 +1,123 @@
package com.android.systemui.plugins.clocks
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.annotation.VisibleForTesting
typealias WeatherTouchAction = (View) -> Unit
data class WeatherData(
val description: String,
val state: WeatherStateIcon,
val useCelsius: Boolean,
val temperature: Int,
val touchAction: WeatherTouchAction? = null,
) {
companion object {
const val DEBUG = true
private const val TAG = "WeatherData"
@VisibleForTesting const val DESCRIPTION_KEY = "description"
@VisibleForTesting const val STATE_KEY = "state"
@VisibleForTesting const val USE_CELSIUS_KEY = "use_celsius"
@VisibleForTesting const val TEMPERATURE_KEY = "temperature"
private const val INVALID_WEATHER_ICON_STATE = -1
fun fromBundle(extras: Bundle, touchAction: WeatherTouchAction? = null): WeatherData? {
val description = extras.getString(DESCRIPTION_KEY)
val state =
WeatherStateIcon.fromInt(extras.getInt(STATE_KEY, INVALID_WEATHER_ICON_STATE))
val temperature = readIntFromBundle(extras, TEMPERATURE_KEY)
if (
description == null ||
state == null ||
!extras.containsKey(USE_CELSIUS_KEY) ||
temperature == null
) {
if (DEBUG) {
Log.w(TAG, "Weather data did not parse from $extras")
}
return null
} else {
val result =
WeatherData(
description = description,
state = state,
useCelsius = extras.getBoolean(USE_CELSIUS_KEY),
temperature = temperature,
touchAction = touchAction
)
if (DEBUG) {
Log.i(TAG, "Weather data parsed $result from $extras")
}
return result
}
}
private fun readIntFromBundle(extras: Bundle, key: String): Int? =
try {
extras.getString(key)?.toInt()
} catch (e: Exception) {
null
}
}
// Values for WeatherStateIcon must stay in sync with go/g3-WeatherStateIcon
enum class WeatherStateIcon(val id: Int) {
UNKNOWN_ICON(0),
// Clear, day & night.
SUNNY(1),
CLEAR_NIGHT(2),
// Mostly clear, day & night.
MOSTLY_SUNNY(3),
MOSTLY_CLEAR_NIGHT(4),
// Partly cloudy, day & night.
PARTLY_CLOUDY(5),
PARTLY_CLOUDY_NIGHT(6),
// Mostly cloudy, day & night.
MOSTLY_CLOUDY_DAY(7),
MOSTLY_CLOUDY_NIGHT(8),
CLOUDY(9),
HAZE_FOG_DUST_SMOKE(10),
DRIZZLE(11),
HEAVY_RAIN(12),
SHOWERS_RAIN(13),
// Scattered showers, day & night.
SCATTERED_SHOWERS_DAY(14),
SCATTERED_SHOWERS_NIGHT(15),
// Isolated scattered thunderstorms, day & night.
ISOLATED_SCATTERED_TSTORMS_DAY(16),
ISOLATED_SCATTERED_TSTORMS_NIGHT(17),
STRONG_TSTORMS(18),
BLIZZARD(19),
BLOWING_SNOW(20),
FLURRIES(21),
HEAVY_SNOW(22),
// Scattered snow showers, day & night.
SCATTERED_SNOW_SHOWERS_DAY(23),
SCATTERED_SNOW_SHOWERS_NIGHT(24),
SNOW_SHOWERS_SNOW(25),
MIXED_RAIN_HAIL_RAIN_SLEET(26),
SLEET_HAIL(27),
TORNADO(28),
TROPICAL_STORM_HURRICANE(29),
WINDY_BREEZY(30),
WINTRY_MIX_RAIN_SNOW(31);
companion object {
fun fromInt(value: Int) = values().firstOrNull { it.id == value }
}
}
override fun toString(): String {
val unit = if (useCelsius) "C" else "F"
return "$state (\"$description\") $temperature°$unit"
}
}
@@ -0,0 +1,22 @@
package com.android.systemui.plugins.clocks
import android.provider.Settings.Global.ZEN_MODE_ALARMS
import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
import android.provider.Settings.Global.ZEN_MODE_OFF
data class ZenData(
val zenMode: ZenMode,
val descriptionId: String?,
) {
enum class ZenMode(val zenMode: Int) {
OFF(ZEN_MODE_OFF),
IMPORTANT_INTERRUPTIONS(ZEN_MODE_IMPORTANT_INTERRUPTIONS),
NO_INTERRUPTIONS(ZEN_MODE_NO_INTERRUPTIONS),
ALARMS(ZEN_MODE_ALARMS);
companion object {
fun fromInt(zenMode: Int) = values().firstOrNull { it.zenMode == zenMode }
}
}
}
@@ -25,34 +25,34 @@ interface TableLogBufferBase {
*
* For Java overloading.
*/
fun logChange(prefix: String = "", columnName: String, value: String?) {
fun logChange(prefix: String, columnName: String, value: String?) {
logChange(prefix, columnName, value, isInitial = false)
}
/** Logs a String? change. */
fun logChange(prefix: String = "", columnName: String, value: String?, isInitial: Boolean)
fun logChange(prefix: String, columnName: String, value: String?, isInitial: Boolean)
/**
* Logs a Boolean change.
*
* For Java overloading.
*/
fun logChange(prefix: String = "", columnName: String, value: Boolean) {
fun logChange(prefix: String, columnName: String, value: Boolean) {
logChange(prefix, columnName, value, isInitial = false)
}
/** Logs a Boolean change. */
fun logChange(prefix: String = "", columnName: String, value: Boolean, isInitial: Boolean)
fun logChange(prefix: String, columnName: String, value: Boolean, isInitial: Boolean)
/**
* Logs an Int? change.
*
* For Java overloading.
*/
fun logChange(prefix: String = "", columnName: String, value: Int?) {
fun logChange(prefix: String, columnName: String, value: Int?) {
logChange(prefix, columnName, value, isInitial = false)
}
/** Logs an Int? change. */
fun logChange(prefix: String = "", columnName: String, value: Int?, isInitial: Boolean)
fun logChange(prefix: String, columnName: String, value: Int?, isInitial: Boolean)
}
@@ -14,7 +14,6 @@
package com.android.systemui.plugins.qs;
import android.graphics.Rect;
import android.view.View;
import androidx.annotation.FloatRange;
@@ -36,7 +35,7 @@ public interface QS extends FragmentBase {
String ACTION = "com.android.systemui.action.PLUGIN_QS";
int VERSION = 16;
int VERSION = 15;
String TAG = "QS";
@@ -90,45 +89,8 @@ public interface QS extends FragmentBase {
*/
int getHeightDiff();
/**
* Returns the header view that contains QQS. This might return null (or throw) if there's no
* actual header view.
*/
View getHeader();
/**
* Returns the top of the header view that contains QQS wrt to the container view
*/
int getHeaderTop();
/**
* Returns the bottom of the header view that contains QQS wrt to the container view
*/
int getHeaderBottom();
/**
* Returns the left bound of the header view that contains QQS wrt to the container view
*/
int getHeaderLeft();
/**
* Fills outBounds with the bounds of the header view (container of QQS) on the screen
*/
void getHeaderBoundsOnScreen(Rect outBounds);
/**
* Returns the height of the header view that contains QQS. It defaults to bottom - top.
*/
default int getHeaderHeight() {
return getHeaderBottom() - getHeaderTop();
}
/**
* Returns whether the header view that contains QQS is shown on screen (similar semantics to
* View.isShown).
*/
boolean isHeaderShown();
default void setHasNotifications(boolean hasNotifications) {
}
@@ -14,6 +14,7 @@
package com.android.systemui.plugins.qs;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@@ -21,7 +22,6 @@ import android.metrics.LogMaker;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceId;
@@ -33,7 +33,6 @@ import com.android.systemui.plugins.qs.QSTile.Icon;
import com.android.systemui.plugins.qs.QSTile.State;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
@ProvidesInterface(version = QSTile.VERSION)
@@ -42,7 +41,7 @@ import java.util.function.Supplier;
@DependsOn(target = Icon.class)
@DependsOn(target = State.class)
public interface QSTile {
int VERSION = 5;
int VERSION = 4;
String getTileSpec();
@@ -78,7 +77,6 @@ public interface QSTile {
void longClick(@Nullable Expandable expandable);
void userSwitch(int currentUser);
int getCurrentTileUser();
/**
* @deprecated not needed as {@link com.android.internal.logging.UiEvent} will use
@@ -94,7 +92,6 @@ public interface QSTile {
CharSequence getTileLabel();
@NonNull
State getState();
default LogMaker populate(LogMaker logMaker) {
@@ -123,36 +120,6 @@ public interface QSTile {
*/
boolean isListening();
/**
* Get this tile's {@link TileDetailsViewModel} through a callback.
*
* Please only override this method if the tile can't get its {@link TileDetailsViewModel}
* synchronously and thus need a callback to defer it.
*
* @return a boolean indicating whether this tile has a {@link TileDetailsViewModel}. The tile's
* {@link TileDetailsViewModel} will be passed to the callback. Please always return true when
* overriding this method. Return false will make the tile display its dialog instead of details
* view, and it will not wait for the callback to be returned before proceeding to show the
* dialog.
*/
default boolean getDetailsViewModel(Consumer<TileDetailsViewModel> callback) {
TileDetailsViewModel tileDetailsViewModel = getDetailsViewModel();
callback.accept(tileDetailsViewModel);
return tileDetailsViewModel != null;
}
/**
* Return this tile's {@link TileDetailsViewModel} to be used to render the TileDetailsView.
*
* Please only override this method if the tile doesn't need a callback to set its
* {@link TileDetailsViewModel}.
*/
default TileDetailsViewModel getDetailsViewModel() {
return null;
}
boolean isDestroyed();
@ProvidesInterface(version = Callback.VERSION)
interface Callback {
static final int VERSION = 2;
@@ -202,7 +169,6 @@ public interface QSTile {
public boolean isTransient = false;
public String expandedAccessibilityClassName;
public boolean handlesLongClick = true;
public boolean handlesSecondaryClick = false;
@Nullable
public Drawable sideViewCustomDrawable;
public String spec;
@@ -217,10 +183,7 @@ public interface QSTile {
}
}
/**
* If the current secondaryLabel value is not empty, ignore the given input and return
* the current value. Otherwise return current value.
*/
/** Get the text for secondaryLabel. */
public CharSequence getSecondaryLabel(CharSequence stateText) {
// Use a local reference as the value might change from other threads
CharSequence localSecondaryLabel = secondaryLabel;
@@ -249,7 +212,6 @@ public interface QSTile {
|| !Objects.equals(other.isTransient, isTransient)
|| !Objects.equals(other.dualTarget, dualTarget)
|| !Objects.equals(other.handlesLongClick, handlesLongClick)
|| !Objects.equals(other.handlesSecondaryClick, handlesSecondaryClick)
|| !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable);
other.spec = spec;
other.icon = icon;
@@ -265,7 +227,6 @@ public interface QSTile {
other.dualTarget = dualTarget;
other.isTransient = isTransient;
other.handlesLongClick = handlesLongClick;
other.handlesSecondaryClick = handlesSecondaryClick;
other.sideViewCustomDrawable = sideViewCustomDrawable;
return changed;
}
@@ -291,13 +252,11 @@ public interface QSTile {
sb.append(",disabledByPolicy=").append(disabledByPolicy);
sb.append(",dualTarget=").append(dualTarget);
sb.append(",isTransient=").append(isTransient);
sb.append(",handlesSecondaryClick=").append(handlesSecondaryClick);
sb.append(",state=").append(state);
sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable);
return sb.append(']');
}
@NonNull
public State copy() {
State state = new State();
copyTo(state);
@@ -333,7 +292,6 @@ public interface QSTile {
return rt;
}
@androidx.annotation.NonNull
@Override
public State copy() {
AdapterState state = new AdapterState();
@@ -346,7 +304,6 @@ public interface QSTile {
class BooleanState extends AdapterState {
public static final int VERSION = 1;
@androidx.annotation.NonNull
@Override
public State copy() {
BooleanState state = new BooleanState();
@@ -122,7 +122,7 @@ public interface NotificationMenuRowPlugin extends Plugin {
public void setAppName(String appName);
public void createMenu(ViewGroup parent);
public void createMenu(ViewGroup parent, StatusBarNotification sbn);
public void resetMenu();
@@ -215,8 +215,9 @@ public interface NotificationMenuRowPlugin extends Plugin {
/**
* Callback used to signal the menu that its parent notification has been updated.
* @param sbn
*/
public void onNotificationUpdated();
public void onNotificationUpdated(StatusBarNotification sbn);
/**
* Callback used to signal the menu that a user is moving the parent notification.