Skip to content

Commit c40275d

Browse files
committed
update
1 parent 76e5395 commit c40275d

File tree

3 files changed

+140
-154
lines changed

3 files changed

+140
-154
lines changed

app/src/main/java/org/blitzortung/android/app/WidgetProvider.kt

Lines changed: 16 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -26,105 +26,56 @@ import android.content.Context
2626
import android.content.Intent
2727
import android.os.Bundle
2828
import android.util.Log
29-
import android.widget.RemoteViews
30-
import android.widget.TextView
31-
import androidx.work.ExistingPeriodicWorkPolicy
32-
import androidx.work.PeriodicWorkRequestBuilder
33-
import androidx.work.WorkManager
29+
import androidx.work.*
3430
import dagger.android.AndroidInjection
35-
import kotlinx.coroutines.Dispatchers
36-
import kotlinx.coroutines.async
37-
import kotlinx.coroutines.runBlocking
38-
import kotlinx.coroutines.withContext
3931
import org.blitzortung.android.alert.handler.AlertDataHandler
4032
import org.blitzortung.android.alert.handler.AlertHandler
41-
import org.blitzortung.android.data.Flags
42-
import org.blitzortung.android.data.Parameters
43-
import org.blitzortung.android.data.TimeInterval
4433
import org.blitzortung.android.data.provider.standard.JsonRpcDataProvider
4534
import org.blitzortung.android.location.LocationHandler
4635
import org.blitzortung.android.map.overlay.color.StrikeColorHandler
47-
import java.text.DateFormat
48-
import java.text.SimpleDateFormat
49-
import java.util.*
5036
import java.util.concurrent.TimeUnit
5137
import javax.inject.Inject
52-
import org.blitzortung.android.alert.handler.Strikes
53-
import org.blitzortung.android.app.view.AlarmView
54-
import org.blitzortung.android.data.DataArea
55-
import org.blitzortung.android.data.provider.calculateLocalCoordinate
56-
import org.blitzortung.android.data.provider.result.DataReceived
57-
5838

