Skip to content

Commit cae8113

Browse files
authored
V2 embedding (sharmadhiraj#7)
* Migrate to v2 embedding. * Implement thread to get installed apps. Fixes screen freeze issue. * Add QUERY_ALL_PACKAGES permission [Android 11+] * Prepare for 1.3.0 plugin release.
1 parent 1e5bfdc commit cae8113

15 files changed

+137
-61
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.3.0
2+
* Android V2 embedding migration
3+
* Fix UI freeze issue during getting instgalled apps
4+
15
## 1.2.0
26
* Null Safety migration
37

android/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ group 'com.sharmadhiraj.installed_apps'
22
version '1.0-SNAPSHOT'
33

44
buildscript {
5-
ext.kotlin_version = '1.5.10'
5+
ext.kotlin_version = '1.6.10'
66
repositories {
77
google()
88
mavenCentral()
99
}
1010

1111
dependencies {
12-
classpath 'com.android.tools.build:gradle:4.2.1'
12+
classpath 'com.android.tools.build:gradle:4.2.2'
1313
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1414
}
1515
}

android/src/main/AndroidManifest.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2-
package="com.sharmadhiraj.installed_apps">
2+
package="com.sharmadhiraj.installed_apps">
3+
4+
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
35
</manifest>
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.sharmadhiraj.installed_apps
22

3+
import android.content.Context
34
import android.content.Intent
45
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
56
import android.content.pm.PackageManager
@@ -9,32 +10,71 @@ import android.widget.Toast
910
import android.widget.Toast.LENGTH_LONG
1011
import android.widget.Toast.LENGTH_SHORT
1112
import com.sharmadhiraj.installed_apps.Util.Companion.convertAppToMap
12-
import com.sharmadhiraj.installed_apps.Util.Companion.getContext
1313
import com.sharmadhiraj.installed_apps.Util.Companion.getPackageManager
14+
import io.flutter.embedding.engine.plugins.FlutterPlugin
15+
import io.flutter.embedding.engine.plugins.activity.ActivityAware
16+
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
17+
import io.flutter.plugin.common.BinaryMessenger
1418
import io.flutter.plugin.common.MethodCall
1519
import io.flutter.plugin.common.MethodChannel
1620
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
17-
import io.flutter.plugin.common.MethodChannel.Result
1821
import io.flutter.plugin.common.PluginRegistry.Registrar
1922
import java.util.Locale.ENGLISH
2023

21-
class InstalledAppsPlugin(private val registrar: Registrar) : MethodCallHandler {
24+
25+
class InstalledAppsPlugin() : MethodCallHandler, FlutterPlugin, ActivityAware {
2226

2327
companion object {
28+
29+
var context: Context? = null
30+
2431
@JvmStatic
2532
fun registerWith(registrar: Registrar) {
26-
val channel = MethodChannel(registrar.messenger(), "installed_apps")
27-
channel.setMethodCallHandler(InstalledAppsPlugin(registrar))
33+
context = registrar.context()
34+
register(registrar.messenger())
2835
}
36+
37+
@JvmStatic
38+
fun register(messenger: BinaryMessenger?) {
39+
val channel = MethodChannel(messenger, "installed_apps")
40+
channel.setMethodCallHandler(InstalledAppsPlugin())
41+
}
42+
}
43+
44+
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
45+
InstalledAppsPlugin.register(binding.getBinaryMessenger())
46+
}
47+
48+
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
49+
}
50+
51+
override fun onAttachedToActivity(activityPluginBinding: ActivityPluginBinding) {
52+
context = activityPluginBinding.getActivity()
2953
}
3054

31-
override fun onMethodCall(call: MethodCall, result: Result) {
55+
override fun onDetachedFromActivityForConfigChanges() {}
56+
57+
override fun onReattachedToActivityForConfigChanges(activityPluginBinding: ActivityPluginBinding) {
58+
context = activityPluginBinding.getActivity()
59+
}
60+
61+
override fun onDetachedFromActivity() {}
62+
63+
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
64+
if (context == null) {
65+
result.error("", "Something went wrong!", null)
66+
return
67+
}
3268
when (call.method) {
3369
"getInstalledApps" -> {
3470
val includeSystemApps = call.argument("exclude_system_apps") ?: true
3571
val withIcon = call.argument("with_icon") ?: false
3672
val packageNamePrefix: String = call.argument("package_name_prefix") ?: ""
37-
result.success(getInstalledApps(includeSystemApps, withIcon, packageNamePrefix))
73+
Thread {
74+
val apps: List<Map<String, Any?>> =
75+
getInstalledApps(includeSystemApps, withIcon, packageNamePrefix)
76+
result.success(apps)
77+
}.start()
3878
}
3979
"startApp" -> {
4080
val packageName: String? = call.argument("package_name")
@@ -47,44 +87,54 @@ class InstalledAppsPlugin(private val registrar: Registrar) : MethodCallHandler
4787
"toast" -> {
4888
val message = call.argument("message") ?: ""
4989
val short = call.argument("short_length") ?: true
50-
toast(message, short);
90+
toast(message, short)
5191
}
5292
"getAppInfo" -> {
5393
val packageName: String = call.argument("package_name") ?: ""
54-
result.success(getAppInfo(getPackageManager(registrar), packageName))
94+
result.success(getAppInfo(getPackageManager(context!!), packageName))
5595
}
5696
"isSystemApp" -> {
5797
val packageName: String = call.argument("package_name") ?: ""
58-
result.success(isSystemApp(getPackageManager(registrar), packageName))
98+
result.success(isSystemApp(getPackageManager(context!!), packageName))
5999
}
60100
else -> result.notImplemented()
61101
}
62102
}
63103

64-
private fun getInstalledApps(excludeSystemApps: Boolean, withIcon: Boolean, packageNamePrefix: String): List<Map<String, Any?>> {
65-
val packageManager = getPackageManager(registrar)
104+
private fun getInstalledApps(
105+
excludeSystemApps: Boolean,
106+
withIcon: Boolean,
107+
packageNamePrefix: String
108+
): List<Map<String, Any?>> {
109+
val packageManager = getPackageManager(context!!)
66110
var installedApps = packageManager.getInstalledApplications(0)
67111
if (excludeSystemApps)
68-
installedApps = installedApps.filter { app -> !isSystemApp(packageManager, app.packageName) }
112+
installedApps =
113+
installedApps.filter { app -> !isSystemApp(packageManager, app.packageName) }
69114
if (packageNamePrefix.isNotEmpty())
70-
installedApps = installedApps.filter { app -> app.packageName.startsWith(packageNamePrefix.toLowerCase(ENGLISH)) }
115+
installedApps = installedApps.filter { app ->
116+
app.packageName.startsWith(
117+
packageNamePrefix.lowercase(ENGLISH)
118+
)
119+
}
71120
return installedApps.map { app -> convertAppToMap(packageManager, app, withIcon) }
72121
}
73122

74123
private fun startApp(packageName: String?): Boolean {
75124
if (packageName.isNullOrBlank()) return false
76125
return try {
77-
val launchIntent = getPackageManager(registrar).getLaunchIntentForPackage(packageName)
78-
registrar.context().startActivity(launchIntent)
79-
true;
126+
val launchIntent = getPackageManager(context!!).getLaunchIntentForPackage(packageName)
127+
context!!.startActivity(launchIntent)
128+
true
80129
} catch (e: Exception) {
81130
print(e)
82131
false
83132
}
84133
}
85134

86135
private fun toast(text: String, short: Boolean) {
87-
Toast.makeText(getContext(registrar), text, if (short) LENGTH_SHORT else LENGTH_LONG).show()
136+
Toast.makeText(context!!, text, if (short) LENGTH_SHORT else LENGTH_LONG)
137+
.show()
88138
}
89139

90140
private fun isSystemApp(packageManager: PackageManager, packageName: String): Boolean {
@@ -97,14 +147,17 @@ class InstalledAppsPlugin(private val registrar: Registrar) : MethodCallHandler
97147
intent.action = ACTION_APPLICATION_DETAILS_SETTINGS
98148
val uri = Uri.fromParts("package", packageName, null)
99149
intent.data = uri
100-
getContext(registrar).startActivity(intent)
150+
context!!.startActivity(intent)
101151
}
102152

103-
private fun getAppInfo(packageManager: PackageManager, packageName: String): Map<String, Any?>? {
153+
private fun getAppInfo(
154+
packageManager: PackageManager,
155+
packageName: String
156+
): Map<String, Any?>? {
104157
var installedApps = packageManager.getInstalledApplications(0)
105158
installedApps = installedApps.filter { app -> app.packageName == packageName }
106159
return if (installedApps.isEmpty()) null
107-
else convertAppToMap(packageManager, installedApps[0], true);
160+
else convertAppToMap(packageManager, installedApps[0], true)
108161
}
109162

110163
}

android/src/main/kotlin/com/sharmadhiraj/installed_apps/Util.kt

+14-10
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,22 @@ import android.graphics.drawable.Drawable
1111
import android.os.Build.VERSION.SDK_INT
1212
import android.os.Build.VERSION_CODES.N_MR1
1313
import android.os.Build.VERSION_CODES.P
14-
import io.flutter.plugin.common.PluginRegistry
1514
import java.io.ByteArrayOutputStream
1615

1716
class Util {
1817

1918
companion object {
2019

21-
fun convertAppToMap(packageManager: PackageManager, app: ApplicationInfo, withIcon: Boolean): HashMap<String, Any?> {
20+
fun convertAppToMap(
21+
packageManager: PackageManager,
22+
app: ApplicationInfo,
23+
withIcon: Boolean
24+
): HashMap<String, Any?> {
2225
val map = HashMap<String, Any?>()
2326
map["name"] = packageManager.getApplicationLabel(app)
2427
map["package_name"] = app.packageName
25-
map["icon"] = if (withIcon) drawableToByteArray(app.loadIcon(packageManager)) else ByteArray(0)
28+
map["icon"] =
29+
if (withIcon) drawableToByteArray(app.loadIcon(packageManager)) else ByteArray(0)
2630
val packageInfo = packageManager.getPackageInfo(app.packageName, 0)
2731
map["version_name"] = packageInfo.versionName
2832
map["version_code"] = getVersionCode(packageInfo)
@@ -38,19 +42,19 @@ class Util {
3842

3943
private fun drawableToBitmap(drawable: Drawable): Bitmap {
4044
if (SDK_INT <= N_MR1) return (drawable as BitmapDrawable).bitmap
41-
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
45+
val bitmap = Bitmap.createBitmap(
46+
drawable.intrinsicWidth,
47+
drawable.intrinsicHeight,
48+
Bitmap.Config.ARGB_8888
49+
)
4250
val canvas = Canvas(bitmap)
4351
drawable.setBounds(0, 0, canvas.width, canvas.height)
4452
drawable.draw(canvas)
4553
return bitmap
4654
}
4755

48-
fun getContext(registrar: PluginRegistry.Registrar): Context {
49-
return registrar.context()
50-
}
51-
52-
fun getPackageManager(registrar: PluginRegistry.Registrar): PackageManager {
53-
return getContext(registrar).packageManager
56+
fun getPackageManager(context: Context): PackageManager {
57+
return context.packageManager
5458
}
5559

5660
@Suppress("DEPRECATION")

example/.flutter-plugins-dependencies

-1
This file was deleted.

example/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,4 @@
6868
!**/ios/**/default.pbxuser
6969
!**/ios/**/default.perspectivev3
7070
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
71+
.flutter-plugins-dependencies

example/android/app/build.gradle

+2-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
2626
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
2727

2828
android {
29-
compileSdkVersion 30
29+
compileSdkVersion 31
3030

3131
sourceSets {
3232
main.java.srcDirs += 'src/main/kotlin'
@@ -37,10 +37,9 @@ android {
3737
}
3838

3939
defaultConfig {
40-
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
4140
applicationId "com.sharmadhiraj.installed_apps_example"
4241
minSdkVersion 16
43-
targetSdkVersion 30
42+
targetSdkVersion 31
4443
versionCode flutterVersionCode.toInteger()
4544
versionName flutterVersionName
4645
}

example/android/app/src/main/AndroidManifest.xml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<activity
1515
android:name=".MainActivity"
1616
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
17+
android:exported="true"
1718
android:hardwareAccelerated="true"
1819
android:launchMode="singleTop"
1920
android:theme="@style/LaunchTheme"

example/ios/Flutter/flutter_export_environment.sh

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
#!/bin/sh
22
# This is a generated file; do not edit or check into version control.
3-
export "FLUTTER_ROOT=/Users/dhirajsharma/Downloads/flutter"
4-
export "FLUTTER_APPLICATION_PATH=/Users/dhirajsharma/Drive/Personal/Projects/Flutter/installed_apps/example"
3+
export "FLUTTER_ROOT=/Users/dhirajsharma/Drive/SDKs/flutter"
4+
export "FLUTTER_APPLICATION_PATH=/Users/dhirajsharma/Drive/Desk/Personal/Projects/Flutter/installed_apps/example"
55
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
66
export "FLUTTER_TARGET=lib/main.dart"
77
export "FLUTTER_BUILD_DIR=build"
8-
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
98
export "FLUTTER_BUILD_NAME=1.0.0"
109
export "FLUTTER_BUILD_NUMBER=1"
1110
export "DART_OBFUSCATION=false"

example/lib/main.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class HomeScreen extends StatelessWidget {
9999
return AlertDialog(
100100
content: Text(text),
101101
actions: <Widget>[
102-
FlatButton(
102+
TextButton(
103103
child: Text("Close"),
104104
onPressed: () => Navigator.of(context).pop(),
105105
),

example/pubspec.lock

+15-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ packages:
77
name: async
88
url: "https://pub.dartlang.org"
99
source: hosted
10-
version: "2.6.1"
10+
version: "2.8.2"
1111
boolean_selector:
1212
dependency: transitive
1313
description:
@@ -21,14 +21,14 @@ packages:
2121
name: characters
2222
url: "https://pub.dartlang.org"
2323
source: hosted
24-
version: "1.1.0"
24+
version: "1.2.0"
2525
charcode:
2626
dependency: transitive
2727
description:
2828
name: charcode
2929
url: "https://pub.dartlang.org"
3030
source: hosted
31-
version: "1.2.0"
31+
version: "1.3.1"
3232
clock:
3333
dependency: transitive
3434
description:
@@ -80,14 +80,21 @@ packages:
8080
name: matcher
8181
url: "https://pub.dartlang.org"
8282
source: hosted
83-
version: "0.12.10"
83+
version: "0.12.11"
84+
material_color_utilities:
85+
dependency: transitive
86+
description:
87+
name: material_color_utilities
88+
url: "https://pub.dartlang.org"
89+
source: hosted
90+
version: "0.1.3"
8491
meta:
8592
dependency: transitive
8693
description:
8794
name: meta
8895
url: "https://pub.dartlang.org"
8996
source: hosted
90-
version: "1.3.0"
97+
version: "1.7.0"
9198
path:
9299
dependency: transitive
93100
description:
@@ -141,7 +148,7 @@ packages:
141148
name: test_api
142149
url: "https://pub.dartlang.org"
143150
source: hosted
144-
version: "0.3.0"
151+
version: "0.4.8"
145152
typed_data:
146153
dependency: transitive
147154
description:
@@ -155,7 +162,7 @@ packages:
155162
name: vector_math
156163
url: "https://pub.dartlang.org"
157164
source: hosted
158-
version: "2.1.0"
165+
version: "2.1.1"
159166
sdks:
160-
dart: ">=2.12.0 <3.0.0"
167+
dart: ">=2.14.0 <3.0.0"
161168
flutter: ">=1.12.0"

0 commit comments

Comments
 (0)