注意: 我知道这个问题以前已经被问过了,但是发布的少数解决方案要么过时,要么对我无效。
嗨,我正在尝试制作一个闹钟应用小部件(倒计时时钟),它将显示Android的不同倒计时持续时间列表,但是我遇到了问题,尝试使其每分钟更新一次。 我尝试使用 AlarmManager
,但出现了问题。
这是我拥有的所有内容:
EventWidget.kt
/**
* Implementation of App Widget functionality.
*/
class EventWidget : AppWidgetProvider() {
class UpdateTimeService : Service() {
private val intentFilter = IntentFilter().apply {
addAction(Intent.ACTION_TIME_TICK)
addAction(Intent.ACTION_TIME_CHANGED)
addAction(Intent.ACTION_TIMEZONE_CHANGED)
}
private val receiver = object: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) = update(context)
}
override fun onCreate() {
super.onCreate()
registerReceiver(receiver, intentFilter)
}
override fun onBind(intent: Intent?): IBinder? = null
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(receiver)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
if (intent != null && UPDATE_TIME == intent.action) update(this)
return START_STICKY
}
companion object {
const val UPDATE_TIME = "com.richardrobinson.countdown2.action.UPDATE_TIME"
}
}
private lateinit var pending: PendingIntent
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
appWidgetIds.forEach {
val intent = Intent(context, EventWidgetService::class.java).apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, it)
data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
}
val rv = RemoteViews(context.packageName, R.layout.event_widget).apply {
setRemoteAdapter(R.id.listView, intent)
setEmptyView(R.id.listView, R.id.emptyView)
}
appWidgetManager.updateAppWidget(it, rv)
}
update(context)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.listView)
super.onUpdate(context, appWidgetManager, appWidgetIds)
}
override fun onEnabled(context: Context) {
// Enter relevant functionality for when the first widget is created
super.onEnabled(context)
val pending = PendingIntent.getService(context, 0, Intent(context, UpdateTimeService::class.java), 0)
val interval: Long = 1000 * 60
(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager).apply {
cancel(pending)
setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), interval, pending)
}
update(context)
}
override fun onReceive(context: Context, intent: Intent?) {
super.onReceive(context, intent)
update(context)
}
override fun onDisabled(context: Context) {
// Enter relevant functionality for when the last widget is disabled
}
}
fun update(context: Context) {
val rv = RemoteViews(context.packageName, R.layout.event_widget).apply {
setRemoteAdapter(R.id.listView, Intent(context, EventWidgetService::class.java))
setEmptyView(R.id.listView, R.id.emptyView)
}
val component = ComponentName(context, EventWidget::class.java)
AppWidgetManager.getInstance(context).apply {
updateAppWidget(component, rv)
val ids = getAppWidgetIds(component)
ids.forEach { updateAppWidget(it, rv) }
notifyAppWidgetViewDataChanged(ids, R.id.listView)
}
}
EventWidgetService.kt
class EventWidgetService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory =
EventRemoteViewsFactory(this.applicationContext, intent)
}
class EventRemoteViewsFactory(private val context: Context, intent: Intent) : RemoteViewsService.RemoteViewsFactory {
private val prefsName = "FlutterSharedPreferences"
override fun onCreate() {
MiniModel.initialize(context.getSharedPreferences(prefsName, Context.MODE_PRIVATE))
}
override fun getLoadingView(): RemoteViews = RemoteViews(context.packageName, R.id.emptyView)
override fun getItemId(position: Int): Long = position.toLong()
override fun onDataSetChanged() {
}
override fun hasStableIds(): Boolean = true
@ExperimentalTime
override fun getViewAt(position: Int): RemoteViews {
return RemoteViews(context.packageName, R.layout.widget_item).apply {
val event = MiniModel.events[position]
val remaining = event.secondsRemaining.inSeconds / (event.end.epochSecond - event.start.epochSecond).toDouble()
setProgressBar(R.id.progressBar, 100, (remaining * 100).toInt(), false)
setTextViewText(R.id.item_text, event.title)
setTextViewText(R.id.details_text, "whatever")
}
}
override fun getCount(): Int = MiniModel.events.count()
override fun getViewTypeCount(): Int = 1
override fun onDestroy() {
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.richardrobinson.countdown2">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:name="io.flutter.app.FlutterApplication"
android:icon="@mipmap/ic_launcher"
android:label="Hourglass">
<receiver android:name=".EventWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/event_widget_info" />
</receiver>
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<!--
This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme).
-->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".EventWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS"/>
<service android:name=".EventWidget$UpdateTimeService">
<intent-filter>
<action android:name="com.richardrobinson.countdown2.action.UPDATE_TIME"/>
</intent-filter>
</service>
</application>
</manifest>