Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f16c7d31 authored by Marcelo Arteiro's avatar Marcelo Arteiro
Browse files

Add Settings management for upcoming ThemeService

This change introduces the new ThemeSettings framework, enabling management and persistence of user theme preferences. Key components include ThemeSettings (data model), ThemeSettingsManager (persistence and loading), ThemeSettingsField (individual property handling), and ThemeStyle (theme type management). The framework serializes theme data to JSON in Settings.Secure, parses JSON, and provides an API for accessing and updating theme preferences. Comprehensive tests have been added to validate core functionality, and the api is under a feature flag for future enabling.

Bug: 333694176
Test: atest FrameworksServicesTests_theme
Flag: android.server.enable_theme_service
Change-Id: I18268db0cb4ee4c0fbd753f11850eeb7230a50ac
parent 97faf816
Loading
Loading
Loading
Loading
+80 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 android.content.theming;

import android.annotation.ColorInt;
import android.annotation.FlaggedApi;
import android.graphics.Color;

import androidx.annotation.Nullable;

import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.regex.Pattern;

/** @hide */
@FlaggedApi(android.server.Flags.FLAG_ENABLE_THEME_SERVICE)
public class FieldColor extends ThemeSettingsField<Integer, String> {
    private static final Pattern COLOR_PATTERN = Pattern.compile("[0-9a-fA-F]{6,8}");

    public FieldColor(
            String key,
            BiConsumer<ThemeSettingsUpdater, Integer> setter,
            Function<ThemeSettings, Integer> getter,
            ThemeSettings defaults
    ) {
        super(key, setter, getter, defaults);
    }

    @Override
    @ColorInt
    @Nullable
    public Integer parse(String primitive) {
        if (primitive == null) {
            return null;
        }
        if (!COLOR_PATTERN.matcher(primitive).matches()) {
            return null;
        }

        try {
            return Color.valueOf(Color.parseColor("#" + primitive)).toArgb();
        } catch (IllegalArgumentException e) {
            return null;
        }
    }

    @Override
    public String serialize(@ColorInt Integer value) {
        return Integer.toHexString(value);
    }

    @Override
    public boolean validate(Integer value) {
        return !value.equals(Color.TRANSPARENT);
    }

    @Override
    public Class<Integer> getFieldType() {
        return Integer.class;
    }

    @Override
    public Class<String> getJsonType() {
        return String.class;
    }
}
+70 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 android.content.theming;

import android.annotation.FlaggedApi;

import androidx.annotation.Nullable;

import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;

/** @hide */
@FlaggedApi(android.server.Flags.FLAG_ENABLE_THEME_SERVICE)
public class FieldColorBoth extends ThemeSettingsField<Boolean, String> {
    public FieldColorBoth(
            String key,
            BiConsumer<ThemeSettingsUpdater, Boolean> setter,
            Function<ThemeSettings, Boolean> getter,
            ThemeSettings defaults
    ) {
        super(key, setter, getter, defaults);
    }

    @Override
    @Nullable
    public Boolean parse(String primitive) {
        return switch (primitive) {
            case "1" -> true;
            case "0" -> false;
            default -> null;
        };
    }

    @Override
    public String serialize(Boolean typedValue) {
        if (typedValue) return "1";
        return "0";
    }

    @Override
    public boolean validate(Boolean value) {
        Objects.requireNonNull(value);
        return true;
    }

    @Override
    public Class<Boolean> getFieldType() {
        return Boolean.class;
    }

    @Override
    public Class<String> getJsonType() {
        return String.class;
    }
}
+64 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 android.content.theming;

import android.annotation.FlaggedApi;

import java.util.function.BiConsumer;
import java.util.function.Function;

/** @hide */
@FlaggedApi(android.server.Flags.FLAG_ENABLE_THEME_SERVICE)
public class FieldColorIndex extends ThemeSettingsField<Integer, String> {
    public FieldColorIndex(
            String key,
            BiConsumer<ThemeSettingsUpdater, Integer> setter,
            Function<ThemeSettings, Integer> getter,
            ThemeSettings defaults
    ) {
        super(key, setter, getter, defaults);
    }

    @Override
    public Integer parse(String primitive) {
        try {
            return Integer.parseInt(primitive);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    @Override
    public String serialize(Integer typedValue) {
        return typedValue.toString();
    }

    @Override
    public boolean validate(Integer value) {
        return value >= -1;
    }

    @Override
    public Class<Integer> getFieldType() {
        return Integer.class;
    }

    @Override
    public Class<String> getJsonType() {
        return String.class;
    }
}
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 android.content.theming;

import android.annotation.FlaggedApi;
import android.annotation.StringDef;

import androidx.annotation.Nullable;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.BiConsumer;
import java.util.function.Function;

/** @hide */
@FlaggedApi(android.server.Flags.FLAG_ENABLE_THEME_SERVICE)
public class FieldColorSource extends ThemeSettingsField<String, String> {
    public FieldColorSource(
            String key,
            BiConsumer<ThemeSettingsUpdater, String> setter,
            Function<ThemeSettings, String> getter,
            ThemeSettings defaults
    ) {
        super(key, setter, getter, defaults);
    }

    @Override
    @Nullable
    @Type
    public String parse(String primitive) {
        return primitive;
    }

    @Override
    public String serialize(@Type String typedValue) {
        return typedValue;
    }

    @Override
    public boolean validate(String value) {
        return switch (value) {
            case "preset", "home_wallpaper", "lock_wallpaper" -> true;
            default -> false;
        };
    }

    @Override
    public Class<String> getFieldType() {
        return String.class;
    }

    @Override
    public Class<String> getJsonType() {
        return String.class;
    }


    @StringDef({"preset", "home_wallpaper", "lock_wallpaper"})
    @Retention(RetentionPolicy.SOURCE)
    @interface Type {
    }
}
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 android.content.theming;

import android.annotation.FlaggedApi;
import android.annotation.Nullable;

import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;

/** @hide */
@FlaggedApi(android.server.Flags.FLAG_ENABLE_THEME_SERVICE)
public class FieldThemeStyle extends ThemeSettingsField<Integer, String> {
    public FieldThemeStyle(
            String key,
            BiConsumer<ThemeSettingsUpdater, Integer> setter,
            Function<ThemeSettings, Integer> getter,
            ThemeSettings defaults
    ) {
        super(key, setter, getter, defaults);
    }

    private static final @ThemeStyle.Type List<Integer> sValidStyles = Arrays.asList(
            ThemeStyle.EXPRESSIVE,
            ThemeStyle.SPRITZ,
            ThemeStyle.TONAL_SPOT, ThemeStyle.FRUIT_SALAD, ThemeStyle.RAINBOW,
            ThemeStyle.VIBRANT,
            ThemeStyle.MONOCHROMATIC);

    @Override
    public String serialize(@ThemeStyle.Type Integer typedValue) {
        return ThemeStyle.toString(typedValue);
    }

    @Override
    public boolean validate(Integer value) {
        return sValidStyles.contains(value);
    }

    @Override
    @Nullable
    @ThemeStyle.Type
    public Integer parse(String primitive) {
        try {
            return ThemeStyle.valueOf(primitive);
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public Class<Integer> getFieldType() {
        return Integer.class;
    }

    @Override
    public Class<String> getJsonType() {
        return String.class;
    }
}
Loading