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

Commit af86d1c8 authored by Jay Thomas Sullivan's avatar Jay Thomas Sullivan Committed by Yi-an Chen
Browse files

[XPD] Support Preference style injection

This change implements "style injection" for the following Preferences:

- settingslib_expressive_preference
- settingslib_expressive_two_target
- settingslib_expressive_selector_with_widget

...where "style injection" means that the Preference's layouts do not
directly reference any <style>s, but instead receive them via a
corresponding theme <attr>. For each view within any layout associated
with one of these preferences, there is now both an <attr> and a
corresponding <style> defined for that view.

Additionally, layouts sometimes <include> each other into a parent-child
relationship, and some layouts have more than one possible parent (i.e.,
are <include>d by more than one other layout), and some apps may wish to
vary a child-layout's style depending on its parent. This is supported
here via "theme injection": each layout is passed an "android:theme"
(i.e., a theme overlay) provided from its parent.

For Preferences that have their own project outside of SettingsTheme,
we define Preference's styles within their own project, rather than
within SettingsTheme. But, because SettingsTheme cannot reference those
projects, we have to apply these styles at runtime rather than within
the themes defined in a SettingsTheme resource XML file.

Bug: 375480783
Test: manual
Flag: com.android.settingslib.widget.theme.flags.is_expressive_design_enabled
Relnote: N/A
Change-Id: Ice46cfd45cdc35aa809f78dfebeeafd3bbc22beb
parent 18fe3c74
Loading
Loading
Loading
Loading
+12 −60
Original line number Original line Diff line number Diff line
@@ -19,91 +19,43 @@
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_height="wrap_content"
    android:background="?android:attr/selectableItemBackground"
    android:theme="?expressiveSelectorWithWidgetPreferenceTheme"
    android:gravity="center_vertical"
    style="?expressiveSelectorWithWidgetPreferenceRootLayoutStyle">
    android:minHeight="?android:attr/listPreferredItemHeightSmall"
    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">


    <LinearLayout
    <LinearLayout
        android:id="@android:id/widget_frame"
        android:id="@android:id/widget_frame"
        android:layout_width="wrap_content"
        style="?expressiveSelectorWithWidgetPreferenceWidgetFrameStyle" />
        android:layout_height="wrap_content"
        android:gravity="center"
        android:minWidth="@dimen/settingslib_expressive_space_medium3"
        android:minHeight="@dimen/settingslib_expressive_space_medium3"
        android:layout_marginEnd="@dimen/settingslib_expressive_space_extrasmall6"
        android:orientation="vertical"/>


    <include layout="@layout/settingslib_expressive_preference_icon_frame" />
    <include layout="@layout/settingslib_expressive_preference_icon_frame" />


    <LinearLayout
    <LinearLayout style="?expressiveSelectorWithWidgetPreferenceTextContainerStyle">
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical"
        android:paddingTop="@dimen/settingslib_expressive_space_small1"
        android:paddingBottom="@dimen/settingslib_expressive_space_small1"
        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">


        <TextView
        <TextView
            android:id="@android:id/title"
            android:id="@android:id/title"
            android:layout_width="wrap_content"
            style="?expressiveSelectorWithWidgetPreferenceTitleStyle" />
            android:layout_height="wrap_content"
            android:maxLines="2"
            android:ellipsize="end"
            android:hyphenationFrequency="normalFast"
            android:lineBreakWordStyle="phrase"
            android:textAppearance="?android:attr/textAppearanceListItem"/>


        <LinearLayout
        <LinearLayout
            android:id="@+id/summary_container"
            android:id="@+id/summary_container"
            android:layout_width="match_parent"
            style="?expressiveSelectorWithWidgetPreferenceSummaryContainerStyle">
            android:layout_height="wrap_content"

            android:visibility="gone">
            <TextView
            <TextView
                android:id="@android:id/summary"
                android:id="@android:id/summary"
                android:layout_width="0dp"
                style="?expressiveSelectorWithWidgetPreferenceSummaryStyle" />
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:textAppearance="?android:attr/textAppearanceSmall"
                android:textAlignment="viewStart"
                android:hyphenationFrequency="normalFast"
                android:lineBreakWordStyle="phrase"
                android:textColor="?android:attr/textColorSecondary"/>


            <TextView
            <TextView
                android:id="@+id/appendix"
                android:id="@+id/appendix"
                android:layout_width="0dp"
                style="?expressiveSelectorWithWidgetPreferenceAppendixStyle" />
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:textAppearance="?android:attr/textAppearanceSmall"
                android:textAlignment="viewEnd"
                android:textColor="?android:attr/textColorSecondary"
                android:maxLines="1"
                android:visibility="gone"
                android:ellipsize="end"/>
        </LinearLayout>
        </LinearLayout>
    </LinearLayout>
    </LinearLayout>


    <LinearLayout
    <LinearLayout
        android:id="@+id/selector_extra_widget_container"
        android:id="@+id/selector_extra_widget_container"
        android:layout_width="wrap_content"
        style="?expressiveSelectorWithWidgetPreferenceExtraWidgetContainerStyle">
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:gravity="center_vertical">


        <include layout="@layout/settingslib_expressive_two_target_divider" />
        <include layout="@layout/settingslib_expressive_two_target_divider" />


        <ImageView
        <ImageView
            android:id="@+id/selector_extra_widget"
            android:id="@+id/selector_extra_widget"
            android:layout_width="match_parent"
            style="?expressiveSelectorWithWidgetPreferenceExtraWidgetImageStyle" />
            android:minWidth="@dimen/settingslib_expressive_space_medium4"
            android:layout_height="fill_parent"
            android:src="@drawable/ic_settings_accent"
            android:scaleType="centerInside"
            android:contentDescription="@string/settings_label"
            android:layout_gravity="center"
            android:background="?android:attr/selectableItemBackground"/>
    </LinearLayout>
    </LinearLayout>
