Skip to content
This repository has been archived by the owner on Jan 15, 2025. It is now read-only.

Commit

Permalink
added app icon/name, begun push notification implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
julius-b committed Dec 21, 2022
1 parent 5d5785e commit acb2d20
Show file tree
Hide file tree
Showing 32 changed files with 220 additions and 59 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ android/release/
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/assetWizardSettings.xml
.idea/libraries/
*.iws
*.iml
Expand Down
30 changes: 8 additions & 22 deletions android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,24 @@ version = "1.0-SNAPSHOT"

dependencies {
implementation(project(":common"))
implementation(compose.material)
implementation("androidx.activity:activity-compose:1.6.1")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.2.2")

implementation("com.squareup.sqldelight:runtime:1.5.4")
implementation("com.squareup.sqldelight:coroutines-extensions:1.5.4")

implementation("com.squareup.moshi:moshi:1.14.0")
implementation("com.squareup.moshi:moshi-kotlin:1.14.0")
implementation("com.squareup.moshi:moshi-adapters:1.14.0")
implementation("com.squareup.moshi:moshi-kotlin-codegen:1.14.0")

implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")

implementation("com.arkivanov.decompose:decompose:1.0.0-beta-01")
implementation("com.arkivanov.decompose:extensions-compose-jetbrains:1.0.0-beta-01")
implementation("com.arkivanov.essenty:parcelable:0.6.0")
implementation("com.arkivanov.essenty:lifecycle:0.6.0")
// common
implementation("com.arkivanov.decompose:decompose:1.0.0-beta-02")
implementation("com.arkivanov.decompose:extensions-compose-jetbrains:1.0.0-beta-02")
implementation("com.arkivanov.essenty:parcelable:0.7.0")
implementation("com.arkivanov.essenty:lifecycle:0.7.0")

implementation("com.arkivanov.mvikotlin:mvikotlin:3.0.2")
implementation("com.arkivanov.mvikotlin:mvikotlin-extensions-coroutines:3.0.2")
implementation("com.arkivanov.mvikotlin:mvikotlin-extensions-reaktive:3.0.2")
implementation("com.arkivanov.mvikotlin:rx:3.0.2") // Disposable

implementation("com.badoo.reaktive:reaktive:1.2.2")
implementation("com.badoo.reaktive:coroutines-interop:1.2.2")

// Android
implementation("com.arkivanov.mvikotlin:mvikotlin-logging:3.0.2")
implementation("com.arkivanov.mvikotlin:mvikotlin-timetravel:3.0.2")
implementation("com.github.UnifiedPush:android-connector:2.1.1")
implementation("androidx.activity:activity-compose:1.6.1")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.2.2")
}

android {
Expand Down
33 changes: 24 additions & 9 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,22 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="app.opia.android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- Theme.AppCompat.Light.NoActionBar -->
<!-- TODO disable usesCleartextTraffic -->
<application
android:allowBackup="false"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.Material3.Light.NoActionBar">
<activity android:name=".MainActivity" android:exported="true">
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".UnifiedPushReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="org.unifiedpush.android.connector.MESSAGE" />
<action android:name="org.unifiedpush.android.connector.UNREGISTERED" />
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED" />
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
</intent-filter>
</receiver>
</application>
</manifest>
Binary file added android/src/main/ic_launcher-playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions android/src/main/java/app/opia/android/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app.opia.android

import android.os.Bundle
import android.util.Log
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material.MaterialTheme
Expand All @@ -14,6 +15,8 @@ import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.defaultComponentContext
import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
import com.arkivanov.mvikotlin.timetravel.store.TimeTravelStoreFactory
import org.unifiedpush.android.connector.INSTANCE_DEFAULT
import org.unifiedpush.android.connector.UnifiedPush

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -28,6 +31,27 @@ class MainActivity : AppCompatActivity() {
}
}
}

registerUnifiedPush()
}

