fix(app): the Pomodoro timer's permissions take effect as soon as they are given
Found on the Pixel 9a with v0.2.1: - Do Not Disturb while focusing: switching it on opens Android's list of apps with Do Not Disturb access; once the person allowed it there and came back, the switch was still off, saying the app may not, and had to be switched on again. The profile's sheet now looks again when the app comes back, and the switch goes on by itself once allowed. - The timer's notification, posted while Android was still asking for leave to notify at the first start, showed nowhere until the timer next changed. It is now posted again as soon as notifications are allowed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
e68654c5f8
commit
c89ef05a20
4 changed files with 101 additions and 2 deletions
|
|
@ -66,7 +66,26 @@ class MainActivity : FlutterActivity() {
|
|||
checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
askedToNotify = true
|
||||
requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1)
|
||||
requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), NOTIFY_REQUEST)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray,
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
// The timer's notification was posted while Android asked, and showed
|
||||
// nowhere: now it may.
|
||||
if (requestCode == NOTIFY_REQUEST &&
|
||||
grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
PomodoroService.showAgain()
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val NOTIFY_REQUEST = 1
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@ class PomodoroService : Service() {
|
|||
appGone = false
|
||||
}
|
||||
|
||||
/** Shows the timer again, as once notifications are allowed: posted before, it showed nowhere. */
|
||||
fun showAgain() {
|
||||
running?.repost()
|
||||
}
|
||||
|
||||
/** Whether the timer was stopped here while the app was gone; forgets it. */
|
||||
fun takeStoppedOutside(context: Context): Boolean {
|
||||
val preferences = context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE)
|
||||
|
|
|
|||
|
|
@ -102,8 +102,19 @@ class _ProfileSheetState() extends State<_ProfileSheet> {
|
|||
late final _name = TextEditingController(text: widget.initial.name);
|
||||
var _quietDenied = false;
|
||||
|
||||
// Asking may open the system's settings, where the person allows it and
|
||||
// comes back: then the switch goes on by itself.
|
||||
late final AppLifecycleListener _comingBack;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_comingBack = AppLifecycleListener(onResume: _lookAgainAtQuiet);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_comingBack.dispose();
|
||||
_name.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
|
@ -115,9 +126,21 @@ class _ProfileSheetState() extends State<_ProfileSheet> {
|
|||
_change(_profile.copyWith(doNotDisturb: false));
|
||||
return;
|
||||
}
|
||||
await _quiet(ask: true);
|
||||
}
|
||||
|
||||
Future<void> _lookAgainAtQuiet() async {
|
||||
if (_quietDenied) {
|
||||
await _quiet(ask: false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeps quiet in this profile if the person allows it, asking them when
|
||||
/// [ask]; says so otherwise.
|
||||
Future<void> _quiet({required bool ask}) async {
|
||||
final allowed = await DependenciesScope.of(
|
||||
context,
|
||||
).pomodoro.mayKeepQuiet(ask: true);
|
||||
).pomodoro.mayKeepQuiet(ask: ask);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_quietDenied = !allowed;
|
||||
|
|
|
|||
|
|
@ -155,6 +155,58 @@ void main() {
|
|||
expect(tester.widget<SwitchListTile>(quiet).value, isTrue);
|
||||
});
|
||||
|
||||
testWidgets("allowed in the system's settings, keeping quiet switches on "
|
||||
'by itself on coming back', (tester) async {
|
||||
await showHome(tester);
|
||||
await tester.tap(find.byTooltip('Pomodoro profiles'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byTooltip('Edit profile').first);
|
||||
await tester.pumpAndSettle();
|
||||
final quiet = find.widgetWithText(
|
||||
SwitchListTile,
|
||||
'Do Not Disturb while focusing',
|
||||
);
|
||||
await tester.ensureVisible(quiet);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(quiet);
|
||||
await tester.pumpAndSettle();
|
||||
expect(tester.widget<SwitchListTile>(quiet).value, isFalse);
|
||||
|
||||
// The person allows it in the system's settings, and comes back.
|
||||
platform.allowsQuiet = true;
|
||||
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.inactive);
|
||||
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.widget<SwitchListTile>(quiet).value, isTrue);
|
||||
expect(find.textContaining('may not keep this device quiet'), findsNothing);
|
||||
expect(platform.askedForQuiet, 1, reason: 'asked once, not again');
|
||||
});
|
||||
|
||||
testWidgets('coming back without allowing it leaves keeping quiet off', (
|
||||
tester,
|
||||
) async {
|
||||
await showHome(tester);
|
||||
await tester.tap(find.byTooltip('Pomodoro profiles'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byTooltip('Edit profile').first);
|
||||
await tester.pumpAndSettle();
|
||||
final quiet = find.widgetWithText(
|
||||
SwitchListTile,
|
||||
'Do Not Disturb while focusing',
|
||||
);
|
||||
await tester.ensureVisible(quiet);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(quiet);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.inactive);
|
||||
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.widget<SwitchListTile>(quiet).value, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('suggests setting up the top bar where it shows no icons, '
|
||||
'until dismissed', (tester) async {
|
||||
platform.suggestTopBar(suggest: true);
|
||||
|
|
|
|||
Loading…
Reference in a new issue