</LinearLayout>
</LinearLayout>
+141 −0
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
    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.
  -->
<resources>
    <style name="ThemeOverlay.ExpressiveSelectorWithWidgetPreference" parent="">
        <item name="expressiveSelectorWithWidgetPreferenceTheme">@style/ExpressiveSelectorWithWidgetPreferenceTheme</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceTheme">
        <item name="expressiveSelectorWithWidgetPreferenceRootLayoutStyle">@style/ExpressiveSelectorWithWidgetPreferenceRootLayoutStyle</item>
        <item name="expressiveSelectorWithWidgetPreferenceWidgetFrameStyle">@style/ExpressiveSelectorWithWidgetPreferenceWidgetFrameStyle</item>
        <item name="expressiveSelectorWithWidgetPreferenceIconStyle">@style/ExpressiveSelectorWithWidgetPreferenceIconStyle</item>
        <item name="expressiveSelectorWithWidgetPreferenceTextContainerStyle">@style/ExpressiveSelectorWithWidgetPreferenceTextContainerStyle</item>
        <item name="expressiveSelectorWithWidgetPreferenceTitleStyle">@style/ExpressiveSelectorWithWidgetPreferenceTitleStyle</item>
        <item name="expressiveSelectorWithWidgetPreferenceSummaryContainerStyle">@style/ExpressiveSelectorWithWidgetPreferenceSummaryContainerStyle</item>
        <item name="expressiveSelectorWithWidgetPreferenceSummaryStyle">@style/ExpressiveSelectorWithWidgetPreferenceSummaryStyle</item>
        <item name="expressiveSelectorWithWidgetPreferenceAppendixStyle">@style/ExpressiveSelectorWithWidgetPreferenceAppendixStyle</item>
        <item name="expressiveSelectorWithWidgetPreferenceExtraWidgetContainerStyle">@style/ExpressiveSelectorWithWidgetPreferenceExtraWidgetContainerStyle</item>
        <item name="expressiveSelectorWithWidgetPreferenceExtraWidgetDividerStyle">@style/ExpressiveSelectorWithWidgetPreferenceExtraWidgetDividerStyle</item>
        <item name="expressiveSelectorWithWidgetPreferenceExtraWidgetImageStyle">@style/ExpressiveSelectorWithWidgetPreferenceExtraWidgetImageStyle</item>
        <!-- We need these because layout-v36/settingslib_preference_selector_with_widget.xml may be used in non-expressive theme. -->
        <item name="expressivePreferenceIconFrameStyle">@style/ExpressivePreferenceIconFrameStyle</item>
        <item name="expressivePreferenceIconStyle">@style/ExpressivePreferenceIconStyle</item>
        <item name="expressiveTwoTargetDividerStyle">@style/ExpressiveTwoTargetDividerStyle</item>
        <item name="expressiveTwoTargetDividerImageViewStyle">@style/ExpressiveTwoTargetDividerImageViewStyle</item>
        <item name="expressiveTwoTargetDividerViewStyle">@style/ExpressiveTwoTargetDividerViewStyle</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceRootLayoutStyle">
        <item name="android:background">?android:attr/selectableItemBackground</item>
        <item name="android:gravity">center_vertical</item>
        <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item>
        <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item>
        <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceWidgetFrameStyle">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:gravity">center</item>
        <item name="android:minWidth">@dimen/settingslib_expressive_space_medium3</item>
        <item name="android:minHeight">@dimen/settingslib_expressive_space_medium3</item>
        <item name="android:layout_marginEnd">@dimen/settingslib_expressive_space_extrasmall6</item>
        <item name="android:orientation">vertical</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceIconStyle">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="maxWidth">@dimen/secondary_app_icon_size</item>
        <item name="maxHeight">@dimen/secondary_app_icon_size</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceTextContainerStyle">
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_weight">1</item>
        <item name="android:orientation">vertical</item>
        <item name="android:paddingTop">@dimen/settingslib_expressive_space_small1</item>
        <item name="android:paddingBottom">@dimen/settingslib_expressive_space_small1</item>
        <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceTitleStyle">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:maxLines">2</item>
        <item name="android:ellipsize">end</item>
        <item name="android:hyphenationFrequency">normalFast</item>
        <item name="android:lineBreakWordStyle">phrase</item>
        <item name="android:textAppearance">?android:attr/textAppearanceListItem</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceSummaryContainerStyle">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:visibility">gone</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceSummaryStyle">
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_weight">1</item>
        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
        <item name="android:textAlignment">viewStart</item>
        <item name="android:hyphenationFrequency">normalFast</item>
        <item name="android:lineBreakWordStyle">phrase</item>
        <item name="android:textColor">?android:attr/textColorSecondary</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceAppendixStyle">
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_weight">1</item>
        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
        <item name="android:textAlignment">viewEnd</item>
        <item name="android:textColor">?android:attr/textColorSecondary</item>
        <item name="android:maxLines">1</item>
        <item name="android:visibility">gone</item>
        <item name="android:ellipsize">end</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceExtraWidgetContainerStyle">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">match_parent</item>
        <item name="android:orientation">horizontal</item>
        <item name="android:gravity">center_vertical</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceExtraWidgetDividerStyle">
        <item name="android:layout_width">.75dp</item>
        <item name="android:layout_height">32dp</item>
        <item name="android:layout_marginTop">@dimen/settingslib_expressive_space_small1</item>
        <item name="android:layout_marginBottom">@dimen/settingslib_expressive_space_small1</item>
        <item name="android:background">?android:attr/dividerVertical</item>
    </style>

    <style name="ExpressiveSelectorWithWidgetPreferenceExtraWidgetImageStyle">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">fill_parent</item>
        <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item>
        <item name="android:src">@drawable/ic_settings_accent</item>
        <item name="android:scaleType">centerInside</item>
        <item name="android:contentDescription">@string/settings_label</item>
        <item name="android:layout_gravity">center</item>
        <item name="android:background">?android:attr/selectableItemBackground</item>
    </style>
