Skip to content

Commit f86956f

Browse files
committed
feat(app): add UnifiedPush support for github flavor
1 parent f080149 commit f86956f

16 files changed

Lines changed: 506 additions & 18 deletions

File tree

android/core/src/main/kotlin/co/anitrend/android/core/koin/Modules.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import co.anitrend.data.settings.feature.IFeatureFlagSetting
5252
import co.anitrend.data.settings.notification.INotificationSettings
5353
import co.anitrend.data.settings.power.IPowerSettings
5454
import co.anitrend.data.settings.privacy.IPrivacySettings
55+
import co.anitrend.data.settings.push.IUnifiedPushSettings
5556
import co.anitrend.data.settings.refresh.IRefreshBehaviourSettings
5657
import co.anitrend.data.settings.sort.ISortOrderSettings
5758
import co.anitrend.data.settings.sync.ISyncSettings
@@ -82,6 +83,7 @@ private val coreModule =
8283
IPowerSettings::class,
8384
IConnectivitySettings::class,
8485
INotificationSettings::class,
86+
IUnifiedPushSettings::class,
8587
IUserSettings::class,
8688
ICacheSettings::class,
8789
ISyncSettings::class,

android/core/src/main/kotlin/co/anitrend/android/core/settings/Settings.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import co.anitrend.arch.extension.settings.EnumSetting
2727
import co.anitrend.arch.extension.settings.FloatSetting
2828
import co.anitrend.arch.extension.settings.IntSetting
2929
import co.anitrend.arch.extension.settings.LongSetting
30+
import co.anitrend.arch.extension.settings.NullableStringSetting
3031
import co.anitrend.arch.extension.settings.SetSetting
3132
import co.anitrend.data.auth.settings.IAuthenticationSettings
3233
import co.anitrend.data.auth.settings.IAuthenticationSettings.Companion.INVALID_USER_ID
@@ -40,6 +41,7 @@ import co.anitrend.data.settings.feature.IFeatureFlagSetting
4041
import co.anitrend.data.settings.notification.INotificationSettings
4142
import co.anitrend.data.settings.power.IPowerSettings
4243
import co.anitrend.data.settings.privacy.IPrivacySettings
44+
import co.anitrend.data.settings.push.IUnifiedPushSettings
4345
import co.anitrend.data.settings.refresh.IRefreshBehaviourSettings
4446
import co.anitrend.data.settings.sort.ISortOrderSettings
4547
import co.anitrend.data.settings.sync.ISyncSettings
@@ -64,7 +66,8 @@ class Settings(
6466
ICacheSettings,
6567
ISyncSettings,
6668
IDeveloperSettings,
67-
IFeatureFlagSetting {
69+
IFeatureFlagSetting,
70+
IUnifiedPushSettings {
6871
override val locale =
6972
EnumSetting(
7073
key = R.string.settings_configuration_locale,
@@ -193,6 +196,22 @@ class Settings(
193196
preference = this,
194197
)
195198

199+
override val unifiedPushEndpoint =
200+
NullableStringSetting(
201+
key = R.string.settings_notifications_unifiedpush_endpoint,
202+
default = null,
203+
resources = context.resources,
204+
preference = this,
205+
)
206+
207+
override val unifiedPushInstance =
208+
NullableStringSetting(
209+
key = R.string.settings_notifications_unifiedpush_instance,
210+
default = null,
211+
resources = context.resources,
212+
preference = this,
213+
)
214+
196215
override val titleLanguage =
197216
EnumSetting(
198217
key = R.string.settings_user_title_language,

android/core/src/main/res/values/settings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
<string name="settings_notifications_enabled" translatable="false">_notificationsEnabled</string>
5151
<string name="settings_notifications_anilist_enabled" translatable="false">_notificationsAniListEnabled</string>
5252
<string name="settings_notifications_announcements_enabled" translatable="false">_notificationsAnnouncementsEnabled</string>
53+
<string name="settings_notifications_unifiedpush_endpoint" translatable="false">_notificationsUnifiedPushEndpoint</string>
54+
<string name="settings_notifications_unifiedpush_instance" translatable="false">_notificationsUnifiedPushInstance</string>
5355

5456
<string name="settings_auto_heap_dump" translatable="false">_autoHeapDump</string>
5557
<string name="settings_show_leak_launcher" translatable="false">_showLeakLauncher</string>

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ dependencies {
2424
/** Material Design */
2525
implementation(libs.google.android.material)
2626

27+
githubImplementation(libs.unifiedpush.connector)
2728
googleImplementation(libs.google.android.gsm.playServicesOssLicenses)
2829
googleImplementation(libs.google.firebase.messaging.ktx)
2930
}

app/core/src/main/res/xml/backup_descriptor.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,8 @@
1919
<full-backup-content>
2020
<!-- https://developer.android.com/guide/topics/data/autobackup#XMLSyntax -->
2121
<!-- Exclude specific shared preferences that contain GCM registration Id -->
22-
<include domain="sharedpref" path="."/>
22+
<include domain="sharedpref" path="." />
23+
<exclude domain="database" path="unifiedpush-connector" />
24+
<exclude domain="database" path="unifiedpush-connector-journal" />
25+
<exclude domain="sharedpref" path="unifiedpush.connector.xml" />
2326
</full-backup-content>

app/src/github/AndroidManifest.xml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,33 @@
2323

2424
<application
2525
android:allowBackup="false"
26-
tools:replace="android:allowBackup" />
26+
tools:replace="android:allowBackup">
27+
28+
<service
29+
android:name=".service.ForegroundService"
30+
android:exported="true">
31+
<intent-filter>
32+
<action android:name="org.unifiedpush.android.connector.RAISE_TO_FOREGROUND" />
33+
</intent-filter>
34+
</service>
35+
36+
<service
37+
android:name=".service.MessagingService"
38+
android:exported="false">
39+
<intent-filter>
40+
<action android:name="org.unifiedpush.android.connector.PUSH_EVENT" />
41+
</intent-filter>
42+
</service>
43+
44+
<provider
45+
android:name="androidx.startup.InitializationProvider"
46+
android:authorities="${applicationId}.androidx-startup"
47+
android:exported="false"
48+
tools:node="merge">
49+
<meta-data
50+
android:name="co.anitrend.initializer.GithubApplicationInitializer"
51+
android:value="androidx.startup" />
52+
</provider>
53+
</application>
2754

2855
</manifest>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package co.anitrend.initializer
2+
3+
import android.content.Context
4+
import androidx.startup.Initializer
5+
import co.anitrend.android.core.koinOf
6+
import co.anitrend.core.initializer.contract.AbstractCoreInitializer
7+
import co.anitrend.core.koin.helper.DynamicFeatureModuleHelper.Companion.loadModules
8+
import co.anitrend.koin.githubAppModules
9+
import co.anitrend.push.PushRegistrationCoordinator
10+
11+
class GithubApplicationInitializer : AbstractCoreInitializer<Unit>() {
12+
/**
13+
* @return A list of dependencies that this [Initializer] depends on. This is
14+
* used to determine initialization order of [Initializer]s.
15+
*
16+
* For e.g. if a [Initializer] `B` defines another [Initializer] `A` as its dependency,
17+
* then `A` gets initialized before `B`.
18+
*/
19+
override fun dependencies(): List<Class<out Initializer<*>>> = listOf(ApplicationInitializer::class.java)
20+
21+
override fun create(context: Context) {
22+
githubAppModules.loadModules()
23+
koinOf<PushRegistrationCoordinator>()
24+
}
25+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package co.anitrend.koin
2+
3+
import co.anitrend.core.R
4+
import co.anitrend.core.koin.helper.DynamicFeatureModuleHelper
5+
import co.anitrend.push.PushRegistrationCoordinator
6+
import co.anitrend.push.UnifiedPushConnector
7+
import org.koin.android.ext.koin.androidApplication
8+
import org.koin.android.ext.koin.androidContext
9+
import org.koin.dsl.module
10+
11+
12+
private val variantModule =
13+
module(createdAtStart = true) {
14+
factory {
15+
UnifiedPushConnector(
16+
context = androidApplication(),
17+
)
18+
}
19+
factory {
20+
PushRegistrationCoordinator(
21+
connector = get(),
22+
messageForDistributor =
23+
androidContext().getString(R.string.application_name),
24+
)
25+
}
26+
}
27+
28+
internal val githubAppModules =
29+
DynamicFeatureModuleHelper(
30+
listOf(variantModule),
31+
)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (C) 2026 AniTrend
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
package co.anitrend.push
18+
19+
import android.content.Context
20+
import org.unifiedpush.android.connector.UnifiedPush
21+
22+
internal class UnifiedPushConnector(
23+
private val context: Context,
24+
) : PushConnector {
25+
override val acknowledgedDistributor: String?
26+
get() = UnifiedPush.getAckDistributor(context)
27+
28+
override val savedDistributor: String?
29+
get() = UnifiedPush.getSavedDistributor(context)
30+
31+
override val availableDistributors: List<String>
32+
get() = UnifiedPush.getDistributors(context)
33+
34+
override fun saveDistributor(distributor: String) {
35+
UnifiedPush.saveDistributor(context, distributor)
36+
}
37+
38+
override fun register(messageForDistributor: String) {
39+
UnifiedPush.register(
40+
context = context,
41+
messageForDistributor = messageForDistributor,
42+
)
43+
}
44+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (C) 2026 AniTrend
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
package co.anitrend.service
18+
19+
import android.app.Service
20+
import android.content.Intent
21+
import android.os.Binder
22+
import android.os.IBinder
23+
24+
class ForegroundService : Service() {
25+
private val binder = Binder()
26+
27+
override fun onBind(intent: Intent): IBinder = binder
28+
}

0 commit comments

Comments
 (0)