Skip to content

Commit b2a45fc

Browse files
uday chauhanuday chauhan
authored andcommitted
Merge remote-tracking branch 'origin/main'
2 parents 2731a96 + 34d9d20 commit b2a45fc

13 files changed

Lines changed: 1238 additions & 41 deletions

File tree

.github/apk-size-baselines.tsv

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
#
99
# This file is updated automatically after successful main builds.
1010
component artifact baseline_bytes budget_mb
11-
mobile-full app-arm64-v8a-release.apk 287576830 35
12-
mobile-streaming app-arm64-v8a-release.apk 20208706 35
13-
tv app-arm64-v8a-release.apk 18825095 35
11+
mobile-full app-arm64-v8a-release.apk 287593362 35
12+
mobile-streaming app-arm64-v8a-release.apk 20226802 35
13+
tv app-arm64-v8a-release.apk 18825099 35

app/lib/core/routing/app_router.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import '../../features/coins/presentation/screens/budget_management_screen.dart'
2020
import '../../features/coins/presentation/screens/groups_list_screen.dart';
2121
import '../../features/coins/presentation/screens/group_detail_screen.dart';
2222
import '../../features/coins/presentation/screens/add_split_expense_screen.dart';
23+
import '../../features/life_track/presentation/screens/track_list_screen.dart';
2324
import '../../core/auth/auth_service.dart';
2425
import '../../core/app/app_shell.dart';
2526
import '../http/http_dog.dart';
@@ -73,6 +74,11 @@ class AppRouter {
7374
GoRoute(path: '/live', redirect: (context, state) => '/music'),
7475
GoRoute(path: '/live/music', redirect: (context, state) => '/music'),
7576
GoRoute(path: '/live/tv', redirect: (context, state) => '/iptv'),
77+
GoRoute(
78+
path: '/life-track',
79+
name: 'life_track',
80+
builder: (context, state) => const TrackListScreen(),
81+
),
7682
GoRoute(
7783
path: '/airo-explore',
7884
name: 'airo_explore',
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import 'package:flutter_riverpod/flutter_riverpod.dart';
2+
3+
import '../services/android_finance_import_preferences.dart';
4+
import '../services/android_finance_import_service.dart';
5+
import '../services/finance_chat_ingestion_service.dart';
6+
import '../../domain/services/finance_message_parser.dart';
7+
import 'expense_providers.dart';
8+
9+
final androidFinanceImportPreferencesProvider =
10+
Provider<AndroidFinanceImportPreferences>(
11+
(ref) => const AndroidFinanceImportPreferences(),
12+
);
13+
14+
final androidFinanceImportPermissionControllerProvider =
15+
StateNotifierProvider<
16+
AndroidFinanceImportPermissionController,
17+
AsyncValue<AndroidFinanceImportPermission>
18+
>((ref) {
19+
final preferences = ref.watch(androidFinanceImportPreferencesProvider);
20+
return AndroidFinanceImportPermissionController(preferences);
21+
});
22+
23+
final androidFinanceImportServiceProvider =
24+
Provider<AndroidFinanceImportService>((ref) {
25+
final preferences = ref.watch(androidFinanceImportPreferencesProvider);
26+
final repository = ref.watch(transactionRepositoryProvider);
27+
return AndroidFinanceImportService(
28+
ingestionService: FinanceChatIngestionService(
29+
parser: const FinanceMessageParser(),
30+
repository: repository,
31+
),
32+
permissionReader: preferences.readPermission,
33+
);
34+
});
35+
36+
class AndroidFinanceImportPermissionController
37+
extends StateNotifier<AsyncValue<AndroidFinanceImportPermission>> {
38+
AndroidFinanceImportPermissionController(this._preferences)
39+
: super(const AsyncValue.loading()) {
40+
_load();
41+
}
42+
43+
final AndroidFinanceImportPreferences _preferences;
44+
45+
Future<void> _load() async {
46+
try {
47+
state = AsyncValue.data(await _preferences.readPermission());
48+
} catch (_) {
49+
state = const AsyncValue.data(AndroidFinanceImportPermission.disabled);
50+
}
51+
}
52+
53+
Future<void> setEnabled(bool enabled) async {
54+
state = const AsyncValue.loading();
55+
try {
56+
await _preferences.setEnabled(enabled);
57+
state = AsyncValue.data(
58+
enabled
59+
? AndroidFinanceImportPermission.enabled
60+
: AndroidFinanceImportPermission.disabled,
61+
);
62+
} catch (_) {
63+
state = const AsyncValue.data(AndroidFinanceImportPermission.disabled);
64+
}
65+
}
66+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import 'package:shared_preferences/shared_preferences.dart';
2+
3+
import 'android_finance_import_service.dart';
4+
5+
class AndroidFinanceImportPreferences {
6+
const AndroidFinanceImportPreferences();
7+
8+
static const String preferenceKey = 'coins_android_finance_import_enabled';
9+
10+
Future<AndroidFinanceImportPermission> readPermission() async {
11+
final prefs = await SharedPreferences.getInstance();
12+
final enabled = prefs.getBool(preferenceKey) ?? false;
13+
return enabled
14+
? AndroidFinanceImportPermission.enabled
15+
: AndroidFinanceImportPermission.disabled;
16+
}
17+
18+
Future<void> setEnabled(bool enabled) async {
19+
final prefs = await SharedPreferences.getInstance();
20+
await prefs.setBool(preferenceKey, enabled);
21+
}
22+
}

app/lib/features/coins/coins.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export 'domain/errors/coins_errors.dart';
6161

6262
// Application - Providers
6363
export 'application/providers/budget_providers.dart';
64+
export 'application/providers/android_import_permission_provider.dart';
6465
export 'application/providers/dashboard_providers.dart';
6566
export 'application/providers/expense_providers.dart';
6667
export 'application/providers/group_providers.dart';
@@ -82,6 +83,7 @@ export 'application/use_cases/update_expense_use_case.dart' hide Result;
8283

8384
// Application - Services
8485
export 'application/services/android_finance_import_service.dart';
86+
export 'application/services/android_finance_import_preferences.dart';
8587
export 'application/services/coins_notification_service.dart';
8688
export 'application/services/coins_platform_support.dart';
8789
export 'application/services/coins_sync_service.dart';

app/lib/features/coins/presentation/screens/coins_dashboard_screen.dart

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_riverpod/flutter_riverpod.dart';
33
import '../../../../core/utils/locale_settings.dart';
4+
import '../../application/providers/android_import_permission_provider.dart';
45
import '../../application/providers/dashboard_providers.dart';
56
import '../../application/providers/expense_providers.dart';
7+
import '../../application/services/android_finance_import_service.dart';
68
import '../../application/services/transaction_review_service.dart';
79
import '../../domain/entities/transaction.dart';
810
import '../../domain/models/budget_status.dart';
@@ -140,12 +142,17 @@ class CoinsDashboardScreen extends ConsumerWidget {
140142
}
141143
}
142144

143-
class _AndroidImportPermissionCard extends StatelessWidget {
145+
class _AndroidImportPermissionCard extends ConsumerWidget {
144146
const _AndroidImportPermissionCard();
145147

146148
@override
147-
Widget build(BuildContext context) {
149+
Widget build(BuildContext context, WidgetRef ref) {
148150
final theme = Theme.of(context);
151+
final permissionState = ref.watch(
152+
androidFinanceImportPermissionControllerProvider,
153+
);
154+
final permission = permissionState.valueOrNull;
155+
final enabled = permission == AndroidFinanceImportPermission.enabled;
149156
return Card(
150157
elevation: 0,
151158
child: Padding(
@@ -166,24 +173,41 @@ class _AndroidImportPermissionCard extends StatelessWidget {
166173
),
167174
),
168175
Chip(
169-
label: const Text('Permission disabled'),
176+
label: Text(
177+
enabled ? 'Permission enabled' : 'Permission disabled',
178+
),
170179
visualDensity: VisualDensity.compact,
171180
backgroundColor: theme.colorScheme.surfaceContainerHighest,
172181
),
173182
],
174183
),
175184
const SizedBox(height: 8),
176185
Text(
177-
'Import bank, UPI, and card alerts only after you enable access. Airo parses locally, ignores OTP/auth messages, and queues matches for review.',
186+
enabled
187+
? 'Airo can now parse supported bank, UPI, and card alerts locally and queue matches for review. OTP/auth messages stay ignored.'
188+
: 'Import bank, UPI, and card alerts only after you enable access. Airo parses locally, ignores OTP/auth messages, and queues matches for review.',
178189
style: theme.textTheme.bodyMedium?.copyWith(
179190
color: theme.colorScheme.onSurfaceVariant,
180191
),
181192
),
182193
const SizedBox(height: 12),
183-
OutlinedButton.icon(
184-
onPressed: null,
185-
icon: const Icon(Icons.lock_outline),
186-
label: const Text('Enable explicitly later'),
194+
SwitchListTile.adaptive(
195+
value: enabled,
196+
contentPadding: EdgeInsets.zero,
197+
title: const Text('Allow local Android import'),
198+
subtitle: const Text(
199+
'This only flips Airo\'s local import gate. Android OS inbox/listener permissions are still a separate future step.',
200+
),
201+
onChanged: permissionState.isLoading
202+
? null
203+
: (value) {
204+
ref
205+
.read(
206+
androidFinanceImportPermissionControllerProvider
207+
.notifier,
208+
)
209+
.setEnabled(value);
210+
},
187211
),
188212
],
189213
),

0 commit comments

Comments
 (0)