5939
class WidgetProvider : AppWidgetProvider() {
6040

61-
@set:Inject
62-
internal lateinit var colorHandler: StrikeColorHandler
63-
64-
@set:Inject
65-
internal lateinit var alertHandler: AlertHandler
66-
67-
@set:Inject
68-
internal lateinit var alertDataHandler: AlertDataHandler
69-
70-
@set:Inject
71-
internal lateinit var locationHandler: LocationHandler
72-
73-
@set:Inject
74-
internal lateinit var dataProvider: JsonRpcDataProvider
75-
76-
var df: DateFormat = SimpleDateFormat("hh:mm:ss")
77-
7841
companion object {
7942
private const val WIDGET_UPDATE_WORK_NAME = "widget_update_work"
8043
private const val UPDATE_INTERVAL_MINUTES = 5L
8144
}
8245

83-
84-
override fun onAppWidgetOptionsChanged(context: Context?, appWidgetManager: AppWidgetManager?, appWidgetId: Int, newOptions: Bundle?) {
85-
AndroidInjection.inject(this, context)
46+
override fun onAppWidgetOptionsChanged(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, newOptions: Bundle?) {
8647
Log.v(Main.LOG_TAG, "WidgetProvider.onAppWidgetOptionsChanged()")
8748
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
88-
89-
// Update the widget with the new size
90-
if (context != null && appWidgetManager != null) {
91-
locationHandler.start()
92-
val views = getUpdatedViews(context, appWidgetManager, appWidgetId)
93-
appWidgetManager.updateAppWidget(appWidgetId, views)
94-
locationHandler.shutdown()
95-
}
49+
update(context, appWidgetManager, intArrayOf(appWidgetId))
9650
}
9751

98-
override fun onReceive(context: Context?, intent: Intent) {
99-
AndroidInjection.inject(this, context)
52+
override fun onReceive(context: Context, intent: Intent) {
10053
val action = intent.action
10154
Log.v(Main.LOG_TAG, "WidgetProvider.onReceive() $action")
10255
if (ACTION_APPWIDGET_UPDATE == action) {
103-
locationHandler.start()
104-
val appWidgetManager = AppWidgetManager.getInstance(context!!)
56+
val appWidgetManager = AppWidgetManager.getInstance(context)
10557
val appWidgetIds = appWidgetManager.getAppWidgetIds(
10658
ComponentName(context, WidgetProvider::class.java)
10759
)
108-
for (appWidgetId in appWidgetIds) {
109-
val views = getUpdatedViews(context, appWidgetManager, appWidgetId)
110-
appWidgetManager.updateAppWidget(appWidgetId, views)
111-
}
112-
locationHandler.shutdown()
60+
update(context, appWidgetManager, appWidgetIds)
11361
}
11462
}
11563

11664
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
117-
AndroidInjection.inject(this, context)
11865
Log.v(Main.LOG_TAG, "WidgetProvider.onUpdate()")
66+
update(context, appWidgetManager, appWidgetIds)
67+
}
11968

120-
locationHandler.start()
69+
private fun update(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
70+
val workManager = WorkManager.getInstance(context)
12171

122-
for (appWidgetId in appWidgetIds) {
123-
val views = getUpdatedViews(context, appWidgetManager, appWidgetId)
124-
appWidgetManager.updateAppWidget(appWidgetId, views)
125-
}
72+
val inputData = workDataOf("appWidgetIds" to appWidgetIds)
73+
74+
val workRequest = OneTimeWorkRequestBuilder<WidgetUpdateWorker>()
75+
.setInputData(inputData)
76+
.build()
12677

127-
locationHandler.shutdown()
78+
workManager.enqueue(workRequest)
12879
}
12980

13081
override fun onEnabled(context: Context) {
@@ -154,92 +105,4 @@ class WidgetProvider : AppWidgetProvider() {
154105
private fun cancelPeriodicUpdates(context: Context) {
155106
WorkManager.getInstance(context).cancelUniqueWork(WIDGET_UPDATE_WORK_NAME)
156107
}
157-
158-
private fun getUpdatedViews(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int): RemoteViews {
159-
// Get widget size from options
160-
val options = appWidgetManager.getAppWidgetOptions(appWidgetId)
161-
val isPortrait = context.resources.configuration.orientation == android.content.res.Configuration.ORIENTATION_PORTRAIT
162-
val widthDp = options.getInt(if (isPortrait) AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH else AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
163-
val heightDp = options.getInt(if (isPortrait) AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT else AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
164-
165-
// Convert dp to pixels
166-
val displayMetrics = context.resources.displayMetrics
167-
val density = displayMetrics.density
168-
169-
// Use reasonable defaults if size is not available
170-
val totalWidthPx = if (widthDp > 0) (widthDp * density).toInt() else 300
171-
val totalHeightPx = if (heightDp > 0) (heightDp * density).toInt() else 300
172-
173-
// Account for LinearLayout padding (10dp on each side)
174-
val paddingPx = (10 * density).toInt()
175-
val availableWidth = (totalWidthPx - paddingPx * 2).coerceAtLeast(100)
176-
177-
// Account for text view height and margin
178-
val textHeightPx = (32 * density).toInt() // Headline text size
179-
val textMarginPx = (4 * density).toInt()
180-
val availableHeight = (totalHeightPx - paddingPx * 2 - textHeightPx - textMarginPx).coerceAtLeast(100)
181-
182-
Log.v(Main.LOG_TAG, "Widget total: ${widthDp}dp x ${heightDp}dp = ${totalWidthPx}px x ${totalHeightPx}px")
183-
Log.v(Main.LOG_TAG, "Widget available for image: ${availableWidth}px x ${availableHeight}px")
184-
185-
val alarmView = AlarmView(context)
186-
alarmView.setColorHandler(colorHandler, 60)
187-
188-
val location = locationHandler.location!!
189-
val scale = 5
190-
val x = calculateLocalCoordinate(location.longitude, scale)
191-
val y = calculateLocalCoordinate(location.latitude, scale)
192-
val dataArea = DataArea(x, y, scale)
193-
194-
val parameters = Parameters(region = 0, gridSize = 5000, interval = TimeInterval(duration = 60), dataArea = dataArea)
195-
val result = runBlocking {
196-
withContext(Dispatchers.Default) {
197-
async {
198-
Log.v(Main.LOG_TAG, "Widget.getUpdatedViews() retrieve running in ${Thread.currentThread().name}")
199-
var result = DataReceived(
200-
referenceTime = System.currentTimeMillis(),
201-
parameters = parameters,
202-
flags = Flags()
203-
)
204-
dataProvider.retrieveData {
205-
result = getStrikesGrid(parameters, null, Flags())
206-
}
207-
Log.v(Main.LOG_TAG, "Widget.getUpdatedViews() check running in ${Thread.currentThread().name}")
208-
Log.v(Main.LOG_TAG, "WidgetProvider.getUpdatedViews() received ${result.parameters}, ${result.gridParameters} strikes")
209-
val strikes = Strikes(result.strikes!!, result.gridParameters)
210-
alertDataHandler.checkStrikes(strikes, location, alertHandler.alertParameters, result.referenceTime)
211-
}.await()
212-
}
213-
}
214-
215-
Log.v(Main.LOG_TAG, "Widget.getUpdatedViews() result in ${Thread.currentThread().name} $result")
216-
alarmView.alertEventConsumer.invoke(result)
217-
218-
// Measure and layout the view with available dimensions
219-
val widthSpec = android.view.View.MeasureSpec.makeMeasureSpec(availableWidth, android.view.View.MeasureSpec.EXACTLY)
220-
val heightSpec = android.view.View.MeasureSpec.makeMeasureSpec(availableHeight, android.view.View.MeasureSpec.EXACTLY)
221-
alarmView.measure(widthSpec, heightSpec)
222-
alarmView.layout(0, 0, alarmView.measuredWidth, alarmView.measuredHeight)
223-
224-
// Create bitmap and draw the view
225-
val bitmap = android.graphics.Bitmap.createBitmap(
226-
alarmView.measuredWidth,
227-
alarmView.measuredHeight,
228-
android.graphics.Bitmap.Config.ARGB_8888
229-
)
230-
val canvas = android.graphics.Canvas(bitmap)
231-
alarmView.draw(canvas)
232-
233-
Log.v(Main.LOG_TAG, "Bitmap created: ${bitmap.width}px x ${bitmap.height}px")
234-
235-
val views = RemoteViews(context.packageName, R.layout.widget)
236-
views.setImageViewBitmap(R.id.alarm_diagram, bitmap)
237-
238-
val format = df.format(Date())
239-
Log.v(Main.LOG_TAG, "WidgetProvider.getUpdatedViews() $format")
240-
views.setTextViewText(R.id.widget_update_time, format)
241-
return views
242-
}
243-
244-
245108
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package org.blitzortung.android.app
2+
3+
import android.appwidget.AppWidgetManager
4+
import android.content.Context
5+
import android.graphics.Bitmap
6+
import android.graphics.Canvas
7+
import android.os.AsyncTask
8+
import android.util.Log
9+
import android.view.View
10+
import android.widget.RemoteViews
11+
import org.blitzortung.android.alert.handler.AlertDataHandler
12+
import org.blitzortung.android.alert.handler.AlertHandler
13+
import org.blitzortung.android.alert.handler.Strikes
14+
import org.blitzortung.android.app.view.AlarmView
15+
import org.blitzortung.android.data.DataArea
16+
import org.blitzortung.android.data.Flags
17+
import org.blitzortung.android.data.Parameters
18+
import org.blitzortung.android.data.TimeInterval
19+
import org.blitzortung.android.data.provider.calculateLocalCoordinate
20+
import org.blitzortung.android.data.provider.data.DataProvider
21+
import org.blitzortung.android.data.provider.result.DataReceived
22+
import org.blitzortung.android.location.LocationHandler
23+
import org.blitzortung.android.map.overlay.color.StrikeColorHandler
24+
import java.text.DateFormat
25+
import java.text.SimpleDateFormat
26+
import java.util.*
27+
28+
internal class WidgetUpdateTask(
29+
private val context: Context,
30+
private val appWidgetManager: AppWidgetManager,
31+
private val appWidgetIds: IntArray,
32+
private val colorHandler: StrikeColorHandler,
33+
private val alertHandler: AlertHandler,
34+
private val alertDataHandler: AlertDataHandler,
35+
private val locationHandler: LocationHandler,
36+
private val dataProvider: DataProvider
37+
) : AsyncTask<Unit, Unit, Unit>() {
38+
39+
private var df: DateFormat = SimpleDateFormat("hh:mm:ss")
40+
41+
override fun doInBackground(vararg params: Unit) {
42+
try {
43+
locationHandler.start()
44+
45+
appWidgetIds.forEach { appWidgetId ->
46+
val views = getUpdatedViews(appWidgetId)
47+
appWidgetManager.updateAppWidget(appWidgetId, views)
48+
}
49+
} catch (e: Throwable) {
50+
Log.e(Main.LOG_TAG, "WidgetUpdateTask.doInBackground() failed", e)
51+
} finally {
52+
locationHandler.shutdown()
53+
}
54+
}
55+
56+
private fun getUpdatedViews(appWidgetId: Int): RemoteViews {
57+
val views = RemoteViews(context.packageName, R.layout.widget)
58+
try {
59+
val options = appWidgetManager.getAppWidgetOptions(appWidgetId)
60+
61+
val displayMetrics = context.resources.displayMetrics
62+
val density = displayMetrics.density
63+
val minWidthDp = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
64+
val minHeightDp = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
65+
66+
val width = (if (minWidthDp > 0) minWidthDp else 100) * density
67+
val height = (if (minHeightDp > 0) minHeightDp else 100) * density
68+
69+
val alarmView = AlarmView(context)
70+
alarmView.setColorHandler(colorHandler, 60)
71+
72+
val location = locationHandler.location
73+
74+
if (location != null) {
75+
val scale = 5
76+
val x = calculateLocalCoordinate(location.longitude, scale)
77+
val y = calculateLocalCoordinate(location.latitude, scale)
78+
val dataArea = DataArea(x, y, scale)
79+
80+
val parameters =
81+
Parameters(region = 0, gridSize = 5000, interval = TimeInterval(duration = 60), dataArea = dataArea)
82+
83+
val result: DataReceived = dataProvider.retrieveData {
84+
getStrikesGrid(parameters, null, Flags())
85+
}
86+
87+
val strikes = result.strikes?.let { Strikes(it, result.gridParameters) }
88+
89+
if (strikes != null) {
90+
val alertResult =
91+
alertDataHandler.checkStrikes(strikes, location, alertHandler.alertParameters, result.referenceTime)
92+
93+
alarmView.alertEventConsumer.invoke(alertResult)
94+
95+
val widthSpec = View.MeasureSpec.makeMeasureSpec(width.toInt(), View.MeasureSpec.EXACTLY)
96+
val heightSpec = View.MeasureSpec.makeMeasureSpec(height.toInt(), View.MeasureSpec.EXACTLY)
97+
alarmView.measure(widthSpec, heightSpec)
98+
alarmView.layout(0, 0, alarmView.measuredWidth, alarmView.measuredHeight)
99+
100+
val bitmap = Bitmap.createBitmap(alarmView.measuredWidth, alarmView.measuredHeight, Bitmap.Config.ARGB_8888)
101+
val canvas = Canvas(bitmap)
102+
alarmView.draw(canvas)
103+
104+
views.setImageViewBitmap(R.id.alarm_diagram, bitmap)
105+
} else {
106+
views.setTextViewText(R.id.widget_update_time, "no strike data")
107+
}
108+
} else {
109+
views.setTextViewText(R.id.widget_update_time, "location not available")
110+
}
111+
112+
} catch (e: Throwable) {
113+
Log.e(Main.LOG_TAG, "WidgetUpdateTask.getUpdatedViews() failed", e)
114+
views.setTextViewText(R.id.widget_update_time, "update failed")
115+
}
116+
117+
val format = df.format(Date())
118+
views.setTextViewText(R.id.widget_update_time, format)
119+
120+
return views
121+
}
122+
}

app/src/main/java/org/blitzortung/android/data/MainDataHandler.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ constructor(
8282
private var period: Int = 0
8383
private var sequenceNumber = AtomicLong()
8484

85-
private var dataProvider: DataProvider? = null
85+
var dataProvider: DataProvider? = null
86+
private set
8687

8788
var parameters = Parameters()
8889
private set

0 commit comments

Comments
 (0)