</resources>
+18 −3
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.settingslib.widget;
package com.android.settingslib.widget;


import android.content.Context;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.AttributeSet;
@@ -86,7 +87,7 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
     */
     */
    public SelectorWithWidgetPreference(@NonNull Context context, @Nullable AttributeSet attrs,
    public SelectorWithWidgetPreference(@NonNull Context context, @Nullable AttributeSet attrs,
            int defStyle) {
            int defStyle) {
        super(context, attrs, defStyle);
        super(applyExpressivePreferenceThemeOverlay(context), attrs, defStyle);
        init(context, attrs, defStyle, /* defStyleRes= */ 0);
        init(context, attrs, defStyle, /* defStyleRes= */ 0);
    }
    }


@@ -98,7 +99,7 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
     * @param attrs   The attributes of the XML tag that is inflating the preference
     * @param attrs   The attributes of the XML tag that is inflating the preference
     */
     */
    public SelectorWithWidgetPreference(Context context, AttributeSet attrs) {
    public SelectorWithWidgetPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
        super(applyExpressivePreferenceThemeOverlay(context), attrs);
        init(context, attrs, /* defStyleAttr= */ 0, /* defStyleRes= */ 0);
        init(context, attrs, /* defStyleAttr= */ 0, /* defStyleRes= */ 0);
    }
    }


