Skip to content

Bug Fix: IllegalArgumentException in drawableToBitmap function and Additional Enhancements #35

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@ class DrawableUtil {
}

private fun drawableToBitmap(drawable: Drawable): Bitmap {
if (drawable is BitmapDrawable) {
if (drawable is BitmapDrawable && drawable.bitmap != null) {
return drawable.bitmap
}
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val width = if (drawable.intrinsicWidth > 0) drawable.intrinsicWidth else 1
val height = if (drawable.intrinsicHeight > 0) drawable.intrinsicHeight else 1
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.PluginRegistry.Registrar
import java.util.Locale.ENGLISH
import java.util.Locale


class InstalledAppsPlugin() : MethodCallHandler, FlutterPlugin, ActivityAware {
Expand Down Expand Up @@ -71,9 +71,16 @@ 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 onlyAppsWithLaunchIntent: Boolean =
call.argument("only_apps_with_launch_intent") ?: true
Thread {
val apps: List<Map<String, Any?>> =
getInstalledApps(includeSystemApps, withIcon, packageNamePrefix)
getInstalledApps(
includeSystemApps,
withIcon,
packageNamePrefix,
onlyAppsWithLaunchIntent
)
result.success(apps)
}.start()
}
Expand Down Expand Up @@ -121,22 +128,36 @@ class InstalledAppsPlugin() : MethodCallHandler, FlutterPlugin, ActivityAware {
private fun getInstalledApps(
excludeSystemApps: Boolean,
withIcon: Boolean,
packageNamePrefix: String
packageNamePrefix: String,
onlyAppsWithLaunchIntent: Boolean,
): List<Map<String, Any?>> {
val packageManager = getPackageManager(context!!)
val packageManager = context!!.packageManager
var installedApps = packageManager.getInstalledApplications(0)
if (excludeSystemApps)
installedApps =
installedApps.filter { app -> !isSystemApp(packageManager, app.packageName) }
if (packageNamePrefix.isNotEmpty())

if (excludeSystemApps) {
installedApps = installedApps.filter { app ->
app.packageName.startsWith(
packageNamePrefix.lowercase(ENGLISH)
)
!isSystemApp(packageManager, app.packageName)
}
return installedApps.map { app -> convertAppToMap(packageManager, app, withIcon) }
}

if (packageNamePrefix.isNotEmpty()) {
installedApps = installedApps.filter { app ->
app.packageName.startsWith(packageNamePrefix.lowercase(Locale.ENGLISH))
}
}

if (onlyAppsWithLaunchIntent) {
installedApps = installedApps.filter { app ->
packageManager.getLaunchIntentForPackage(app.packageName) != null
}
}

return installedApps.map { app ->
convertAppToMap(packageManager, app, withIcon)
}
}


private fun startApp(packageName: String?): Boolean {
if (packageName.isNullOrBlank()) return false
return try {
Expand Down
11 changes: 11 additions & 0 deletions android/src/main/kotlin/com/sharmadhiraj/installed_apps/Util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,20 @@ class Util {
map["version_code"] = getVersionCode(packageInfo)
map["built_with"] = BuiltWithUtil.getPlatform(packageInfo.applicationInfo)
map["installed_timestamp"] = File(packageInfo.applicationInfo.sourceDir).lastModified()
map["system_app"] = isSystemApp(packageManager, app.packageName)
return map
}

private fun isSystemApp(packageManager: PackageManager, packageName: String): Boolean {
return try {
val appInfo = packageManager.getApplicationInfo(packageName, 0)
(appInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0 ||
(appInfo.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
} catch (e: PackageManager.NameNotFoundException) {
false
}
}


fun getPackageManager(context: Context): PackageManager {
return context.packageManager
Expand Down
3 changes: 3 additions & 0 deletions lib/app_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class AppInfo {
int versionCode;
BuiltWith builtWith;
int installedTimestamp;
bool isSystemApp;

AppInfo({
required this.name,
Expand All @@ -17,6 +18,7 @@ class AppInfo {
required this.versionCode,
required this.builtWith,
required this.installedTimestamp,
required this.isSystemApp,
});

factory AppInfo.create(dynamic data) {
Expand All @@ -28,6 +30,7 @@ class AppInfo {
versionCode: data["version_code"] ?? 1,
builtWith: parseBuiltWith(data["built_with"]),
installedTimestamp: data["installed_timestamp"] ?? 0,
isSystemApp: data["system_app"] ?? false,
);
}

Expand Down
3 changes: 3 additions & 0 deletions lib/installed_apps.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// [onlyAppsWithLaunchIntent] is an optional parameter to only include apps that have launch intent.
///
/// Returns a list of [AppInfo] objects representing the installed apps.
static Future<List<AppInfo>> getInstalledApps([
bool excludeSystemApps = true,
bool withIcon = false,
String packageNamePrefix = "",
bool onlyAppsWithLaunchIntent = false,
]) async {
dynamic apps = await _channel.invokeMethod(
"getInstalledApps",
{
"exclude_system_apps": excludeSystemApps,
"with_icon": withIcon,
"package_name_prefix": packageNamePrefix,
"only_apps_with_launch_intent": onlyAppsWithLaunchIntent,
},
);
return AppInfo.parseList(apps);
Expand Down