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

Commit 9e2ac046 authored by Matías Hernández's avatar Matías Hernández
Browse files

Changes to the trigger segment

* Switch is always visible, even without a configuration activity
* Custom icon, title, and summary for some mode types (such as SCHEDULE_TIME, etc).
* Default texts in case of missing trigger description.
* Different icons for having/missing configuration activity.
* Move the section to the top of the screen.

Bug: 349376785
Test: atest ZenModeTriggerLinkPreferenceControllerTest
Flag: android.app.modes_ui
Change-Id: I960318899cf4da20ffc5765818429d5790d05067
parent 51bb0fe4
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
<!--
Copyright (C) 2024 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="?android:attr/colorControlNormal"
    android:viewportHeight="960"
    android:viewportWidth="960"
    android:autoMirrored="true">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,600L200,600L200,760Q200,760 200,760Q200,760 200,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,360L120,360L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM420,680L364,622L466,520L120,520L120,440L466,440L364,338L420,280L620,480L420,680Z" />
</vector>
 No newline at end of file
+25 −0
Original line number Diff line number Diff line
<!--
Copyright (C) 2024 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="?android:attr/colorControlNormal"
    android:viewportHeight="960"
    android:viewportWidth="960">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M352,840L200,840Q167,840 143.5,816.5Q120,793 120,760L120,608Q168,608 204,577.5Q240,547 240,500Q240,453 204,422.5Q168,392 120,392L120,240Q120,207 143.5,183.5Q167,160 200,160L360,160Q360,118 389,89Q418,60 460,60Q502,60 531,89Q560,118 560,160L720,160Q753,160 776.5,183.5Q800,207 800,240L800,400Q842,400 871,429Q900,458 900,500Q900,542 871,571Q842,600 800,600L800,760Q800,793 776.5,816.5Q753,840 720,840L568,840Q568,790 536.5,755Q505,720 460,720Q415,720 383.5,755Q352,790 352,840ZM200,760L285,760Q309,694 362,667Q415,640 460,640Q505,640 558,667Q611,694 635,760L720,760L720,520L800,520Q808,520 814,514Q820,508 820,500Q820,492 814,486Q808,480 800,480L720,480L720,240L480,240L480,160Q480,152 474,146Q468,140 460,140Q452,140 446,146Q440,152 440,160L440,240L200,240L200,328Q254,348 287,395Q320,442 320,500Q320,557 287,604Q254,651 200,672L200,760ZM460,500L460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500L460,500L460,500L460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500L460,500L460,500L460,500L460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500L460,500L460,500L460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500Q460,500 460,500Z" />
</vector>
 No newline at end of file
+15 −2
Original line number Diff line number Diff line
@@ -8124,9 +8124,9 @@
    <!--  Do not disturb: Subtitle for the Visual signals option to toggle on/off visual signals/alerts when the screen is on/when screen is off. [CHAR LIMIT=30] -->
    <string name="zen_mode_visual_signals_settings_subtitle">Allow visual signals</string>
    <!-- Do not disturb: mode page section title [CHAR LIMIT=80] -->
    <!-- Priority Modes: mode page section title [CHAR LIMIT=80] -->
    <string name="mode_interruption_filter_title">Stay focused</string>
    <!-- Do not disturb: mode page section title [CHAR LIMIT=80] -->
    <!-- Priority Modes: mode page section title [CHAR LIMIT=80] -->
    <string name="mode_device_effects_title">Additional actions</string>
    <!-- Summary for the Sound Do not Disturb option when DND isn't currently on. [CHAR LIMIT=NONE]-->
