Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for custom serializer #179

Merged
merged 9 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ dependencies {
Don't know how to do that?? Take a look at
the [application class example](./app/src/main/java/io/wax911/emojifysample/App.kt)

**In order for EmojiInitilizer to work you need to add in your build.gradle `implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2"`

wax911 marked this conversation as resolved.
Show resolved Hide resolved
```kotlin
class App : Application() {

Expand All @@ -193,6 +195,60 @@ class App : Application() {
}
```

### Optional - Custom Serializer

`EmojiInitializer` is using kotlinx.serialization as default serializer. If you wish to use another serializer

#### Step 1. Create Custom EmojiInitializer and implement IEmojiDeserializer (moshi for this example)
```kotlin
class CustomEmojiInitializer: AEmojiInitializer() {
class MoshiDeserializer: IEmojiDeserializer {
private val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build()

override fun decodeFromStream(inputStream: InputStream): List<Emoji> {
val myType = Types.newParameterizedType(List::class.java, Emoji::class.java)
return moshi.adapter<List<Emoji>>(myType).fromJson(inputStream.source().buffer()) ?: listOf()
}
}

override val serializer: IEmojiDeserializer = MoshiDeserializer()
}
```

#### Step 2. Modify your application class
```kotlin
class App : Application() {

/**
* Application scope bound emojiManager, you could keep a reference to this object in a
* dependency injector framework like as a singleton in `Hilt`, `Dagger` or `Koin`
*/
internal val emojiManager: EmojiManager by lazy {
// should already be initialized if we haven't disabled initialization in manifest
// see: https://developer.android.com/topic/libraries/app-startup#disable-individual
AppInitializer.getInstance(this)
.initializeComponent(CustomEmojiInitializer::class.java)
}
}
```

#### Step 3. Modify AndroidManifest.xml of your application
```xml
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.package.CustomEmojiInitializer"
android:value="androidx.startup" />
<meta-data
android:name="io.wax911.emojify.initializer.EmojiInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
```

## Screenshots

<img src="https://github.com/wax911/android-emojify/raw/master/screenshots/device-2017-09-25-155538.png" width="365px"/> <img src="https://github.com/wax911/android-emojify/raw/master/screenshots/device-2017-09-25-155600.png" width="365px"/> <img src="https://github.com/wax911/android-emojify/raw/master/screenshots/device-2017-09-25-155617.png" width="365px"/> <img src="https://github.com/wax911/android-emojify/raw/master/screenshots/device-2017-09-25-155644.png" width="365px"/>
Expand Down
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ dependencies {
implementation(libs.google.android.material)
implementation(libs.androidx.constraintlayout)

implementation(libs.moshi.kotlin)

implementation(libs.jetbrains.kotlinx.coroutines.android)
implementation(libs.jetbrains.kotlinx.coroutines.core)

Expand Down
14 changes: 14 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<!-- <provider-->
<!-- android:name="androidx.startup.InitializationProvider"-->
<!-- android:authorities="${applicationId}.androidx-startup"-->
<!-- android:exported="false"-->
<!-- tools:node="merge">-->
<!-- <meta-data-->
<!-- android:name="io.wax911.emojifysample.CustomEmojiInitializer"-->
<!-- android:value="androidx.startup" />-->
<!-- <meta-data-->
<!-- android:name="io.wax911.emojify.initializer.EmojiInitializer"-->
<!-- android:value="androidx.startup"-->
<!-- tools:node="remove" />-->
<!-- </provider>-->
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.wax911.emojifysample

import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import io.wax911.emojify.initializer.AEmojiInitializer
import io.wax911.emojify.initializer.IEmojiDeserializer
import io.wax911.emojify.model.Emoji
import okio.buffer
import okio.source
import java.io.InputStream

class CustomEmojiInitializer: AEmojiInitializer() {
class MoshiDeserializer: IEmojiDeserializer {
private val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build()

override fun decodeFromStream(inputStream: InputStream): List<Emoji> {
val myType = Types.newParameterizedType(List::class.java, Emoji::class.java)
return moshi.adapter<List<Emoji>>(myType).fromJson(inputStream.source().buffer()) ?: listOf()
}
}

override val serializer: IEmojiDeserializer = MoshiDeserializer()
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private fun DefaultConfig.applyAdditionalConfiguration(project: Project) {
internal fun Project.configureAndroid(): Unit = baseExtension().run {
compileSdkVersion(34)
defaultConfig {
minSdk = 23
minSdk = 21
targetSdk = 34
versionCode = props[PropertyTypes.CODE].toInt()
versionName = props[PropertyTypes.VERSION]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2024 AniTrend
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.wax911.emojify.deserializer

import io.wax911.emojify.initializer.IEmojiDeserializer
import io.wax911.emojify.model.Emoji
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import java.io.InputStream

class KotlinxDeserializer : IEmojiDeserializer {
private val json = Json { isLenient = true }

override fun decodeFromStream(inputStream: InputStream): List<Emoji> {
val deserializer = ListSerializer(Emoji.serializer())
return json.decodeFromStream(deserializer, inputStream)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2021 AniTrend
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.wax911.emojify.initializer

import android.content.Context
import android.content.res.AssetManager
import androidx.startup.Initializer
import io.wax911.emojify.EmojiManager
import io.wax911.emojify.model.Emoji
import kotlinx.serialization.SerializationException
import java.io.IOException

abstract class AEmojiInitializer : Initializer<EmojiManager> {
abstract val serializer: IEmojiDeserializer

/**
* Initializes emoji objects from an asset file in the library directory
*
* @param assetManager provide an assert manager
* @param path location where emoji data can be found
*
* @throws IOException when the provided [assetManager] cannot open [path]
* @throws SerializationException when an error occurs during deserialization
*/
@Throws(IOException::class, SerializationException::class)
fun initEmojiData(
assetManager: AssetManager,
path: String = DEFAULT_PATH,
): List<Emoji> {
return assetManager.open(path).use { inputStream ->
serializer.decodeFromStream(inputStream)
}
}

/**
* Initializes and a component given the application [Context]
*
* @param context The application context.
*/
override fun create(context: Context): EmojiManager {
val emojiManagerDefault = EmojiManager(emptyList())
val result =
runCatching {
val emojis = initEmojiData(context.assets)
EmojiManager(emojis)
}.onFailure { it.printStackTrace() }
return result.getOrNull() ?: emojiManagerDefault
}

/**
* @return A list of dependencies that this [Initializer] depends on. This is
* used to determine initialization order of [Initializer]s.
*
* For e.g. if a [Initializer] `B` defines another
* [Initializer] `A` as its dependency, then `A` gets initialized before `B`.
*/
override fun dependencies() = emptyList<Class<out Initializer<*>>>()

companion object {
/**
* Default location with assets where emojis can be found
*/
internal const val DEFAULT_PATH = "emoticons/emoji.json"
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 AniTrend
* Copyright 2024 AniTrend
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,67 +16,11 @@

package io.wax911.emojify.initializer

import android.content.Context
import android.content.res.AssetManager
import androidx.startup.Initializer
import io.wax911.emojify.EmojiManager
import io.wax911.emojify.model.Emoji
import kotlinx.serialization.SerializationException
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import java.io.IOException
import io.wax911.emojify.deserializer.KotlinxDeserializer

class EmojiInitializer : Initializer<EmojiManager> {
class EmojiInitializer : AEmojiInitializer() {
/**
* Initializes emoji objects from an asset file in the library directory
*
* @param assetManager provide an assert manager
* @param path location where emoji data can be found
*
* @throws IOException when the provided [assetManager] cannot open [path]
* @throws SerializationException when an error occurs during deserialization
* Kotlinx implementation is needed in your project for this to work
*/
@Throws(IOException::class, SerializationException::class)
fun initEmojiData(
assetManager: AssetManager,
path: String = DEFAULT_PATH,
): List<Emoji> {
assetManager.open(path).use { inputStream ->
val json = Json { isLenient = true }
val deserializer = ListSerializer(Emoji.serializer())
return json.decodeFromStream(deserializer, inputStream)
}
}

/**
* Initializes and a component given the application [Context]
*
* @param context The application context.
*/
override fun create(context: Context): EmojiManager {
val emojiManagerDefault = EmojiManager(emptyList())
val result =
runCatching {
val emojis = initEmojiData(context.assets)
EmojiManager(emojis)
}.onFailure { it.printStackTrace() }
return result.getOrNull() ?: emojiManagerDefault
}

/**
* @return A list of dependencies that this [Initializer] depends on. This is
* used to determine initialization order of [Initializer]s.
*
* For e.g. if a [Initializer] `B` defines another
* [Initializer] `A` as its dependency, then `A` gets initialized before `B`.
*/
override fun dependencies() = emptyList<Class<out Initializer<*>>>()

companion object {
/**
* Default location with assets where emojis can be found
*/
internal const val DEFAULT_PATH = "emoticons/emoji.json"
}
override val serializer: IEmojiDeserializer = KotlinxDeserializer()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2024 AniTrend
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.wax911.emojify.initializer

import io.wax911.emojify.model.Emoji
import java.io.InputStream

interface IEmojiDeserializer {
/**
* Decodes the given [InputStream] to an object of type List<[Emoji]>
*/
fun decodeFromStream(inputStream: InputStream): List<Emoji>
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ jetbrains-kotlinx-datetime = "0.5.0"
jetbrains-kotlinx-serialization = "1.6.2"

mockk = "1.13.9"
moshi-kotlin = "1.15.0"


[plugins]
gradle-versions = { id = "com.github.ben-manes.versions", version = "0.51.0" }


[libraries]
moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi-kotlin" }
timber = { module = "com.jakewharton.timber:timber", version = "5.0.1" }
junit = { module = "junit:junit", version = "4.13.2" }

Expand Down
4 changes: 2 additions & 2 deletions gradle/version.properties
yoobi marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version=1.7.1
code=1007001000
version=1.8.0
code=1008000000
Loading