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
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
{
"githubPullRequests.ignoredPullRequestBranches": ["master"]
}
9 changes: 9 additions & 0 deletions analysis_options.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,15 @@

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
analyzer:
exclude:
- build/**
- android/**
- ios/**
- web/**
- windows/**
- macos/**
- linux/**
include: package:flutter_lints/flutter.yaml

linter:
Expand Down
4 changes: 3 additions & 1 deletion lib/main.dart
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

import 'providers/telegram_proxy_provider.dart';
import 'providers/v2ray_provider.dart';
import 'providers/language_provider.dart';
Expand All@@ -10,6 +11,7 @@ import 'screens/main_navigation_screen.dart';
import 'screens/privacy_welcome_screen.dart';
import 'services/update_service.dart';
import 'theme/app_theme.dart';

import 'dart:async';

void main() async {
Expand DownExpand Up@@ -129,4 +131,4 @@ class _MyAppState extends State<MyApp> {
),
);
}
}
}
1 change: 1 addition & 0 deletions lib/providers/language_provider.dart
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';

import '../models/app_language.dart';
import '../services/language_service.dart';

Expand Down
1 change: 1 addition & 0 deletions lib/providers/telegram_proxy_provider.dart
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import 'package:flutter/foundation.dart';

import '../models/telegram_proxy.dart';
import '../services/telegram_proxy_service.dart';

Expand Down
60 changes: 36 additions & 24 deletions lib/providers/v2ray_provider.dart
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
// ignore_for_file: unused_element, avoid_print, unused_local_variable

import 'package:flutter/widgets.dart';
import 'package:flutter_v2ray_client/flutter_v2ray.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../models/v2ray_config.dart';
import '../models/subscription.dart';
import '../services/v2ray_service.dart';
Expand All@@ -20,7 +23,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
bool _isLoadingServers = false;
bool _isProxyMode = false;
bool _isInitializing = true;
bool _isUpdatingSubscriptions = false; // Track when updates are in progress
bool _isUpdatingSubscriptions = false; // Track when updates are in progress

// Method channel for VPN control
static const platform = MethodChannel('com.cloud.pira/vpn_control');
Expand All@@ -36,7 +39,8 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
String get errorMessage => _errorMessage;
V2RayService get v2rayService => _v2rayService;
bool get isProxyMode => _isProxyMode;
bool get isUpdatingSubscriptions => _isUpdatingSubscriptions; // Getter for update state
bool get isUpdatingSubscriptions =>
_isUpdatingSubscriptions; // Getter for update state

// Expose V2Ray status for real-time traffic monitoring
V2RayStatus? get currentStatus => _v2rayService.currentStatus;
Expand DownExpand Up@@ -101,7 +105,9 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {

// Load subscriptions
await loadSubscriptions();
debugPrint('Loaded ${_subscriptions.length} subscriptions during initialization');
debugPrint(
'Loaded ${_subscriptions.length} subscriptions during initialization',
);

// Load proxy mode setting from SharedPreferences
final prefs = await SharedPreferences.getInstance();
Expand DownExpand Up@@ -330,15 +336,15 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
_setLoading(true);
try {
_subscriptions = await _v2rayService.loadSubscriptions();

// Debug information
debugPrint('Loaded ${_subscriptions.length} subscriptions');
for (var sub in _subscriptions) {
debugPrint(' Subscription: ${sub.name} with ${sub.configIds.length} configs');
debugPrint(
' Subscription: ${sub.name} with ${sub.configIds.length} configs',
);
}



// Ensure configs are loaded and match subscription config IDs
if (_configs.isEmpty) {
_configs = await _v2rayService.loadConfigs();
Expand All@@ -350,8 +356,10 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
for (var subscription in _subscriptions) {
final configIds = subscription.configIds;
final existingConfigIds = _configs.map((c) => c.id).toSet();

debugPrint('Subscription "${subscription.name}" has ${configIds.length} config IDs, ${existingConfigIds.length} existing configs');

debugPrint(
'Subscription "${subscription.name}" has ${configIds.length} config IDs, ${existingConfigIds.length} existing configs',
);

// Check if any config IDs in the subscription are missing from the configs list
final missingConfigIds = configIds
Expand All@@ -373,7 +381,9 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
_subscriptions[index] = subscription.copyWith(
configIds: updatedConfigIds,
);
debugPrint('Updated subscription "${subscription.name}" to have ${updatedConfigIds.length} config IDs');
debugPrint(
'Updated subscription "${subscription.name}" to have ${updatedConfigIds.length} config IDs',
);
}
}
}
Expand All@@ -389,7 +399,9 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
Future<void> addConfig(V2RayConfig config) async {
// Add config and display it immediately
_configs.add(config);
debugPrint('Added config: ${config.remark} (${config.id}) - Total configs: ${_configs.length}');
debugPrint(
'Added config: ${config.remark} (${config.id}) - Total configs: ${_configs.length}',
);

// Save the configuration immediately to display it
await _v2rayService.saveConfigs(_configs);
Expand DownExpand Up@@ -493,9 +505,11 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
_configs.addAll(configs);

final newConfigIds = configs.map((c) => c.id).toList();

// Debug information
debugPrint('Adding subscription "$name" with ${configs.length} configs and IDs: $newConfigIds');
debugPrint(
'Adding subscription "$name" with ${configs.length} configs and IDs: $newConfigIds',
);

// Create subscription
final subscription = Subscription(
Expand All@@ -511,7 +525,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
// Save both configs and subscription
await _v2rayService.saveConfigs(_configs);
await _v2rayService.saveSubscriptions(_subscriptions);

// Debug information
debugPrint('Subscription "$name" added successfully');

Expand DownExpand Up@@ -655,7 +669,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
_setLoading(true);
_errorMessage = '';
_isLoadingServers = true;
_isUpdatingSubscriptions = true; // Set update state
_isUpdatingSubscriptions = true; // Set update state
notifyListeners();

// Clear all ping cache before updating subscriptions
Expand DownExpand Up@@ -735,8 +749,8 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
} finally {
_setLoading(false);
_isLoadingServers = false;
_isUpdatingSubscriptions = false; // Clear update state
notifyListeners(); // Notify listeners that update state has changed
_isUpdatingSubscriptions = false; // Clear update state
notifyListeners(); // Notify listeners that update state has changed
}
}

Expand All@@ -752,7 +766,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
notifyListeners();
}

Future<void> connectToServer(V2RayConfig config, bool _isProxyMode) async {
Future<void> connectToServer(V2RayConfig config, bool isProxyMode) async {
_isConnecting = true;
_errorMessage = '';
notifyListeners();
Expand DownExpand Up@@ -789,7 +803,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {

// Connect to server with timeout
success = await _v2rayService
.connect(config, _isProxyMode)
.connect(config, isProxyMode)
.timeout(
const Duration(seconds: 30), // Timeout for connection
onTimeout: () {
Expand DownExpand Up@@ -851,14 +865,13 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
for (int i = 0; i < _configs.length; i++) {
if (_configs[i].id == config.id) {
_configs[i].isConnected = true;
_configs[i].isProxyMode =
_isProxyMode; // Update proxy mode status
_configs[i].isProxyMode = isProxyMode; // Update proxy mode status
} else {
_configs[i].isConnected = false;
}
}
_selectedConfig = config;
_isProxyMode = _isProxyMode; // Update provider's proxy mode state
isProxyMode = isProxyMode; // Update provider's proxy mode state

// Persist the changes with error handling
try {
Expand DownExpand Up@@ -1116,8 +1129,7 @@ class V2RayProvider with ChangeNotifier, WidgetsBindingObserver {
final subscription = Subscription(
id: DateTime.now().millisecondsSinceEpoch.toString(),
name: name,
url:
'file://subscription', // Special indicator for file-based subscriptions
url: 'file://subscription', // Special indicator for file-based subscriptions
lastUpdated: DateTime.now(),
configIds: newConfigIds,
);
Expand Down
8 changes: 4 additions & 4 deletions lib/screens/about_screen.dart
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';

import '../providers/language_provider.dart';
import '../utils/app_localizations.dart';

class AboutScreen extends StatelessWidget {
const AboutScreen({Key? key}) : super(key: key);
const AboutScreen({super.key});

Future<void> _launchUrl(String url) async {
final Uri uri = Uri.parse(url);
Expand DownExpand Up@@ -104,9 +105,8 @@ class AboutScreen extends StatelessWidget {
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Theme.of(
context,
).colorScheme.outline.withValues(alpha: 0.2),
color: Theme.of(context).colorScheme.outline
.withValues(alpha: 0.2),
),
),
child: Text(
Expand Down
10 changes: 7 additions & 3 deletions lib/screens/backup_restore_screen.dart
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
// ignore_for_file: unnecessary_null_comparison

import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:path_provider/path_provider.dart';
import 'package:file_picker/file_picker.dart';

import '../providers/language_provider.dart';
import '../utils/app_localizations.dart';
import '../theme/app_theme.dart';
Expand DownExpand Up@@ -85,12 +89,12 @@ class _BackupRestoreScreenState extends State<BackupRestoreScreen> {
});

try {
final result = await FilePicker.platform.pickFiles(
final result = await FilePicker.pickFiles(
type: FileType.custom,
allowedExtensions: ['json'],
);

if (result == null || result.files.isEmpty) {
if (result == null || result.isEmpty) {
setState(() {
_statusMessage = context.tr(
TranslationKeys.backupRestoreNoFileSelected,
Expand All@@ -99,7 +103,7 @@ class _BackupRestoreScreenState extends State<BackupRestoreScreen> {
return;
}

final file = File(result.files.single.path!);
final file = File(result.single.path!);
final jsonString = await file.readAsString();
final data = jsonDecode(jsonString) as Map<String, dynamic>;

Expand Down
1 change: 1 addition & 0 deletions lib/screens/battery_settings_screen.dart
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';

import '../theme/app_theme.dart';
import '../utils/app_localizations.dart';
import '../providers/language_provider.dart';
Expand Down
6 changes: 5 additions & 1 deletion lib/screens/blocked_apps_screen.dart
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
// ignore_for_file: use_build_context_synchronously

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:provider/provider.dart';

import '../theme/app_theme.dart';
import '../services/v2ray_service.dart';
import '../utils/app_localizations.dart';
import '../providers/language_provider.dart';

import 'package:flutter/foundation.dart';

import 'dart:convert';
import 'dart:typed_data';

class BlockedAppsScreen extends StatefulWidget {
const BlockedAppsScreen({super.key});
Expand Down
Loading