Skip to content
Open
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
12 changes: 11 additions & 1 deletion lib/domain/interfaces/config_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,17 @@ abstract class ConfigProvider extends ChangeNotifier {

int get nightStartMinute; // Minute when night mode starts (0-59), default 0
set nightStartMinute(int value);


// black Screen in Darkness
bool get darkScreenEnabled; // Enable dark screen in darkness
set darkScreenEnabled(bool value);

int get darkScreenThreshold; // Threshold for dark screen
set darkScreenThreshold(int value);

int get darkScreenOffset; // Offset for hysteresis
set darkScreenOffset(int value);

bool get useNativeScreenOff; // Use Device Admin lockNow() for true screen off (Android)
set useNativeScreenOff(bool value);

Expand Down
25 changes: 25 additions & 0 deletions lib/infrastructure/services/json_config_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,31 @@ class JsonConfigService extends ConfigProvider {
set useNativeScreenOff(bool value) {
_config['use_native_screen_off'] = value;
}

// Dark screen in darkeness settings
@override
bool get darkScreenEnabled => _config['dark_screen_enabled'] ?? false;

@override
set darkScreenEnabled(bool value) {
_config['dark_screen_enabled'] = value;
}

@override
int get darkScreenThreshold => _config['dark_screen_threshold'] ?? 10;

@override
set darkScreenThreshold(int value) {
_config['dark_screen_threshold'] = value;
}

@override
int get darkScreenOffset => _config['dark_screen_offset'] ?? 5;

@override
set darkScreenOffset(int value) {
_config['dark_screen_offset'] = value;
}

// Custom photo directory (for "local folder" mode)
@override
Expand Down
8 changes: 7 additions & 1 deletion lib/l10n/app_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,13 @@
"grantDeviceAdmin": "Geräte-Admin gewähren",
"deviceAdminEnabled": "Geräte-Admin aktiviert - Bildschirm wird komplett ausgeschaltet",
"screenLockWarning": "Wichtig: Die Bildschirmsperre (PIN/Muster/Passwort) muss deaktiviert sein, damit das automatische Aufwachen funktioniert. Gehe zu Einstellungen → Sicherheit → Bildschirmsperre → Keine.",


"darkScreenEnabled": "Bildschirm bei Dunkelheit dunkel schalten",
"darkScreenEnabledSubtitle": "Keine Anzeige in der Dunkelheit",
"darkScreenThreshold": "Schwellenwert für dunklen Bildschirm",
"darkScreenOffset": "Einschaltverzögerung",


"deviceAdminActive": "Geräte-Admin aktiv",
"deviceAdminUninstallWarning": "Um diese App zu deinstallieren, muss zuerst die Geräte-Admin-Berechtigung in den Android-Einstellungen deaktiviert werden.",
"openDeviceAdminSettings": "Geräte-Admin-Einstellungen öffnen",
Expand Down
7 changes: 6 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,12 @@
"grantDeviceAdmin": "Grant Device Admin",
"deviceAdminEnabled": "Device Admin enabled - screen will turn off completely",
"screenLockWarning": "Important: Screen lock (PIN/Pattern/Password) must be disabled for automatic wake-up to work. Go to Settings → Security → Screen lock → None.",


"darkScreenEnabled": "Dark screen in darkness",
"darkScreenEnabledSubtitle": "No picture displayed in darkness",
"darkScreenThreshold": "Threshold for dark screen",
"darkScreenOffset": "Offset for screen on",

"deviceAdminActive": "Device Admin Active",
"deviceAdminUninstallWarning": "To uninstall this app, you must first disable Device Admin permission in Android settings.",
"openDeviceAdminSettings": "Open Device Admin Settings",
Expand Down
24 changes: 24 additions & 0 deletions lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,30 @@ abstract class AppLocalizations {
/// **'Important: Screen lock (PIN/Pattern/Password) must be disabled for automatic wake-up to work. Go to Settings → Security → Screen lock → None.'**
String get screenLockWarning;

/// No description provided for @darkScreenEnabled.
///
/// In en, this message translates to:
/// **'Dark screen in darkness'**
String get darkScreenEnabled;

/// No description provided for @darkScreenEnabledSubtitle.
///
/// In en, this message translates to:
/// **'No picture displayed in darkness'**
String get darkScreenEnabledSubtitle;

/// No description provided for @darkScreenThreshold.
///
/// In en, this message translates to:
/// **'Threshold for dark screen'**
String get darkScreenThreshold;

/// No description provided for @darkScreenOffset.
///
/// In en, this message translates to:
/// **'Offset for screen on'**
String get darkScreenOffset;

/// No description provided for @deviceAdminActive.
///
/// In en, this message translates to:
Expand Down
12 changes: 12 additions & 0 deletions lib/l10n/app_localizations_de.dart
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,18 @@ class AppLocalizationsDe extends AppLocalizations {
String get screenLockWarning =>
'Wichtig: Die Bildschirmsperre (PIN/Muster/Passwort) muss deaktiviert sein, damit das automatische Aufwachen funktioniert. Gehe zu Einstellungen → Sicherheit → Bildschirmsperre → Keine.';

@override
String get darkScreenEnabled => 'Bildschirm bei Dunkelheit dunkel schalten';

@override
String get darkScreenEnabledSubtitle => 'Keine Anzeige in der Dunkelheit';

@override
String get darkScreenThreshold => 'Schwellenwert für dunklen Bildschirm';

@override
String get darkScreenOffset => 'Einschaltverzögerung';

@override
String get deviceAdminActive => 'Geräte-Admin aktiv';

Expand Down
12 changes: 12 additions & 0 deletions lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,18 @@ class AppLocalizationsEn extends AppLocalizations {
String get screenLockWarning =>
'Important: Screen lock (PIN/Pattern/Password) must be disabled for automatic wake-up to work. Go to Settings → Security → Screen lock → None.';

@override
String get darkScreenEnabled => 'Dark screen in darkness';

@override
String get darkScreenEnabledSubtitle => 'No picture displayed in darkness';

@override
String get darkScreenThreshold => 'Threshold for dark screen';

@override
String get darkScreenOffset => 'Offset for screen on';

@override
String get deviceAdminActive => 'Device Admin Active';

Expand Down
124 changes: 120 additions & 4 deletions lib/ui/screens/settings_screen.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:file_picker/file_picker.dart';
import 'package:light_sensor/light_sensor.dart';
import '../../l10n/app_localizations.dart';
import 'package:path_provider/path_provider.dart';
import 'package:photo_manager/photo_manager.dart';
Expand Down Expand Up @@ -52,6 +54,13 @@ class _SettingsScreenState extends State<SettingsScreen> with WidgetsBindingObse
late TimeOfDay _nightStartTime;
late bool _useNativeScreenOff;
bool _deviceAdminEnabled = false;

// Auto dark screen settings
late bool _darkScreenEnabled;
late int _darkScreenThreshold;
late int _darkScreenOffset;
int? _currentLux;
StreamSubscription? _luxSubscription;

// Screen orientation setting
late String _screenOrientation;
Expand Down Expand Up @@ -118,7 +127,20 @@ class _SettingsScreenState extends State<SettingsScreen> with WidgetsBindingObse
_dayStartTime = TimeOfDay(hour: config.dayStartHour, minute: config.dayStartMinute);
_nightStartTime = TimeOfDay(hour: config.nightStartHour, minute: config.nightStartMinute);
_useNativeScreenOff = config.useNativeScreenOff;


//Dark screen setting
_darkScreenEnabled = config.darkScreenEnabled;
_darkScreenThreshold = config.darkScreenThreshold;
_darkScreenOffset = config.darkScreenOffset;

LightSensor.hasSensor().then((has) {
if (has) {
_luxSubscription = LightSensor.luxStream().listen((lux) {
if (mounted) setState(() => _currentLux = lux);
});
}
});

// Screen orientation
_screenOrientation = config.screenOrientation;

Expand Down Expand Up @@ -180,6 +202,8 @@ class _SettingsScreenState extends State<SettingsScreen> with WidgetsBindingObse
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_nextcloudUrlController.dispose();
//_luxSubscription?.cancel();
_luxSubscription = null;
super.dispose();
}

Expand Down Expand Up @@ -236,7 +260,13 @@ class _SettingsScreenState extends State<SettingsScreen> with WidgetsBindingObse
config.nightStartHour = _nightStartTime.hour;
config.nightStartMinute = _nightStartTime.minute;
config.useNativeScreenOff = _useNativeScreenOff;


// Display off in darkness settings
config.darkScreenEnabled = _darkScreenEnabled;
config.darkScreenThreshold = _darkScreenThreshold;
config.darkScreenOffset = _darkScreenOffset;


// Screen orientation
config.screenOrientation = _screenOrientation;

Expand Down Expand Up @@ -463,9 +493,28 @@ class _SettingsScreenState extends State<SettingsScreen> with WidgetsBindingObse
const SizedBox(height: 24),
const Divider(),
const SizedBox(height: 16),

// === ANDROID SETTINGS (only on Android) ===
if (Platform.isAndroid) ...[

const SizedBox(height: 8),

SwitchListTile(
title: Text(AppLocalizations.of(context)!.darkScreenEnabled),
subtitle: Text(AppLocalizations.of(context)!.darkScreenEnabledSubtitle),
secondary: const Icon(Icons.nightlight_round),
value: _darkScreenEnabled,
onChanged: (value) {
setState(() => _darkScreenEnabled = value);
},
),

if (_darkScreenEnabled) _buildDarkScreenSettings(),

const SizedBox(height: 24),
const Divider(),
const SizedBox(height: 16),

_buildSectionHeader(AppLocalizations.of(context)!.sectionAndroid),
const SizedBox(height: 8),

Expand Down Expand Up @@ -511,7 +560,9 @@ class _SettingsScreenState extends State<SettingsScreen> with WidgetsBindingObse
setState(() => _keepAliveEnabled = value);
},
),




const SizedBox(height: 24),
const Divider(),
const SizedBox(height: 16),
Expand Down Expand Up @@ -1514,6 +1565,71 @@ class _SettingsScreenState extends State<SettingsScreen> with WidgetsBindingObse
],
];
}

