I am making an android launcher using jetpack compose and want to add the functionality to use all the installed app widgets on home screen but I just can't find good sources. Can anyone point me in the right direction.
An article on medium and this page is the best reference i could find but even they aren't explaining it completely and I find many errors while following their method.
I also asked Chat GPT to write the code to draw all the installed app widgets but it also doesn't work.
here is the GPT code
class MainActivity : ComponentActivity() {
private lateinit var appWidgetHost: AppWidgetHost
private lateinit var appWidgetManager: AppWidgetManager
private lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
appWidgetHost = AppWidgetHost(this, APP_WIDGET_HOST_ID)
appWidgetManager = AppWidgetManager.getInstance(this)
requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
if (isGranted) {
// Permission granted, continue with widget setup
println("permission")
setupWidgets()
} else {
setContent{
WidgetsScreenExampleTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
println("no permission")
Text(
text = "permission not found",
style = TextStyle(color = Color.Black, fontSize = TextUnit(10f, TextUnitType.Sp))
)
}
}
}
}
}
checkAndRequestWidgetPermission()
}
private fun checkAndRequestWidgetPermission() {
val permission = "android.permission.BIND_APPWIDGET"
when {
ContextCompat.checkSelfPermission(
this,
permission
) == PackageManager.PERMISSION_GRANTED -> {
// Permission already granted, continue with widget setup
setupWidgets()
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
// Permission not granted, request it from the user
requestPermissionLauncher.launch(permission)
}
else -> {
// For versions below Android M, permission is granted at installation time
// Continue with widget setup
setupWidgets()
}
}
}
private fun setupWidgets() {
// Continue with widget setup here
setContent {
WidgetsScreenExampleTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
WidgetsView(
Modifier
.fillMaxSize(),
appWidgetHost,
appWidgetManager,
APP_WIDGET_HOST_ID
)
}
}
}
}
companion object {
const val APP_WIDGET_HOST_ID = 1
}
}
@Composable
fun AppWidgetHostView(
appWidgetManager: AppWidgetManager,
appWidgetHost: AppWidgetHost,
appWidgetId: Int,
modifier: Modifier = Modifier
) {
val appWidgetHostView = appWidgetHost.createView(
LocalContext.current,
appWidgetId,
appWidgetManager.getAppWidgetInfo(appWidgetId)
)
DisposableEffect(appWidgetHostView) {
onDispose {
appWidgetHost.deleteAppWidgetId(appWidgetId)
}
}
// Measure the app widget size
appWidgetHostView.measure(
View.MeasureSpec.makeMeasureSpec(100.dp.value.toInt(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(100.dp.value.toInt(), View.MeasureSpec.EXACTLY)
)
// Layout the app widget
appWidgetHostView.layout(
0,
0,
appWidgetHostView.measuredWidth,
appWidgetHostView.measuredHeight
)
// Draw the app widget
AndroidView(
factory = { appWidgetHostView },
modifier = modifier
)
}
@Composable
fun AppWidgetListItem(appWidget: AppWidget, appWidgetHostId: Int) {
val context = LocalContext.current
val appWidgetHost = AppWidgetHost(context, appWidgetHostId)
val appWidgetManager = AppWidgetManager.getInstance(context)
val (isExpanded, setExpanded) = remember { mutableStateOf(false) }
// val remoteViews = appWidgetManager.getAppWidgetViews(appWidget.appWidgetId)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.background(MaterialTheme.colorScheme.background)
.clickable { setExpanded(!isExpanded) }
) {
Text(text = appWidget.appWidgetInfo.label)
if (isExpanded) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp)
) {
AppWidgetHostView(
appWidgetManager = appWidgetManager,
appWidgetHost = appWidgetHost,
appWidgetId = appWidget.appWidgetId,
modifier = Modifier.fillMaxWidth()
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WidgetsView(
modifier: Modifier = Modifier,
appWidgetHost: AppWidgetHost,
appWidgetManager: AppWidgetManager,
appWidgetHostId: Int
) {
val context = LocalContext.current
val appWidgetIds = appWidgetHost.appWidgetIds
val appWidgets = appWidgetIds.map { appWidgetId ->
val appWidgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId)
if (appWidgetInfo != null) {
AppWidget(appWidgetId, appWidgetInfo)
} else {
null
}
}.mapNotNull { it }
Column {
TopAppBar(
title = {
Text(text = "Installed Widgets")
},
actions = {
IconButton(onClick = { /* Handle settings action */ }) {
Icon(imageVector = Icons.Default.Star, contentDescription = null)
}
}
)
LazyColumn {
items(appWidgets.size) { i ->
AppWidgetListItem(appWidgets[i], appWidgetHostId)
}
}
}
}
If anyone has any feedback or pointer to a good resource, then please provide it.
Thank you for all the helpful comments.
Yes This works fine. I just wanted to do it entirely inside kotlin as I didn't want to use XML. But I guess It is required according to my understanding.