@@ -9483,6 +9483,19 @@
    <!-- Priority Modes: Hint for the EditText for editing a mode's name [CHAR LIMIT=30] -->
    <string name="zen_mode_edit_name_hint">Mode name</string>
    <!-- Priority Modes: Trigger title for modes of type SCHEDULE_CALENDAR. [CHAR LIMIT=30] -->
    <string name="zen_mode_trigger_title_schedule_calendar">Calendar events</string>
    <!-- Priority Modes: Trigger title for modes of type BEDTIME. [CHAR LIMIT=30] -->
    <string name="zen_mode_trigger_title_bedtime">Sleep schedule</string>
    <!-- Priority Modes: Trigger title for modes of type DRIVING. [CHAR LIMIT=30] -->
    <string name="zen_mode_trigger_title_driving">While driving</string>
    <!-- Priority Modes: Generic trigger title for modes of other types [CHAR LIMIT=30] -->
    <string name="zen_mode_trigger_title_generic">Linked to app</string>
    <!-- Priority Modes: Generic trigger summary for modes where the owner app did not provide a triggerDescription but did provide a configurationActivity to call [CHAR LIMIT=60] -->
    <string name="zen_mode_trigger_summary_settings_in_app">Info and settings in <xliff:g id="app_name" example="The Awesome App">%1$s</xliff:g></string>
    <!-- Priority Modes: Generic trigger summary for modes where the owner app did not provide neither a triggerDescription nor a configurationActivity to call [CHAR LIMIT=60] -->
    <string name="zen_mode_trigger_summary_managed_by_app">Managed by <xliff:g id="app_name" example="The Awesome App">%1$s</xliff:g></string>
    <!-- Content description for help icon button [CHAR LIMIT=20] -->
    <string name="warning_button_text">Warning</string>
+15 −8
Original line number Diff line number Diff line
@@ -28,6 +28,21 @@
            android:selectable="false"
            android:layout="@layout/modes_activation_button"/>

    <!-- automatic trigger section; preference changes programmatically depending on type -->
    <PreferenceCategory
        android:key="zen_automatic_trigger_category"
        android:title="@string/zen_mode_automatic_trigger_title">
        <!-- For configuring the trigger on tap  and enabling/disabling the mode with the switch. -->
        <com.android.settingslib.PrimarySwitchPreference
            android:key="zen_automatic_trigger_settings" />
        <!-- For adding a trigger for custom manual modes (no switch). -->
        <Preference
            android:key="zen_add_automatic_trigger"
            android:title="@string/zen_mode_select_schedule"
            android:icon="@drawable/ic_add_24dp"
            settings:isPreferenceVisible="false" />
    </PreferenceCategory>

    <PreferenceCategory
            android:title="@string/mode_interruption_filter_title"
            android:key="modes_filters">
@@ -49,14 +64,6 @@
            android:title="@string/zen_category_exceptions" />
    </PreferenceCategory>

    <!-- automatic trigger section; preference changes programmatically depending on type -->
    <PreferenceCategory
        android:key="zen_automatic_trigger_category"
        android:title="@string/zen_mode_automatic_trigger_title">
        <com.android.settingslib.PrimarySwitchPreference
            android:key="zen_automatic_trigger_settings" />
    </PreferenceCategory>

    <PreferenceCategory
            android:title="@string/mode_device_effects_title"
            android:key="modes_additional_actions">
+135 −46
Original line number Diff line number Diff line
@@ -16,14 +16,26 @@

package com.android.settings.notification.modes;

import static android.app.AutomaticZenRule.TYPE_BEDTIME;
import static android.app.AutomaticZenRule.TYPE_DRIVING;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
import static android.service.notification.ZenModeConfig.tryParseScheduleConditionId;

import static com.google.common.base.Preconditions.checkNotNull;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenModeConfig;
import android.util.Log;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
@@ -35,6 +47,8 @@ import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;

import com.google.common.base.Strings;

/**
 * Preference controller for the link to an individual mode's configuration page.
 */
