From 013957baf7cfbbfd0084d2b25a0b4561f9772980 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 12 Jul 2026 20:23:43 +0000 Subject: [PATCH] Revamp Settings menu structure; add version footer (#112, #113) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the settings by axis instead of the grown-organically mix: - General: language, temperature unit, and the status notification (show-rate moved here from the Battery Drain group) - Alerts: WHAT to be notified about — one category per alert event in battery-lifecycle order (levels slider moved from General, then warning, critical, full, temperature, fast drain, charging), so Temperature no longer sits between Warning and Critical - Notification Behaviour: HOW alerts sound/appear and WHEN they are allowed — silent-mode handling, vibrate, quiet hours (with "critical ignores quiet hours" moved next to the window it overrides), sticky Wording only, no logic changes (all preference keys and defaults are unchanged, so nothing migrates): - "Apply Silent Mode" -> "Mute alerts in silent mode" with summaries that say what ON does - Quiet-hours toggle/time labels and sticky summaries rewritten - Root headers get descriptive summaries - Fix swapped Arabic vibrate on/off summaries Version footer (#113): the Settings root now ends with a "Version x.y.z" row; tapping opens the About dialog. The dialog + version lookup moved from MainActivity into a shared AboutDialog helper. Also delete the unused pref_headers.xml / pref_headers_legacy.xml. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_015ZMe8rrw7eQKz69R4Xw3t5 --- .../service/BatteryRateTracker.java | 4 +- .../service/NotificationService.java | 4 +- .../simplebatterynotifier/ui/AboutDialog.java | 79 ++++++++++ .../ui/MainActivity.java | 53 +------ .../ui/SettingsActivity.java | 11 ++ .../fragment/GenericPreferenceFragment.java | 8 +- app/src/main/res/layout/dialog_about.xml | 2 +- app/src/main/res/values-ar/strings.xml | 56 ++++--- app/src/main/res/values/strings.xml | 59 +++++--- ...{pref_notification.xml => pref_alerts.xml} | 137 ++++++++---------- app/src/main/res/xml/pref_behaviour.xml | 93 ++++++++++++ app/src/main/res/xml/pref_general.xml | 31 +--- app/src/main/res/xml/pref_headers.xml | 30 ---- app/src/main/res/xml/pref_headers_legacy.xml | 27 ---- app/src/main/res/xml/pref_headers_root.xml | 24 ++- app/src/main/res/xml/pref_time_settings.xml | 29 ---- 16 files changed, 346 insertions(+), 301 deletions(-) create mode 100644 app/src/main/java/com/almothafar/simplebatterynotifier/ui/AboutDialog.java rename app/src/main/res/xml/{pref_notification.xml => pref_alerts.xml} (79%) create mode 100644 app/src/main/res/xml/pref_behaviour.xml delete mode 100644 app/src/main/res/xml/pref_headers.xml delete mode 100644 app/src/main/res/xml/pref_headers_legacy.xml delete mode 100644 app/src/main/res/xml/pref_time_settings.xml diff --git a/app/src/main/java/com/almothafar/simplebatterynotifier/service/BatteryRateTracker.java b/app/src/main/java/com/almothafar/simplebatterynotifier/service/BatteryRateTracker.java index 9845a94..604d571 100644 --- a/app/src/main/java/com/almothafar/simplebatterynotifier/service/BatteryRateTracker.java +++ b/app/src/main/java/com/almothafar/simplebatterynotifier/service/BatteryRateTracker.java @@ -61,7 +61,7 @@ public final class BatteryRateTracker { static final int MAX_PLAUSIBLE_RATE_PPH = 500; // The "red / high drain" limit shared with the fast-drain alert (#109): default and accepted range. - // MIN/MAX must match the slider bounds (android:min/android:max) in pref_notification.xml; they are + // MIN/MAX must match the slider bounds (android:min/android:max) in pref_alerts.xml; they are // enforced when the preference is read, so an out-of-range stored value can't skew the red line. public static final int DEFAULT_DRAIN_LIMIT_PPH = 20; public static final int MIN_DRAIN_LIMIT_PPH = 5; @@ -363,7 +363,7 @@ public static int getDrainLimitPercentPerHour(final Context context) { /** * Clamps a stored drain limit to the accepted range, so a corrupt or out-of-range preference value * can't skew the red line here or the fast-drain trigger in #109. The bounds mirror the slider's - * {@code android:min}/{@code android:max} in {@code pref_notification.xml}. Pure so it is unit-testable. + * {@code android:min}/{@code android:max} in {@code pref_alerts.xml}. Pure so it is unit-testable. * * @param stored the raw persisted limit in %/h * diff --git a/app/src/main/java/com/almothafar/simplebatterynotifier/service/NotificationService.java b/app/src/main/java/com/almothafar/simplebatterynotifier/service/NotificationService.java index 571574e..8f3e3b1 100644 --- a/app/src/main/java/com/almothafar/simplebatterynotifier/service/NotificationService.java +++ b/app/src/main/java/com/almothafar/simplebatterynotifier/service/NotificationService.java @@ -87,7 +87,7 @@ public final class NotificationService { private static final String ZEN_MODE = "zen_mode"; private static final int ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1; - // Charge-connected notification style (values persisted by the ListPreference in pref_notification.xml). + // Charge-connected notification style (values persisted by the ListPreference in pref_alerts.xml). // Toast is the default so plugging in stays low-clutter (issue #122). static final String CHARGE_STYLE_TOAST = "toast"; static final String CHARGE_STYLE_NOTIFICATION = "notification"; @@ -842,7 +842,7 @@ private static boolean isVibrationEnabled(final Context context, final SharedPre * @return true if alerts are allowed at the current time */ private static boolean isWithinNotificationWindow(final Context context, final SharedPreferences prefs) { - // Default ON to match the toggle's XML default (pref_time_settings.xml). Reading false here let a + // Default ON to match the toggle's XML default (pref_behaviour.xml). Reading false here let a // fresh install that never opened Time Settings alert around the clock, so quiet hours silently // did nothing (issue #111). Now quiet hours apply by default (06:30–23:30). final boolean limitedTime = prefs.getBoolean(context.getString(R.string._pref_key_notifications_time_range), true); diff --git a/app/src/main/java/com/almothafar/simplebatterynotifier/ui/AboutDialog.java b/app/src/main/java/com/almothafar/simplebatterynotifier/ui/AboutDialog.java new file mode 100644 index 0000000..cf5a33b --- /dev/null +++ b/app/src/main/java/com/almothafar/simplebatterynotifier/ui/AboutDialog.java @@ -0,0 +1,79 @@ +package com.almothafar.simplebatterynotifier.ui; + +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.util.Log; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import com.almothafar.simplebatterynotifier.R; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +/** + * Shared "About the app" dialog and app-version lookup. + *

+ * Used from two entry points: the main screen's overflow menu (#136) and the version footer at + * the bottom of the Settings root screen (#113), so both stay one implementation. + */ +public final class AboutDialog { + + private static final String TAG = "AboutDialog"; + + private AboutDialog() { + } + + /** + * Show the "About the app" dialog: what the app is, the current version, developer credit, + * the open-source license, and a short accuracy/warranty note. + * + * @param activity The host activity + */ + public static void show(final Activity activity) { + final View content = activity.getLayoutInflater().inflate(R.layout.dialog_about, null); + + final TextView versionView = content.findViewById(R.id.aboutVersion); + versionView.setText(activity.getString(R.string.about_version, appVersionName(activity))); + + final TextView developerView = content.findViewById(R.id.aboutDeveloper); + developerView.setText(activity.getString(R.string.about_developer, activity.getString(R.string.developer_name))); + + new MaterialAlertDialogBuilder(activity) + .setView(content) + .setPositiveButton(android.R.string.ok, null) + .setNeutralButton(R.string.about_view_github, (dialog, which) -> openProjectPage(activity)) + .show(); + } + + /** + * The app's version name (e.g. "2.0.71"), or an empty string if it can't be read. + * + * @param context Any context of this app + */ + public static String appVersionName(final Context context) { + try { + return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; + } catch (PackageManager.NameNotFoundException e) { + // The app can always resolve its own package, so this is effectively unreachable; + // log it rather than swallow silently, and degrade to an empty version string. + Log.w(TAG, "Unable to read app version name", e); + return ""; + } + } + + /** + * Open the project's GitHub page in a browser. + */ + private static void openProjectPage(final Activity activity) { + final Uri uri = Uri.parse(activity.getString(R.string.about_github_url)); + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, uri)); + } catch (ActivityNotFoundException e) { + Toast.makeText(activity, R.string.no_browser_found, Toast.LENGTH_SHORT).show(); + } + } +} diff --git a/app/src/main/java/com/almothafar/simplebatterynotifier/ui/MainActivity.java b/app/src/main/java/com/almothafar/simplebatterynotifier/ui/MainActivity.java index 13465b0..6933e26 100644 --- a/app/src/main/java/com/almothafar/simplebatterynotifier/ui/MainActivity.java +++ b/app/src/main/java/com/almothafar/simplebatterynotifier/ui/MainActivity.java @@ -15,7 +15,6 @@ import android.util.Log; import android.view.Menu; import android.view.MenuItem; -import android.view.View; import android.widget.TextView; import android.widget.Toast; import com.almothafar.simplebatterynotifier.ui.fragment.BatteryDetailsFragment; @@ -105,7 +104,7 @@ public boolean onOptionsItemSelected(final MenuItem item) { return true; } if (id == R.id.action_about) { - showAboutDialog(); + AboutDialog.show(this); return true; } return super.onOptionsItemSelected(item); @@ -463,38 +462,6 @@ private void openBatteryInsights() { startActivity(intent); } - /** - * Show the "About the app" dialog: what the app is, the current version, developer credit, - * the open-source license, and a short accuracy/warranty note. - */ - private void showAboutDialog() { - final View content = getLayoutInflater().inflate(R.layout.dialog_about, null); - - final TextView versionView = content.findViewById(R.id.aboutVersion); - versionView.setText(getString(R.string.about_version, appVersionName())); - - final TextView developerView = content.findViewById(R.id.aboutDeveloper); - developerView.setText(getString(R.string.about_developer, getString(R.string.developer_name))); - - new MaterialAlertDialogBuilder(this) - .setView(content) - .setPositiveButton(android.R.string.ok, null) - .setNeutralButton(R.string.about_view_github, (dialog, which) -> openProjectPage()) - .show(); - } - - /** - * Open the project's GitHub page in a browser. - */ - private void openProjectPage() { - final Uri uri = Uri.parse(getString(R.string.about_github_url)); - try { - startActivity(new Intent(Intent.ACTION_VIEW, uri)); - } catch (ActivityNotFoundException e) { - Toast.makeText(this, R.string.no_browser_found, Toast.LENGTH_SHORT).show(); - } - } - /** * Show the feedback chooser: report on GitHub, or email the developer. */ @@ -534,7 +501,7 @@ private void openGitHubIssues() { private void emailDeveloper() { final Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", getString(R.string.feedback_support_email), null)); - intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.feedback_email_subject, appVersionName())); + intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.feedback_email_subject, AboutDialog.appVersionName(this))); intent.putExtra(Intent.EXTRA_TEXT, deviceInfoBlock()); try { startActivity(intent); @@ -543,26 +510,12 @@ private void emailDeveloper() { } } - /** - * The app's version name (e.g. "2.0.71"), or an empty string if it can't be read. - */ - private String appVersionName() { - try { - return getPackageManager().getPackageInfo(getPackageName(), 0).versionName; - } catch (PackageManager.NameNotFoundException e) { - // The app can always resolve its own package, so this is effectively unreachable; - // log it rather than swallow silently, and degrade to an empty version string. - Log.w(TAG, "Unable to read app version name", e); - return ""; - } - } - /** * A short diagnostic block appended to a feedback email so reports carry the essentials. */ private String deviceInfoBlock() { return "\n\n---\n" - + "App: " + appVersionName() + "\n" + + "App: " + AboutDialog.appVersionName(this) + "\n" + "Device: " + Build.MANUFACTURER + " " + Build.MODEL + "\n" + "Android: " + Build.VERSION.RELEASE + " (API " + Build.VERSION.SDK_INT + ")"; } diff --git a/app/src/main/java/com/almothafar/simplebatterynotifier/ui/SettingsActivity.java b/app/src/main/java/com/almothafar/simplebatterynotifier/ui/SettingsActivity.java index ac4d28c..685f6c0 100644 --- a/app/src/main/java/com/almothafar/simplebatterynotifier/ui/SettingsActivity.java +++ b/app/src/main/java/com/almothafar/simplebatterynotifier/ui/SettingsActivity.java @@ -139,6 +139,17 @@ public static class HeaderFragment extends PreferenceFragmentCompat { @Override public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { setPreferencesFromResource(R.xml.pref_headers_root, rootKey); + + // Version footer (#113): show the running app version at the bottom of the root + // screen; tapping it opens the shared About dialog (same one as the main menu, #136). + final Preference about = findPreference(getString(R.string._pref_key_about)); + if (nonNull(about)) { + about.setTitle(getString(R.string.about_version, AboutDialog.appVersionName(requireContext()))); + about.setOnPreferenceClickListener(pref -> { + AboutDialog.show(requireActivity()); + return true; + }); + } } } } diff --git a/app/src/main/java/com/almothafar/simplebatterynotifier/ui/fragment/GenericPreferenceFragment.java b/app/src/main/java/com/almothafar/simplebatterynotifier/ui/fragment/GenericPreferenceFragment.java index 44c4f74..7f06712 100644 --- a/app/src/main/java/com/almothafar/simplebatterynotifier/ui/fragment/GenericPreferenceFragment.java +++ b/app/src/main/java/com/almothafar/simplebatterynotifier/ui/fragment/GenericPreferenceFragment.java @@ -120,11 +120,11 @@ public void onCreatePreferences(final Bundle savedInstanceState, final String ro if (category.equals(getString(R.string.pref_category_general))) { setPreferencesFromResource(R.xml.pref_general, rootKey); configureLanguagePreference(); - } else if (category.equals(getString(R.string.pref_category_notifications))) { - setPreferencesFromResource(R.xml.pref_notification, rootKey); + } else if (category.equals(getString(R.string.pref_category_alerts))) { + setPreferencesFromResource(R.xml.pref_alerts, rootKey); configureTemperatureThreshold(); - } else if (category.equals(getString(R.string.pref_category_time_settings))) { - setPreferencesFromResource(R.xml.pref_time_settings, rootKey); + } else if (category.equals(getString(R.string.pref_category_behaviour))) { + setPreferencesFromResource(R.xml.pref_behaviour, rootKey); } } } diff --git a/app/src/main/res/layout/dialog_about.xml b/app/src/main/res/layout/dialog_about.xml index 436e8ea..681b5c6 100644 --- a/app/src/main/res/layout/dialog_about.xml +++ b/app/src/main/res/layout/dialog_about.xml @@ -1,5 +1,5 @@ - + مدخل يو اس بي شاحن لاسلكي + عام - التنبيهات والأصوات - إعدادات الوقت - إعدادات التطبيق العامة، ويمكنك تحديد المستويات - إعدادات كيفية وصول التنبيهات، وتحديدها، والأصوات - إعدادات الوقت للتنبيهات + التنبيهات + سلوك التنبيهات + لغة التطبيق، ووحدة الحرارة، وإشعار الحالة + ما الذي يُنبَّه له — مستويات البطارية، اكتمال الشحن، الحرارة، الاستهلاك السريع، الشحن + كيف تُصدر التنبيهات صوتاً وكيف تظهر ومتى يُسمح بها — وضع الصامت، الاهتزاز، ساعات الهدوء الرنين صامت @@ -62,19 +63,21 @@ وحدة الحرارة إطفاء تشغيل - التنبيهات ثابتة - التنبيهات غير ثابتة - تنبيه ثابت - صوت التنبيه سيسمع حتى لو في وضعية الصامت - وضعية الصامت لن يتم تجاهلها ولن تسمع التنبيهات في وضعية صامت - تفعيل وضعية صامت - التنبيه الصوتي في أي وقت من اليوم - التنبيه الصوتي محدود بفترة محددة في اليوم - وقت محدد للتنبيهات - وقت البداية للتنبيه - وقت النهاية للتنبيه - التنبيه بالرجاج مفعل - التنبيه بالرجاج معطل + لا يمكن إزالة إشعارات التنبيه بالسحب + يمكن إزالة إشعارات التنبيه بشكل طبيعي + إشعارات ثابتة + + تُصدر التنبيهات صوتاً حتى عندما يكون الهاتف في وضع الصامت أو عدم الإزعاج + تبقى التنبيهات صامتة عندما يكون الهاتف في وضع الصامت أو عدم الإزعاج + كتم التنبيهات في وضع الصامت + يمكن أن تُصدر التنبيهات صوتاً في أي وقت من اليوم + تُصدر التنبيهات صوتاً فقط بين الوقتين أدناه، وتبقى صامتة خارجهما + ساعات الهدوء + التنبيهات مسموحة من + التنبيهات مسموحة حتى + + التنبيه بالرجاج معطل + التنبيه بالرجاج مفعل صوت المستوى الحرج صوت مستوى التنبيه صوت تنبيه اكتمال الشحن @@ -247,6 +250,8 @@ برنامج حر ومفتوح المصدر، مرخَّص بموجب رخصة Apache 2.0. قراءات البطارية وأرقام الصحة والشحن مصدرها جهازك وهي تقديرية — اعتبرها إرشادية لا قياسات دقيقة. يُقدَّم هذا التطبيق «كما هو» دون أي ضمان من أي نوع. عرض على GitHub + + اضغط لمزيد من المعلومات حول التطبيق اللغة @@ -255,11 +260,14 @@ العرض - حدود مستوى البطارية - إعدادات أخرى - إعدادات التنبيهات + إشعار الحالة + مستويات البطارية البطارية الممتلئة - مستوى التحذير - الحرارة - مستوى البطارية الحرج + تنبيه التحذير + ارتفاع الحرارة + التنبيه الحرج + الشحن + الصوت والاهتزاز + ساعات الهدوء + المظهر diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 39afde8..9929cb2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -43,12 +43,13 @@ USB Port Wireless Charger + General - Notifications & Sounds - Notification Time Settings - General app settings, and you can set the levels here - Settings for how to get your notifications, what you want to notify, and sounds - When to get notifications + Alerts + Notification Behaviour + App language, temperature unit, and the status notification + What to be notified about — battery levels, full charge, temperature, fast drain, charging + How alerts sound and appear, and when they are allowed — silent mode, vibration, quiet hours Ringtone Silent @@ -65,17 +66,20 @@ Temperature Unit OFF ON - Notification is Sticky - Notification is not Sticky - Sticky Notification - Notification sound will play even if phone in silent mode - Silent mode will not be ignored, and you will NOT hear notification sound if phone in silent mode - Apply Silent Mode - Notification sounds any moment of a Day - Notification sounds only within specific time in a Day - Limited Notifications Time Range - Notify Start Time - Notify End Time + Alert notifications can\'t be swiped away + Alert notifications can be dismissed normally + Sticky notifications + + Alert sounds play even while the phone is in silent mode or Do Not Disturb + Alerts stay silent while the phone is in silent mode or Do Not Disturb + Mute alerts in silent mode + + Alerts can sound at any time of day + Alerts sound only between the times below, and stay quiet outside them + Quiet hours + Alerts allowed from + Alerts allowed until Notifications Vibration Disabled Notifications Vibration Enabled Critical Level Sound @@ -259,6 +263,8 @@ Battery readings, health and charging figures come from your device and are estimates — treat them as guidance, not exact measurements. This app is provided “as is”, without warranty of any kind. View on GitHub https://github.com/almothafar/SimpleBatteryNotifier + + Tap for more about this app Language @@ -270,21 +276,26 @@ Display - Battery Level Thresholds - Other Settings - Notification Settings + Status Notification + Battery Levels Full Battery - Warning Level - Temperature - Critical Battery Level + Warning Alert + High Temperature + Critical Alert + Charging + Sound & Vibration + Quiet Hours + Appearance category pref_category_general - pref_category_notifications - pref_category_time_settings + pref_category_alerts + pref_category_behaviour + + key_about key_warn_battery_level key_critical_battery_level diff --git a/app/src/main/res/xml/pref_notification.xml b/app/src/main/res/xml/pref_alerts.xml similarity index 79% rename from app/src/main/res/xml/pref_notification.xml rename to app/src/main/res/xml/pref_alerts.xml index fcc6113..0b56121 100644 --- a/app/src/main/res/xml/pref_notification.xml +++ b/app/src/main/res/xml/pref_alerts.xml @@ -1,37 +1,72 @@ + + - + + + + + + + + + + + + @@ -60,30 +95,6 @@ - - - - - - - - @@ -168,47 +179,21 @@ app:adjustable="true" app:iconSpaceReserved="false" /> - - + - - - - - diff --git a/app/src/main/res/xml/pref_behaviour.xml b/app/src/main/res/xml/pref_behaviour.xml new file mode 100644 index 0000000..67c4e11 --- /dev/null +++ b/app/src/main/res/xml/pref_behaviour.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml index 8b8fad9..92642eb 100644 --- a/app/src/main/res/xml/pref_general.xml +++ b/app/src/main/res/xml/pref_general.xml @@ -31,36 +31,19 @@ + - - - - - - diff --git a/app/src/main/res/xml/pref_headers.xml b/app/src/main/res/xml/pref_headers.xml deleted file mode 100644 index 434de15..0000000 --- a/app/src/main/res/xml/pref_headers.xml +++ /dev/null @@ -1,30 +0,0 @@ - - -

- -
- -
- -
- -
- -
- - diff --git a/app/src/main/res/xml/pref_headers_legacy.xml b/app/src/main/res/xml/pref_headers_legacy.xml deleted file mode 100644 index c1a57cf..0000000 --- a/app/src/main/res/xml/pref_headers_legacy.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/pref_headers_root.xml b/app/src/main/res/xml/pref_headers_root.xml index ab0f9e2..cdeb573 100644 --- a/app/src/main/res/xml/pref_headers_root.xml +++ b/app/src/main/res/xml/pref_headers_root.xml @@ -15,24 +15,32 @@ + android:value="@string/pref_category_alerts" /> + android:value="@string/pref_category_behaviour" /> + + + diff --git a/app/src/main/res/xml/pref_time_settings.xml b/app/src/main/res/xml/pref_time_settings.xml deleted file mode 100644 index 0c6d0c0..0000000 --- a/app/src/main/res/xml/pref_time_settings.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - -