해당 포스팅에선 위젯에서 각 뷰 마다의 다른 인텐트 설정,AlramManager를 이용한 업데이트, 앱 내부에서 위젯 업데이트 등을 다루겠습니다.
위와 같이 각 아이콘마다 인텐트에 다른 값을 넘겨주기 위해 아래와 같이 PendingIntent를 설정할 경우
Flage에 따라 각각 독립적으로 넘길 수 없었다.
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
...
setOnClickPendingIntent(R.id.containerThanks,
Intent(context, MainActivity::class.java)
.putExtra("data", 0)
.let {
PendingIntent.getActivity(context, 0, it, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
}
)
setOnClickPendingIntent(R.id.containerSave,
Intent(context, MainActivity::class.java)
.putExtra("data", 1)
.let {
PendingIntent.getActivity(context, 0, it, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
}
)
...
}
이에 경우 action을 추가할 경우 독립적으로 Intent를 넘겨줄 수 있었다.
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
...
setOnClickPendingIntent(R.id.containerThanks,
Intent(context, MainActivity::class.java)
.apply {
action = "thanks"
putExtra("data", 0)
}.let {
PendingIntent.getActivity(context, 0, it, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
}
)
setOnClickPendingIntent(R.id.containerSave,
Intent(context, MainActivity::class.java)
.apply {
action = "save"
putExtra("data", 1)
}.let {
PendingIntent.getActivity(context, 0, it, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
}
)
...
}
AppWidgetProvider를 구현하고 브로드캐스트 리시버를 설정했다면,
onReceive()
를 통해 위젯의 활동이 메서드로 연결된다
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
}
이를 통해 앱 내부에서 앱을 업데이트 할 일이 있을 경우 브로드캐스트를 통해 onUpdate
를 호출할 수도 있지만 이는 비싼 작업이기에 따로 액션을 만들어서 넘겨준다
먼저 매니페스트 파일에 해당 액션을 받을 수 있도록 intent-filter
에 추가한다.
<receiver android:name="ExampleAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<!-- 추가 -->
<action android:name="android.action.APPWIDGET_UPDATE_COUNT" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/example_appwidget_info" />
</receiver>
쓰기 쉽도록 해당 상수를 정의하고
class ExampleAppWidgetProvider : AppWidgetProvider() {
companion object{
const val ACTION_UPDATE_COUNT = "android.action.APPWIDGET_UPDATE_COUNT"
}
...
원하는 Intent를 담아서 브로드캐스트 한다.
context.sendBroadcast(
Intent(ExampleAppWidgetProvider.ACTION_UPDATE_COUNT).apply {
component = ComponentName(context, ExampleAppWidgetProvider::class.java)
putExtra("data", widgetData)
}
)
해당 브로드캐스트는 아래의 onReceive()
를 통해 액션이 전달된다.
class ExampleAppWidgetProvider : AppWidgetProvider() {
...
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
when(intent.action){
ACTION_UPDATE_COUNT ->{
// TODO update count
}
}
}
}
예를 들어 하루마다 날짜가 넘어가도록 위젯이 업데이트 돼야한다면
AppWidgetProviderInfo.xml
에서 정의한 updatePeriodMillis
로는 위젯이 시작한 시점이기 때문에 이는 0
으로 설정하고
AlarmManager
를 이용해 자정마다 브로드캐스틀 날려 자동으로 업데이트 할 수 있다.
해당 시나리오에선 onEnabled()
에서 알람 등록을, onDisabled()
에서 알람 해지를 하면 되겠다.
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.getBroadcast(
context,
1,
Intent(context, ExampleAppWidgetProvider::class.java).apply { action = AppWidgetManager.ACTION_APPWIDGET_UPDATE },
PendingIntent.FLAG_MUTABLE
)
} else {
PendingIntent.getBroadcast(
context,
1,
Intent(context, ExampleAppWidgetProvider::class.java).apply { action = AppWidgetManager.ACTION_APPWIDGET_UPDATE },
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
}
val calendar: Calendar = Calendar.getInstance().apply {
timeInMillis = System.currentTimeMillis()
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE,0)
set(Calendar.SECOND, 0)
}
alarmManager.setInexactRepeating(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
AlarmManager.INTERVAL_DAY,
pendingIntent
)
다른 시각을 원할 경우 calendar
설정을 바꾸면 된다.
AlarmManager에 대한 참조 문서
AlarmManager의 경우 재부팅 시 예약이 날아가기 때문에
android.intent.action.BOOT_COMPLETED
액션 리시버를 등록해서 재 예약 해야 한다.
자세한 내용
그 외 겪었던 장애상황
ImageView
에는 Tint
속성이 있어 xml상에서 색상을 덮어씌울 수 있는데
Widget의 initialLayout
의 레이아웃에는 적용이 되지 않았다
그래서 위젯 등록시 ColorFilter
를 코드로 설정해주어 해결할 수 있다
ColorFilter를 RemoteViews에서 사용 시 다음과 같이 setInt()
로 설정 할 수 있다.
val remoteViews = RemoteViews(context.packageName, layout).apply {
setInt(R.id.widgetImg,"setColorFilter", ContextCompat.getColor(context, R.color.color_primary))
}
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews)
해당 질문은 스택오버플로우에도 나와있다.
안드로이드에서 절전모드를 실행시키면 백그라운드 네트워크를 제한하기에
위젯 생성 시 API 호출로 데이터를 가져오는 경우 TimeOut 에러가 날 수 있다.
이로 인해 새로 고침 버튼을 따로 두거나 JobScheduler등을 이용해 처리해야 한다.