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
50 changes: 50 additions & 0 deletions Kiosk/android/app/src/main/kotlin/com/secgo/kiosk/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.os.Build
import android.provider.Settings
import android.util.Log
Expand Down Expand Up @@ -43,6 +44,55 @@ class MainActivity : FlutterActivity() {
"getLatestWechatNotification" -> result.success(getLatestWechatNotification())
"getLatestWechatPaymentNotification" -> result.success(getLatestWechatPaymentNotification())
"getActiveWechatNotificationsSnapshot" -> result.success(getActiveWechatNotificationsSnapshot())
"openLauncherHome" -> {
val intent =
Intent(Intent.ACTION_MAIN).apply {
addCategory(Intent.CATEGORY_HOME)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(intent)
result.success(true)
}
"openApp" -> {
val pkg = call.argument<String>("packageName")?.trim()
if (pkg.isNullOrBlank()) {
result.success(false)
return@setMethodCallHandler
}
val launchIntent = packageManager.getLaunchIntentForPackage(pkg)
if (launchIntent == null) {
result.success(false)
return@setMethodCallHandler
}
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(launchIntent)
result.success(true)
}
"listLaunchableApps" -> {
val intent =
Intent(Intent.ACTION_MAIN).apply {
addCategory(Intent.CATEGORY_LAUNCHER)
}

val activities =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager.queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(0))
} else {
@Suppress("DEPRECATION")
packageManager.queryIntentActivities(intent, 0)
}

val resultList =
activities.map { ri ->
val label = ri.loadLabel(packageManager)?.toString() ?: ""
val pkg = ri.activityInfo?.packageName ?: ""
mapOf(
"packageName" to pkg,
"label" to label,
)
}.filter { it["packageName"]?.isNotBlank() == true }
result.success(resultList)
}
"openNotificationListenerSettings" -> {
startActivity(Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
result.success(true)
Expand Down
14 changes: 13 additions & 1 deletion Kiosk/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,17 @@
"retry": "Retry",
"serverNoIp": "Failed to start server: No IP address found",
"restoreComplete": "Restore Complete",
"returningHome": "Refreshing data..."
"returningHome": "Refreshing data...",
"openLauncher": "Open Launcher",
"launcherTarget": "Target: {target}",
"launcherDefault": "Launcher",
"homeAppPackageTitle": "Home app package (optional)",
"homeAppPackageHint": "e.g. com.secgo.home",
"homeAppNotSet": "Not set (use Launcher)",
"homeAppTitle": "Home App (optional)",
"searchApps": "Search apps",
"noAppsFound": "No apps found",
"homeAppOpenFailed": "Can't open {package}. Opening Launcher instead.",
"clear": "Clear",
"save": "Save"
}
72 changes: 72 additions & 0 deletions Kiosk/lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,78 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Refreshing data...'**
String get returningHome;

/// No description provided for @openLauncher.
///
/// In en, this message translates to:
/// **'Open Launcher'**
String get openLauncher;

/// No description provided for @launcherTarget.
///
/// In en, this message translates to:
/// **'Target: {target}'**
String launcherTarget(Object target);

/// No description provided for @launcherDefault.
///
/// In en, this message translates to:
/// **'Launcher'**
String get launcherDefault;

/// No description provided for @homeAppPackageTitle.
///
/// In en, this message translates to:
/// **'Home app package (optional)'**
String get homeAppPackageTitle;

/// No description provided for @homeAppPackageHint.
///
/// In en, this message translates to:
/// **'e.g. com.secgo.home'**
String get homeAppPackageHint;

/// No description provided for @homeAppNotSet.
///
/// In en, this message translates to:
/// **'Not set (use Launcher)'**
String get homeAppNotSet;

/// No description provided for @homeAppTitle.
///
/// In en, this message translates to:
/// **'Home App (optional)'**
String get homeAppTitle;

/// No description provided for @searchApps.
///
/// In en, this message translates to:
/// **'Search apps'**
String get searchApps;

/// No description provided for @noAppsFound.
///
/// In en, this message translates to:
/// **'No apps found'**
String get noAppsFound;

/// No description provided for @homeAppOpenFailed.
///
/// In en, this message translates to:
/// **'Can\'t open {package}. Opening Launcher instead.'**
String homeAppOpenFailed(Object package);

/// No description provided for @clear.
///
/// In en, this message translates to:
/// **'Clear'**
String get clear;

/// No description provided for @save.
///
/// In en, this message translates to:
/// **'Save'**
String get save;
}

