Add flow for updating zen mode status on modes pages

The main responsibility for updating stored ZenMode data when updated is the Settings page, which reloads mode data whenever any changes are detected. This change adds a process for that Settings page to then update the ZenMode data associated with all controllers on that page.

Flag: android.app.modes_ui
Bug: 335259054
Test: manual with toy implementations
Change-Id: Ie1692da54d962c90378085d8bd8573e5b93f8eb6
This commit is contained in:
Yuri Lin
2024-04-24 18:53:41 -04:00
parent f6f2f6135d
commit b77b2ec7c8
3 changed files with 236 additions and 89 deletions

View File

@@ -0,0 +1,80 @@
/*
* 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.
*/
package com.android.settings.notification.modes;
import android.app.AutomaticZenRule;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.android.settingslib.core.AbstractPreferenceController;
/**
* Base class for any preference controllers pertaining to any single Zen mode.
*/
abstract class AbstractZenModePreferenceController extends AbstractPreferenceController {
@Nullable
protected ZenModesBackend mBackend;
@Nullable // only until updateZenMode() is called
private ZenMode mZenMode;
@NonNull
final String mKey;
// ZenModesBackend should only be passed in if the preference controller may set the user's
// policy for this zen mode. Otherwise, if the preference controller is essentially read-only
// and leads to a further Settings screen, backend should be null.
AbstractZenModePreferenceController(@NonNull Context context, @NonNull String key,
@Nullable ZenModesBackend backend) {
super(context);
mBackend = backend;
mKey = key;
}
@Override
@NonNull
public String getPreferenceKey() {
return mKey;
}
// Called by the parent Fragment onStart, which means it will happen before resume.
public void updateZenMode(@NonNull Preference preference, @NonNull ZenMode zenMode) {
mZenMode = zenMode;
updateState(preference);
}
@Nullable
public ZenMode getMode() {
return mZenMode;
}
@Nullable
public AutomaticZenRule getAZR() {
if (mZenMode == null || mZenMode.getRule() == null) {
return null;
}
return mZenMode.getRule();
}
/** Implementations of this class should override
* {@link AbstractPreferenceController#updateState(Preference)} to specify what should
* happen when the preference is updated */
}

View File

@@ -0,0 +1,156 @@
/*
* 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.
*/
package com.android.settings.notification.modes;
import android.app.AutomaticZenRule;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.List;
/**
* Base class for Settings pages used to configure individual modes.
*/
abstract class ZenModeSettingsBase extends ZenModesSettingsBase {
static final String TAG = "ZenModeSettings";
static final String MODE_ID = "MODE_ID";
@Nullable // only until reloadMode() is called
private ZenMode mZenMode;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// TODO: b/322373473 - Update if modes page ends up using a different method of passing id
Bundle bundle = getArguments();
if (bundle != null && bundle.containsKey(MODE_ID)) {
String id = bundle.getString(MODE_ID);
if (!reloadMode(id)) {
Log.d(TAG, "Mode id " + id + " not found");
toastAndFinish();
}
} else {
Log.d(TAG, "Mode id required to set mode config settings");
toastAndFinish();
}
}
/**
* Refresh stored ZenMode data.
* @param id the mode ID
* @return whether we successfully got mode data from the backend.
*/
private boolean reloadMode(String id) {
mZenMode = mBackend.getMode(id);
if (mZenMode == null) {
return false;
}
return true;
}
/**
* Refresh ZenMode data any time the system's zen mode state changes (either the zen mode value
* itself, or the config), and also (once updated) update the info for all controllers.
*/
@Override
protected void updateZenModeState() {
if (mZenMode == null) {
// This shouldn't happen, but guard against it in case
toastAndFinish();
return;
}
String id = mZenMode.getId();
if (!reloadMode(id)) {
Log.d(TAG, "Mode id=" + id + " not found");
toastAndFinish();
}
updateControllers();
}
private void updateControllers() {
if (getPreferenceControllers() == null || mZenMode == null) {
return;
}
final PreferenceScreen screen = getPreferenceScreen();
if (screen == null) {
Log.d(TAG, "PreferenceScreen not found");
return;
}
for (List<AbstractPreferenceController> list : getPreferenceControllers()) {
for (AbstractPreferenceController controller : list) {
if (!controller.isAvailable()) {
continue;
}
try {
// Find preference associated with controller
final String key = controller.getPreferenceKey();
final Preference preference = screen.findPreference(key);
if (preference != null) {
AbstractZenModePreferenceController zenController =
(AbstractZenModePreferenceController) controller;
zenController.updateZenMode(preference, mZenMode);
} else {
Log.d(TAG,
String.format("Cannot find preference with key %s in Controller %s",
key, controller.getClass().getSimpleName()));
}
} catch (ClassCastException e) {
// Skip any controllers that aren't AbstractZenModePreferenceController.
Log.d(TAG, "Could not cast: " + controller.getClass().getSimpleName());
}
}
}
}
private void toastAndFinish() {
Toast.makeText(mContext, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT)
.show();
this.finish();
}
/**
* Get current mode data.
*/
@Nullable
public ZenMode getMode() {
return mZenMode;
}
/**
* Get AutomaticZenRule associated with current mode data, or null if it doesn't exist.
*/
@Nullable
public AutomaticZenRule getAZR() {
if (mZenMode == null) {
return null;
}
return mZenMode.getRule();
}
}

View File

@@ -1,89 +0,0 @@
/*
* 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.
*/
package com.android.settings.notification.modes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import com.android.settings.R;
/**
* Base class for Settings pages used to configure individual modes.
*/
abstract class ZenModesRuleSettingsBase extends ZenModesSettingsBase {
static final String TAG = "ZenModesRuleSettings";
static final String MODE_ID = "MODE_ID";
@Nullable
protected ZenMode mZenMode;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// TODO: b/322373473 - Update if modes page ends up using a different method of passing id
Bundle bundle = getArguments();
if (bundle != null && bundle.containsKey(MODE_ID)) {
String id = bundle.getString(MODE_ID);
if (!reloadMode(id)) {
Log.d(TAG, "Mode id " + id + " not found");
toastAndFinish();
}
} else {
Log.d(TAG, "Mode id required to set mode config settings");
toastAndFinish();
}
}
/**
* Refresh stored ZenMode data.
* @param id the mode ID
* @return whether we successfully got mode data from the backend.
*/
private boolean reloadMode(String id) {
mZenMode = mBackend.getMode(id);
return mZenMode != null;
}
/**
* Refresh ZenMode data any time the system's zen mode state changes (either the zen mode value
* itself, or the config).
*/
@Override
protected void updateZenModeState() {
if (mZenMode == null) {
// This shouldn't happen, but guard against it in case
toastAndFinish();
return;
}
String id = mZenMode.getId();
if (!reloadMode(id)) {
Log.d(TAG, "Mode id=" + id + " not found");
toastAndFinish();
}
}
private void toastAndFinish() {
Toast.makeText(mContext, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT)
.show();
this.finish();
}
}