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

Commit 44ad7fc0 authored by Santiago Etchebehere's avatar Santiago Etchebehere Committed by Android (Google) Code Review
Browse files

Merge "Add developer settings for theme overlays."

parents 90f75b2f 6f8048b0
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -9507,14 +9507,25 @@
    <!-- Notification log debug tool: the word 'none' -->
    <string name="notification_log_details_ranking_none">Ranking object doesn\'t contain this key.</string>
    <!-- [CHAR_LIMIT=NONE] Developer Settings: Title of the settings category for theme overlays. -->
    <string name="theme_customization_category">Theming</string>
    <!-- [CHAR_LIMIT=NONE] Developer Settings: Title of the setting which enables overlays to customize accent color. -->
    <string name="theme_customization_accent_color_title">Accent color</string>
    <!-- [CHAR_LIMIT=NONE] Developer Settings: Title of the setting which enables overlays to customize headline and body fonts. -->
    <string name="theme_customization_font_title">Headline / Body font</string>
    <!-- [CHAR_LIMIT=NONE] Developer Settings: Title of the setting which enables overlays to customize the adaptive icon shape (e.g. launcher and quick settings icons). -->
    <string name="theme_customization_icon_shape_title">Icon shape</string>
    <!-- [CHAR_LIMIT=NONE] Developer Settings: Label for the option that turns off customizations for a given category.-->
    <string name="theme_customization_device_default">Device default</string>
    <!-- [CHAR_LIMIT=NONE] Developer Settings: Title of the setting which turns on emulation of a display cutout. -->
    <string name="display_cutout_emulation">Display cutout</string>
    <!-- [CHAR_LIMIT=NONE] Developer Settings: Search keywords for the setting which turns on emulation of a display cutout. -->
    <string name="display_cutout_emulation_keywords">display cutout, notch</string>
    <!-- [CHAR_LIMIT=NONE] Developer Settings: Label for the option that turns off display cutout emulation, (i.e. on devices whose screen actually has a cutout, selecting this option will show that cutout).-->
    <string name="display_cutout_emulation_device_default">Device default</string>
    <!-- [CHAR_LIMIT=NONE] Developer Settings: Label for the option that turns off all overlays in a given category. -->
    <string name="overlay_option_device_default">Device default</string>
    <!-- [CHAR_LIMIT=60] Label for special access screen -->
    <string name="special_access">Special app access</string>
+15 −0
Original line number Diff line number Diff line
@@ -559,4 +559,19 @@

    </com.android.settings.development.autofill.AutofillPreferenceCategory>

    <PreferenceCategory
        android:key="theme_customization_category"
        android:title="@string/theme_customization_category"
        android:order="1200"> <!-- Incremented by 100 from last order (i.e. 1100) -->

        <ListPreference
            android:key="android.theme.customization.accent_color"
            android:title="@string/theme_customization_accent_color_title"/>
        <ListPreference
            android:key="android.theme.customization.font"
            android:title="@string/theme_customization_font_title"/>
        <ListPreference
            android:key="android.theme.customization.adaptive_icon_shape"
            android:title="@string/theme_customization_icon_shape_title"/>
    </PreferenceCategory>
</PreferenceScreen>
+6 −0
Original line number Diff line number Diff line
@@ -492,6 +492,12 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
        controllers.add(new DefaultLaunchPreferenceController(context, "inactive_apps"));
        controllers.add(new AutofillLoggingLevelPreferenceController(context, lifecycle));
        controllers.add(new AutofillResetOptionsPreferenceController(context));
        controllers.add(new OverlayCategoryPreferenceController(context,
                "android.theme.customization.accent_color"));
        controllers.add(new OverlayCategoryPreferenceController(context,
                "android.theme.customization.font"));
        controllers.add(new OverlayCategoryPreferenceController(context,
                "android.theme.customization.adaptive_icon_shape"));
        return controllers;
    }

+2 −138
Original line number Diff line number Diff line
@@ -16,50 +16,22 @@

package com.android.settings.development;

import static android.os.UserHandle.USER_SYSTEM;

import android.content.Context;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.view.DisplayCutout;

import androidx.annotation.VisibleForTesting;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;

import java.util.Comparator;
import java.util.List;