class _AppLocalizationsDelegate
Expand Down
40 changes: 40 additions & 0 deletions Kiosk/lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,44 @@ class AppLocalizationsEn extends AppLocalizations {

@override
String get returningHome => 'Refreshing data...';

@override
String get openLauncher => 'Open Launcher';

@override
String launcherTarget(Object target) {
return 'Target: $target';
}

@override
String get launcherDefault => 'Launcher';

@override
String get homeAppPackageTitle => 'Home app package (optional)';

@override
String get homeAppPackageHint => 'e.g. com.secgo.home';

@override
String get homeAppNotSet => 'Not set (use Launcher)';

@override
String get homeAppTitle => 'Home App (optional)';

@override
String get searchApps => 'Search apps';

@override
String get noAppsFound => 'No apps found';

@override
String homeAppOpenFailed(Object package) {
return 'Can\'t open $package. Opening Launcher instead.';
}

@override
String get clear => 'Clear';

@override
String get save => 'Save';
}
40 changes: 40 additions & 0 deletions Kiosk/lib/l10n/app_localizations_zh.dart
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,44 @@ class AppLocalizationsZh extends AppLocalizations {

@override
String get returningHome => '正在刷新数据...';

@override
String get openLauncher => '打开桌面启动器';

@override
String launcherTarget(Object target) {
return '目标:$target';
}

@override
String get launcherDefault => '桌面启动器';

@override
String get homeAppPackageTitle => '首页应用包名(可选)';

@override
String get homeAppPackageHint => '例如:com.secgo.home';

@override
String get homeAppNotSet => '未设置(使用桌面启动器)';

@override
String get homeAppTitle => '首页应用(可选)';

@override
String get searchApps => '搜索应用';

@override
String get noAppsFound => '未找到应用';

@override
String homeAppOpenFailed(Object package) {
return '无法打开 $package,将打开桌面启动器。';
}

@override
String get clear => '清除';

@override
String get save => '保存';
}
14 changes: 13 additions & 1 deletion Kiosk/lib/l10n/app_zh.arb
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,17 @@
"pinLength": "PIN码至少需要4位数字",
"confirmPin": "确认PIN码",
"pinMismatch": "两次输入的PIN码不一致",
"saveAndContinue": "保存并继续"
"saveAndContinue": "保存并继续",
"openLauncher": "打开桌面启动器",
"launcherTarget": "目标:{target}",
"launcherDefault": "桌面启动器",
"homeAppPackageTitle": "首页应用包名(可选)",
"homeAppPackageHint": "例如:com.secgo.home",
"homeAppNotSet": "未设置(使用桌面启动器)",
"homeAppTitle": "首页应用(可选)",
"searchApps": "搜索应用",
"noAppsFound": "未找到应用",
"homeAppOpenFailed": "无法打开 {package},将打开桌面启动器。",
"clear": "清除",
"save": "保存"
}
55 changes: 52 additions & 3 deletions Kiosk/lib/screens/main_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,48 @@ class _MainScreenState extends State<MainScreen> {
);
}

Future<bool> _confirmAdminPin() async {
final expected = _settingsService.getPin();
if (expected == null || expected.isEmpty) return true;

final l10n = AppLocalizations.of(context)!;
final controller = TextEditingController();
final ok = await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (dialogContext) {
return AlertDialog(
title: Text(l10n.adminConfirm),
content: TextField(
controller: controller,
obscureText: true,
keyboardType: TextInputType.number,
decoration: InputDecoration(labelText: l10n.enterPin),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext, false),
child: Text(l10n.cancel),
),
TextButton(
onPressed: () {
if (controller.text.trim() == expected) {
Navigator.pop(dialogContext, true);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(l10n.invalidPin)),
);
}
},
child: Text(l10n.confirm),
),
],
);
},
);
return ok ?? false;
}

Future<void> _resumePendingPaymentIfAny() async {
if (!mounted) return;
final pendingId = _settingsService.getPendingPaymentOrderId();
Expand Down Expand Up @@ -634,11 +676,18 @@ class _MainScreenState extends State<MainScreen> {
// Settings Button (Hidden access)
IconButton(
icon: const Icon(Icons.settings, color: Colors.grey),
onPressed: () {
Navigator.push(
context,
onPressed: () async {
final navigator = Navigator.of(context);
final ok = await _confirmAdminPin();
if (!ok || !mounted) return;
final result = await navigator.push(
MaterialPageRoute(builder: (_) => const SettingsScreen()),
);
if (!mounted) return;
if (result == 'reset') {
_clearCart();
setState(() => _isProcessing = false);
}
},
),
],
Expand Down
Loading