Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App permissions added #36

Open
wants to merge 49 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
6e9ee17
Update Util.kt
bugrevealingbme Oct 20, 2024
aef2384
Update InstalledAppsPlugin.kt
bugrevealingbme Oct 20, 2024
fca7f05
Update app_info.dart
bugrevealingbme Oct 20, 2024
bd1e664
Update Util.kt
bugrevealingbme Oct 20, 2024
6670c4d
Update app_info.dart
bugrevealingbme Oct 20, 2024
37a37ff
Update app_info.dart
bugrevealingbme Oct 20, 2024
835ee26
Update app_info.dart
bugrevealingbme Oct 20, 2024
50a040f
Update app_info.dart
bugrevealingbme Oct 20, 2024
9392970
Update app_info.dart
bugrevealingbme Oct 20, 2024
bd13c0d
Update app_info.dart
bugrevealingbme Oct 20, 2024
57f902c
Update Util.kt
bugrevealingbme Oct 20, 2024
c338d55
Update app_info.dart
bugrevealingbme Oct 20, 2024
fe79441
Update app_info.dart
bugrevealingbme Oct 20, 2024
cdbb371
Update Util.kt
bugrevealingbme Oct 20, 2024
3387e9b
Update BuiltWithUtil.kt
bugrevealingbme Nov 19, 2024
5be269e
Update BuiltWithUtil.kt
bugrevealingbme Nov 19, 2024
6caa61a
Update BuiltWithUtil.kt
bugrevealingbme Nov 19, 2024
2f673bb
Update BuiltWithUtil.kt
bugrevealingbme Nov 19, 2024
3d1f502
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 19, 2024
ea03232
Create PlatfromType.kt
bugrevealingbme Nov 19, 2024
2816b61
Update Util.kt
bugrevealingbme Nov 19, 2024
3aa46dc
Update installed_apps.dart
bugrevealingbme Nov 19, 2024
d6ab727
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 19, 2024
443f5a5
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 27, 2024
21ff9cf
Update installed_apps.dart
bugrevealingbme Nov 27, 2024
02dc38a
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 27, 2024
450df3f
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 27, 2024
fa60995
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 27, 2024
afce2ae
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 27, 2024
918e072
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 27, 2024
2d2c204
Update installed_apps.dart
bugrevealingbme Nov 27, 2024
6d0e305
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 27, 2024
fc363ee
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 27, 2024
6a6ca65
Update installed_apps.dart
bugrevealingbme Nov 27, 2024
eb12710
Update installed_apps.dart
bugrevealingbme Nov 27, 2024
6069d7e
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 28, 2024
e108b21
Update installed_apps.dart
bugrevealingbme Nov 28, 2024
6354733
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 28, 2024
aad18f7
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 28, 2024
96ce2ee
Update installed_apps.dart
bugrevealingbme Nov 28, 2024
5f6ed55
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 28, 2024
271ddc5
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 28, 2024
bafd416
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 28, 2024
0d1400f
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 28, 2024
022c23d
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 29, 2024
e3d37cb
Update installed_apps.dart
bugrevealingbme Nov 29, 2024
5e8d667
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 29, 2024
4a56c9e
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 29, 2024
028d181
Update InstalledAppsPlugin.kt
bugrevealingbme Nov 29, 2024
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
Original file line number Diff line number Diff line change
@@ -55,4 +55,4 @@ class BuiltWithUtil {

}

}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package com.sharmadhiraj.installed_apps

import android.util.Log
import android.provider.Settings
import android.app.ActivityManager
import android.view.accessibility.AccessibilityManager
import android.app.AppOpsManager
import android.os.Build
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.pm.PackageManager
import android.content.pm.ApplicationInfo
import android.net.Uri
import android.app.usage.UsageStatsManager
import android.provider.Settings.ACTION_USAGE_ACCESS_SETTINGS
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
import android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import android.widget.Toast.LENGTH_SHORT
@@ -22,7 +31,6 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.PluginRegistry.Registrar
import java.util.Locale.ENGLISH


