インストール済みのアプリを一覧表示させる


# 動作画面
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/d435109d-7575-30a8-b164-5cfadb6e4a58.gif" width="300">

# 解説
デフォルトで[PackageManager](https://developer.android.com/reference/android/content/pm/PackageManager)というアプリを管理するマネージャが用意されているみたいなので、こちらを使用して実装します。今回のView側はJetpackComposeで実装しているので難しいロジックを考える必要は無いですね。

```MainActivity.kt
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関数が使えるので変換も便利ですね。
```AppData.kt
data class AppData(val label: String, val icon: Drawable, val packageName: String)
```
ダークテーマによって背景色が変化するので、`Divider`のColorもそれに対応しています。
Previewの部分で`!!`を用いてnullableな型の変数を強制的にnon-nullにしていますが、Previewなので特例で使用しています。基本的には適切なnull処理をしましょう。
```HomeScreen.kt
@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`を使用しているのでこちらを追加することでほぼ全てのアプリを参照することが出来ます。

```AndroidManifest.xml
<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レベルに応じて分岐させましょう。
```kotlin
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>"
```

# ソースコード
https://github.com/momomomo111/ComposeMoveOtherApp