@@ -109,7 +110,7 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
     * @param isCheckbox Whether this preference should display as a checkbox.
     * @param isCheckbox Whether this preference should display as a checkbox.
     */
     */
    public SelectorWithWidgetPreference(Context context, boolean isCheckbox) {
    public SelectorWithWidgetPreference(Context context, boolean isCheckbox) {
        super(context, null);
        super(applyExpressivePreferenceThemeOverlay(context), null);
        mIsCheckBox = isCheckbox;
        mIsCheckBox = isCheckbox;
        init(context, /* attrs= */ null, /* defStyleAttr= */ 0, /* defStyleRes= */ 0);
        init(context, /* attrs= */ null, /* defStyleAttr= */ 0, /* defStyleRes= */ 0);
    }
    }
@@ -249,6 +250,20 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
        a.recycle();
        a.recycle();
    }
    }


    @NonNull
    private static Context applyExpressivePreferenceThemeOverlay(@NonNull Context context) {
        TypedArray typedArray = context.obtainStyledAttributes(new int[] {
                com.android.settingslib.widget.theme.R.attr
                        .expressiveSelectorWithWidgetPreferenceTheme});
        // Since the context is shared, only try to apply the theme if it 's not resolved.
        if (typedArray.getResourceId(0, Resources.ID_NULL) == Resources.ID_NULL) {
            context.getTheme().applyStyle(
                    R.style.ThemeOverlay_ExpressiveSelectorWithWidgetPreference, false);
        }
        typedArray.recycle();
        return context;
    }

    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
    public View getExtraWidget() {
    public View getExtraWidget() {
        return mExtraWidget;
        return mExtraWidget;
+7 −18
Original line number Original line Diff line number Diff line
@@ -19,26 +19,15 @@
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_height="wrap_content"
    android:minHeight="72dp"
    android:filterTouchesWhenObscured="false"
    android:gravity="center_vertical"
    android:theme="?expressivePreferenceTheme"
    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
    style="?expressivePreferenceRootLayoutStyle">
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
    android:background="?android:attr/selectableItemBackground"
    android:clipToPadding="false"
    android:baselineAligned="false"
    android:filterTouchesWhenObscured="false">


    <include layout="@layout/settingslib_expressive_preference_icon_frame" />
    <include layout="@layout/settingslib_expressive_preference_icon_frame" />


    <include layout="@layout/settingslib_expressive_preference_text_frame" />
    <include layout="@layout/settingslib_expressive_preference_text_frame" />


    <!-- Preference should place its actual preference widget here. -->
    <LinearLayout
    <LinearLayout
        android:id="@android:id/widget_frame"
        android:id="@android:id/widget_frame"
        android:layout_width="wrap_content"
        style="?expressivePreferenceWidgetFrameStyle" />
        android:layout_height="match_parent"
        android:gravity="end|center_vertical"
        android:minWidth="@dimen/settingslib_expressive_space_medium4"
        android:orientation="vertical"/>

</LinearLayout>
</LinearLayout>
+4 −11
Original line number Original line Diff line number Diff line
@@ -14,21 +14,14 @@
    See the License for the specific language governing permissions and
    See the License for the specific language governing permissions and
    limitations under the License.
    limitations under the License.
  -->
  -->

<LinearLayout
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/icon_frame"
    android:id="@+id/icon_frame"
    android:layout_width="wrap_content"
    android:filterTouchesWhenObscured="false"
    android:layout_height="wrap_content"
    style="?expressivePreferenceIconFrameStyle">
    android:minWidth="@dimen/settingslib_expressive_space_medium3"
    android:minHeight="@dimen/settingslib_expressive_space_medium3"
    android:gravity="center"
    android:layout_marginEnd="@dimen/settingslib_expressive_space_extrasmall6"
    android:filterTouchesWhenObscured="false">


    <androidx.preference.internal.PreferenceImageView
    <androidx.preference.internal.PreferenceImageView
        android:id="@android:id/icon"
        android:id="@android:id/icon"
        android:layout_width="@dimen/settingslib_expressive_space_medium3"
        style="?expressivePreferenceIconStyle" />
        android:layout_height="@dimen/settingslib_expressive_space_medium3"
        android:scaleType="centerInside"/>

</LinearLayout>
</LinearLayout>
Loading