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
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,16 @@ Example project: [GitHub](https://github.com/sharmadhiraj/installed_apps/tree/ma
List<AppInfo> apps = await InstalledApps.getInstalledApps(
// Optional: whether to exclude system apps from the list. Default is true.
excludeSystemApps: true,

// Optional: whether to exclude apps that cannot be launched (no launch intent). Default is true.
excludeNonLaunchableApps: true,

// Optional: whether to include app icons in the result. Default is false.
withIcon: false,

// Optional: filter apps whose package names start with this prefix. Default is null (no filtering).
packageNamePrefix: "com.example",

// Optional: filter apps by platform type (Flutter, React Native, etc.). Default is null (no filtering).
platformType: PlatformType.flutter,
);
Expand All @@ -83,6 +83,8 @@ class AppInfo {
int installedTimestamp;
bool isSystemApp;
bool isLaunchableApp;
bool hasMultipleSigners;
List<String> certificateHashes;
}
```

Expand Down
37 changes: 36 additions & 1 deletion android/src/main/kotlin/com/sharmadhiraj/installed_apps/Util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.P
import android.util.Base64
import android.util.Log
import java.io.File
import java.security.MessageDigest

class Util {
companion object {
Expand Down Expand Up @@ -38,6 +41,8 @@ class Util {
map["is_system_app"] = isSystemApp(packageManager, packageInfo.packageName)
map["is_launchable_app"] =
isLaunchableApp(packageManager, packageInfo.packageName)
map["has_multiple_signers"] = hasMultipleSigners(packageManager, packageInfo.packageName)
map["certificate_hashes"] = getCertificateHashes(packageManager, packageInfo.packageName)
}
} else {
map["name"] = "Unknown"
Expand Down Expand Up @@ -75,5 +80,35 @@ class Util {
false
}
}

fun hasMultipleSigners(packageManager: PackageManager, packageName: String): Boolean {
return packageManager
.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES)
.signingInfo
.hasMultipleSigners()
}

fun getCertificateHashes(packageManager: PackageManager, packageName: String): List<String> {
val packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES)
val signingInfo = packageInfo.signingInfo

// https://developer.android.com/reference/android/content/pm/SigningInfo#getApkContentsSigners()
val signatures = if (signingInfo.hasMultipleSigners()) {
signingInfo.apkContentsSigners
} else {
signingInfo.signingCertificateHistory
}

val hashes = signatures.map {
signature -> MessageDigest
.getInstance("SHA-256")
.digest(signature.toByteArray())
.joinToString(":") {
"%02X".format(it)
}
}

return hashes
}
}
}
}
6 changes: 6 additions & 0 deletions lib/app_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class AppInfo {
final int installedTimestamp;
final bool isSystemApp;
final bool isLaunchableApp;
final bool hasMultipleSigners;
final List<String> certificateHashes;

const AppInfo({
required this.name,
Expand All @@ -21,6 +23,8 @@ class AppInfo {
required this.installedTimestamp,
required this.isSystemApp,
required this.isLaunchableApp,
required this.hasMultipleSigners,
required this.certificateHashes,
});

factory AppInfo.create(dynamic data) {
Expand All @@ -34,6 +38,8 @@ class AppInfo {
installedTimestamp: data["installed_timestamp"] ?? 0,
isSystemApp: data["is_system_app"] ?? false,
isLaunchableApp: data["is_launchable_app"] ?? true,
hasMultipleSigners: data["has_multiple_signers"] ?? false,
certificateHashes: data["certificate_hashes"].cast<String>() ?? [],
);
}

Expand Down