public class EmulateDisplayCutoutPreferenceController extends
        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
        PreferenceControllerMixin {
public class EmulateDisplayCutoutPreferenceController extends OverlayCategoryPreferenceController {

    private static final String KEY = "display_cutout_emulation";
    private static final Comparator<OverlayInfo> OVERLAY_INFO_COMPARATOR =
            Comparator.comparingInt(a -> a.priority);

    private final IOverlayManager mOverlayManager;
    private final boolean mAvailable;

    private ListPreference mPreference;
    private PackageManager mPackageManager;

    @VisibleForTesting
    EmulateDisplayCutoutPreferenceController(Context context, PackageManager packageManager,
            IOverlayManager overlayManager) {
        super(context);
        mOverlayManager = overlayManager;
        mPackageManager = packageManager;
        mAvailable = overlayManager != null && getOverlayInfos().length > 0;
        super(context, packageManager, overlayManager, DisplayCutout.EMULATION_OVERLAY_CATEGORY);
    }

    public EmulateDisplayCutoutPreferenceController(Context context) {
@@ -67,116 +39,8 @@ public class EmulateDisplayCutoutPreferenceController extends
                .asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE)));
    }

    @Override
    public boolean isAvailable() {
        return mAvailable;
    }

    @Override
    public String getPreferenceKey() {
        return KEY;
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        setPreference((ListPreference) screen.findPreference(getPreferenceKey()));
    }

    @VisibleForTesting
    void setPreference(ListPreference preference) {
        mPreference = preference;
    }

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        return setEmulationOverlay((String) newValue);
    }

    private boolean setEmulationOverlay(String packageName) {
        OverlayInfo[] overlays = getOverlayInfos();
        String currentPackageName = null;
        for (OverlayInfo o : overlays) {
            if (o.isEnabled()) {
                currentPackageName = o.packageName;
            }
        }

        if (TextUtils.isEmpty(packageName) && TextUtils.isEmpty(currentPackageName)
                || TextUtils.equals(packageName, currentPackageName)) {
            // Already set.
            return true;
        }

        final boolean result;
        try {
            if (TextUtils.isEmpty(packageName)) {
                result = mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM);
            } else {
                result = mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM);
            }
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
        updateState(mPreference);
        return result;
    }

    @Override
    public void updateState(Preference preference) {
        OverlayInfo[] overlays = getOverlayInfos();

        CharSequence[] pkgs = new CharSequence[overlays.length + 1];
        CharSequence[] labels = new CharSequence[pkgs.length];

        int current = 0;
        pkgs[0] = "";
        labels[0] = mContext.getString(R.string.display_cutout_emulation_device_default);

        for (int i = 0; i < overlays.length; i++) {
            OverlayInfo o = overlays[i];
            pkgs[i+1] = o.packageName;
            if (o.isEnabled()) {
                current = i+1;
            }
        }
        for (int i = 1; i < pkgs.length; i++) {
            try {
                labels[i] = mPackageManager.getApplicationInfo(pkgs[i].toString(), 0)
                        .loadLabel(mPackageManager);
            } catch (PackageManager.NameNotFoundException e) {
                labels[i] = pkgs[i];
            }
        }

        mPreference.setEntries(labels);
        mPreference.setEntryValues(pkgs);
        mPreference.setValueIndex(current);
        mPreference.setSummary(labels[current]);
    }

    private OverlayInfo[] getOverlayInfos() {
        List<OverlayInfo> overlayInfos;
        try {
            overlayInfos = mOverlayManager.getOverlayInfosForTarget("android", USER_SYSTEM);
            for (int i = overlayInfos.size() - 1; i >= 0; i--) {
                if (!DisplayCutout.EMULATION_OVERLAY_CATEGORY.equals(
                        overlayInfos.get(i).category)) {
                    overlayInfos.remove(i);
                }
            }
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
        overlayInfos.sort(OVERLAY_INFO_COMPARATOR);
        return overlayInfos.toArray(new OverlayInfo[overlayInfos.size()]);
    }

    @Override
    protected void onDeveloperOptionsSwitchDisabled() {
        super.onDeveloperOptionsSwitchDisabled();
        setEmulationOverlay("");
        updateState(mPreference);
    }

}
+188 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.settings.development;

import static android.os.UserHandle.USER_SYSTEM;

import android.content.Context;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;

import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import androidx.annotation.VisibleForTesting;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;

/**
 * Preference controller to allow users to choose an overlay from a list for a given category.
 * The chosen overlay is enabled exclusively within its category. A default option is also
 * exposed that disables all overlays in the given category.
 */
