Skip to content
Draft
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
flutter.tar.xz
2 changes: 2 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
<uses-permission android:name="android.permission.BIND_SCREENING_SERVICE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- Permission needed for flutter_appavailability to get installed apps -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />

<uses-feature
android:name="android.hardware.telephony"
Expand Down
7 changes: 7 additions & 0 deletions lib/src/models/issue_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Issue {
final int? hunt;
final int? domain;
final String? closedBy;
final List<Map<String, String>>? installedApps;

final DateFormat dateFormatter = DateFormat('d MMMM, yyyy');

Expand Down Expand Up @@ -71,6 +72,7 @@ class Issue {
this.hunt,
this.domain,
this.closedBy,
this.installedApps,
});

factory Issue.fromJson(Map<String, dynamic> responseData) {
Expand Down Expand Up @@ -105,6 +107,9 @@ class Issue {
hunt: responseData["hunt"],
domain: responseData["domain"],
closedBy: responseData["closed_by"],
installedApps: responseData["installed_apps"] != null
? List<Map<String, String>>.from(responseData["installed_apps"])
: null,
tags: Tag.fromSnapshot(responseData["tags"]),
);
}
Expand All @@ -125,6 +130,8 @@ class Issue {
'description': description,
'label': label,
'tags': tagId,
if (installedApps != null && installedApps!.isNotEmpty)
'installed_apps': installedApps,
// "hunt": null,
// "domain": null,
// "closed_by": null,
Expand Down
24 changes: 24 additions & 0 deletions lib/src/pages/home/report_bug.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:flutter/services.dart'; // For method channel
import 'dart:convert';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_appavailability/flutter_appavailability.dart';

import '../../models/tags_model.dart';
import '../../util/api/tags_api.dart';
import '../../util/widgets/installed_apps_widget.dart';

/// Report Bug and Start Bug Hunt Page, namesake, used for
/// posting bugs, companies and individuals
Expand Down Expand Up @@ -78,6 +80,7 @@ class _ReportFormState extends ConsumerState<ReportForm> {
late List<bool> _labelsState;
static const platform = MethodChannel('com.apps.blt/channel');
bool _isSnackBarVisible = false;
List<Application> _selectedApps = []; // Selected apps for vulnerability scanning

void showSnackBar(BuildContext context, String message) {
if (!_isSnackBarVisible) {
Expand Down Expand Up @@ -1103,6 +1106,16 @@ class _ReportFormState extends ConsumerState<ReportForm> {
);
}),
),
// Installed Apps Section
SizedBox(height: 16.0),
InstalledAppsWidget(
isDarkMode: isDarkMode,
onAppsSelected: (List<Application> selectedApps) {
setState(() {
_selectedApps = selectedApps;
});
},
),
Padding(
padding: EdgeInsets.fromLTRB(0, 16, 0, 16),
child: Text(
Expand Down Expand Up @@ -1148,6 +1161,16 @@ class _ReportFormState extends ConsumerState<ReportForm> {
for (int i = 0; i < _labels.length; i++) {
if (_labelsState[i]) tags.add(_labels[i]);
}

// Convert selected apps to the format expected by the API
List<Map<String, String>>? appsData;
if (_selectedApps.isNotEmpty) {
appsData = _selectedApps.map((app) => {
'appName': app.appName,
'packageName': app.packageName,
}).toList();
}

Issue issue = Issue(
user: currentUser!,
url: _titleController.text,
Expand All @@ -1160,6 +1183,7 @@ class _ReportFormState extends ConsumerState<ReportForm> {
"Dart ${Platform.version.substring(0, 7) + Platform.operatingSystem}",
label: _selectedIssueCategoriesIndex,
tags: tags,
installedApps: appsData,
);
await IssueApiClient.postIssue(issue, widget.parentContext);
} else {
Expand Down
44 changes: 44 additions & 0 deletions lib/src/util/services/installed_apps_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:flutter_appavailability/flutter_appavailability.dart';
import 'package:permission_handler/permission_handler.dart';

/// Service class to handle installed apps functionality
/// for vulnerability scanning and bug reporting
class InstalledAppsService {
/// Request permission and get list of installed apps
static Future<List<Application>> getInstalledApps() async {
try {
// Get installed apps list
List<Application> apps = await AppAvailability.getInstalledApps();
return apps;
} catch (e) {
// Handle any errors that might occur
print('Error getting installed apps: $e');
return [];
}
}

/// Check if a specific app is installed by package name
static Future<bool> isAppInstalled(String packageName) async {
try {
await AppAvailability.checkAvailability(packageName);
return true;
} catch (e) {
return false;
}
}

/// Filter system apps from the list (optional functionality)
static List<Application> filterUserApps(List<Application> apps) {
return apps.where((app) =>
!app.packageName.startsWith('com.android.') &&
!app.packageName.startsWith('com.google.android.') &&
app.appName.isNotEmpty
).toList();
}

/// Sort apps alphabetically by name
static List<Application> sortAppsByName(List<Application> apps) {
apps.sort((a, b) => a.appName.compareTo(b.appName));
return apps;
}
}
Loading
Loading