@@ -42,26 +56,29 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc
    private static final String TAG = "ZenModeSetTriggerLink";

    @VisibleForTesting
    protected static final String AUTOMATIC_TRIGGER_PREF_KEY = "zen_automatic_trigger_settings";
    static final String AUTOMATIC_TRIGGER_KEY = "zen_automatic_trigger_settings";
    static final String ADD_TRIGGER_KEY = "zen_add_automatic_trigger";

    private final DashboardFragment mFragment;
    private final PackageManager mPackageManager;
    private final ConfigurationActivityHelper mConfigurationActivityHelper;
    private final ZenServiceListing mServiceListing;
    private final DashboardFragment mFragment;

    ZenModeSetTriggerLinkPreferenceController(Context context, String key,
            DashboardFragment fragment, ZenModesBackend backend) {
        this(context, key, fragment, backend,
        this(context, key, fragment, backend, context.getPackageManager(),
                new ConfigurationActivityHelper(context.getPackageManager()),
                new ZenServiceListing(context));
    }

    @VisibleForTesting
    ZenModeSetTriggerLinkPreferenceController(Context context, String key,
            DashboardFragment fragment, ZenModesBackend backend,
            DashboardFragment fragment, ZenModesBackend backend, PackageManager packageManager,
            ConfigurationActivityHelper configurationActivityHelper,
            ZenServiceListing serviceListing) {
        super(context, key, backend);
        mFragment = fragment;
        mPackageManager = packageManager;
        mConfigurationActivityHelper = configurationActivityHelper;
        mServiceListing = serviceListing;
    }
@@ -83,53 +100,126 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc
        // This controller is expected to govern a preference category so that it controls the
        // availability of the entire preference category if the mode doesn't have a way to
        // automatically trigger (such as manual DND).
        PrimarySwitchPreference switchPref = ((PreferenceCategory) preference).findPreference(
                AUTOMATIC_TRIGGER_PREF_KEY);
        if (switchPref == null) {
        if (zenMode.isManualDnd()) {
            return;
        }
        switchPref.setChecked(zenMode.getRule().isEnabled());
        switchPref.setOnPreferenceChangeListener(mSwitchChangeListener);
        switchPref.setSummary(zenMode.getRule().getTriggerDescription());
        switchPref.setIcon(null);
        switchPref.setOnPreferenceClickListener(null);
        switchPref.setIntent(null);
        PrimarySwitchPreference triggerPref = checkNotNull(
                ((PreferenceCategory) preference).findPreference(AUTOMATIC_TRIGGER_KEY));
        Preference addTriggerPref = checkNotNull(
                ((PreferenceCategory) preference).findPreference(ADD_TRIGGER_KEY));

        boolean isAddTrigger = zenMode.isSystemOwned() && zenMode.getType() != TYPE_SCHEDULE_TIME
                && zenMode.getType() != TYPE_SCHEDULE_CALENDAR;

        if (isAddTrigger) {
            triggerPref.setVisible(false);
            addTriggerPref.setVisible(true);
            addTriggerPref.setOnPreferenceClickListener(unused -> {
                ZenModeScheduleChooserDialog.show(mFragment, mOnScheduleOptionListener);
                return true;
            });
        } else {
            addTriggerPref.setVisible(false);
            triggerPref.setVisible(true);
            triggerPref.setChecked(zenMode.getRule().isEnabled());
            triggerPref.setOnPreferenceChangeListener(mSwitchChangeListener);

            if (zenMode.isSystemOwned()) {
            if (zenMode.getType() == TYPE_SCHEDULE_TIME) {
                switchPref.setTitle(R.string.zen_mode_set_schedule_link);
                setUpForSystemOwnedTrigger(triggerPref, zenMode);
            } else {
                setUpForAppTrigger(triggerPref, zenMode);
            }
        }
    }

    private void setUpForSystemOwnedTrigger(Preference preference, ZenMode mode) {
        if (mode.getType() == TYPE_SCHEDULE_TIME) {
            // TODO: b/332937635 - set correct metrics category
                switchPref.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
                        ZenModeSetScheduleFragment.class, zenMode.getId(), 0).toIntent());
            } else if (zenMode.getType() == TYPE_SCHEDULE_CALENDAR) {
                switchPref.setTitle(R.string.zen_mode_set_calendar_link);
                switchPref.setIcon(null);
            preference.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
                    ZenModeSetScheduleFragment.class, mode.getId(), 0).toIntent());

            // [Clock Icon] 9:00 - 17:00 / Sun-Mon
            preference.setIcon(com.android.internal.R.drawable.ic_zen_mode_type_schedule_time);
            ZenModeConfig.ScheduleInfo schedule =
                    tryParseScheduleConditionId(mode.getRule().getConditionId());
            if (schedule != null) {
                preference.setTitle(SystemZenRules.getTimeSummary(mContext, schedule));
                preference.setSummary(SystemZenRules.getShortDaysSummary(mContext, schedule));
            } else {
                // Fallback, but shouldn't happen.
                Log.wtf(TAG, "SCHEDULE_TIME mode without schedule: " + mode);
                preference.setTitle(R.string.zen_mode_set_schedule_link);
                preference.setSummary(null);
            }
        } else if (mode.getType() == TYPE_SCHEDULE_CALENDAR) {
            // TODO: b/332937635 - set correct metrics category
                switchPref.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
                        ZenModeSetCalendarFragment.class, zenMode.getId(), 0).toIntent());
            preference.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
                    ZenModeSetCalendarFragment.class, mode.getId(), 0).toIntent());

            // [Event Icon] Calendar Events / <Calendar name>
            preference.setIcon(
                    com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar);
            preference.setTitle(R.string.zen_mode_trigger_title_schedule_calendar);
            preference.setSummary(mode.getTriggerDescription());
        } else {
                switchPref.setTitle(R.string.zen_mode_select_schedule);
                switchPref.setIcon(R.drawable.ic_add_24dp);
                switchPref.setSummary("");
                // TODO: b/342156843 - Hide the switch (needs support in SettingsLib).
                switchPref.setOnPreferenceClickListener(clickedPreference -> {
                    ZenModeScheduleChooserDialog.show(mFragment, mOnScheduleOptionListener);
                    return true;
                });
            Log.wtf(TAG, "Unexpected type for system-owned mode: " + mode);
        }
    }

    @SuppressLint("SwitchIntDef")
    private void setUpForAppTrigger(Preference preference, ZenMode mode) {
        // App-owned mode may have triggerDescription, configurationActivity, or both/neither.
        Intent configurationIntent =
                mConfigurationActivityHelper.getConfigurationActivityIntentForMode(
                        mode, mServiceListing::findService);

        @StringRes int title = switch (mode.getType()) {
            case TYPE_BEDTIME -> R.string.zen_mode_trigger_title_bedtime;
            case TYPE_DRIVING -> R.string.zen_mode_trigger_title_driving;
            default -> R.string.zen_mode_trigger_title_generic;
        };

        String summary;
        if (!Strings.isNullOrEmpty(mode.getTriggerDescription())) {
            summary = mode.getTriggerDescription();
        } else if (!Strings.isNullOrEmpty(mode.getRule().getPackageName())) {
            String appName = null;
            try {
                ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
                        mode.getRule().getPackageName(), 0);
                appName = appInfo.loadLabel(mPackageManager).toString();
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "Couldn't resolve owner for mode: " + mode);
            }

            if (appName != null) {
                summary = mContext.getString(
                        configurationIntent != null
                                ? R.string.zen_mode_trigger_summary_settings_in_app
                                : R.string.zen_mode_trigger_summary_managed_by_app,
                        appName);
            } else {
            Intent intent = mConfigurationActivityHelper.getConfigurationActivityIntentForMode(
                    zenMode, mServiceListing::findService);
            if (intent != null) {
                preference.setVisible(true);
                switchPref.setTitle(R.string.zen_mode_configuration_link_title);
                switchPref.setSummary(zenMode.getRule().getTriggerDescription());
                switchPref.setIntent(intent);
                summary = null;
            }
        } else {
                Log.i(TAG, "No intent found for " + zenMode.getRule().getName());
                preference.setVisible(false);
            Log.e(TAG, "Mode without package! " + mode);
            summary = null;
        }

        @DrawableRes int icon;
        if (mode.getType() == TYPE_BEDTIME) {
            icon = com.android.internal.R.drawable.ic_zen_mode_type_schedule_time; // Clock
        } else if (mode.getType() == TYPE_DRIVING) {
            icon = com.android.internal.R.drawable.ic_zen_mode_type_driving; // Car
        } else {
            icon = configurationIntent != null ? R.drawable.ic_zen_mode_trigger_with_activity
                    : R.drawable.ic_zen_mode_trigger_without_activity;
        }

        preference.setTitle(title);
        preference.setSummary(summary);
        preference.setIcon(icon);
        preference.setIntent(configurationIntent);
    }

    @VisibleForTesting
@@ -137,10 +227,10 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc
            conditionId -> saveMode(mode -> {
                mode.setCustomModeConditionId(mContext, conditionId);
                return mode;
                // TODO: b/342156843 - Maybe jump to the corresponding schedule editing screen?
            });

    @VisibleForTesting
    protected Preference.OnPreferenceChangeListener mSwitchChangeListener = (p, newValue) -> {
    private final Preference.OnPreferenceChangeListener mSwitchChangeListener = (p, newValue) -> {
        final boolean newEnabled = (Boolean) newValue;
        return saveMode((zenMode) -> {
            if (newEnabled != zenMode.getRule().isEnabled()) {
@@ -148,6 +238,5 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc
            }
            return zenMode;
        });
        // TODO: b/342156843 - Do we want to jump to the corresponding schedule editing screen?
    };
}
Loading