// TODO need user account id
private fun registerUnifiedPush() {
val currentDist = UnifiedPush.getDistributor(this)
if (currentDist.isNotEmpty()) {
Log.d(TAG, "registerUP > currentDist: $currentDist")
UnifiedPush.registerApp(this)
return
}
val distributors = UnifiedPush.getDistributors(this)
Log.d(TAG, "registerUP > distributors: $distributors")
if (distributors.isEmpty()) {
Log.e(TAG, "registerUP > no push notifications :[")
return
}
val userDistrib = distributors[0]
UnifiedPush.saveDistributor(this, userDistrib)
UnifiedPush.registerApp(this, INSTANCE_DEFAULT)
}

private fun opiaRoot(componentContext: ComponentContext): OpiaRoot = OpiaRootComponent(
Expand Down
51 changes: 51 additions & 0 deletions android/src/main/java/app/opia/android/Notifier.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package app.opia.android

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import java.util.concurrent.ThreadLocalRandom

class Notifier(var context: Context) {
private var gNM: NotificationManager? = null
private var channelId = "Opia-Android" // TODO per account

init {
channelId = context.packageName
createNotificationChannel()
gNM = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}

fun sendNotification(title: String, text: String, priority: Int) {
val notificationBuilder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(context, channelId)
} else {
Notification.Builder(context)
}

val notification = notificationBuilder.setSmallIcon(R.mipmap.ic_launcher).setTicker(text)
.setWhen(System.currentTimeMillis()).setContentTitle(title).setContentText(text)
.setPriority(priority).build()

gNM!!.notify(ThreadLocalRandom.current().nextInt(), notification)
}

private fun createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && channelId.isNotEmpty()) {
val name = context.packageName
val descriptionText = "Opia"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(channelId, name, importance).apply {
description = descriptionText
}

// Register the channel with the system
val notificationManager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
}
58 changes: 58 additions & 0 deletions android/src/main/java/app/opia/android/UnifiedPushReceiver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package app.opia.android

import android.content.Context
import android.content.Intent
import android.util.Log
import android.widget.Toast
import org.unifiedpush.android.connector.EXTRA_BYTES_MESSAGE
import org.unifiedpush.android.connector.MessagingReceiver
import org.unifiedpush.android.connector.UnifiedPush
import java.net.URLDecoder

const val TAG = "OpiaApp"
const val DEBUG = true

class UnifiedPushReceiver : MessagingReceiver() {
// instance: accountId/subscription?
override fun onMessage(context: Context, message: ByteArray, instance: String) {
Log.d(TAG, "UPReceiver [$instance] > message: $message")
val dict = URLDecoder.decode(String(message), "UTF-8").split("&")
val params = dict.associate {
try {
it.split("=")[0] to it.split("=")[1]
} catch (e: Exception) {
"" to ""
}
}
val text = params["message"] ?: "New notification"
val priority = params["priority"]?.toInt() ?: 8
val title = params["title"] ?: context.getString(R.string.app_name)
Notifier(context).sendNotification(title, text, priority)
}

override fun onNewEndpoint(context: Context, endpoint: String, instance: String) {
Log.d(TAG, "UPReceiver [$instance] > new endpoint: $endpoint - syncing...")
}

override fun onRegistrationFailed(context: Context, instance: String) {
Log.e(TAG, "UPReceiver [$instance] > registration failed")
Toast.makeText(context, "Registration Failed", Toast.LENGTH_SHORT).show()
UnifiedPush.forceRemoveDistributor(context)
}

override fun onUnregistered(context: Context, instance: String) {
Log.w(TAG, "UPReceiver [$instance] > unregistered")
val appName = context.getString(R.string.app_name)
Toast.makeText(context, "$appName is unregistered", Toast.LENGTH_SHORT).show()
}

override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "UPReceiver > event received")
if (DEBUG) {
Log.d(TAG,
"raw bytes: " + intent.getByteArrayExtra(EXTRA_BYTES_MESSAGE)
?.joinToString("") { byte -> "%02x".format(byte) })
}
super.onReceive(context, intent)
}
}
5 changes: 5 additions & 0 deletions android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
5 changes: 5 additions & 0 deletions android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Binary file added android/src/main/res/mipmap-hdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added android/src/main/res/mipmap-mdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added android/src/main/res/mipmap-xhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions android/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#010E44</color>
<color name="colorPrimaryDark">#20212C</color>
<color name="colorAccent">#5BB6CB</color>
</resources>
4 changes: 4 additions & 0 deletions android/src/main/res/values/ic_launcher_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#82A3A9</color>
</resources>
4 changes: 4 additions & 0 deletions android/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">Opia</string>
</resources>
10 changes: 10 additions & 0 deletions android/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<!-- Theme.AppCompat.Light.NoActionBar -->
<style name="AppTheme" parent="Theme.Material3.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ allprojects {
mavenCentral()
mavenLocal()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
maven(url = uri("https://jitpack.io"))
}