class InstalledAppsPlugin() : MethodCallHandler, FlutterPlugin, ActivityAware {

companion object {
@@ -71,13 +79,24 @@ class InstalledAppsPlugin() : MethodCallHandler, FlutterPlugin, ActivityAware {
val includeSystemApps = call.argument("exclude_system_apps") ?: true
val withIcon = call.argument("with_icon") ?: false
val packageNamePrefix: String = call.argument("package_name_prefix") ?: ""
val platformTypeName: String = call.argument("platform_type") ?: ""
Thread {
val apps: List<Map<String, Any?>> =
getInstalledApps(includeSystemApps, withIcon, packageNamePrefix)
val apps: List<Map<String, Any?>> =
getInstalledApps(includeSystemApps, withIcon, packageNamePrefix, PlatformType.fromString(platformTypeName))
result.success(apps)
}.start()
}

"getRunningApps" -> {
val excludeSystemApps = call.argument("exclude_system_apps") ?: true
val withIcon = call.argument("with_icon") ?: false
val platformTypeName: String = call.argument("platform_type") ?: ""
Thread {
val apps = getRunningApps(excludeSystemApps, withIcon, PlatformType.fromString(platformTypeName))
result.success(apps)
}.start()
}

"startApp" -> {
val packageName: String? = call.argument("package_name")
result.success(startApp(packageName))
@@ -96,7 +115,9 @@ class InstalledAppsPlugin() : MethodCallHandler, FlutterPlugin, ActivityAware {

"getAppInfo" -> {
val packageName: String = call.argument("package_name") ?: ""
result.success(getAppInfo(getPackageManager(context!!), packageName))
val platformTypeName: String = call.argument("platform_type") ?: ""
val platformType: PlatformType? = PlatformType.fromString(platformTypeName)
result.success(getAppInfo(getPackageManager(context!!), packageName, platformType))
}

"isSystemApp" -> {
@@ -114,29 +135,162 @@ class InstalledAppsPlugin() : MethodCallHandler, FlutterPlugin, ActivityAware {
result.success(isAppInstalled(packageName))
}

"checkUsageAccessPermission" -> {
val isGranted = isUsageAccessGranted()
result.success(isGranted)
}

"checkAccessibilityPermission" -> {
result.success(isAccessibilityPermissionGranted())
}

"requestAccessibilityPermission" -> {
checkAndRequestAccessibilityPermission()
result.success(null) // İşlem başlatıldığı için geri dönecek bir sonuç yok
}

"openUsageAccessSettings" -> {
openUsageAccessSettings()
result.success(null)
}

else -> result.notImplemented()
}
}

private fun openUsageAccessSettings() {
val intent = Intent().apply {
flags = FLAG_ACTIVITY_NEW_TASK
action = ACTION_USAGE_ACCESS_SETTINGS
}

context!!.startActivity(intent)
}

private fun checkAndRequestAccessibilityPermission() {
val intent = Intent().apply {
flags = FLAG_ACTIVITY_NEW_TASK
action = ACTION_ACCESSIBILITY_SETTINGS
}

context!!.startActivity(intent)
}

private fun isAccessibilityPermissionGranted(): Boolean {
val accessibilityManager = context!!.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager

val enabledServices = Settings.Secure.getString(
context!!.contentResolver,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
)
val myServiceName = "${context!!.packageName}/net.permission.man.MyAccessibilityService"

// Kontrol: MyAccessibilityService etkin mi?
return enabledServices?.contains(myServiceName) == true &&
accessibilityManager.isEnabled
}

private fun isUsageAccessGranted(): Boolean {
val appOpsManager = context!!.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
appOpsManager.unsafeCheckOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
context!!.packageName
)
} else {
appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
context!!.packageName
)
}
return mode == AppOpsManager.MODE_ALLOWED
}

private fun getInstalledApps(
excludeSystemApps: Boolean,
withIcon: Boolean,
packageNamePrefix: String
packageNamePrefix: String,
platformType: PlatformType?,
): List<Map<String, Any?>> {
val packageManager = getPackageManager(context!!)
var installedApps = packageManager.getInstalledApplications(0)
var installedApps = packageManager.getInstalledApplications(PackageManager.GET_PERMISSIONS)
if (excludeSystemApps)
installedApps =
installedApps.filter { app -> !isSystemApp(packageManager, app.packageName) }
installedApps = installedApps.filter { app -> !isSystemApp(packageManager, app.packageName) }
if (packageNamePrefix.isNotEmpty())
installedApps = installedApps.filter { app ->
app.packageName.startsWith(
packageNamePrefix.lowercase(ENGLISH)
)
}
return installedApps.map { app -> convertAppToMap(packageManager, app, withIcon) }
return installedApps.map { app -> convertAppToMap(packageManager, app, withIcon, platformType) }
}

