-Search and discover BTTV emotes to add to your Gboard stickers and use them anywhere
+Search and discover BTTV emotes to add to your keyboard stickers and use them anywhere
## Features
- Sort emotes by top, trending, shared or global
- Search emotes by name
-- Collect multiple emotes into a Gboard sticker pack
+- Collect multiple emotes into a keyboard sticker pack
- Use the sticker pack to add the image of an emote anywhere that supports images (WhatsApp, Messenger, Telegram...)
## Screenshots
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 486e9b2..f89cdb0 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -23,6 +23,7 @@ if (flutterVersionName == null) {
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
apply plugin: 'com.google.gms.google-services'
@@ -83,5 +84,6 @@ flutter {
dependencies {
implementation platform("com.google.firebase:firebase-bom:26.5.0")
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation "com.google.firebase:firebase-appindexing"
+ implementation 'com.github.bumptech.glide:glide:4.12.0'
+ kapt 'com.github.bumptech.glide:compiler:4.12.0'
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index e3165a6..a8b9038 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -6,10 +6,25 @@
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
- if (call.method == "generatePack") {
- val intent = Intent(baseContext, StickerIndexingService::class.java)
- intent.putExtra("emotes", call.arguments as ArrayList>)
- startService(intent)
- }
- }
- }
}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/drakota/bttvstickers/PackAdapter.kt b/android/app/src/main/kotlin/com/drakota/bttvstickers/PackAdapter.kt
new file mode 100644
index 0000000..76b8b3e
--- /dev/null
+++ b/android/app/src/main/kotlin/com/drakota/bttvstickers/PackAdapter.kt
@@ -0,0 +1,41 @@
+package com.drakota.bttvstickers
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseAdapter
+import android.widget.ImageView
+import com.bumptech.glide.Glide
+import org.json.JSONArray
+import org.json.JSONObject
+
+
+class PackAdapter(var context: Context, var emotes: JSONArray) : BaseAdapter() {
+ private val inflator: LayoutInflater = LayoutInflater.from(context)
+
+ override fun getCount(): Int {
+ return emotes.length()
+ }
+
+ override fun getItem(position: Int): JSONObject {
+ return emotes.get(position) as JSONObject
+ }
+
+ override fun getItemId(position: Int): Long {
+ return position.toLong()
+ }
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val emote = emotes.get(position) as JSONObject
+ val view: View
+ if (convertView == null) {
+ view = inflator.inflate(R.layout.emote, parent, false)
+ } else {
+ view = convertView
+ }
+ val imageView = view.findViewById(R.id.EmoteImage)
+ val url = emote.getString("imageUrl")
+ Glide.with(context).load(url).into(imageView)
+ return view
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/drakota/bttvstickers/StickerIndexingService.kt b/android/app/src/main/kotlin/com/drakota/bttvstickers/StickerIndexingService.kt
deleted file mode 100644
index 54b25b3..0000000
--- a/android/app/src/main/kotlin/com/drakota/bttvstickers/StickerIndexingService.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.drakota.bttvstickers
-
-import android.app.IntentService
-import android.content.Intent
-import android.net.Uri
-import android.util.Log
-import com.google.firebase.appindexing.FirebaseAppIndex
-import com.google.firebase.appindexing.FirebaseAppIndexingInvalidArgumentException
-import com.google.firebase.appindexing.Indexable
-import com.google.firebase.appindexing.builders.Indexables
-import com.google.firebase.appindexing.builders.StickerBuilder
-import java.io.IOException
-
-class StickerIndexingService() : IntentService("StickerIndexingService") {
- private val TAG = "StickerIndexingService"
- private val STICKER_PACK_NAME = "BTTV Stickers"
- private val STICKER_PACK_URL = "bttvstickers://emote/pack/bttvstickers"
- private val STICKER_PACK_IMAGE = "android.resource://com.drakota.bttvstickers/" + R.mipmap.ic_launcher
- private val STICKER_URL_PATTERN = "bttvstickers://emote/%s"
-
- override fun onHandleIntent(intent: Intent?) {
- try {
- clearStickerPack()
- val emotes = intent?.getSerializableExtra("emotes")!! as ArrayList>
- if (emotes.isEmpty()) {
- // If we are clearing the pack, just clear it and return
- return
- }
- val stickers = getIndexableStickers(emotes)
- val stickerPack = getIndexableStickerPack(emotes)
- val indexables = ArrayList(stickers)
- indexables.add(stickerPack)
-
- val task = FirebaseAppIndex
- .getInstance(applicationContext)
- .update(*indexables.toTypedArray())
-
- task.addOnFailureListener { e ->
- Log.d(TAG, "Failed to install stickers", e)
- }
- } catch (e: IOException) {
- Log.e(TAG, "Unable to set stickers", e)
- } catch (e: FirebaseAppIndexingInvalidArgumentException) {
- Log.e(TAG, "Unable to set stickers", e)
- }
- }
-
- private fun getIndexableStickers(emotes: ArrayList>): List {
- val stickerBuilders = getStickerBuilders(emotes)
- return stickerBuilders.map { it.build() }
- }
-
- private fun getStickerBuilders(emotes: ArrayList>): List {
- val stickerBuilders = arrayListOf()
-
- for (emote in emotes) {
- val stickerBuilder = Indexables.stickerBuilder()
- .setName(emote["code"])
- .setUrl(String.format(STICKER_URL_PATTERN, emote["id"]))
- .setImage(emote["imageUrl"])
- .setIsPartOf(Indexables.stickerPackBuilder()
- .setName(STICKER_PACK_NAME)
- .setUrl(STICKER_PACK_URL))
- stickerBuilders.add(stickerBuilder)
- }
-
- return stickerBuilders
- }
-
- private fun getIndexableStickerPack(emotes: ArrayList>): Indexable {
- val packImageUri = Uri.parse(STICKER_PACK_IMAGE).toString()
- val stickerBuilders = getStickerBuilders(emotes)
- return Indexables.stickerPackBuilder()
- .setName(STICKER_PACK_NAME)
- .setUrl(STICKER_PACK_URL)
- .setImage(packImageUri)
- .setHasSticker(*stickerBuilders.toTypedArray())
- .build()
- }
-
- private fun clearStickerPack() {
- val task = FirebaseAppIndex
- .getInstance(applicationContext)
- .removeAll()
-
- task.addOnFailureListener { e ->
- Log.d(TAG, "Failed to clear stickers", e)
- }
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/drakota/bttvstickers/StickerInputMethod.kt b/android/app/src/main/kotlin/com/drakota/bttvstickers/StickerInputMethod.kt
new file mode 100644
index 0000000..baa4824
--- /dev/null
+++ b/android/app/src/main/kotlin/com/drakota/bttvstickers/StickerInputMethod.kt
@@ -0,0 +1,84 @@
+package com.drakota.bttvstickers
+
+import android.content.ClipDescription
+import android.content.Context
+import android.inputmethodservice.InputMethodService
+import android.net.Uri
+import android.os.Build
+import android.os.Vibrator
+import android.view.View
+import android.widget.GridView
+import androidx.core.content.FileProvider
+import androidx.core.view.inputmethod.InputConnectionCompat
+import androidx.core.view.inputmethod.InputContentInfoCompat
+import com.bumptech.glide.Glide
+import org.json.JSONObject
+import java.io.File
+
+
+class StickerInputMethod : InputMethodService() {
+ override fun onCreate() {
+ super.onCreate()
+ }
+
+ private fun send(emote: JSONObject) {
+ val future = Glide
+ .with(applicationContext)
+ .downloadOnly()
+ .load(emote.getString("imageUrl"))
+ .submit()
+
+ Thread {
+ val temp = future.get()
+ val cacheFile = File(applicationContext.externalCacheDir, "cached_sticker")
+ temp.copyTo(cacheFile, true)
+
+ val contentUri = if (Build.VERSION.SDK_INT >= 24)
+ FileProvider.getUriForFile(
+ applicationContext,
+ applicationContext.packageName + ".fileProvider",
+ cacheFile
+ )
+ else
+ Uri.fromFile(cacheFile)
+
+ commitImage(emote, contentUri)
+ }.start()
+ }
+
+ private fun commitImage(emote: JSONObject, contentUri: Uri) {
+ val mimeType = "image/" + emote.getString("imageType")
+ val inputContentInfo = InputContentInfoCompat(
+ contentUri,
+ ClipDescription(emote.getString("code"), arrayOf(mimeType)),
+ null
+ )
+ val inputConnection = currentInputConnection
+ val editorInfo = currentInputEditorInfo
+ var flags = 0
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
+ flags = flags or InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION
+ }
+ InputConnectionCompat.commitContent(inputConnection, editorInfo, inputContentInfo, flags, null)
+ }
+
+ override fun onCreateInputView(): View? {
+ val vibratorService = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+ val root: View = layoutInflater.inflate(R.layout.keyboard, null)
+ val emoteList: GridView = root.findViewById(R.id.EmoteList)
+ val directoryName = "/data/user/0/com.drakota.bttvstickers/app_flutter/pack.json"
+
+ val pack = JSONObject(File(directoryName).readText(Charsets.UTF_8))
+ val emotes = pack.getJSONArray("emotes")
+
+ val customAdapter = PackAdapter(applicationContext, emotes)
+ emoteList.adapter = customAdapter
+ emoteList.setOnItemClickListener { _, _, position, _ ->
+ val selectedEmote = emotes.get(position) as JSONObject
+ send(selectedEmote)
+ Utils.performHapticFeedback(applicationContext, 15)
+ }
+
+ return root
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/drakota/bttvstickers/Utils.kt b/android/app/src/main/kotlin/com/drakota/bttvstickers/Utils.kt
new file mode 100644
index 0000000..6e3dc39
--- /dev/null
+++ b/android/app/src/main/kotlin/com/drakota/bttvstickers/Utils.kt
@@ -0,0 +1,22 @@
+package com.drakota.bttvstickers
+
+import android.os.Build
+import android.os.VibrationEffect
+import android.os.Vibrator
+import android.content.Context
+
+class Utils {
+ companion object {
+ fun performHapticFeedback(context: Context, ms: Long) {
+ val vibratorService = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+ vibratorService.let { vs ->
+ if (Build.VERSION.SDK_INT >= 26) {
+ vs.vibrate(VibrationEffect.createOneShot(ms, VibrationEffect.DEFAULT_AMPLITUDE))
+ } else {
+ @Suppress("DEPRECATION")
+ vs.vibrate(ms)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/rounded_corners.xml b/android/app/src/main/res/drawable/rounded_corners.xml
new file mode 100644
index 0000000..7e498e2
--- /dev/null
+++ b/android/app/src/main/res/drawable/rounded_corners.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/emote.xml b/android/app/src/main/res/layout/emote.xml
new file mode 100644
index 0000000..58727c5
--- /dev/null
+++ b/android/app/src/main/res/layout/emote.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/keyboard.xml b/android/app/src/main/res/layout/keyboard.xml
new file mode 100644
index 0000000..945e4f2
--- /dev/null
+++ b/android/app/src/main/res/layout/keyboard.xml
@@ -0,0 +1,15 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/xml/method.xml b/android/app/src/main/res/xml/method.xml
new file mode 100644
index 0000000..d7e2014
--- /dev/null
+++ b/android/app/src/main/res/xml/method.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/xml/provider_paths.xml b/android/app/src/main/res/xml/provider_paths.xml
new file mode 100644
index 0000000..bfd5267
--- /dev/null
+++ b/android/app/src/main/res/xml/provider_paths.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/build.gradle b/android/build.gradle
index e3e354a..484b399 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -3,6 +3,7 @@ buildscript {
repositories {
google()
jcenter()
+ mavenCentral()
}
dependencies {
@@ -16,6 +17,7 @@ allprojects {
repositories {
google()
jcenter()
+ mavenCentral()
}
}
diff --git a/lib/utils/file_helpers.dart b/lib/utils/file_helpers.dart
index 9ac1775..1d42461 100644
--- a/lib/utils/file_helpers.dart
+++ b/lib/utils/file_helpers.dart
@@ -6,6 +6,7 @@ import 'package:path_provider/path_provider.dart';
Future get _localPath async {
final directory = await getApplicationDocumentsDirectory();
+ print(directory);
return directory.path;
}
diff --git a/pubspec.yaml b/pubspec.yaml
index b83c828..b8058b2 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: bttvstickers
-description: Use any BetterTTV emotes in messaging apps with Gboard stickers.
+description: Use any BetterTTV emotes in messaging apps with stickers.
# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
From 6cdb28b820f4f00b84f7f70d77854bffbc2827d6 Mon Sep 17 00:00:00 2001
From: Jonathan Bouchard
Date: Thu, 8 Apr 2021 08:25:19 -0400
Subject: [PATCH 2/3] Completed migration to standalone keyboard
---
README.md | 10 +-
android/app/build.gradle | 1 +
.../com/drakota/bttvstickers/PackAdapter.kt | 17 +--
.../bttvstickers/StickerInputMethod.kt | 61 +++++-----
.../src/main/res/drawable/rounded_corners.xml | 18 ---
android/app/src/main/res/layout/emote.xml | 37 +++---
android/app/src/main/res/layout/keyboard.xml | 18 +--
assets/icons/question.svg | 1 +
assets/images/activate1.png | Bin 0 -> 66847 bytes
assets/images/activate2.png | Bin 0 -> 33174 bytes
assets/images/activate3.png | Bin 0 -> 49746 bytes
assets/images/activate4.png | Bin 0 -> 63563 bytes
assets/images/activate5.png | Bin 0 -> 64951 bytes
assets/images/usage1.png | Bin 0 -> 126485 bytes
assets/images/usage2.png | Bin 0 -> 85221 bytes
assets/images/usage3.png | Bin 0 -> 663032 bytes
{assets/images => images}/banner.png | Bin
{assets/images => images/screenshots}/sc1.png | Bin
{assets/images => images/screenshots}/sc2.png | Bin
{assets/images => images/screenshots}/sc3.png | Bin
{assets/images => images/screenshots}/sc4.png | Bin
lib/constants.dart | 6 +-
lib/models/pack.dart | 9 +-
lib/routes.dart | 6 +-
lib/screens/home_screen.dart | 2 +-
lib/screens/search_screen.dart | 2 +-
lib/screens/settings_screen.dart | 99 +++++++++-------
lib/screens/tutorial_screen.dart | 109 ++++++++++++++++++
lib/utils/file_helpers.dart | 1 -
lib/widgets/clearable_textfield.dart | 2 +-
...vgbuttonicon.dart => svg_button_icon.dart} | 0
lib/widgets/tutorial_card.dart | 33 ++++++
pubspec.yaml | 3 +-
33 files changed, 294 insertions(+), 141 deletions(-)
delete mode 100644 android/app/src/main/res/drawable/rounded_corners.xml
create mode 100644 assets/icons/question.svg
create mode 100644 assets/images/activate1.png
create mode 100644 assets/images/activate2.png
create mode 100644 assets/images/activate3.png
create mode 100644 assets/images/activate4.png
create mode 100644 assets/images/activate5.png
create mode 100644 assets/images/usage1.png
create mode 100644 assets/images/usage2.png
create mode 100644 assets/images/usage3.png
rename {assets/images => images}/banner.png (100%)
rename {assets/images => images/screenshots}/sc1.png (100%)
rename {assets/images => images/screenshots}/sc2.png (100%)
rename {assets/images => images/screenshots}/sc3.png (100%)
rename {assets/images => images/screenshots}/sc4.png (100%)
create mode 100644 lib/screens/tutorial_screen.dart
rename lib/widgets/{svgbuttonicon.dart => svg_button_icon.dart} (100%)
create mode 100644 lib/widgets/tutorial_card.dart
diff --git a/README.md b/README.md
index b9ed478..e446668 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
@@ -21,16 +21,16 @@ Search and discover BTTV emotes to add to your keyboard stickers and use them an