script-astra/Android/Sdk/sources/android-35/android/service/notification/SystemZenRules.java

263 lines
11 KiB
Java
Raw Normal View History

2025-01-20 15:15:20 +00:00
/*
* 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 android.service.notification;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AutomaticZenRule;
import android.app.Flags;
import android.content.Context;
import android.service.notification.ZenModeConfig.EventInfo;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
import android.text.format.DateFormat;
import android.util.Log;
import com.android.internal.R;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.Objects;
/**
* Helper methods for schedule-type (system-owned) rules.
* @hide
*/
public final class SystemZenRules {
private static final String TAG = "SystemZenRules";
public static final String PACKAGE_ANDROID = "android";
/** Updates existing system-owned rules to use the new Modes fields (type, etc). */
@FlaggedApi(Flags.FLAG_MODES_API)
public static void maybeUpgradeRules(Context context, ZenModeConfig config) {
for (ZenRule rule : config.automaticRules.values()) {
if (isSystemOwnedRule(rule) && rule.type == AutomaticZenRule.TYPE_UNKNOWN) {
upgradeSystemProviderRule(context, rule);
}
}
}
/**
* Returns whether the rule corresponds to a system ConditionProviderService (i.e. it is owned
* by the "android" package).
*/
public static boolean isSystemOwnedRule(ZenRule rule) {
return PACKAGE_ANDROID.equals(rule.pkg);
}
@FlaggedApi(Flags.FLAG_MODES_API)
private static void upgradeSystemProviderRule(Context context, ZenRule rule) {
ScheduleInfo scheduleInfo = ZenModeConfig.tryParseScheduleConditionId(rule.conditionId);
if (scheduleInfo != null) {
rule.type = AutomaticZenRule.TYPE_SCHEDULE_TIME;
rule.triggerDescription = getTriggerDescriptionForScheduleTime(context, scheduleInfo);
return;
}
EventInfo eventInfo = ZenModeConfig.tryParseEventConditionId(rule.conditionId);
if (eventInfo != null) {
rule.type = AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
rule.triggerDescription = getTriggerDescriptionForScheduleEvent(context, eventInfo);
return;
}
Log.wtf(TAG, "Couldn't determine type of system-owned ZenRule " + rule);
}
/**
* Updates the {@link ZenRule#triggerDescription} of the system-owned rule based on the schedule
* or event condition encoded in its {@link ZenRule#conditionId}.
*
* @return {@code true} if the trigger description was updated.
*/
public static boolean updateTriggerDescription(Context context, ZenRule rule) {
ScheduleInfo scheduleInfo = ZenModeConfig.tryParseScheduleConditionId(rule.conditionId);
if (scheduleInfo != null) {
return updateTriggerDescription(rule,
getTriggerDescriptionForScheduleTime(context, scheduleInfo));
}
EventInfo eventInfo = ZenModeConfig.tryParseEventConditionId(rule.conditionId);
if (eventInfo != null) {
return updateTriggerDescription(rule,
getTriggerDescriptionForScheduleEvent(context, eventInfo));
}
Log.wtf(TAG, "Couldn't determine type of system-owned ZenRule " + rule);
return false;
}
private static boolean updateTriggerDescription(ZenRule rule, String triggerDescription) {
if (!Objects.equals(rule.triggerDescription, triggerDescription)) {
rule.triggerDescription = triggerDescription;
return true;
}
return false;
}
/**
* Returns a suitable trigger description for a time-schedule rule (e.g. "Mon-Fri, 8:00-10:00"),
* using the Context's current locale.
*/
@Nullable
public static String getTriggerDescriptionForScheduleTime(Context context,
@NonNull ScheduleInfo schedule) {
final StringBuilder sb = new StringBuilder();
String daysSummary = getShortDaysSummary(context, schedule);
if (daysSummary == null) {
// no use outputting times without dates
return null;
}
sb.append(daysSummary);
sb.append(context.getString(R.string.zen_mode_trigger_summary_divider_text));
sb.append(context.getString(
R.string.zen_mode_trigger_summary_range_symbol_combination,
timeString(context, schedule.startHour, schedule.startMinute),
timeString(context, schedule.endHour, schedule.endMinute)));
return sb.toString();
}
/**
* Returns an ordered summarized list of the days on which this schedule applies, with
* adjacent days grouped together ("Sun-Wed" instead of "Sun,Mon,Tue,Wed").
*/
@Nullable
private static String getShortDaysSummary(Context context, @NonNull ScheduleInfo schedule) {
// Compute a list of days with contiguous days grouped together, for example: "Sun-Thu" or
// "Sun-Mon,Wed,Fri"
final int[] days = schedule.days;
if (days != null && days.length > 0) {
final StringBuilder sb = new StringBuilder();
final Calendar cStart = Calendar.getInstance(getLocale(context));
final Calendar cEnd = Calendar.getInstance(getLocale(context));
int[] daysOfWeek = getDaysOfWeekForLocale(cStart);
// the i for loop goes through days in order as determined by locale. as we walk through
// the days of the week, keep track of "start" and "last seen" as indicators for
// what's contiguous, and initialize them to something not near actual indices
int startDay = Integer.MIN_VALUE;
int lastSeenDay = Integer.MIN_VALUE;
for (int i = 0; i < daysOfWeek.length; i++) {
final int day = daysOfWeek[i];
// by default, output if this day is *not* included in the schedule, and thus
// ends a previously existing block. if this day is included in the schedule
// after all (as will be determined in the inner for loop), then output will be set
// to false.
boolean output = (i == lastSeenDay + 1);
for (int j = 0; j < days.length; j++) {
if (day == days[j]) {
// match for this day in the schedule (indicated by counter i)
if (i == lastSeenDay + 1) {
// contiguous to the block we're walking through right now, record it
// (specifically, i, the day index) and move on to the next day
lastSeenDay = i;
output = false;
} else {
// it's a match, but not 1 past the last match, we are starting a new
// block
startDay = i;
lastSeenDay = i;
}
// if there is a match on the last day, also make sure to output at the end
// of this loop, and mark the day as the last day we'll have seen in the
// scheduled days.
if (i == daysOfWeek.length - 1) {
output = true;
}
break;
}
}
// output in either of 2 cases: this day is not a match, so has ended any previous
// block, or this day *is* a match but is the last day of the week, so we need to
// summarize
if (output) {
// either describe just the single day if startDay == lastSeenDay, or
// output "startDay - lastSeenDay" as a group
if (sb.length() > 0) {
sb.append(
context.getString(R.string.zen_mode_trigger_summary_divider_text));
}
SimpleDateFormat dayFormat = new SimpleDateFormat("EEE", getLocale(context));
if (startDay == lastSeenDay) {
// last group was only one day
cStart.set(Calendar.DAY_OF_WEEK, daysOfWeek[startDay]);
sb.append(dayFormat.format(cStart.getTime()));
} else {
// last group was a contiguous group of days, so group them together
cStart.set(Calendar.DAY_OF_WEEK, daysOfWeek[startDay]);
cEnd.set(Calendar.DAY_OF_WEEK, daysOfWeek[lastSeenDay]);
sb.append(context.getString(
R.string.zen_mode_trigger_summary_range_symbol_combination,
dayFormat.format(cStart.getTime()),
dayFormat.format(cEnd.getTime())));
}
}
}
if (sb.length() > 0) {
return sb.toString();
}
}
return null;
}
/**
* Convenience method for representing the specified time in string format.
*/
private static String timeString(Context context, int hour, int minute) {
final Calendar c = Calendar.getInstance(getLocale(context));
c.set(Calendar.HOUR_OF_DAY, hour);
c.set(Calendar.MINUTE, minute);
return DateFormat.getTimeFormat(context).format(c.getTime());
}
private static int[] getDaysOfWeekForLocale(Calendar c) {
int[] daysOfWeek = new int[7];
int currentDay = c.getFirstDayOfWeek();
for (int i = 0; i < daysOfWeek.length; i++) {
if (currentDay > 7) currentDay = 1;
daysOfWeek[i] = currentDay;
currentDay++;
}
return daysOfWeek;
}
private static Locale getLocale(Context context) {
return context.getResources().getConfiguration().getLocales().get(0);
}
/**
* Returns a suitable trigger description for a calendar-schedule rule (either the name of the
* calendar, or a message indicating all calendars are included).
*/
public static String getTriggerDescriptionForScheduleEvent(Context context,
@NonNull EventInfo event) {
if (event.calName != null) {
return event.calName;
} else {
return context.getResources().getString(
R.string.zen_mode_trigger_event_calendar_any);
}
}
private SystemZenRules() {}
}