Skip to content

Commit cb93c1d

Browse files
committed
WIP widget activities grid
1 parent 6e58325 commit cb93c1d

16 files changed

Lines changed: 631 additions & 114 deletions

File tree

domain/src/main/java/com/example/util/simpletimetracker/domain/notifications/interactor/UpdateExternalViewsInteractor.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,8 @@ class UpdateExternalViewsInteractor @Inject constructor(
485485
}
486486
is Update.WidgetUniversal -> {
487487
widgetInteractor.updateWidgets(WidgetType.UNIVERSAL)
488+
// TODO WIDGET add goals and add more update sources
489+
widgetInteractor.updateWidgets(WidgetType.GRID)
488490
}
489491
is Update.WidgetSingleTypes -> {
490492
widgetInteractor.updateWidgets(WidgetType.RECORD_TYPE)

domain/src/main/java/com/example/util/simpletimetracker/domain/widget/model/WidgetType.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.example.util.simpletimetracker.domain.widget.model
33
enum class WidgetType {
44
RECORD_TYPE,
55
UNIVERSAL,
6+
GRID,
67
STATISTICS_CHART,
78
QUICK_SETTINGS,
89
}

features/feature_widget/src/main/AndroidManifest.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@
5050
android:resource="@xml/widget_single_info" />
5151
</receiver>
5252

53+
<receiver
54+
android:name=".grid.WidgetGridProvider"
55+
android:exported="false"
56+
android:label="@string/widget_grid_name">
57+
<intent-filter>
58+
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
59+
</intent-filter>
60+
<meta-data
61+
android:name="android.appwidget.provider"
62+
android:resource="@xml/widget_grid_info" />
63+
</receiver>
64+
5365
<receiver
5466
android:name=".universal.WidgetUniversalProvider"
5567
android:exported="false"
@@ -86,6 +98,9 @@
8698
android:resource="@xml/widget_quick_settings_info" />
8799
</receiver>
88100

101+
<service android:name=".grid.WidgetGridRemoteViewsService"
102+
android:permission="android.permission.BIND_REMOTEVIEWS" />
103+
89104
</application>
90105

91106
</manifest>

features/feature_widget/src/main/java/com/example/util/simpletimetracker/feature_widget/common/WidgetManager.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import android.content.ComponentName
55
import android.content.Context
66
import android.content.Intent
77
import com.example.util.simpletimetracker.domain.widget.model.WidgetType
8+
import com.example.util.simpletimetracker.feature_widget.grid.WidgetGridProvider
89
import com.example.util.simpletimetracker.feature_widget.quickSettings.WidgetQuickSettingsProvider
910
import com.example.util.simpletimetracker.feature_widget.single.WidgetSingleProvider
1011
import com.example.util.simpletimetracker.feature_widget.statistics.WidgetStatisticsChartProvider
@@ -18,6 +19,7 @@ class WidgetManager @Inject constructor(
1819
@ApplicationContext private val context: Context,
1920
) {
2021

22+
// TODO WIDGET add update
2123
fun updateSingleWidget(widgetId: Int) {
2224
val intent = Intent(context, WidgetSingleProvider::class.java)
2325
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
@@ -58,6 +60,7 @@ class WidgetManager @Inject constructor(
5860
val providers = widgetsToUpdate.map { type ->
5961
when (type) {
6062
WidgetType.RECORD_TYPE -> WidgetSingleProvider::class.java
63+
WidgetType.GRID -> WidgetGridProvider::class.java
6164
WidgetType.UNIVERSAL -> WidgetUniversalProvider::class.java
6265
WidgetType.STATISTICS_CHART -> WidgetStatisticsChartProvider::class.java
6366
WidgetType.QUICK_SETTINGS -> WidgetQuickSettingsProvider::class.java
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.example.util.simpletimetracker.feature_widget.common
2+
3+
import android.content.Context
4+
import com.example.util.simpletimetracker.core.extension.toParams
5+
import com.example.util.simpletimetracker.core.interactor.CompleteTypesStateInteractor
6+
import com.example.util.simpletimetracker.core.interactor.RecordRepeatInteractor
7+
import com.example.util.simpletimetracker.domain.base.REPEAT_BUTTON_ITEM_ID
8+
import com.example.util.simpletimetracker.domain.record.interactor.AddRunningRecordMediator
9+
import com.example.util.simpletimetracker.domain.record.interactor.RemoveRunningRecordMediator
10+
import com.example.util.simpletimetracker.domain.record.interactor.RunningRecordInteractor
11+
import com.example.util.simpletimetracker.domain.record.model.RecordDataSelectionDialogResult
12+
import com.example.util.simpletimetracker.domain.recordType.interactor.RecordTypeInteractor
13+
import com.example.util.simpletimetracker.feature_widget.single.WidgetSingleTagSelectionActivity
14+
import com.example.util.simpletimetracker.navigation.params.screen.RecordTagSelectionParams
15+
import kotlinx.coroutines.delay
16+
import javax.inject.Inject
17+
18+
class WidgetTypeClickManager @Inject constructor(
19+
private val recordRepeatInteractor: RecordRepeatInteractor,
20+
private val recordTypeInteractor: RecordTypeInteractor,
21+
private val completeTypesStateInteractor: CompleteTypesStateInteractor,
22+
private val runningRecordInteractor: RunningRecordInteractor,
23+
private val addRunningRecordMediator: AddRunningRecordMediator,
24+
private val removeRunningRecordMediator: RemoveRunningRecordMediator,
25+
) {
26+
27+
suspend fun onClick(
28+
context: Context?,
29+
recordTypeId: Long,
30+
onWidgetUpdate: () -> Unit,
31+
) {
32+
if (recordTypeId == REPEAT_BUTTON_ITEM_ID) {
33+
recordRepeatInteractor.repeatExternal()
34+
return
35+
}
36+
37+
val type = recordTypeInteractor.get(recordTypeId)
38+
39+
// If recordType removed - update widget and exit
40+
if (type == null) {
41+
onWidgetUpdate()
42+
return
43+
}
44+
45+
if (type.defaultDuration > 0) {
46+
completeTypesStateInteractor.widgetTypeIds += recordTypeId
47+
onWidgetUpdate()
48+
delay(1000)
49+
completeTypesStateInteractor.widgetTypeIds -= recordTypeId
50+
onWidgetUpdate()
51+
}
52+
53+
val runningRecord = runningRecordInteractor.get(recordTypeId)
54+
if (runningRecord != null) {
55+
// Stop running record, add new record
56+
removeRunningRecordMediator.removeWithRecordAdd(runningRecord)
57+
} else {
58+
// Start running record
59+
addRunningRecordMediator.tryStartTimer(
60+
typeId = recordTypeId,
61+
onNeedToShowTagSelection = {
62+
showTagSelection(context, recordTypeId, it)
63+
},
64+
)
65+
}
66+
}
67+
68+
private fun showTagSelection(
69+
context: Context?,
70+
typeId: Long,
71+
result: RecordDataSelectionDialogResult,
72+
) {
73+
context ?: return
74+
75+
WidgetSingleTagSelectionActivity.getStartIntent(
76+
context = context,
77+
data = RecordTagSelectionParams(typeId, result.toParams()),
78+
).let(context::startActivity)
79+
}
80+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package com.example.util.simpletimetracker.feature_widget.grid
2+
3+
import android.app.PendingIntent
4+
import android.appwidget.AppWidgetManager
5+
import android.appwidget.AppWidgetProvider
6+
import android.content.Context
7+
import android.content.Intent
8+
import android.os.Bundle
9+
import android.widget.RemoteViews
10+
import com.example.util.simpletimetracker.core.extension.allowDiskRead
11+
import com.example.util.simpletimetracker.core.utils.PendingIntents
12+
import com.example.util.simpletimetracker.domain.widget.interactor.WidgetInteractor
13+
import com.example.util.simpletimetracker.domain.widget.model.WidgetType
14+
import com.example.util.simpletimetracker.feature_widget.R
15+
import com.example.util.simpletimetracker.feature_widget.common.WidgetTypeClickManager
16+
import dagger.hilt.android.AndroidEntryPoint
17+
import kotlinx.coroutines.MainScope
18+
import kotlinx.coroutines.launch
19+
import javax.inject.Inject
20+
21+
22+
@AndroidEntryPoint
23+
class WidgetGridProvider : AppWidgetProvider() {
24+
25+
@Inject
26+
lateinit var widgetTypeClickManager: WidgetTypeClickManager
27+
28+
@Inject
29+
lateinit var widgetInteractor: WidgetInteractor
30+
31+
// TODO WIDGET use WidgetViewsHolder
32+
// TODO WIDGET add preview
33+
// TODO WIDGET translate strings
34+
// TODO WIDGET show tag selection
35+
// TODO Lunch activity is not ticking
36+
37+
override fun onReceive(context: Context?, intent: Intent?) {
38+
super.onReceive(context, intent)
39+
if (intent?.action == ITEM_CLICK_ACTION) onClick(context, intent)
40+
}
41+
42+
override fun onUpdate(
43+
context: Context?,
44+
appWidgetManager: AppWidgetManager?,
45+
appWidgetIds: IntArray?,
46+
) {
47+
appWidgetIds?.forEach { widgetId ->
48+
updateAppWidget(context, appWidgetManager, widgetId)
49+
}
50+
}
51+
52+
override fun onAppWidgetOptionsChanged(
53+
context: Context?,
54+
appWidgetManager: AppWidgetManager?,
55+
appWidgetId: Int,
56+
newOptions: Bundle?,
57+
) {
58+
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
59+
updateAppWidget(context, appWidgetManager, appWidgetId)
60+
}
61+
62+
private fun updateAppWidget(
63+
context: Context?,
64+
appWidgetManager: AppWidgetManager?,
65+
appWidgetId: Int,
66+
) {
67+
if (context == null || appWidgetManager == null) return
68+
69+
val serviceIntent = Intent(context, WidgetGridRemoteViewsService::class.java)
70+
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
71+
72+
val views = RemoteViews(context.packageName, R.layout.widgets_layout)
73+
views.setRemoteAdapter(R.id.listWidgets, serviceIntent)
74+
// TODO WIDGET set empty view
75+
76+
// TODO WIDGET fillInIntent not working
77+
// val clickIntent = Intent(context, WidgetGridProvider::class.java)
78+
// clickIntent.action = ITEM_CLICK_ACTION
79+
// val clickPendingIntent = PendingIntent.getBroadcast(
80+
// context,
81+
// 0,
82+
// clickIntent,
83+
// PendingIntents.getFlags()
84+
// )
85+
// views.setPendingIntentTemplate(R.id.listWidgets, clickPendingIntent)
86+
87+
appWidgetManager.updateAppWidget(appWidgetId, views)
88+
// TODO WIDGET
89+
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.listWidgets)
90+
}
91+
92+
private fun onClick(
93+
context: Context?,
94+
intent: Intent,
95+
) {
96+
allowDiskRead { MainScope() }.launch {
97+
widgetTypeClickManager.onClick(
98+
context = context,
99+
recordTypeId = intent.getLongExtra(TYPE_ID_EXTRA, 0),
100+
onWidgetUpdate = { widgetInteractor.updateWidgets(WidgetType.GRID) }
101+
)
102+
}
103+
}
104+
105+
companion object {
106+
// TODO WIDGET
107+
const val TYPE_ID_EXTRA =
108+
"com.example.util.simpletimetracker.feature_widget.grid.typeIdExtra"
109+
const val ITEM_CLICK_ACTION =
110+
"com.yourdomain.app.ITEM_CLICK_ACTION"
111+
}
112+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.example.util.simpletimetracker.feature_widget.grid
2+
3+
import android.content.Intent
4+
import android.widget.RemoteViewsService
5+
import dagger.hilt.android.AndroidEntryPoint
6+
import javax.inject.Inject
7+
8+
@AndroidEntryPoint
9+
class WidgetGridRemoteViewsService : RemoteViewsService() {
10+
11+
@Inject
12+
lateinit var widgetGridRemoveViewsFactory: WidgetGridRemoveViewsFactory
13+
14+
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
15+
widgetGridRemoveViewsFactory.intent = intent
16+
return widgetGridRemoveViewsFactory
17+
}
18+
}

0 commit comments

Comments
 (0)