private fun getRunningApps(
excludeSystemApps: Boolean,
withIcon: Boolean,
platformType: PlatformType?
): List<Map<String, Any?>> {
val packageManager = getPackageManager(context!!)
val runningApps = mutableListOf<Map<String, Any?>>()

// 1. Kullanıcı İstatistiklerini Kullanma
val usageStatsManager = context!!.getSystemService(Context.USAGE_STATS_SERVICE) as android.app.usage.UsageStatsManager
val currentTime = System.currentTimeMillis()
val endTime = currentTime
val startTime = currentTime - 1000 * 60 * 60 * 1 // Last 1 hour

val usageStats = usageStatsManager.queryUsageStats(
android.app.usage.UsageStatsManager.INTERVAL_DAILY,
startTime,
endTime
)

if (usageStats != null) {
val uniquePackages = mutableSetOf<String>()

usageStats.forEach { stat ->
val packageName = stat.packageName
if (packageName != null && uniquePackages.add(packageName)) {
try {
val appInfo = packageManager.getApplicationInfo(packageName, 0)
val isSystemApp = (appInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0
if (!excludeSystemApps || !isSystemApp) {
val appMap = convertAppToMap(packageManager, appInfo, withIcon, platformType)
runningApps.add(appMap)
}
} catch (e: PackageManager.NameNotFoundException) {
// Paket bulunamadı, devam et
}
}
}
}

// 2. Çalışan İşlemleri Kullanma (Eski Yöntem)
val activityManager = context!!.getSystemService(Context.ACTIVITY_SERVICE) as android.app.ActivityManager
val runningProcesses = activityManager.runningAppProcesses
runningProcesses?.forEach { processInfo ->
processInfo.pkgList?.forEach { packageName ->
try {
if (!runningApps.any { it["packageName"] == packageName }) { // Eğer daha önce eklenmediyse
val appInfo = packageManager.getApplicationInfo(packageName, 0)
val isSystemApp = (appInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0
if (!excludeSystemApps || !isSystemApp) {
val appMap = convertAppToMap(packageManager, appInfo, withIcon, platformType)
runningApps.add(appMap)
}
}
} catch (e: PackageManager.NameNotFoundException) {
// Uygulama bilgisi bulunamadı, devam et
}
}
}

return runningApps
}


private fun startApp(packageName: String?): Boolean {
if (packageName.isNullOrBlank()) return false
return try {
@@ -178,12 +332,13 @@ class InstalledAppsPlugin() : MethodCallHandler, FlutterPlugin, ActivityAware {

private fun getAppInfo(
packageManager: PackageManager,
packageName: String
packageName: String,
platformType: PlatformType?
): Map<String, Any?>? {
var installedApps = packageManager.getInstalledApplications(0)
installedApps = installedApps.filter { app -> app.packageName == packageName }
return if (installedApps.isEmpty()) null
else convertAppToMap(packageManager, installedApps[0], true)
else convertAppToMap(packageManager, installedApps[0], true, platformType)
}

private fun uninstallApp(packageName: String): Boolean {
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.sharmadhiraj.installed_apps
enum class PlatformType(val value: String) {
FLUTTER("flutter"),
REACT_NATIVE("react_native"),
XAMARIN("xamarin"),
IONIC("ionic"),
NATIVE_OR_OTHERS("native_or_others");

companion object {
fun fromString(platform: String): PlatformType? {
if (platform.isEmpty()) return null;
return when (platform.lowercase()) {
"flutter" -> FLUTTER
"react_native" -> REACT_NATIVE
"xamarin" -> XAMARIN
"ionic" -> IONIC
else -> NATIVE_OR_OTHERS
}
}
}
}
26 changes: 22 additions & 4 deletions android/src/main/kotlin/com/sharmadhiraj/installed_apps/Util.kt
Original file line number Diff line number Diff line change
@@ -15,21 +15,39 @@ class Util {
fun convertAppToMap(
packageManager: PackageManager,
app: ApplicationInfo,
withIcon: Boolean
withIcon: Boolean,
platformType: PlatformType?,
): HashMap<String, Any?> {
val map = HashMap<String, Any?>()
map["name"] = packageManager.getApplicationLabel(app)
map["package_name"] = app.packageName
map["icon"] =
if (withIcon) DrawableUtil.drawableToByteArray(app.loadIcon(packageManager))
else ByteArray(0)
val packageInfo = packageManager.getPackageInfo(app.packageName, 0)

val packageInfo = packageManager.getPackageInfo(app.packageName, PackageManager.GET_PERMISSIONS)
map["version_name"] = packageInfo.versionName
map["version_code"] = getVersionCode(packageInfo)
map["built_with"] = BuiltWithUtil.getPlatform(packageInfo.applicationInfo)
map["built_with"] = platformType?.value ?: BuiltWithUtil.getPlatform(packageInfo.applicationInfo)
map["installed_timestamp"] = File(packageInfo.applicationInfo.sourceDir).lastModified()

if (packageInfo.requestedPermissions != null) {
val permissionsStatus = packageInfo.requestedPermissions.map { permission ->
val isGranted = checkPermissionStatus(packageManager, app.packageName, permission)
mapOf("permission" to permission, "granted" to isGranted)
}
map["permissions"] = permissionsStatus
} else {
map["permissions"] = emptyList<Map<String, Any?>>()
}

return map
}

fun checkPermissionStatus(packageManager: PackageManager, packageName: String, permission: String): Boolean {
val permissionCheck = packageManager.checkPermission(permission, packageName)
return permissionCheck == PackageManager.PERMISSION_GRANTED
}


fun getPackageManager(context: Context): PackageManager {
@@ -44,4 +62,4 @@ class Util {

}

}
}
15 changes: 15 additions & 0 deletions lib/app_info.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import 'dart:typed_data';

class PermissionStatus {
final String permission;
final bool granted;

PermissionStatus({required this.permission, required this.granted});
}

class AppInfo {
String name;
Uint8List? icon;
@@ -8,6 +15,7 @@ class AppInfo {
int versionCode;
BuiltWith builtWith;
int installedTimestamp;
List<PermissionStatus> permissions;

AppInfo({
required this.name,
@@ -17,6 +25,7 @@ class AppInfo {
required this.versionCode,
required this.builtWith,
required this.installedTimestamp,
required this.permissions,
});

factory AppInfo.create(dynamic data) {
@@ -28,6 +37,12 @@ class AppInfo {
versionCode: data["version_code"] ?? 1,
builtWith: parseBuiltWith(data["built_with"]),
installedTimestamp: data["installed_timestamp"] ?? 0,
permissions: (data["permissions"] as List<dynamic>?)
?.map((perm) => PermissionStatus(
permission: perm["permission"],
granted: perm["granted"],
))
.toList() ?? [],
);
}

70 changes: 68 additions & 2 deletions lib/installed_apps.dart
Original file line number Diff line number Diff line change
@@ -10,19 +10,22 @@ class InstalledApps {
/// [excludeSystemApps] specifies whether to exclude system apps from the list.
/// [withIcon] specifies whether to include app icons in the list.
/// [packageNamePrefix] is an optional parameter to filter apps with package names starting with a specific prefix.
/// [platformType] is an optional parameter to set the app type. Default is [AppPlatformType.flutter].
///
/// Returns a list of [AppInfo] objects representing the installed apps.
static Future<List<AppInfo>> getInstalledApps([
bool excludeSystemApps = true,
bool withIcon = false,
String packageNamePrefix = "",
BuiltWith platformType = BuiltWith.flutter,
]) async {
dynamic apps = await _channel.invokeMethod(
"getInstalledApps",
{
"exclude_system_apps": excludeSystemApps,
"with_icon": withIcon,
"package_name_prefix": packageNamePrefix,
"platform_type": platformType.name,
},
);
return AppInfo.parseList(apps);
@@ -40,6 +43,42 @@ class InstalledApps {
);
}

/// isAccessibilityPermissionGranted
static Future<bool> isAccessibilityPermissionGranted() async {
return await _channel.invokeMethod<bool>('checkAccessibilityPermission') ?? false;
}

/// requestAccessibilityPermission
static Future<void> requestAccessibilityPermission() async {
await _channel.invokeMethod('requestAccessibilityPermission');
}

/// closeBackgroundApps
static Future<bool> closeBackgroundApps(List<String> packages) async {
return await _channel.invokeMethod("closeBackgroundApps", {
"package_names": packages,
}) ?? false;
}

/// Getting running apps
/// [excludeSystemApps] specifies whether to exclude system apps from the list.wld
/// Returns a list of [AppInfo] objects representing the installed apps.
static Future<List<AppInfo>> getRunningApps({
bool excludeSystemApps = true,
bool withIcon = true,
}) async {
dynamic apps = await _channel.invokeMethod(
"getRunningApps",
{
"exclude_system_apps": excludeSystemApps,
"with_icon": withIcon,
},
);

return AppInfo.parseList(apps);
}


/// Opens the settings screen (App Info) of an app with the specified package name.
///
/// [packageName] is the package name of the app whose settings screen should be opened.
@@ -50,6 +89,27 @@ class InstalledApps {
);
}

/// Check Usage Access Permission
///
static Future<bool> isUsageAccessGranted() async {
try {
final bool isGranted =
await _channel.invokeMethod('checkUsageAccessPermission');
return isGranted;
} on PlatformException catch (e) {
print("Error checking Usage Access permission: ${e.message}");
return false;
}
}

/// Opens Usage Access Settings
///
static openUsageAccessSettings() {
_channel.invokeMethod(
"openUsageAccessSettings"
);
}

/// Displays a toast message on the device.
///
/// [message] is the message to display.
@@ -69,10 +129,16 @@ class InstalledApps {
/// [packageName] is the package name of the app to retrieve information for.
///
/// Returns [AppInfo] for the given package name, or null if not found.
static Future<AppInfo?> getAppInfo(String packageName) async {
static Future<AppInfo?> getAppInfo(
String packageName,
BuiltWith? platformType,
) async {
var app = await _channel.invokeMethod(
"getAppInfo",
{"package_name": packageName},
{
"package_name": packageName,
"platform_type": platformType?.name ?? '',
},
);
if (app == null) {
return null;