Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* 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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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);
Expand All @@ -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 + ")";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/layout/dialog_about.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Content of the "About the app" dialog (see MainActivity#showAboutDialog) -->
<!-- Content of the "About the app" dialog (see AboutDialog#show) -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
Expand Down
56 changes: 32 additions & 24 deletions app/src/main/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@
<string name="charger_usb">مدخل يو اس بي</string>
<string name="charger_wireless">شاحن لاسلكي</string>

<!-- Settings root screen: the three headers (#112) -->
<string name="pref_header_general">عام</string>
<string name="pref_header_notifications">التنبيهات والأصوات</string>
<string name="pref_header_time_settings">إعدادات الوقت</string>
<string name="pref_header_summary_general">إعدادات التطبيق العامة، ويمكنك تحديد المستويات</string>
<string name="pref_header_summary_notifications">إعدادات كيفية وصول التنبيهات، وتحديدها، والأصوات</string>
<string name="pref_header_summary_time_settings">إعدادات الوقت للتنبيهات</string>
<string name="pref_header_alerts">التنبيهات</string>
<string name="pref_header_behaviour">سلوك التنبيهات</string>
<string name="pref_header_summary_general">لغة التطبيق، ووحدة الحرارة، وإشعار الحالة</string>
<string name="pref_header_summary_alerts">ما الذي يُنبَّه له — مستويات البطارية، اكتمال الشحن، الحرارة، الاستهلاك السريع، الشحن</string>
<string name="pref_header_summary_behaviour">كيف تُصدر التنبيهات صوتاً وكيف تظهر ومتى يُسمح بها — وضع الصامت، الاهتزاز، ساعات الهدوء</string>

<string name="pref_title_ringtone">الرنين</string>
<string name="pref_ringtone_silent">صامت</string>
Expand All @@ -62,19 +63,21 @@
<string name="temperature_unit">وحدة الحرارة</string>
<string name="off">إطفاء</string>
<string name="on">تشغيل</string>
<string name="notification_is_sticky">التنبيهات ثابتة</string>
<string name="notification_is_not_sticky">التنبيهات غير ثابتة</string>
<string name="sticky_notification">تنبيه ثابت</string>
<string name="notifications_apply_silent_mode_summary_off">صوت التنبيه سيسمع حتى لو في وضعية الصامت</string>
<string name="notifications_apply_silent_mode_summary_on">وضعية الصامت لن يتم تجاهلها ولن تسمع التنبيهات في وضعية صامت</string>
<string name="notifications_apply_silent_mode">تفعيل وضعية صامت</string>
<string name="notifications_time_range_summary_off">التنبيه الصوتي في أي وقت من اليوم</string>
<string name="notifications_time_range_summary_on">التنبيه الصوتي محدود بفترة محددة في اليوم</string>
<string name="notifications_time_range">وقت محدد للتنبيهات</string>
<string name="notifications_time_range_start">وقت البداية للتنبيه</string>
<string name="notifications_time_range_end">وقت النهاية للتنبيه</string>
<string name="notifications_vibrate_summary_off">التنبيه بالرجاج مفعل</string>
<string name="notifications_vibrate_summary_on">التنبيه بالرجاج معطل</string>
<string name="notification_is_sticky">لا يمكن إزالة إشعارات التنبيه بالسحب</string>
<string name="notification_is_not_sticky">يمكن إزالة إشعارات التنبيه بشكل طبيعي</string>
<string name="sticky_notification">إشعارات ثابتة</string>
<!-- Reworded from "Apply Silent Mode" (#112): same key and meaning (ON = respect silent/DnD) -->
<string name="mute_alerts_in_silent_mode_summary_off">تُصدر التنبيهات صوتاً حتى عندما يكون الهاتف في وضع الصامت أو عدم الإزعاج</string>
<string name="mute_alerts_in_silent_mode_summary_on">تبقى التنبيهات صامتة عندما يكون الهاتف في وضع الصامت أو عدم الإزعاج</string>
<string name="mute_alerts_in_silent_mode">كتم التنبيهات في وضع الصامت</string>
<string name="notifications_time_range_summary_off">يمكن أن تُصدر التنبيهات صوتاً في أي وقت من اليوم</string>
<string name="notifications_time_range_summary_on">تُصدر التنبيهات صوتاً فقط بين الوقتين أدناه، وتبقى صامتة خارجهما</string>
<string name="notifications_time_range">ساعات الهدوء</string>
<string name="notifications_time_range_start">التنبيهات مسموحة من</string>
<string name="notifications_time_range_end">التنبيهات مسموحة حتى</string>
<!-- Previously swapped (off said "enabled"); fixed as part of #112's wording pass -->
<string name="notifications_vibrate_summary_off">التنبيه بالرجاج معطل</string>
<string name="notifications_vibrate_summary_on">التنبيه بالرجاج مفعل</string>
<string name="notifications_alert_sound_ringtone">صوت المستوى الحرج</string>
<string name="notifications_warning_sound_ringtone">صوت مستوى التنبيه</string>
<string name="notifications_full_sound_ringtone">صوت تنبيه اكتمال الشحن</string>
Expand Down Expand Up @@ -247,6 +250,8 @@
<string name="about_open_source">برنامج حر ومفتوح المصدر، مرخَّص بموجب رخصة Apache 2.0.</string>
<string name="about_disclaimer">قراءات البطارية وأرقام الصحة والشحن مصدرها جهازك وهي تقديرية — اعتبرها إرشادية لا قياسات دقيقة. يُقدَّم هذا التطبيق «كما هو» دون أي ضمان من أي نوع.</string>
<string name="about_view_github">عرض على GitHub</string>
<!-- Summary of the version footer at the bottom of the Settings root screen (#113) -->
<string name="about_footer_hint">اضغط لمزيد من المعلومات حول التطبيق</string>

<!-- App language picker -->
<string name="pref_language_category">اللغة</string>
Expand All @@ -255,11 +260,14 @@

<!-- Settings category titles -->
<string name="pref_cat_title_display">العرض</string>
<string name="pref_cat_title_thresholds">حدود مستوى البطارية</string>
<string name="pref_cat_title_other">إعدادات أخرى</string>
<string name="pref_cat_title_notification">إعدادات التنبيهات</string>
<string name="pref_cat_title_status_notification">إشعار الحالة</string>
<string name="pref_cat_title_thresholds">مستويات البطارية</string>
<string name="pref_cat_title_full_battery">البطارية الممتلئة</string>
<string name="pref_cat_title_warning">مستوى التحذير</string>
<string name="pref_cat_title_temperature">الحرارة</string>
<string name="pref_cat_title_critical">مستوى البطارية الحرج</string>
<string name="pref_cat_title_warning">تنبيه التحذير</string>
<string name="pref_cat_title_temperature">ارتفاع الحرارة</string>
<string name="pref_cat_title_critical">التنبيه الحرج</string>
<string name="pref_cat_title_charging">الشحن</string>
<string name="pref_cat_title_sound_vibration">الصوت والاهتزاز</string>
<string name="pref_cat_title_quiet_hours">ساعات الهدوء</string>
<string name="pref_cat_title_appearance">المظهر</string>
</resources>
Loading