Widget _buildDarkScreenSettings() {
final displayValue = '${(_darkScreenThreshold)} lx';
return Padding(
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.mode_night_outlined, size: 20),
const SizedBox(width: 12),
Expanded(child: Text(AppLocalizations.of(context)!.darkScreenThreshold)),
if (_currentLux != null) ...[
Icon(Icons.wb_sunny_outlined, size: 16, color: Colors.grey),
const SizedBox(width: 4),
Text(
'${_currentLux!.round()} lx',
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.grey),
),
const SizedBox(width: 12),
],
Text(
displayValue,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
Slider(
value: _darkScreenThreshold.toDouble(),
min: 0,
max: 100,
divisions: 100,
onChanged: (value) {
setState(() => _darkScreenThreshold = value.round());
},
),
Row(
children: [
const Icon(Icons.brightness_6, size: 20),
const SizedBox(width: 12),
Expanded(child: Text(AppLocalizations.of(context)!.darkScreenOffset)),
Text(
'${(_darkScreenOffset)} lx',
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
Slider(
value: _darkScreenOffset.toDouble(),
min: 1,
max: 20,
divisions: 20,
onChanged: (value) {
setState(() => _darkScreenOffset = value.round());
},
),
],
)
);
}

Future<void> _selectTime({required bool isDay}) async {
final initialTime = isDay ? _dayStartTime : _nightStartTime;
Expand Down
Loading