動作画面

解説
デフォルトでPackageManagerというアプリを管理するマネージャが用意されているみたいなので、こちらを使用して実装します。今回のView側はJetpackComposeで実装しているので難しいロジックを考える必要は無いですね。
class MainActivity : ComponentActivity() {
@SuppressLint("QueryPermissionsNeeded")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val flags =
PackageManager.MATCH_UNINSTALLED_PACKAGES or PackageManager.MATCH_DISABLED_COMPONENTS
val packageManager = packageManager
val installedAppList = packageManager.getInstalledApplications(flags)
val appDataList = installedAppList.map {
AppData(
label = it.loadLabel(packageManager).toString(),
icon = it.loadIcon(packageManager),
packageName = it.packageName
)
}
setContent {
ComposeMoveOtherAppTheme {
HomeScreen(appDataList)
}
}
}
}
可読性を考慮してdata class
も用意。
Kotlinではmap関数が使えるので変換も便利ですね。
data class AppData(val label: String, val icon: Drawable, val packageName: String)
ダークテーマによって背景色が変化するので、Divider
のColorもそれに対応しています。
Previewの部分で!!
を用いてnullableな型の変数を強制的にnon-nullにしていますが、Previewなので特例で使用しています。基本的には適切なnull処理をしましょう。
@Composable
fun HomeScreen(appDataList: List<AppData>) {
val dividerColor = if (isSystemInDarkTheme()) Color.White else Color.Black
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
LazyColumn(
modifier = Modifier.padding(8.dp)
) {
items(appDataList) { data ->
Row(verticalAlignment = Alignment.CenterVertically) {
Image(
painter = rememberDrawablePainter(drawable = data.icon),
contentDescription = null,
modifier = Modifier.size(64.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Column {
Text(text = data.label, fontWeight = FontWeight.Bold)
Text(text = data.packageName)
}
}
Spacer(modifier = Modifier.height(4.dp))
Divider(color = dividerColor)
Spacer(modifier = Modifier.height(4.dp))
}
}
}
}
@Preview(showSystemUi = true)
@Composable
private fun PreviewHomeScreen() {
val context = LocalContext.current
HomeScreen(
mutableListOf(
AppData(
label = "test1",
icon = context.getDrawable(R.drawable.ic_launcher_foreground)!!,
packageName = "com.hoge.test1"
),
AppData(
label = "test2",
icon = context.getDrawable(R.drawable.ic_launcher_foreground)!!,
packageName = "com.hoge.test2"
),
AppData(
label = "test3",
icon = context.getDrawable(R.drawable.ic_launcher_foreground)!!,
packageName = "com.hoge.test3"
)
)
)
}
この状態だとPlayStoreからダウンロードされる多くのアプリの情報を取得出来ません。なので、AndroidManifest.xml
の<manifest>
タグ直下に以下の内容を追記します。実はAPIレベル30から他のパッケージの情報を見つけることが出来ない仕様になっています。
https://developer.android.com/training/package-visibility/declaring?hl=ja
ほとんどのAndroidアプリはandroid.intent.action.MAIN
を使用しているのでこちらを追加することでほぼ全てのアプリを参照することが出来ます。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
</intent>
</queries>
注意点
-
getInstalledApplications
を呼び出す際には@SuppressLint("QueryPermissionsNeeded")
をつけろと怒られるので記述しましょう。 -
PackageManager.MATCH_UNINSTALLED_PACKAGES or PackageManager.MATCH_DISABLED_COMPONENTS
の部分はAPIレベル24以上でないと使えないです。APIレベル23以下の場合であればこのようにAPIレベルに応じて分岐させましょう。
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
PackageManager.MATCH_UNINSTALLED_PACKAGES or PackageManager.MATCH_DISABLED_COMPONENTS
} else {
PackageManager.GET_UNINSTALLED_PACKAGES or PackageManager.GET_DISABLED_COMPONENTS
}
-
rememberDrawablePainter
を使うには以下のライブラリをdependenciesに追加する必要があります
implementation "com.google.accompanist:accompanist-drawablepainter:<version>"
ソースコード