public class OverlayCategoryPreferenceController extends DeveloperOptionsPreferenceController
        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
    @VisibleForTesting
    static final String PACKAGE_DEVICE_DEFAULT = "package_device_default";
    private static final String OVERLAY_TARGET_PACKAGE = "android";
    private static final Comparator<OverlayInfo> OVERLAY_INFO_COMPARATOR =
            Comparator.comparingInt(a -> a.priority);
    private final IOverlayManager mOverlayManager;
    private final boolean mAvailable;
    private final String mCategory;
    private final PackageManager mPackageManager;

    private ListPreference mPreference;

    @VisibleForTesting
    OverlayCategoryPreferenceController(Context context, PackageManager packageManager,
            IOverlayManager overlayManager, String category) {
        super(context);
        mOverlayManager = overlayManager;
        mPackageManager = packageManager;
        mCategory = category;
        mAvailable = overlayManager != null && !getOverlayInfos().isEmpty();
    }

    public OverlayCategoryPreferenceController(Context context, String category) {
        this(context, context.getPackageManager(), IOverlayManager.Stub
                .asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE)), category);
    }

    @Override
    public boolean isAvailable() {
        return mAvailable;
    }

    @Override
    public String getPreferenceKey() {
        return mCategory;
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        setPreference((ListPreference) screen.findPreference(getPreferenceKey()));
    }

    @VisibleForTesting
    void setPreference(ListPreference preference) {
        mPreference = preference;
    }

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        return setOverlay((String) newValue);
    }

    private boolean setOverlay(String packageName) {
        String currentPackageName = null;
        for (OverlayInfo o : getOverlayInfos()) {
            if (o.isEnabled()) {
                currentPackageName = o.packageName;
            }
        }

        if (PACKAGE_DEVICE_DEFAULT.equals(packageName) && TextUtils.isEmpty(currentPackageName)
                || TextUtils.equals(packageName, currentPackageName)) {
            // Already set.
            return true;
        }

        final boolean result;
        try {
            if (PACKAGE_DEVICE_DEFAULT.equals(packageName)) {
                result = mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM);
            } else {
                result = mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM);
            }
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
        updateState(mPreference);
        return result;
    }

    @Override
    public void updateState(Preference preference) {
        final List<String> pkgs = new ArrayList<>();
        final List<String> labels = new ArrayList<>();

        String selectedPkg = PACKAGE_DEVICE_DEFAULT;
        String selectedLabel = mContext.getString(R.string.overlay_option_device_default);

        // Add the default package / label before all of the overlays
        pkgs.add(selectedPkg);
        labels.add(selectedLabel);

        for (OverlayInfo overlayInfo : getOverlayInfos()) {
            pkgs.add(overlayInfo.packageName);
            try {
                labels.add(mPackageManager.getApplicationInfo(overlayInfo.packageName, 0)
                        .loadLabel(mPackageManager).toString());
            } catch (PackageManager.NameNotFoundException e) {
                labels.add(overlayInfo.packageName);
            }
            if (overlayInfo.isEnabled()) {
                selectedPkg = pkgs.get(pkgs.size() - 1);
                selectedLabel = labels.get(labels.size() - 1);
            }
        }

        mPreference.setEntries(labels.toArray(new String[labels.size()]));
        mPreference.setEntryValues(pkgs.toArray(new String[pkgs.size()]));
        mPreference.setValue(selectedPkg);
        mPreference.setSummary(selectedLabel);
    }

    private List<OverlayInfo> getOverlayInfos() {
        final List<OverlayInfo> filteredInfos = new ArrayList<>();
        try {
            List<OverlayInfo> overlayInfos = mOverlayManager
                    .getOverlayInfosForTarget(OVERLAY_TARGET_PACKAGE, USER_SYSTEM);
            for (OverlayInfo overlayInfo : overlayInfos) {
                if (mCategory.equals(overlayInfo.category)) {
                    filteredInfos.add(overlayInfo);
                }
            }
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
        filteredInfos.sort(OVERLAY_INFO_COMPARATOR);
        return filteredInfos;
    }

    @Override
    protected void onDeveloperOptionsSwitchDisabled() {
        super.onDeveloperOptionsSwitchDisabled();
        // STOPSHIP b/122308197: reset the overlays to the set in
        // Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES
        setOverlay(PACKAGE_DEVICE_DEFAULT);
        updateState(mPreference);
    }

}
Loading