tasks.withType<KotlinCompile>() {
Expand Down
18 changes: 9 additions & 9 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ kotlin {
api(compose.foundation)
api(compose.material)
api(compose.materialIconsExtended)
api(compose.ui)
api("androidx.appcompat:appcompat:1.5.1")
implementation("androidx.compose.ui:ui:1.3.1")
implementation("androidx.compose.ui:ui-text:1.3.1")
api("androidx.compose.ui:ui-text:1.3.2")

implementation("com.squareup.sqldelight:runtime:1.5.4")
implementation("com.squareup.sqldelight:coroutines-extensions:1.5.4")
Expand All @@ -45,10 +45,10 @@ kotlin {
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")

implementation("com.arkivanov.decompose:decompose:1.0.0-beta-01")
implementation("com.arkivanov.decompose:extensions-compose-jetbrains:1.0.0-beta-01")
implementation("com.arkivanov.essenty:parcelable:0.6.0")
implementation("com.arkivanov.essenty:lifecycle:0.6.0")
implementation("com.arkivanov.decompose:decompose:1.0.0-beta-02")
implementation("com.arkivanov.decompose:extensions-compose-jetbrains:1.0.0-beta-02")
implementation("com.arkivanov.essenty:parcelable:0.7.0")
implementation("com.arkivanov.essenty:lifecycle:0.7.0")

implementation("com.arkivanov.mvikotlin:mvikotlin:3.0.2")
implementation("com.arkivanov.mvikotlin:mvikotlin-extensions-coroutines:3.0.2")
Expand All @@ -58,8 +58,8 @@ kotlin {
implementation("com.badoo.reaktive:reaktive:1.2.2")
implementation("com.badoo.reaktive:coroutines-interop:1.2.2")

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.6.4") // coroutines Main
implementation("com.michael-bull.kotlin-result:kotlin-result:1.1.16") // Result type
implementation("com.russhwolf:multiplatform-settings:1.0.0-RC")

implementation("ch.oxc.nikea:nikea-kt:1.0-SNAPSHOT")
}
Expand Down Expand Up @@ -88,7 +88,7 @@ kotlin {
api(compose.preview)
implementation("com.squareup.sqldelight:sqlite-driver:1.5.4")

runtimeOnly("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.6.4") // coroutines Main
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.6.4") // coroutines Main
implementation("com.arkivanov.mvikotlin:mvikotlin-main:3.0.2") // DefaultStoreFactory
}
}
Expand All @@ -98,7 +98,7 @@ kotlin {

android {
// NOTE: needs to be different from actual android to prevent 'Type app.opia.android.Buildconfig is defined multiple times'
namespace = "app.opia"
namespace = "app.opia.common"
compileSdk = 33
buildToolsVersion = "30.0.3"

Expand Down
Loading

0 comments on commit acb2d20

Please sign in to comment.