Restructure battery bar scenario settings

This commit is contained in:
ajp_anton
2026-06-22 01:37:08 +00:00
parent b0270b8ceb
commit dd215cde82
8 changed files with 800 additions and 449 deletions
@@ -386,12 +386,7 @@ final class BatteryBarController {
return; return;
} }
SbtSettings settings = RuntimeSettingsCache.get(root.getContext()); SbtSettings settings = RuntimeSettingsCache.get(root.getContext());
if (!settings.batteryBarEnabled || root.getWidth() <= 0 || root.getHeight() <= 0) { if (root.getWidth() <= 0 || root.getHeight() <= 0) {
removeBatteryBarView(root);
return;
}
int color = resolveBarColor(root, settings);
if (!shouldRenderOnRoot(root)) {
removeBatteryBarView(root); removeBatteryBarView(root);
return; return;
} }
@@ -402,6 +397,15 @@ final class BatteryBarController {
if (transientStatusBarReveal) { if (transientStatusBarReveal) {
scenario = visibleStatusBarScenarioForRoot(root); scenario = visibleStatusBarScenarioForRoot(root);
} }
if (!isBatteryBarEnabled(settings, scenario)) {
removeBatteryBarView(root);
return;
}
int color = resolveBarColor(root, settings);
if (!shouldRenderOnRoot(root)) {
removeBatteryBarView(root);
return;
}
BatteryBarGeometry geometry = BatteryBarGeometry.resolve(settings, scenario); BatteryBarGeometry geometry = BatteryBarGeometry.resolve(settings, scenario);
if (isFullscreenScenario(scenario)) { if (isFullscreenScenario(scenario)) {
removeEmbeddedBatteryBarView(root); removeEmbeddedBatteryBarView(root);
@@ -543,6 +547,7 @@ final class BatteryBarController {
+ plugged + "|" + plugged + "|"
+ color + "|" + color + "|"
+ settings.batteryBarEnabled + "|" + settings.batteryBarEnabled + "|"
+ settings.batteryBarScenarioEnabled + "|"
+ settings.batteryBarPosition + "|" + settings.batteryBarPosition + "|"
+ settings.batteryBarAlignment + "|" + settings.batteryBarAlignment + "|"
+ settings.batteryBarThicknessDp + "|" + settings.batteryBarThicknessDp + "|"
@@ -579,6 +584,21 @@ final class BatteryBarController {
: BatteryBarGeometry.Scenario.UNLOCKED_PORTRAIT; : BatteryBarGeometry.Scenario.UNLOCKED_PORTRAIT;
} }
private boolean isBatteryBarEnabled(
SbtSettings settings,
BatteryBarGeometry.Scenario scenario
) {
if (settings == null || scenario == null) {
return false;
}
if (scenario == BatteryBarGeometry.Scenario.UNLOCKED_PORTRAIT
|| scenario == BatteryBarGeometry.Scenario.UNLOCKED_LANDSCAPE) {
return settings.batteryBarEnabled;
}
Boolean scenarioEnabled = settings.batteryBarScenarioEnabled.get(scenario.id);
return scenarioEnabled == null || scenarioEnabled;
}
private BatteryBarGeometry.Scenario visibleStatusBarScenarioForRoot(View root) { private BatteryBarGeometry.Scenario visibleStatusBarScenarioForRoot(View root) {
boolean landscape = isLandscapeForRoot(root, isFullscreenSystemBarState()); boolean landscape = isLandscapeForRoot(root, isFullscreenSystemBarState());
if (isPhoneRoot(root) && isUnlockedScene() && isTransparentStatusBarActive()) { if (isPhoneRoot(root) && isUnlockedScene() && isTransparentStatusBarActive()) {
@@ -1064,7 +1084,7 @@ final class BatteryBarController {
? geometry ? geometry
: BatteryBarGeometry.global(settings); : BatteryBarGeometry.global(settings);
int thickness = Math.max(1, dpToPx(getContext(), activeGeometry.thicknessDp)); int thickness = Math.max(1, dpToPx(getContext(), activeGeometry.thicknessDp));
int edgeOffset = Math.max(0, dpToPx(getContext(), settings.batteryBarEdgeOffsetDp)); int edgeOffset = Math.max(0, dpToPx(getContext(), activeGeometry.edgeOffsetDp));
float fraction = resolveFraction(batteryLevel, settings); float fraction = resolveFraction(batteryLevel, settings);
if (shouldDrawCurvedTop(activeGeometry)) { if (shouldDrawCurvedTop(activeGeometry)) {
buildCurvedPath( buildCurvedPath(
@@ -21,6 +21,7 @@ public final class BatteryBarGeometry {
public final String position; public final String position;
public final String alignment; public final String alignment;
public final int thicknessDp; public final int thicknessDp;
public final int edgeOffsetDp;
public final String curvedGeometryMode; public final String curvedGeometryMode;
public BatteryBarGeometry( public BatteryBarGeometry(
@@ -28,13 +29,19 @@ public final class BatteryBarGeometry {
String alignment, String alignment,
int thicknessDp int thicknessDp
) { ) {
this(position, alignment, thicknessDp, SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT); this(
position,
alignment,
thicknessDp,
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT,
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT);
} }
public BatteryBarGeometry( public BatteryBarGeometry(
String position, String position,
String alignment, String alignment,
int thicknessDp, int thicknessDp,
int edgeOffsetDp,
String curvedGeometryMode String curvedGeometryMode
) { ) {
this.position = sanitizePosition(position); this.position = sanitizePosition(position);
@@ -43,6 +50,10 @@ public final class BatteryBarGeometry {
thicknessDp, thicknessDp,
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MIN, SbtDefaults.BATTERY_BAR_THICKNESS_DP_MIN,
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MAX); SbtDefaults.BATTERY_BAR_THICKNESS_DP_MAX);
this.edgeOffsetDp = clamp(
edgeOffsetDp,
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX);
this.curvedGeometryMode = sanitizeCurvedGeometryMode(curvedGeometryMode); this.curvedGeometryMode = sanitizeCurvedGeometryMode(curvedGeometryMode);
} }
@@ -57,6 +68,7 @@ public final class BatteryBarGeometry {
settings.batteryBarPosition, settings.batteryBarPosition,
settings.batteryBarAlignment, settings.batteryBarAlignment,
settings.batteryBarThicknessDp, settings.batteryBarThicknessDp,
settings.batteryBarEdgeOffsetDp,
settings.batteryBarCurvedGeometryMode); settings.batteryBarCurvedGeometryMode);
} }
@@ -80,18 +92,19 @@ public final class BatteryBarGeometry {
return safeFallback; return safeFallback;
} }
String[] parts = value.split("\\|", -1); String[] parts = value.split("\\|", -1);
if (parts.length != 4) { if (parts.length != 5) {
return safeFallback; return safeFallback;
} }
return new BatteryBarGeometry( return new BatteryBarGeometry(
parts[0], parts[0],
parts[1], parts[1],
parseInt(parts[2], safeFallback.thicknessDp), parseInt(parts[2], safeFallback.thicknessDp),
parts[3]); parseInt(parts[3], safeFallback.edgeOffsetDp),
parts[4]);
} }
public String encode() { public String encode() {
return position + "|" + alignment + "|" + thicknessDp + "|" + curvedGeometryMode; return position + "|" + alignment + "|" + thicknessDp + "|" + edgeOffsetDp + "|" + curvedGeometryMode;
} }
public String signature() { public String signature() {
@@ -51,7 +51,7 @@ public final class SbtDefaults {
public static final boolean AOD_KEEP_VISIBLE_DURING_CALL_DEFAULT = false; public static final boolean AOD_KEEP_VISIBLE_DURING_CALL_DEFAULT = false;
public static final boolean BATTERY_LOCKSCREEN_UNPLUGGED_PERCENT_DEFAULT = true; public static final boolean BATTERY_LOCKSCREEN_UNPLUGGED_PERCENT_DEFAULT = true;
public static final boolean BATTERY_AOD_UNPLUGGED_PERCENT_DEFAULT = true; public static final boolean BATTERY_AOD_UNPLUGGED_PERCENT_DEFAULT = true;
public static final boolean BATTERY_BAR_ENABLED_DEFAULT = false; public static final boolean BATTERY_BAR_ENABLED_DEFAULT = true;
public static final String BATTERY_BAR_POSITION_DEFAULT = "top"; public static final String BATTERY_BAR_POSITION_DEFAULT = "top";
public static final String BATTERY_BAR_ALIGNMENT_DEFAULT = "ltr"; public static final String BATTERY_BAR_ALIGNMENT_DEFAULT = "ltr";
public static final int BATTERY_BAR_THICKNESS_DP_DEFAULT = 2; public static final int BATTERY_BAR_THICKNESS_DP_DEFAULT = 2;
@@ -78,6 +78,8 @@ public final class SbtSettings {
public static final String KEY_BATTERY_BAR_GEOMETRY_OVERRIDE_ENABLED_PREFIX = public static final String KEY_BATTERY_BAR_GEOMETRY_OVERRIDE_ENABLED_PREFIX =
"battery_bar_geometry_override_enabled_"; "battery_bar_geometry_override_enabled_";
public static final String KEY_BATTERY_BAR_GEOMETRY_PREFIX = "battery_bar_geometry_"; public static final String KEY_BATTERY_BAR_GEOMETRY_PREFIX = "battery_bar_geometry_";
public static final String KEY_BATTERY_BAR_SCENARIO_ENABLED_PREFIX =
"battery_bar_scenario_enabled_";
public static final String KEY_BATTERY_BAR_MIN_LEVEL = "battery_bar_min_level"; public static final String KEY_BATTERY_BAR_MIN_LEVEL = "battery_bar_min_level";
public static final String KEY_BATTERY_BAR_MAX_LEVEL = "battery_bar_max_level"; public static final String KEY_BATTERY_BAR_MAX_LEVEL = "battery_bar_max_level";
public static final String KEY_BATTERY_BAR_DEFAULT_DISCHARGE_COLOR = "battery_bar_default_discharge_color"; public static final String KEY_BATTERY_BAR_DEFAULT_DISCHARGE_COLOR = "battery_bar_default_discharge_color";
@@ -283,6 +285,10 @@ public final class SbtSettings {
return KEY_BATTERY_BAR_GEOMETRY_PREFIX + scenarioId; return KEY_BATTERY_BAR_GEOMETRY_PREFIX + scenarioId;
} }
public static String batteryBarScenarioEnabledKey(String scenarioId) {
return KEY_BATTERY_BAR_SCENARIO_ENABLED_PREFIX + scenarioId;
}
private static String suffixedKey(String base, int index) { private static String suffixedKey(String base, int index) {
return indexedKey(base, index); return indexedKey(base, index);
} }
@@ -316,6 +322,7 @@ public final class SbtSettings {
public final String batteryBarCurvedGeometryMode; public final String batteryBarCurvedGeometryMode;
public final Set<String> batteryBarGeometryOverrideEnabledScenarios; public final Set<String> batteryBarGeometryOverrideEnabledScenarios;
public final Map<String, String> batteryBarGeometryOverrides; public final Map<String, String> batteryBarGeometryOverrides;
public final Map<String, Boolean> batteryBarScenarioEnabled;
public final int batteryBarMinLevel; public final int batteryBarMinLevel;
public final int batteryBarMaxLevel; public final int batteryBarMaxLevel;
public final String batteryBarDefaultDischargeColor; public final String batteryBarDefaultDischargeColor;
@@ -449,6 +456,7 @@ public final class SbtSettings {
String batteryBarCurvedGeometryMode, String batteryBarCurvedGeometryMode,
Set<String> batteryBarGeometryOverrideEnabledScenarios, Set<String> batteryBarGeometryOverrideEnabledScenarios,
Map<String, String> batteryBarGeometryOverrides, Map<String, String> batteryBarGeometryOverrides,
Map<String, Boolean> batteryBarScenarioEnabled,
int batteryBarMinLevel, int batteryBarMinLevel,
int batteryBarMaxLevel, int batteryBarMaxLevel,
String batteryBarDefaultDischargeColor, String batteryBarDefaultDischargeColor,
@@ -593,6 +601,7 @@ public final class SbtSettings {
this.batteryBarGeometryOverrideEnabledScenarios = this.batteryBarGeometryOverrideEnabledScenarios =
sanitizeBatteryBarScenarioSet(batteryBarGeometryOverrideEnabledScenarios); sanitizeBatteryBarScenarioSet(batteryBarGeometryOverrideEnabledScenarios);
this.batteryBarGeometryOverrides = sanitizeBatteryBarScenarioMap(batteryBarGeometryOverrides); this.batteryBarGeometryOverrides = sanitizeBatteryBarScenarioMap(batteryBarGeometryOverrides);
this.batteryBarScenarioEnabled = sanitizeBatteryBarScenarioEnabledMap(batteryBarScenarioEnabled);
int clampedMinLevel = clampInt( int clampedMinLevel = clampInt(
batteryBarMinLevel, batteryBarMinLevel,
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN, SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN,
@@ -886,6 +895,7 @@ public final class SbtSettings {
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT, SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT,
Collections.emptySet(), Collections.emptySet(),
Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyMap(),
SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT,
SbtDefaults.BATTERY_BAR_MAX_LEVEL_DEFAULT, SbtDefaults.BATTERY_BAR_MAX_LEVEL_DEFAULT,
SbtDefaults.BATTERY_BAR_DEFAULT_DISCHARGE_COLOR_DEFAULT, SbtDefaults.BATTERY_BAR_DEFAULT_DISCHARGE_COLOR_DEFAULT,
@@ -1224,6 +1234,7 @@ public final class SbtSettings {
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT), SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT),
readBatteryBarScenarioSetFromPrefs(prefs), readBatteryBarScenarioSetFromPrefs(prefs),
readBatteryBarScenarioMapFromPrefs(prefs), readBatteryBarScenarioMapFromPrefs(prefs),
readBatteryBarScenarioEnabledMapFromPrefs(prefs),
clampInt(prefs.getInt(KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT), clampInt(prefs.getInt(KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT),
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN, SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN,
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MAX), SbtDefaults.BATTERY_BAR_MIN_LEVEL_MAX),
@@ -1531,6 +1542,7 @@ public final class SbtSettings {
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT), SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT),
readBatteryBarScenarioSetFromBundle(bundle), readBatteryBarScenarioSetFromBundle(bundle),
readBatteryBarScenarioMapFromBundle(bundle), readBatteryBarScenarioMapFromBundle(bundle),
readBatteryBarScenarioEnabledMapFromBundle(bundle),
clampInt(bundle.getInt(KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT), clampInt(bundle.getInt(KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT),
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN, SbtDefaults.BATTERY_BAR_MIN_LEVEL_MIN,
SbtDefaults.BATTERY_BAR_MIN_LEVEL_MAX), SbtDefaults.BATTERY_BAR_MIN_LEVEL_MAX),
@@ -1948,6 +1960,34 @@ public final class SbtSettings {
return result; return result;
} }
private static Map<String, Boolean> readBatteryBarScenarioEnabledMapFromPrefs(SharedPreferences prefs) {
if (prefs == null) {
return Collections.emptyMap();
}
HashMap<String, Boolean> result = new HashMap<>();
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
String key = batteryBarScenarioEnabledKey(scenario.id);
if (prefs.contains(key)) {
result.put(scenario.id, prefs.getBoolean(key, true));
}
}
return result;
}
private static Map<String, Boolean> readBatteryBarScenarioEnabledMapFromBundle(Bundle bundle) {
if (bundle == null) {
return Collections.emptyMap();
}
HashMap<String, Boolean> result = new HashMap<>();
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
String key = batteryBarScenarioEnabledKey(scenario.id);
if (bundle.containsKey(key)) {
result.put(scenario.id, bundle.getBoolean(key, true));
}
}
return result;
}
private static Set<String> sanitizeBatteryBarScenarioSet(Set<String> source) { private static Set<String> sanitizeBatteryBarScenarioSet(Set<String> source) {
if (source == null || source.isEmpty()) { if (source == null || source.isEmpty()) {
return Collections.emptySet(); return Collections.emptySet();
@@ -1976,6 +2016,21 @@ public final class SbtSettings {
return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result); return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result);
} }
private static Map<String, Boolean> sanitizeBatteryBarScenarioEnabledMap(Map<String, Boolean> source) {
if (source == null || source.isEmpty()) {
return Collections.emptyMap();
}
HashMap<String, Boolean> result = new HashMap<>();
for (Map.Entry<String, Boolean> entry : source.entrySet()) {
String id = entry.getKey();
Boolean value = entry.getValue();
if (isBatteryBarScenarioId(id) && value != null) {
result.put(id, value);
}
}
return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result);
}
private static boolean isBatteryBarScenarioId(String id) { private static boolean isBatteryBarScenarioId(String id) {
if (id == null) { if (id == null) {
return false; return false;
@@ -107,8 +107,12 @@ public class SbtSettingsProvider extends ContentProvider {
for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) { for (BatteryBarGeometry.Scenario scenario : BatteryBarGeometry.Scenario.values()) {
String enabledKey = BatteryBarGeometry.enabledKey(scenario); String enabledKey = BatteryBarGeometry.enabledKey(scenario);
String valueKey = BatteryBarGeometry.valueKey(scenario); String valueKey = BatteryBarGeometry.valueKey(scenario);
String scenarioEnabledKey = SbtSettings.batteryBarScenarioEnabledKey(scenario.id);
out.putBoolean(enabledKey, prefs.getBoolean(enabledKey, false)); out.putBoolean(enabledKey, prefs.getBoolean(enabledKey, false));
out.putString(valueKey, prefs.getString(valueKey, null)); out.putString(valueKey, prefs.getString(valueKey, null));
if (prefs.contains(scenarioEnabledKey)) {
out.putBoolean(scenarioEnabledKey, prefs.getBoolean(scenarioEnabledKey, true));
}
} }
out.putInt(SbtSettings.KEY_BATTERY_BAR_MIN_LEVEL, out.putInt(SbtSettings.KEY_BATTERY_BAR_MIN_LEVEL,
prefs.getInt(SbtSettings.KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT)); prefs.getInt(SbtSettings.KEY_BATTERY_BAR_MIN_LEVEL, SbtDefaults.BATTERY_BAR_MIN_LEVEL_DEFAULT));
@@ -25,10 +25,12 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.google.android.material.button.MaterialButton; import com.google.android.material.button.MaterialButton;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.slider.RangeSlider; import com.google.android.material.slider.RangeSlider;
import com.google.android.material.switchmaterial.SwitchMaterial; import com.google.android.material.switchmaterial.SwitchMaterial;
@@ -85,9 +87,9 @@ public final class BatteryBarFragment extends Fragment {
TextView colourHelp = root.findViewById(R.id.battery_bar_colour_help); TextView colourHelp = root.findViewById(R.id.battery_bar_colour_help);
MaterialButton addThresholdButton = root.findViewById(R.id.battery_bar_add_threshold_button); MaterialButton addThresholdButton = root.findViewById(R.id.battery_bar_add_threshold_button);
LinearLayout thresholdsContainer = root.findViewById(R.id.battery_bar_thresholds_container); LinearLayout thresholdsContainer = root.findViewById(R.id.battery_bar_thresholds_container);
View geometrySection = root.findViewById(R.id.battery_bar_geometry_section); View baseControls = root.findViewById(R.id.battery_bar_base_controls);
LinearLayout scenarioOverridesContainer = LinearLayout scenarioCardsContainer =
root.findViewById(R.id.battery_bar_scenario_overrides_container); root.findViewById(R.id.battery_bar_scenario_cards_container);
mappingSlider.setValueFrom(0f); mappingSlider.setValueFrom(0f);
mappingSlider.setValueTo(100f); mappingSlider.setValueTo(100f);
mappingSlider.setStepSize(1f); mappingSlider.setStepSize(1f);
@@ -130,7 +132,7 @@ public final class BatteryBarFragment extends Fragment {
minLevelLabel, minLevelLabel,
maxLevelLabel, maxLevelLabel,
false); false);
setSectionEnabled(geometrySection, enabled.isChecked()); setSectionEnabled(baseControls, enabled.isChecked());
updateColourButtonLabel( updateColourButtonLabel(
defaultDischargeButton, defaultDischargeButton,
R.string.battery_bar_default_discharge_colour, R.string.battery_bar_default_discharge_colour,
@@ -154,7 +156,7 @@ public final class BatteryBarFragment extends Fragment {
enabled.setOnCheckedChangeListener((buttonView, isChecked) -> { enabled.setOnCheckedChangeListener((buttonView, isChecked) -> {
prefs.edit().putBoolean(SbtSettings.KEY_BATTERY_BAR_ENABLED, isChecked).apply(); prefs.edit().putBoolean(SbtSettings.KEY_BATTERY_BAR_ENABLED, isChecked).apply();
SbtSettings.ensureReadable(requireContext()); SbtSettings.ensureReadable(requireContext());
setSectionEnabled(geometrySection, isChecked); setSectionEnabled(baseControls, isChecked);
}); });
positionGroup.setOnCheckedChangeListener((group, checkedId) -> { positionGroup.setOnCheckedChangeListener((group, checkedId) -> {
@@ -192,8 +194,8 @@ public final class BatteryBarFragment extends Fragment {
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN, SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX, SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX,
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT); SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT);
renderScenarioOverrides(scenarioOverridesContainer, prefs); renderScenarioCards(scenarioCardsContainer, prefs);
setSectionEnabled(geometrySection, enabled.isChecked()); setSectionEnabled(baseControls, enabled.isChecked());
mappingSlider.addOnChangeListener((slider, value, fromUser) -> { mappingSlider.addOnChangeListener((slider, value, fromUser) -> {
if (!fromUser) { if (!fromUser) {
@@ -434,17 +436,16 @@ public final class BatteryBarFragment extends Fragment {
return; return;
} }
view.setAlpha(enabled ? 1f : 0.82f); view.setAlpha(enabled ? 1f : 0.82f);
if (view.getClass() != TextView.class) {
view.setEnabled(enabled);
}
if (view instanceof ViewGroup) { if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view; ViewGroup group = (ViewGroup) view;
for (int i = 0; i < group.getChildCount(); i++) { for (int i = 0; i < group.getChildCount(); i++) {
setSectionEnabled(group.getChildAt(i), enabled); setSectionEnabled(group.getChildAt(i), enabled);
} }
} else { } else if (view.getClass() == TextView.class) {
if (view.getClass() == TextView.class) {
view.setEnabled(true); view.setEnabled(true);
} else {
view.setEnabled(enabled);
}
} }
} }
@@ -504,142 +505,531 @@ public final class BatteryBarFragment extends Fragment {
} }
} }
private void renderScenarioOverrides(LinearLayout container, SharedPreferences prefs) { private void renderScenarioCards(LinearLayout container, SharedPreferences prefs) {
if (container == null) { if (container == null) {
return; return;
} }
container.removeAllViews(); container.removeAllViews();
addScenarioOverrideRow(container, prefs, new ScenarioGroup( addScenarioCard(container, prefs, new ScenarioGroup(
R.string.battery_bar_scenario_group_unlocked,
BatteryBarGeometry.Scenario.UNLOCKED_PORTRAIT,
BatteryBarGeometry.Scenario.UNLOCKED_LANDSCAPE));
addScenarioOverrideRow(container, prefs, new ScenarioGroup(
R.string.battery_bar_scenario_group_transparent, R.string.battery_bar_scenario_group_transparent,
BatteryBarGeometry.Scenario.TRANSPARENT_PORTRAIT, BatteryBarGeometry.Scenario.TRANSPARENT_PORTRAIT,
BatteryBarGeometry.Scenario.TRANSPARENT_LANDSCAPE)); BatteryBarGeometry.Scenario.TRANSPARENT_LANDSCAPE));
addScenarioOverrideRow(container, prefs, new ScenarioGroup( addScenarioCard(container, prefs, new ScenarioGroup(
R.string.battery_bar_scenario_group_fullscreen, R.string.battery_bar_scenario_group_fullscreen,
BatteryBarGeometry.Scenario.FULLSCREEN_PORTRAIT, BatteryBarGeometry.Scenario.FULLSCREEN_PORTRAIT,
BatteryBarGeometry.Scenario.FULLSCREEN_LANDSCAPE)); BatteryBarGeometry.Scenario.FULLSCREEN_LANDSCAPE));
addScenarioOverrideRow(container, prefs, new ScenarioGroup( addScenarioCard(container, prefs, new ScenarioGroup(
R.string.battery_bar_lockscreen_override, R.string.battery_bar_lockscreen_override,
BatteryBarGeometry.Scenario.LOCKSCREEN)); BatteryBarGeometry.Scenario.LOCKSCREEN));
} }
private void addScenarioOverrideRow( private void addScenarioCard(
LinearLayout container, LinearLayout container,
SharedPreferences prefs, SharedPreferences prefs,
ScenarioGroup scenarioGroup ScenarioGroup scenarioGroup
) { ) {
Context context = container.getContext(); Context context = container.getContext();
LinearLayout row = new LinearLayout(context); MaterialCardView card = new MaterialCardView(context);
row.setGravity(Gravity.CENTER_VERTICAL); LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
row.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT); ViewGroup.LayoutParams.WRAP_CONTENT);
rowParams.topMargin = ViewTreeSupport.dp(context, 8); cardParams.topMargin = ViewTreeSupport.dp(context, 16);
row.setLayoutParams(rowParams); card.setLayoutParams(cardParams);
card.setUseCompatPadding(true);
card.setStrokeColor(ContextCompat.getColor(context, R.color.sbt_card_outline));
card.setStrokeWidth(ViewTreeSupport.dp(context, 1));
LinearLayout textColumn = new LinearLayout(context); LinearLayout content = new LinearLayout(context);
textColumn.setOrientation(LinearLayout.VERTICAL); content.setOrientation(LinearLayout.VERTICAL);
textColumn.setLayoutParams(new LinearLayout.LayoutParams( content.setPadding(
ViewTreeSupport.dp(context, 16),
ViewTreeSupport.dp(context, 16),
ViewTreeSupport.dp(context, 16),
ViewTreeSupport.dp(context, 16));
card.addView(content);
TextView title = sectionTitle(context, scenarioGroup.labelRes());
content.addView(title);
SwitchMaterial enabledSwitch = new SwitchMaterial(context);
enabledSwitch.setText(R.string.battery_bar_enabled);
enabledSwitch.setChecked(scenarioGroupEnabled(prefs, scenarioGroup));
content.addView(enabledSwitch);
SwitchMaterial useBaseSwitch = new SwitchMaterial(context);
useBaseSwitch.setText(R.string.battery_bar_use_base_settings);
useBaseSwitch.setChecked(scenarioGroupUsesBase(prefs, scenarioGroup));
content.addView(useBaseSwitch);
LinearLayout controlsRoot = new LinearLayout(context);
controlsRoot.setOrientation(LinearLayout.VERTICAL);
content.addView(controlsRoot);
GeometryControls controls = createGeometryControls(context, controlsRoot);
BatteryBarGeometry geometry = scenarioGroupGeometryFromPrefs(prefs, scenarioGroup);
applyGeometryToControls(controls, geometry);
Runnable refreshEnabled = () -> setSectionEnabled(
controlsRoot,
enabledSwitch.isChecked() && !useBaseSwitch.isChecked());
enabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
setScenarioGroupEnabled(prefs, scenarioGroup, isChecked);
refreshEnabled.run();
});
useBaseSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
applyGeometryToControls(controls, globalGeometryFromPrefs(prefs));
}
setScenarioGroupUseBase(prefs, scenarioGroup, isChecked, geometryFromControls(controls, prefs));
refreshEnabled.run();
});
bindScenarioGeometryControls(prefs, scenarioGroup, controls);
refreshEnabled.run();
container.addView(card);
}
private GeometryControls createGeometryControls(Context context, LinearLayout root) {
root.addView(sectionLabel(context, R.string.battery_bar_position_title));
RadioGroup positionGroup = new RadioGroup(context);
positionGroup.setLayoutParams(matchWrapParams(8));
RadioButton positionFollow = optionRadio(context, R.string.battery_bar_curved_geometry_length);
RadioButton positionTop = optionRadio(context, R.string.battery_bar_position_top);
RadioButton positionBottom = optionRadio(context, R.string.battery_bar_position_bottom);
positionFollow.setId(R.id.battery_bar_curved_geometry_length);
positionTop.setId(R.id.battery_bar_position_top);
positionBottom.setId(R.id.battery_bar_position_bottom);
positionGroup.addView(positionFollow);
positionGroup.addView(positionTop);
positionGroup.addView(positionBottom);
root.addView(positionGroup);
root.addView(sectionLabel(context, R.string.battery_bar_alignment_title));
LinearLayout alignmentRow = new LinearLayout(context);
alignmentRow.setOrientation(LinearLayout.HORIZONTAL);
alignmentRow.setLayoutParams(matchWrapParams(8));
RadioButton alignLtr = new RadioButton(context);
RadioButton alignCenter = new RadioButton(context);
RadioButton alignRtl = new RadioButton(context);
View alignLtrCell = alignmentCell(context, alignLtr, R.drawable.sbt_align_ltr);
View alignCenterCell = alignmentCell(context, alignCenter, R.drawable.sbt_align_center);
View alignRtlCell = alignmentCell(context, alignRtl, R.drawable.sbt_align_rtl);
alignmentRow.addView(alignLtrCell);
alignmentRow.addView(alignCenterCell);
alignmentRow.addView(alignRtlCell);
root.addView(alignmentRow);
root.addView(sectionLabel(context, R.string.battery_bar_thickness_title));
NumberControls thickness = numberControls(context);
root.addView(thickness.row);
root.addView(sectionLabel(context, R.string.battery_bar_edge_offset_title));
NumberControls edgeOffset = numberControls(context);
root.addView(edgeOffset.row);
return new GeometryControls(
positionGroup,
positionFollow,
positionTop,
positionBottom,
alignLtrCell,
alignCenterCell,
alignRtlCell,
alignLtr,
alignCenter,
alignRtl,
thickness.input,
thickness.minus,
thickness.plus,
edgeOffset.input,
edgeOffset.minus,
edgeOffset.plus);
}
private TextView sectionTitle(Context context, int textRes) {
TextView view = new TextView(context);
view.setText(textRes);
view.setTextAppearance(android.R.style.TextAppearance_Material_Medium);
view.setAllCaps(true);
view.setLetterSpacing(0.03f);
view.setTypeface(view.getTypeface(), android.graphics.Typeface.BOLD);
return view;
}
private TextView sectionLabel(Context context, int textRes) {
TextView view = new TextView(context);
view.setText(textRes);
view.setTextAppearance(android.R.style.TextAppearance_Material_Body2);
view.setLayoutParams(matchWrapParams(16));
return view;
}
private LinearLayout.LayoutParams matchWrapParams(int topMarginDp) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.topMargin = ViewTreeSupport.dp(requireContext(), topMarginDp);
return params;
}
private RadioButton optionRadio(Context context, int textRes) {
RadioButton radio = new RadioButton(context);
radio.setText(textRes);
radio.setMinHeight(ViewTreeSupport.dp(context, 36));
return radio;
}
private View alignmentCell(Context context, RadioButton radio, int drawableRes) {
LinearLayout cell = new LinearLayout(context);
cell.setOrientation(LinearLayout.VERTICAL);
cell.setGravity(Gravity.CENTER);
cell.setClickable(true);
cell.setFocusable(true);
cell.setPadding(
ViewTreeSupport.dp(context, 4),
ViewTreeSupport.dp(context, 4),
ViewTreeSupport.dp(context, 4),
ViewTreeSupport.dp(context, 4));
cell.setLayoutParams(new LinearLayout.LayoutParams(
0, 0,
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
1f)); 1f));
SwitchMaterial enabledSwitch = new SwitchMaterial(context); radio.setMinHeight(0);
enabledSwitch.setText(scenarioGroup.labelRes()); radio.setMinWidth(0);
enabledSwitch.setLayoutParams(new LinearLayout.LayoutParams( radio.setPadding(0, 0, 0, 0);
ViewGroup.LayoutParams.MATCH_PARENT, radio.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT)); ViewGroup.LayoutParams.WRAP_CONTENT));
textColumn.addView(enabledSwitch); cell.addView(radio);
TextView summary = new TextView(context); AppCompatImageView image = new AppCompatImageView(context);
summary.setTextAppearance(android.R.style.TextAppearance_Material_Body2); LinearLayout.LayoutParams imageParams = new LinearLayout.LayoutParams(
summary.setPadding(ViewTreeSupport.dp(context, 48), 0, 0, 0); ViewTreeSupport.dp(context, 24),
textColumn.addView(summary); ViewTreeSupport.dp(context, 24));
imageParams.topMargin = ViewTreeSupport.dp(context, 4);
MaterialButton editButton = new MaterialButton(context, null, com.google.android.material.R.attr.materialButtonOutlinedStyle); image.setLayoutParams(imageParams);
editButton.setText(R.string.battery_bar_edit_override); image.setImageResource(drawableRes);
cell.addView(image);
row.addView(textColumn); return cell;
row.addView(editButton);
container.addView(row);
bindScenarioOverride(scenarioGroup, enabledSwitch, editButton, summary, prefs);
} }
private void bindScenarioOverride( private NumberControls numberControls(Context context) {
ScenarioGroup scenarioGroup, LinearLayout row = new LinearLayout(context);
SwitchMaterial enabledSwitch, row.setGravity(Gravity.CENTER_VERTICAL);
MaterialButton editButton, row.setOrientation(LinearLayout.HORIZONTAL);
TextView summary, row.setLayoutParams(matchWrapParams(8));
SharedPreferences prefs
) { MaterialButton minus = new MaterialButton(
if (scenarioGroup == null || prefs == null) { context,
null,
com.google.android.material.R.attr.materialButtonOutlinedStyle);
minus.setText("-");
minus.setLayoutParams(new LinearLayout.LayoutParams(
ViewTreeSupport.dp(context, 40),
ViewGroup.LayoutParams.WRAP_CONTENT));
ViewTreeSupport.styleStepperButton(minus);
EditText input = new EditText(context);
LinearLayout.LayoutParams inputParams = new LinearLayout.LayoutParams(
ViewTreeSupport.dp(context, 72),
ViewGroup.LayoutParams.WRAP_CONTENT);
inputParams.leftMargin = ViewTreeSupport.dp(context, 8);
inputParams.rightMargin = ViewTreeSupport.dp(context, 8);
input.setLayoutParams(inputParams);
input.setEms(3);
input.setGravity(Gravity.CENTER);
input.setInputType(InputType.TYPE_CLASS_NUMBER);
input.setSingleLine(true);
input.setMaxLines(1);
input.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
MaterialButton plus = new MaterialButton(
context,
null,
com.google.android.material.R.attr.materialButtonOutlinedStyle);
plus.setText("+");
plus.setLayoutParams(new LinearLayout.LayoutParams(
ViewTreeSupport.dp(context, 40),
ViewGroup.LayoutParams.WRAP_CONTENT));
ViewTreeSupport.styleStepperButton(plus);
row.addView(minus);
row.addView(input);
row.addView(plus);
return new NumberControls(row, input, minus, plus);
}
private void applyGeometryToControls(GeometryControls controls, BatteryBarGeometry geometry) {
if (controls == null || geometry == null) {
return; return;
} }
boolean enabled = scenarioGroupEnabled(prefs, scenarioGroup); applyPositionSelection(
if (enabledSwitch != null) { controls.positionFollow,
enabledSwitch.setChecked(enabled); controls.positionTop,
enabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { controls.positionBottom,
SharedPreferences.Editor editor = prefs.edit(); geometry.position,
BatteryBarGeometry geometry = scenarioGroupGeometryFromPrefs(prefs, scenarioGroup); geometry.curvedGeometryMode);
for (BatteryBarGeometry.Scenario scenario : scenarioGroup.scenarios()) { applyAlignmentSelection(controls.alignLtr, controls.alignRtl, controls.alignCenter, geometry.alignment);
editor.putBoolean(BatteryBarGeometry.enabledKey(scenario), isChecked); setIntInput(controls.thicknessInput, geometry.thicknessDp);
if (isChecked) { setIntInput(controls.edgeOffsetInput, geometry.edgeOffsetDp);
editor.putString(BatteryBarGeometry.valueKey(scenario), geometry.encode());
} }
private void bindScenarioGeometryControls(
SharedPreferences prefs,
ScenarioGroup scenarioGroup,
GeometryControls controls
) {
if (prefs == null || scenarioGroup == null || controls == null) {
return;
}
controls.positionGroup.setOnCheckedChangeListener((group, checkedId) ->
persistScenarioGeometry(prefs, scenarioGroup, geometryFromControls(controls, prefs)));
bindScenarioAlignmentCell(prefs, scenarioGroup, controls, controls.alignRtlCell, "rtl");
bindScenarioAlignmentCell(prefs, scenarioGroup, controls, controls.alignCenterCell, "center");
bindScenarioAlignmentCell(prefs, scenarioGroup, controls, controls.alignLtrCell, "ltr");
bindScenarioAlignmentCell(prefs, scenarioGroup, controls, controls.alignRtl, "rtl");
bindScenarioAlignmentCell(prefs, scenarioGroup, controls, controls.alignCenter, "center");
bindScenarioAlignmentCell(prefs, scenarioGroup, controls, controls.alignLtr, "ltr");
bindScenarioNumberControl(
prefs,
scenarioGroup,
controls,
controls.thicknessInput,
controls.thicknessMinus,
controls.thicknessPlus,
false);
bindScenarioNumberControl(
prefs,
scenarioGroup,
controls,
controls.edgeOffsetInput,
controls.edgeOffsetMinus,
controls.edgeOffsetPlus,
true);
}
private void bindScenarioAlignmentCell(
SharedPreferences prefs,
ScenarioGroup scenarioGroup,
GeometryControls controls,
View clickTarget,
String value
) {
if (clickTarget == null) {
return;
}
clickTarget.setOnClickListener(v -> {
if (!clickTarget.isEnabled()) {
return;
}
applyAlignmentSelection(controls.alignLtr, controls.alignRtl, controls.alignCenter, value);
persistScenarioGeometry(prefs, scenarioGroup, geometryFromControls(controls, prefs));
});
}
private void bindScenarioNumberControl(
SharedPreferences prefs,
ScenarioGroup scenarioGroup,
GeometryControls controls,
EditText input,
MaterialButton minus,
MaterialButton plus,
boolean edgeOffset
) {
final boolean[] updating = new boolean[]{false};
if (minus != null) {
minus.setOnClickListener(v -> {
int current = scenarioNumberValue(input, controls, prefs, edgeOffset);
setScenarioNumberValue(input, current - 1, edgeOffset, updating);
persistScenarioGeometry(prefs, scenarioGroup, geometryFromControls(controls, prefs));
});
}
if (plus != null) {
plus.setOnClickListener(v -> {
int current = scenarioNumberValue(input, controls, prefs, edgeOffset);
setScenarioNumberValue(input, current + 1, edgeOffset, updating);
persistScenarioGeometry(prefs, scenarioGroup, geometryFromControls(controls, prefs));
});
}
if (input == null) {
return;
}
input.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (updating[0] || s == null || s.toString().trim().isEmpty()) {
return;
}
setScenarioNumberValue(
input,
scenarioNumberValue(input, controls, prefs, edgeOffset),
edgeOffset,
updating);
persistScenarioGeometry(prefs, scenarioGroup, geometryFromControls(controls, prefs));
}
});
input.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
return;
}
setScenarioNumberValue(
input,
scenarioNumberValue(input, controls, prefs, edgeOffset),
edgeOffset,
updating);
persistScenarioGeometry(prefs, scenarioGroup, geometryFromControls(controls, prefs));
});
}
private int scenarioNumberValue(
EditText input,
GeometryControls controls,
SharedPreferences prefs,
boolean edgeOffset
) {
BatteryBarGeometry fallback = globalGeometryFromPrefs(prefs);
int current = ViewTreeSupport.parseInt(
input,
edgeOffset ? fallback.edgeOffsetDp : fallback.thicknessDp);
return edgeOffset
? ViewTreeSupport.clamp(
current,
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX)
: ViewTreeSupport.clamp(
current,
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MIN,
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MAX);
}
private void setScenarioNumberValue(
EditText input,
int value,
boolean edgeOffset,
boolean[] updating
) {
int clamped = edgeOffset
? ViewTreeSupport.clamp(
value,
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MIN,
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_MAX)
: ViewTreeSupport.clamp(
value,
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MIN,
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MAX);
persistIntInput(input, clamped, updating);
}
private void persistIntInput(EditText input, int value, boolean[] updating) {
if (input == null) {
return;
}
updating[0] = true;
input.setText(String.valueOf(value));
input.setSelection(input.getText().length());
updating[0] = false;
}
private BatteryBarGeometry geometryFromControls(GeometryControls controls, SharedPreferences prefs) {
BatteryBarGeometry fallback = globalGeometryFromPrefs(prefs);
if (controls == null) {
return fallback;
}
int checkedPosition = controls.positionGroup.getCheckedRadioButtonId();
String alignment = controls.alignRtl.isChecked()
? "rtl"
: controls.alignCenter.isChecked() ? "center" : "ltr";
return new BatteryBarGeometry(
positionValue(checkedPosition),
alignment,
scenarioNumberValue(controls.thicknessInput, controls, prefs, false),
scenarioNumberValue(controls.edgeOffsetInput, controls, prefs, true),
curvedGeometryValue(checkedPosition));
}
private void persistScenarioGeometry(
SharedPreferences prefs,
ScenarioGroup scenarioGroup,
BatteryBarGeometry geometry
) {
if (prefs == null || scenarioGroup == null || geometry == null) {
return;
}
SharedPreferences.Editor editor = prefs.edit();
for (BatteryBarGeometry.Scenario scenario : scenarioGroup.scenarios()) {
editor.putBoolean(BatteryBarGeometry.enabledKey(scenario), true);
editor.putString(BatteryBarGeometry.valueKey(scenario), geometry.encode());
} }
editor.apply(); editor.apply();
SbtSettings.ensureReadable(requireContext()); SbtSettings.ensureReadable(requireContext());
updateScenarioSummary(scenarioGroup, summary, editButton, prefs);
});
}
if (editButton != null) {
editButton.setOnClickListener(v -> showScenarioGeometryDialog(
scenarioGroup,
prefs,
summary,
editButton));
}
updateScenarioSummary(scenarioGroup, summary, editButton, prefs);
}
private void updateScenarioSummary(
ScenarioGroup scenarioGroup,
TextView summary,
MaterialButton editButton,
SharedPreferences prefs
) {
if (scenarioGroup == null || prefs == null) {
return;
}
boolean enabled = scenarioGroupEnabled(prefs, scenarioGroup);
BatteryBarGeometry geometry = scenarioGroupGeometryFromPrefs(prefs, scenarioGroup);
if (summary != null) {
summary.setText(getString(
R.string.battery_bar_override_summary,
batteryBarPositionLabel(geometry),
geometry.alignment,
geometry.thicknessDp));
summary.setAlpha(enabled ? 1f : 0.65f);
}
if (editButton != null) {
editButton.setEnabled(enabled);
}
} }
private boolean scenarioGroupEnabled(SharedPreferences prefs, ScenarioGroup scenarioGroup) { private boolean scenarioGroupEnabled(SharedPreferences prefs, ScenarioGroup scenarioGroup) {
if (prefs == null || scenarioGroup == null) { if (prefs == null || scenarioGroup == null) {
return true;
}
for (BatteryBarGeometry.Scenario scenario : scenarioGroup.scenarios()) {
if (!prefs.getBoolean(SbtSettings.batteryBarScenarioEnabledKey(scenario.id), true)) {
return false; return false;
} }
}
return true;
}
private void setScenarioGroupEnabled(
SharedPreferences prefs,
ScenarioGroup scenarioGroup,
boolean enabled
) {
if (prefs == null || scenarioGroup == null) {
return;
}
SharedPreferences.Editor editor = prefs.edit();
for (BatteryBarGeometry.Scenario scenario : scenarioGroup.scenarios()) {
editor.putBoolean(SbtSettings.batteryBarScenarioEnabledKey(scenario.id), enabled);
}
editor.apply();
SbtSettings.ensureReadable(requireContext());
}
private boolean scenarioGroupUsesBase(SharedPreferences prefs, ScenarioGroup scenarioGroup) {
if (prefs == null || scenarioGroup == null) {
return true;
}
for (BatteryBarGeometry.Scenario scenario : scenarioGroup.scenarios()) { for (BatteryBarGeometry.Scenario scenario : scenarioGroup.scenarios()) {
if (prefs.getBoolean(BatteryBarGeometry.enabledKey(scenario), false)) { if (prefs.getBoolean(BatteryBarGeometry.enabledKey(scenario), false)) {
return false;
}
}
return true; return true;
} }
private void setScenarioGroupUseBase(
SharedPreferences prefs,
ScenarioGroup scenarioGroup,
boolean useBase,
BatteryBarGeometry current
) {
if (prefs == null || scenarioGroup == null) {
return;
} }
return false; SharedPreferences.Editor editor = prefs.edit();
for (BatteryBarGeometry.Scenario scenario : scenarioGroup.scenarios()) {
editor.putBoolean(BatteryBarGeometry.enabledKey(scenario), !useBase);
if (!useBase && current != null) {
editor.putString(BatteryBarGeometry.valueKey(scenario), current.encode());
}
}
editor.apply();
SbtSettings.ensureReadable(requireContext());
} }
private BatteryBarGeometry globalGeometryFromPrefs(SharedPreferences prefs) { private BatteryBarGeometry globalGeometryFromPrefs(SharedPreferences prefs) {
@@ -653,6 +1043,9 @@ public final class BatteryBarFragment extends Fragment {
prefs.getInt( prefs.getInt(
SbtSettings.KEY_BATTERY_BAR_THICKNESS_DP, SbtSettings.KEY_BATTERY_BAR_THICKNESS_DP,
SbtDefaults.BATTERY_BAR_THICKNESS_DP_DEFAULT), SbtDefaults.BATTERY_BAR_THICKNESS_DP_DEFAULT),
prefs.getInt(
SbtSettings.KEY_BATTERY_BAR_EDGE_OFFSET_DP,
SbtDefaults.BATTERY_BAR_EDGE_OFFSET_DP_DEFAULT),
prefs.getString( prefs.getString(
SbtSettings.KEY_BATTERY_BAR_CURVED_GEOMETRY_MODE, SbtSettings.KEY_BATTERY_BAR_CURVED_GEOMETRY_MODE,
SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT)); SbtDefaults.BATTERY_BAR_CURVED_GEOMETRY_MODE_DEFAULT));
@@ -675,113 +1068,46 @@ public final class BatteryBarFragment extends Fragment {
return globalGeometryFromPrefs(prefs); return globalGeometryFromPrefs(prefs);
} }
for (BatteryBarGeometry.Scenario scenario : scenarioGroup.scenarios()) { for (BatteryBarGeometry.Scenario scenario : scenarioGroup.scenarios()) {
if (prefs.contains(BatteryBarGeometry.valueKey(scenario))) { if (prefs.getBoolean(BatteryBarGeometry.enabledKey(scenario), false)
&& prefs.contains(BatteryBarGeometry.valueKey(scenario))) {
return scenarioGeometryFromPrefs(prefs, scenario); return scenarioGeometryFromPrefs(prefs, scenario);
} }
} }
return globalGeometryFromPrefs(prefs); return globalGeometryFromPrefs(prefs);
} }
private void showScenarioGeometryDialog(
ScenarioGroup scenarioGroup,
SharedPreferences prefs,
TextView summary,
MaterialButton editButton
) {
BatteryBarGeometry current = scenarioGroupGeometryFromPrefs(prefs, scenarioGroup);
LinearLayout content = new LinearLayout(requireContext());
content.setOrientation(LinearLayout.VERTICAL);
int padding = Math.round(20 * getResources().getDisplayMetrics().density);
content.setPadding(padding, 0, padding, 0);
TextView positionTitle = dialogLabel(R.string.battery_bar_position_title);
content.addView(positionTitle);
RadioGroup positionGroup = new RadioGroup(requireContext());
RadioButton positionFollow = dialogRadio(R.string.battery_bar_curved_geometry_length);
RadioButton positionTop = dialogRadio(R.string.battery_bar_position_top);
RadioButton positionBottom = dialogRadio(R.string.battery_bar_position_bottom);
positionFollow.setId(R.id.battery_bar_curved_geometry_length);
positionTop.setId(R.id.battery_bar_position_top);
positionBottom.setId(R.id.battery_bar_position_bottom);
positionGroup.addView(positionFollow);
positionGroup.addView(positionTop);
positionGroup.addView(positionBottom);
applyPositionSelection(positionFollow, positionTop, positionBottom,
current.position, current.curvedGeometryMode);
content.addView(positionGroup);
TextView alignmentTitle = dialogLabel(R.string.battery_bar_alignment_title);
alignmentTitle.setPadding(0, padding / 2, 0, 0);
content.addView(alignmentTitle);
RadioGroup alignmentGroup = new RadioGroup(requireContext());
RadioButton alignmentLtr = dialogRadio(R.string.battery_bar_alignment_ltr);
RadioButton alignmentCenter = dialogRadio(R.string.battery_bar_alignment_center);
RadioButton alignmentRtl = dialogRadio(R.string.battery_bar_alignment_rtl);
alignmentGroup.addView(alignmentLtr);
alignmentGroup.addView(alignmentCenter);
alignmentGroup.addView(alignmentRtl);
applyAlignmentSelection(alignmentLtr, alignmentRtl, alignmentCenter, current.alignment);
content.addView(alignmentGroup);
EditText thicknessInput = dialogNumberInput(current.thicknessDp);
content.addView(dialogLabel(R.string.battery_bar_thickness_title));
content.addView(thicknessInput);
androidx.appcompat.app.AlertDialog dialog = new MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.battery_bar_override_dialog_title, getString(scenarioGroup.labelRes())))
.setView(content)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, (dialogInterface, which) -> {
int checkedPosition = positionGroup.getCheckedRadioButtonId();
BatteryBarGeometry updated = new BatteryBarGeometry(
positionValue(checkedPosition),
alignmentRtl.isChecked()
? "rtl"
: alignmentCenter.isChecked() ? "center" : "ltr",
ViewTreeSupport.clamp(
ViewTreeSupport.parseInt(thicknessInput, current.thicknessDp),
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MIN,
SbtDefaults.BATTERY_BAR_THICKNESS_DP_MAX),
curvedGeometryValue(checkedPosition));
SharedPreferences.Editor editor = prefs.edit();
for (BatteryBarGeometry.Scenario scenario : scenarioGroup.scenarios()) {
editor.putString(BatteryBarGeometry.valueKey(scenario), updated.encode());
}
editor.apply();
SbtSettings.ensureReadable(requireContext());
updateScenarioSummary(scenarioGroup, summary, editButton, prefs);
})
.show();
tintDialogButtons(dialog, requireContext());
}
private record ScenarioGroup( private record ScenarioGroup(
int labelRes, int labelRes,
BatteryBarGeometry.Scenario... scenarios BatteryBarGeometry.Scenario... scenarios
) { ) {
} }
private TextView dialogLabel(int resId) { private record GeometryControls(
TextView view = new TextView(requireContext()); RadioGroup positionGroup,
view.setText(resId); RadioButton positionFollow,
view.setTextAppearance(android.R.style.TextAppearance_Material_Body1); RadioButton positionTop,
return view; RadioButton positionBottom,
View alignLtrCell,
View alignCenterCell,
View alignRtlCell,
RadioButton alignLtr,
RadioButton alignCenter,
RadioButton alignRtl,
EditText thicknessInput,
MaterialButton thicknessMinus,
MaterialButton thicknessPlus,
EditText edgeOffsetInput,
MaterialButton edgeOffsetMinus,
MaterialButton edgeOffsetPlus
) {
} }
private RadioButton dialogRadio(int resId) { private record NumberControls(
RadioButton radioButton = new RadioButton(requireContext()); LinearLayout row,
radioButton.setId(View.generateViewId()); EditText input,
radioButton.setText(resId); MaterialButton minus,
return radioButton; MaterialButton plus
} ) {
private EditText dialogNumberInput(int value) {
EditText input = new EditText(requireContext());
input.setInputType(InputType.TYPE_CLASS_NUMBER);
input.setSingleLine(true);
input.setGravity(Gravity.CENTER);
input.setText(String.valueOf(value));
return input;
} }
private void adjustMapping(SharedPreferences prefs, private void adjustMapping(SharedPreferences prefs,
+105 -167
View File
@@ -17,38 +17,6 @@
android:text="@string/battery_bar_title" android:text="@string/battery_bar_title"
android:textAppearance="?attr/textAppearanceHeadline5" /> android:textAppearance="?attr/textAppearanceHeadline5" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:cardUseCompatPadding="true"
app:strokeColor="@color/sbt_card_outline"
app:strokeWidth="1dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/battery_bar_master_toggle_title"
android:textAppearance="?attr/textAppearanceSubtitle1"
android:textAllCaps="true"
android:letterSpacing="0.03"
android:textStyle="bold" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/battery_bar_enabled_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/battery_bar_enabled" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<LinearLayout <LinearLayout
android:id="@+id/battery_bar_geometry_section" android:id="@+id/battery_bar_geometry_section"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -72,12 +40,32 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/battery_bar_position_title" android:text="@string/battery_bar_base_settings_title"
android:textAppearance="?attr/textAppearanceSubtitle1" android:textAppearance="?attr/textAppearanceSubtitle1"
android:textAllCaps="true" android:textAllCaps="true"
android:letterSpacing="0.03" android:letterSpacing="0.03"
android:textStyle="bold" /> android:textStyle="bold" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/battery_bar_enabled_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/battery_bar_enabled" />
<LinearLayout
android:id="@+id/battery_bar_base_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/battery_bar_position_title"
android:textAppearance="?attr/textAppearanceBody2" />
<RadioGroup <RadioGroup
android:id="@+id/battery_bar_position_group" android:id="@+id/battery_bar_position_group"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -110,111 +98,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:text="@string/battery_bar_thickness_title"
android:textAppearance="?attr/textAppearanceBody2" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/battery_bar_thickness_minus"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="-" />
<EditText
android:id="@+id/battery_bar_thickness_input"
android:layout_width="72dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:ems="3"
android:gravity="center"
android:importantForAutofill="no"
android:inputType="number"
android:maxLines="1"
android:singleLine="true" />
<com.google.android.material.button.MaterialButton
android:id="@+id/battery_bar_thickness_plus"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="+" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/battery_bar_edge_offset_title"
android:textAppearance="?attr/textAppearanceBody2" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/battery_bar_edge_offset_minus"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="-" />
<EditText
android:id="@+id/battery_bar_edge_offset_input"
android:layout_width="72dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:ems="3"
android:gravity="center"
android:importantForAutofill="no"
android:inputType="number"
android:maxLines="1"
android:singleLine="true" />
<com.google.android.material.button.MaterialButton
android:id="@+id/battery_bar_edge_offset_plus"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="+" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:cardUseCompatPadding="true"
app:strokeColor="@color/sbt_card_outline"
app:strokeWidth="1dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/battery_bar_alignment_title" android:text="@string/battery_bar_alignment_title"
android:textAppearance="?attr/textAppearanceSubtitle1" android:textAppearance="?attr/textAppearanceBody2" />
android:textAllCaps="true"
android:letterSpacing="0.03"
android:textStyle="bold" />
<LinearLayout <LinearLayout
android:id="@+id/battery_bar_alignment_row" android:id="@+id/battery_bar_alignment_row"
@@ -307,48 +192,101 @@
app:srcCompat="@drawable/sbt_align_rtl" /> app:srcCompat="@drawable/sbt_align_rtl" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
app:cardUseCompatPadding="true" android:text="@string/battery_bar_thickness_title"
app:strokeColor="@color/sbt_card_outline"
app:strokeWidth="1dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:letterSpacing="0.03"
android:text="@string/battery_bar_scenario_overrides_title"
android:textAllCaps="true"
android:textAppearance="?attr/textAppearanceSubtitle1"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/battery_bar_scenario_overrides_hint"
android:textAppearance="?attr/textAppearanceBody2" /> android:textAppearance="?attr/textAppearanceBody2" />
<LinearLayout <LinearLayout
android:id="@+id/battery_bar_scenario_overrides_container" android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:orientation="vertical" /> android:gravity="center_vertical"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/battery_bar_thickness_minus"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="-" />
<EditText
android:id="@+id/battery_bar_thickness_input"
android:layout_width="72dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:ems="3"
android:gravity="center"
android:importantForAutofill="no"
android:inputType="number"
android:maxLines="1"
android:singleLine="true" />
<com.google.android.material.button.MaterialButton
android:id="@+id/battery_bar_thickness_plus"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="+" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/battery_bar_edge_offset_title"
android:textAppearance="?attr/textAppearanceBody2" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/battery_bar_edge_offset_minus"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="-" />
<EditText
android:id="@+id/battery_bar_edge_offset_input"
android:layout_width="72dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:ems="3"
android:gravity="center"
android:importantForAutofill="no"
android:inputType="number"
android:maxLines="1"
android:singleLine="true" />
<com.google.android.material.button.MaterialButton
android:id="@+id/battery_bar_edge_offset_plus"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="+" />
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<LinearLayout
android:id="@+id/battery_bar_scenario_cards_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
+2 -7
View File
@@ -134,8 +134,9 @@
<string name="status_chips_hide_battery_label">Hide charging battery chip (unlocked)</string> <string name="status_chips_hide_battery_label">Hide charging battery chip (unlocked)</string>
<string name="misc_title">Misc</string> <string name="misc_title">Misc</string>
<string name="battery_bar_title">Battery bar</string> <string name="battery_bar_title">Battery bar</string>
<string name="battery_bar_master_toggle_title">Master toggle</string> <string name="battery_bar_base_settings_title">Base settings</string>
<string name="battery_bar_enabled">Enable battery bar</string> <string name="battery_bar_enabled">Enable battery bar</string>
<string name="battery_bar_use_base_settings">Use base settings</string>
<string name="battery_bar_position_title">Position</string> <string name="battery_bar_position_title">Position</string>
<string name="battery_bar_position_top">Top edge of screen</string> <string name="battery_bar_position_top">Top edge of screen</string>
<string name="battery_bar_position_bottom">Bottom of status bar</string> <string name="battery_bar_position_bottom">Bottom of status bar</string>
@@ -146,15 +147,9 @@
<string name="battery_bar_thickness_title">Thickness (dp)</string> <string name="battery_bar_thickness_title">Thickness (dp)</string>
<string name="battery_bar_edge_offset_title">Edge offset (dp)</string> <string name="battery_bar_edge_offset_title">Edge offset (dp)</string>
<string name="battery_bar_curved_geometry_length">Follow screen edges</string> <string name="battery_bar_curved_geometry_length">Follow screen edges</string>
<string name="battery_bar_scenario_overrides_title">Scenario overrides</string>
<string name="battery_bar_scenario_overrides_hint">Optional geometry overrides. Disabled scenarios use the default geometry above.</string>
<string name="battery_bar_scenario_group_unlocked">Unlocked</string>
<string name="battery_bar_scenario_group_transparent">Transparent statusbar</string> <string name="battery_bar_scenario_group_transparent">Transparent statusbar</string>
<string name="battery_bar_scenario_group_fullscreen">Fullscreen</string> <string name="battery_bar_scenario_group_fullscreen">Fullscreen</string>
<string name="battery_bar_lockscreen_override">Lockscreen</string> <string name="battery_bar_lockscreen_override">Lockscreen</string>
<string name="battery_bar_edit_override">Edit</string>
<string name="battery_bar_override_dialog_title">Edit %1$s geometry</string>
<string name="battery_bar_override_summary">%1$s, %2$s, %3$ddp thick</string>
<string name="battery_bar_mapping_title">Battery mapping</string> <string name="battery_bar_mapping_title">Battery mapping</string>
<string name="battery_bar_mapping_min_label">...0 at %1$d%%</string> <string name="battery_bar_mapping_min_label">...0 at %1$d%%</string>
<string name="battery_bar_mapping_max_label">...full at %1$d%%</string> <string name="battery_bar_mapping_max_label">...full at %1$d%%</string>