add help docs, refactor import and sending functionality

This commit is contained in:
Kieran BW 2022-03-03 00:33:07 +00:00
parent 22f1b38da1
commit 4276c16a98
12 changed files with 394 additions and 214 deletions

View File

@ -10,6 +10,8 @@ patch-level version changes can be found in [commit messages](../../commits/mast
## Next_Ver - 2022/xx/xx
- New Feature: Improve error messages for the user per https://github.com/FredHappyface/Android.EweSticker/issues/39
- New Feature: Add help (HELP.md) to provide guidance for error messages
- Bugfix: Refactor in response bug found when investigating https://github.com/FredHappyface/Android.EweSticker/issues/37
- Bugfix: Back button now enabled in fresh install per https://github.com/FredHappyface/Android.EweSticker/issues/38
- Update navbar theme (dark/light rather than the app accent colour)
- Update dependencies

95
HELP.md Normal file
View File

@ -0,0 +1,95 @@
<!-- omit in toc -->
# Help Guide
Support is provided via GitHub issues, please note this is provided on a voluntary basis
Please take a look at [Error Codes](#error-codes) first. This may provide some useful information
for why you are getting a certain error code. If an issue is created that is answered by this section
you'll likely be asked if you've read this :)
- [Error Codes](#error-codes)
- [E031](#e031)
- [E032](#e032)
- [E033](#e033)
- [E040](#e040)
- [E041](#e041)
- [E050](#e050)
- [Reach out](#reach-out)
## Error Codes
### E031
Some stickers failed to import (some number imported). Max stickers reached
This means that the total number of stickers that you are trying to import exceeds the
maximum number of stickers supported by EweSticker. Try and import fewer stickers,
see [Tutorial](/TUTORIAL.md)
**NOTE:** that the maximum pack size is currently **128** and the total maximum number of stickers supported
is **4096**
If you feel strongly that the maximum limit should be increased, open an issue (use the
'Question' template) and make a request - Be sure to explain why this would be useful. Simply saying
'I want 20000 stickers!' will likely result in the issue being closed
### E032
Some stickers failed to import (some number imported). Max pack size reached
This means that one of your sticker packs contains a number of stickers that exceeds the
maximum pack size supported by EweSticker. Try splitting the pack up into smaller chunks,
see [Tutorial](/TUTORIAL.md)
**NOTE:** that the maximum pack size is currently **128** and the total maximum number of stickers supported
is **4096**
If you feel strongly that the maximum limit should be increased, open an issue (use the
'Question' template) and make a request - Be sure to explain why this would be useful. Simply saying
'I want 20000 stickers!' will likely result in the issue being closed
### E033
Some stickers failed to import (some number imported). Unsupported formats found
This could be for a few reasons, perhaps you have a non sticker file in the sticker directory such
as a document in the wrong place. Alternatively this may result in a seemingly valid sticker not being
imported. Chances are that the sticker is not in a [supported format](/README.md#features).
### E040
(image type) not supported here
The application you are using doesn't support a sticker format or the compat-format
Unfortunately, nothing can be done by EweSticker to solve this, you may need to contact the application
developer you are trying to send a sticker to
### E041
Unexpected IOException when converting sticker
This is an unexpected error and happened when creating a compat-sticker to send to the application.
Please open an issue and provide as much information as you can. E.g. Android Version, phone
manufacturer, app you are trying to send the sticker in
### E050
IllegalStateException when switching packs. Try switching away from and back to EweSticker
This sometimes happens if you leave EweSticker as the current keyboard and switch back to it. The best
way to solve this is to tap the back button in the pack selector and switch back to EweSticker.
Please open an issue and provide as much information as you can. E.g. Android Version, phone
manufacturer, app you are trying to send the sticker in.
## Reach out
Support is provided via GitHub issues, please note this is provided on a voluntary basis
you are therefore not entitled to free customer service (that is not to say that contributions/ issues and questions are not welcome - more reminding you that project maintainers are well within their rights to prioritize other issues).
https://github.com/FredHappyface/.github/blob/master/SUPPORT.md provides a little more info
from the types of support you can expect
Please make sure to read https://github.com/FredHappyface/Android.EweSticker/issues/21 before
opening an issue, this ay seem a bit grumpy but chances are I won't be able to help with your
issue if you do not fill in the template provided
To open a new issue click the following link: https://github.com/FredHappyface/Android.EweSticker/issues/new/choose
**NOTE:** you will need to have a GitHub account to open issues (create one at https://github.com/signup)

View File

@ -16,6 +16,7 @@ Sticker-board for android inspired by uSticker (forked from woosticker).
- [Features](#features)
- [Screenshots](#screenshots)
- [How to use](#how-to-use)
- [Help](#help)
- [Lint with](#lint-with)
- [Language Information](#language-information)
- [Kotlin and Android Version](#kotlin-and-android-version)
@ -72,6 +73,10 @@ Sticker-board for android inspired by uSticker (forked from woosticker).
See the [Tutorial](/TUTORIAL.md) for more information.
## Help
See the [Help](/HELP.md) for more information.
## Lint with
```txt

View File

@ -1,9 +1,18 @@
<!-- omit in toc -->
# Tutorial
See below for a step-by-step tutorial on how to use EweSticker with your existing
sticker collection.
- [Step 1 - Create Sticker Directory (and transfer to device)](#step-1---create-sticker-directory-and-transfer-to-device)
- [Step 2 - Download EweSticker](#step-2---download-ewesticker)
- [Get it on F-Droid](#get-it-on-f-droid)
- [Get it on Google Play](#get-it-on-google-play)
- [Download the APK](#download-the-apk)
- [Step 3 - Select Directory with EweSticker (and wait...)](#step-3---select-directory-with-ewesticker-and-wait)
- [Step 4 - Activate the keyboard](#step-4---activate-the-keyboard)
- [Step 5 - Send Stickers in your favourite apps](#step-5---send-stickers-in-your-favourite-apps)
## Step 1 - Create Sticker Directory (and transfer to device)
<img src="readme-assets/tutorial/step1.png" alt="Step 1" width="600">
@ -23,6 +32,9 @@ The sticker directory has the following structure:
Then transfer this to your phone/ device. Plugging this into a PC is a pretty
convenient way to do this.
**NOTE:** that the maximum pack size is currently **128** and the total maximum number of stickers supported
is **4096**
## Step 2 - Download EweSticker
### Get it on F-Droid

View File

@ -8,7 +8,7 @@
android:label="@string/app_name"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning"
android:dataExtractionRules="@xml/data_extraction_rules">
android:dataExtractionRules="@xml/data_extraction_rules" tools:targetApi="s">
<activity
android:name="com.fredhappyface.ewesticker.MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"

View File

@ -1,8 +1,6 @@
package com.fredhappyface.ewesticker
import android.content.ClipDescription
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.inputmethodservice.InputMethodService
import android.os.Build.VERSION.SDK_INT
import android.view.View
@ -12,11 +10,6 @@ import android.view.inputmethod.InputMethodManager
import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.RelativeLayout
import androidx.core.content.FileProvider
import androidx.core.graphics.drawable.toBitmap
import androidx.core.view.inputmethod.EditorInfoCompat
import androidx.core.view.inputmethod.InputConnectionCompat
import androidx.core.view.inputmethod.InputContentInfoCompat
import androidx.core.view.iterator
import androidx.gridlayout.widget.GridLayout
import androidx.preference.PreferenceManager
@ -27,13 +20,8 @@ import coil.decode.ImageDecoderDecoder
import coil.fetch.VideoFrameFileFetcher
import coil.imageLoader
import coil.load
import coil.request.ImageRequest
import kotlinx.coroutines.*
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.*
import kotlin.collections.HashMap
import kotlin.math.min
/**
@ -62,7 +50,7 @@ class ImageKeyboard : InputMethodService() {
private var recentCache = Cache()
// onStartInput
private lateinit var supportedMimes: List<String>
private lateinit var stickerSender: StickerSender
// onCreateInputView
private lateinit var keyboardRoot: ViewGroup
@ -83,7 +71,7 @@ class ImageKeyboard : InputMethodService() {
override fun onCreate() {
// Misc
super.onCreate()
val scale = applicationContext.resources.displayMetrics.density
val scale = baseContext.resources.displayMetrics.density
// Setup coil
val imageLoader =
ImageLoader.Builder(baseContext)
@ -112,7 +100,7 @@ class ImageKeyboard : InputMethodService() {
(this.sharedPreferences.getInt("iconSize", 80) * scale)
})
.toInt()
this.toaster = Toaster(applicationContext)
this.toaster = Toaster(baseContext)
// Load Packs
this.loadedPacks = HashMap()
val packs =
@ -145,7 +133,7 @@ class ImageKeyboard : InputMethodService() {
* @return View keyboardLayout
*/
override fun onCreateInputView(): View {
val keyboardLayout = View.inflate(applicationContext, R.layout.keyboard_layout, null)
val keyboardLayout = View.inflate(baseContext, R.layout.keyboard_layout, null)
this.keyboardRoot = keyboardLayout.findViewById(R.id.keyboardRoot)
this.packsList = keyboardLayout.findViewById(R.id.packsList)
this.packContent = keyboardLayout.findViewById(R.id.packContent)
@ -183,8 +171,15 @@ class ImageKeyboard : InputMethodService() {
* @param restarting
*/
override fun onStartInput(info: EditorInfo?, restarting: Boolean) {
this.supportedMimes =
Utils.getSupportedMimes().filter { isCommitContentSupported(info, it) }
this.stickerSender = StickerSender(
this.baseContext,
this.toaster,
this.internalDir,
this.currentInputConnection,
this.currentInputEditorInfo,
this.compatCache,
this.imageLoader
)
}
/** When leaving some input field update the caches */
@ -197,91 +192,6 @@ class ImageKeyboard : InputMethodService() {
super.onFinishInput()
}
/**
* In the event that a mimetype is unsupported by a InputConnectionCompat (looking at you,
* Signal) create a temporary png and send that. In the event that png is not supported, alert
* the user.
*
* @param file: File
*/
private suspend fun doFallbackCommitContent(file: File) {
// PNG might not be supported
if ("image/png" !in this.supportedMimes) {
toaster.toast(getString(R.string.fallback_040, file.extension))
return
}
// Create a new compatSticker and convert the sticker to png
val compatStickerName = file.hashCode().toString()
val compatSticker = File(this.internalDir, "__compatSticker__/$compatStickerName.png")
if (!compatSticker.exists()) {
// If the sticker doesn't exist then create
compatSticker.parentFile?.mkdirs()
try {
val request =
ImageRequest.Builder(baseContext)
.data(file)
.target { result ->
val bitmap = result.toBitmap()
bitmap.compress(
Bitmap.CompressFormat.PNG, 90, FileOutputStream(compatSticker)
)
}
.build()
imageLoader.execute(request)
} catch (ignore: IOException) {
toaster.toast(getString(R.string.fallback_041))
}
}
// Send the compatSticker!
doCommitContent("image/png", compatSticker)
// Remove old stickers
val remSticker = this.compatCache.add(compatStickerName)
remSticker?.let { File(this.internalDir, "__compatSticker__/$remSticker.png").delete() }
}
/**
* Send a sticker file to a InputConnectionCompat
*
* @param mimeType String
* @param file File
*/
private fun doCommitContent(mimeType: String, file: File) {
// ContentUri, ClipDescription, linkUri
val inputContentInfoCompat =
InputContentInfoCompat(
FileProvider.getUriForFile(this, "com.fredhappyface.ewesticker.inputcontent", file),
ClipDescription(file.name, arrayOf(mimeType)),
null
)
// InputConnection, EditorInfo, InputContentInfoCompat, int flags, null opts
InputConnectionCompat.commitContent(
currentInputConnection,
currentInputEditorInfo,
inputContentInfoCompat,
InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION,
null
)
}
/**
* Check if the sticker is supported by the receiver
*
* @param editorInfo: EditorInfo - the editor/ receiver
* @param mimeType: String - the image mimetype
* @return boolean - is the mimetype supported?
*/
private fun isCommitContentSupported(editorInfo: EditorInfo?, mimeType: String?): Boolean {
editorInfo?.packageName ?: return false
mimeType ?: return false
currentInputConnection ?: return false
EditorInfoCompat.getContentMimeTypes(editorInfo).forEach {
if (ClipDescription.compareMimeTypes(mimeType, it)) {
return true
}
}
return false
}
/**
* Swap the pack layout every time a pack is selected. If already cached use that otherwise
* create the pack layout
@ -362,12 +272,7 @@ class ImageKeyboard : InputMethodService() {
imgButton.setOnClickListener {
val file = it.tag as File
this.recentCache.add(file.absolutePath)
val stickerType = Utils.getMimeType(file)
if (stickerType == null || stickerType !in this.supportedMimes) {
CoroutineScope(Dispatchers.Main).launch { doFallbackCommitContent(file) }
return@setOnClickListener
}
doCommitContent(stickerType, file)
stickerSender.sendSticker(file)
}
imgButton.setOnLongClickListener { view: View ->
val file = view.tag as File
@ -415,7 +320,7 @@ class ImageKeyboard : InputMethodService() {
if (SDK_INT >= 28) {
this.switchToPreviousInputMethod()
} else {
(applicationContext.getSystemService(INPUT_METHOD_SERVICE) as
(baseContext.getSystemService(INPUT_METHOD_SERVICE) as
InputMethodManager)
.showInputMethodPicker()
}

View File

@ -3,7 +3,6 @@ package com.fredhappyface.ewesticker
import android.app.Activity
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@ -16,31 +15,18 @@ import android.widget.SeekBar.OnSeekBarChangeListener
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceManager
import java.io.File
import java.nio.file.Files
import java.util.*
import java.util.concurrent.Executors
private const val MAX_FILES = 4096
private const val MAX_PACK_SIZE = 128
/** MainActivity class inherits from the AppCompatActivity class - provides the settings view */
class MainActivity : AppCompatActivity() {
// init
private val supportedMimes = Utils.getSupportedMimes()
// onCreate
private lateinit var sharedPreferences: SharedPreferences
private lateinit var contextView: View
private lateinit var toaster: Toaster
// importSticker(s)
private var filesLeft = MAX_FILES
private var packSizes: MutableMap<String, Int> = mutableMapOf()
private var totalStickers = 0
/**
* Sets up content view, shared prefs, etc.
*
@ -53,7 +39,7 @@ class MainActivity : AppCompatActivity() {
// Set late-init attrs
this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
this.contextView = findViewById(R.id.activityMainRoot)
this.toaster = Toaster(applicationContext)
this.toaster = Toaster(baseContext)
refreshStickerDirPath()
// Update UI with config
seekBar(findViewById(R.id.iconsPerXSb), findViewById(R.id.iconsPerXLbl), "iconsPerX", 3)
@ -72,13 +58,14 @@ class MainActivity : AppCompatActivity() {
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val editor = this.sharedPreferences.edit()
editor.putString("stickerDirPath", result.data?.data.toString())
val stickerDirPath = result.data?.data.toString()
editor.putString("stickerDirPath", stickerDirPath)
editor.putString("lastUpdateDate", Calendar.getInstance().time.toString())
editor.putString("recentCache", "")
editor.putString("compatCache", "")
editor.apply()
refreshStickerDirPath()
importStickers()
importStickers(stickerDirPath)
}
}
@ -98,46 +85,14 @@ class MainActivity : AppCompatActivity() {
* @param view: View
*/
fun chooseDir(view: View) {
this.filesLeft = MAX_FILES
this.totalStickers = 0
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
chooseDirResultLauncher.launch(intent)
}
/**
* Copies stickers from source to internal storage
*
* @param sticker sticker to copy over
*
* @return 1 if sticker imported successfully else 0
*/
private fun importSticker(sticker: DocumentFile) {
// Exit if sticker is unsupported or if pack size > MAX_PACK_SIZE
val parentDir = sticker.parentFile?.name ?: "__default__"
val packSize = this.packSizes[parentDir] ?: 0
if (packSize > MAX_PACK_SIZE) {
this.toaster.setState(2); return
}
if (sticker.type !in this.supportedMimes) {
this.toaster.setState(3); return
}
this.packSizes[parentDir] = packSize + 1
// Copy sticker to app storage
val destSticker = File(filesDir, "stickers/${parentDir}/${sticker.name}")
destSticker.parentFile?.mkdirs()
try {
val inputStream = contentResolver.openInputStream(sticker.uri)
Files.copy(inputStream, destSticker.toPath())
inputStream?.close()
} catch (e: java.lang.Exception) {
}
this.totalStickers++
}
/** Import files from storage to internal directory */
private fun importStickers() {
private fun importStickers(stickerDirPath: String) {
// Use worker thread because this takes several seconds
val executor = Executors.newSingleThreadExecutor()
val handler = Handler(Looper.getMainLooper())
@ -147,30 +102,21 @@ class MainActivity : AppCompatActivity() {
val button = findViewById<Button>(R.id.updateStickerPackInfoBtn)
button.isEnabled = false
executor.execute {
File(filesDir, "stickers").deleteRecursively()
val stickerDirPath =
this.sharedPreferences.getString(
"stickerDirPath", resources.getString(R.string.update_sticker_pack_info_path)
val totalStickers =
StickerImporter(baseContext, this.toaster).importStickers(
stickerDirPath
)
val leafNodes =
fileWalk(DocumentFile.fromTreeUri(applicationContext, Uri.parse(stickerDirPath)))
if (leafNodes.size > MAX_FILES) {
toaster.setState(1)
}
for (file in leafNodes.take(MAX_FILES)) {
importSticker(file)
}
handler.post {
toaster.toastOnState(
arrayOf(
getString(R.string.imported_020, this.totalStickers),
getString(R.string.imported_031, this.totalStickers),
getString(R.string.imported_032, this.totalStickers),
getString(R.string.imported_033, this.totalStickers),
getString(R.string.imported_020, totalStickers),
getString(R.string.imported_031, totalStickers),
getString(R.string.imported_032, totalStickers),
getString(R.string.imported_033, totalStickers),
)
)
val editor = this.sharedPreferences.edit()
editor.putInt("numStickersImported", this.totalStickers)
editor.putInt("numStickersImported", totalStickers)
editor.apply()
refreshStickerDirPath()
button.isEnabled = true
@ -178,25 +124,6 @@ class MainActivity : AppCompatActivity() {
}
}
/**
* Get a MutableSet of DocumentFiles from a root node
*
* @param rootNode parent dir to get all files from
* @return MutableSet<DocumentFile> set of files
*/
private fun fileWalk(rootNode: DocumentFile?): MutableSet<DocumentFile> {
val leafNodes = mutableSetOf<DocumentFile>()
if (rootNode == null || this.filesLeft < 0) {
return leafNodes
}
val files = rootNode.listFiles()
for (file in files) {
if (file.isFile) leafNodes.add(file)
if (file.isDirectory) leafNodes.addAll(fileWalk(file))
}
this.filesLeft -= files.size
return leafNodes
}
/**
* Add toggle logic for each toggle/ checkbox in the layout

View File

@ -0,0 +1,94 @@
package com.fredhappyface.ewesticker
import android.content.Context
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import java.io.File
import java.nio.file.Files
private const val MAX_FILES = 4096
private const val MAX_PACK_SIZE = 128
/**
* TODO
*
* @property context
* @property toaster
*/
class StickerImporter(
private val context: Context,
private val toaster: Toaster,
) {
private val supportedMimes = Utils.getSupportedMimes()
private var filesLeft = MAX_FILES
private var packSizes: MutableMap<String, Int> = mutableMapOf()
private var totalStickers = 0
/**
* TODO
*
* @param stickerDirPath
*/
fun importStickers(stickerDirPath: String): Int {
File(this.context.filesDir, "stickers").deleteRecursively()
val leafNodes =
fileWalk(DocumentFile.fromTreeUri(context, Uri.parse(stickerDirPath)))
if (leafNodes.size > MAX_FILES) {
this.toaster.setState(1)
}
for (file in leafNodes.take(MAX_FILES)) {
importSticker(file)
}
return this.totalStickers
}
/**
* Copies stickers from source to internal storage
*
* @param sticker sticker to copy over
*
* @return 1 if sticker imported successfully else 0
*/
private fun importSticker(sticker: DocumentFile) {
// Exit if sticker is unsupported or if pack size > MAX_PACK_SIZE
val parentDir = sticker.parentFile?.name ?: "__default__"
val packSize = this.packSizes[parentDir] ?: 0
if (packSize > MAX_PACK_SIZE) {
this.toaster.setState(2); return
}
if (sticker.type !in this.supportedMimes) {
this.toaster.setState(3); return
}
this.packSizes[parentDir] = packSize + 1
// Copy sticker to app storage
val destSticker = File(this.context.filesDir, "stickers/${parentDir}/${sticker.name}")
destSticker.parentFile?.mkdirs()
try {
val inputStream = context.contentResolver.openInputStream(sticker.uri)
Files.copy(inputStream, destSticker.toPath())
inputStream?.close()
} catch (e: java.lang.Exception) {
}
this.totalStickers++
}
/**
* Get a MutableSet of DocumentFiles from a root node
*
* @param rootNode parent dir to get all files from
* @return MutableSet<DocumentFile> set of files
*/
private fun fileWalk(rootNode: DocumentFile?): MutableSet<DocumentFile> {
val leafNodes = mutableSetOf<DocumentFile>()
if (rootNode == null || this.filesLeft < 0) {
return leafNodes
}
val files = rootNode.listFiles()
for (file in files) {
if (file.isFile) leafNodes.add(file)
if (file.isDirectory) leafNodes.addAll(fileWalk(file))
}
this.filesLeft -= files.size
return leafNodes
}
}

View File

@ -0,0 +1,151 @@
package com.fredhappyface.ewesticker
import android.content.ClipDescription
import android.content.Context
import android.graphics.Bitmap
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import androidx.core.content.FileProvider
import androidx.core.graphics.drawable.toBitmap
import androidx.core.view.inputmethod.EditorInfoCompat
import androidx.core.view.inputmethod.InputConnectionCompat
import androidx.core.view.inputmethod.InputContentInfoCompat
import coil.ImageLoader
import coil.request.ImageRequest
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
/**
* TODO
*
* @property context
* @property toaster
* @property internalDir
* @property currentInputConnection
* @property currentInputEditorInfo
* @property compatCache
* @property imageLoader
*/
class StickerSender(
private val context: Context,
private val toaster: Toaster,
private val internalDir: File,
private val currentInputConnection: InputConnection?,
private val currentInputEditorInfo: EditorInfo?,
private val compatCache: Cache,
private val imageLoader: ImageLoader,
) {
private val supportedMimes =
Utils.getSupportedMimes()
.filter { isCommitContentSupported(this.currentInputEditorInfo, it) }
/**
* TODO
*
* @param file
*/
fun sendSticker(file: File) {
val stickerType = Utils.getMimeType(file)
if (stickerType == null || stickerType !in this.supportedMimes) {
CoroutineScope(Dispatchers.Main).launch { doFallbackCommitContent(file) }
return
}
doCommitContent(stickerType, file)
}
/**
* In the event that a mimetype is unsupported by a InputConnectionCompat (looking at you,
* Signal) create a temporary png and send that. In the event that png is not supported, alert
* the user.
*
* @param file: File
*/
private suspend fun doFallbackCommitContent(file: File) {
// PNG might not be supported
if ("image/png" !in this.supportedMimes) {
this.toaster.toast(context.getString(R.string.fallback_040, file.extension))
return
}
// Create a new compatSticker and convert the sticker to png
val compatStickerName = file.hashCode().toString()
val compatSticker = File(this.internalDir, "__compatSticker__/$compatStickerName.png")
if (!compatSticker.exists()) {
// If the sticker doesn't exist then create
compatSticker.parentFile?.mkdirs()
try {
val request =
ImageRequest.Builder(this.context)
.data(file)
.target { result ->
val bitmap = result.toBitmap()
bitmap.compress(
Bitmap.CompressFormat.PNG, 90, FileOutputStream(compatSticker)
)
}
.build()
this.imageLoader.execute(request)
} catch (ignore: IOException) {
this.toaster.toast(this.context.getString(R.string.fallback_041))
}
}
// Send the compatSticker!
doCommitContent("image/png", compatSticker)
// Remove old stickers
val remSticker = this.compatCache.add(compatStickerName)
remSticker?.let { File(this.internalDir, "__compatSticker__/$remSticker.png").delete() }
}
/**
* Send a sticker file to a InputConnectionCompat
*
* @param mimeType String
* @param file File
*/
private fun doCommitContent(mimeType: String, file: File) {
// ContentUri, ClipDescription, linkUri
val inputContentInfoCompat =
InputContentInfoCompat(
FileProvider.getUriForFile(
this.context,
"com.fredhappyface.ewesticker.inputcontent",
file
),
ClipDescription(file.name, arrayOf(mimeType)),
null
)
// InputConnection, EditorInfo, InputContentInfoCompat, int flags, null opts
if (this.currentInputConnection == null || this.currentInputEditorInfo == null) return
InputConnectionCompat.commitContent(
this.currentInputConnection,
this.currentInputEditorInfo,
inputContentInfoCompat,
InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION,
null
)
}
/**
* Check if the sticker is supported by the receiver
*
* @param editorInfo: EditorInfo - the editor/ receiver
* @param mimeType: String - the image mimetype
* @return boolean - is the mimetype supported?
*/
private fun isCommitContentSupported(editorInfo: EditorInfo?, mimeType: String?): Boolean {
editorInfo?.packageName ?: return false
mimeType ?: return false
this.currentInputConnection ?: return false
EditorInfoCompat.getContentMimeTypes(editorInfo).forEach {
if (ClipDescription.compareMimeTypes(mimeType, it)) {
return true
}
}
return false
}
}

View File

@ -8,7 +8,7 @@ import android.widget.Toast
* android.content.Context to the constructor and call the 'toast' function (others as below)
* toaster.state keeps track of an error state or similar.
*
* @property context: android.content.Context. e.g. applicationContext
* @property context: android.content.Context. e.g. baseContext
*/
class Toaster(private val context: Context) {
private var state = 0
@ -46,19 +46,6 @@ class Toaster(private val context: Context) {
}
}
/**
* Call toaster.toastOnNonZero with a messages to create a toast notification of the state is
* not zero. Context is set when Toaster is instantiated. Duration is determined based on
* text length.
*
* @param string: String. Message to output
*/
fun toastOnNonZero(string: String) {
if (this.state != 0) {
toast(string)
}
}
/**
* Set the state to some integer value
*
@ -72,4 +59,4 @@ class Toaster(private val context: Context) {
}
}
}
}

View File

@ -52,6 +52,7 @@ Copyright © Randy Zhou</string>
<string name="links_heading">Enlaces</string>
<string name="links_text">"- Proyecto: https://github.com/FredHappyface/Android.EweSticker
- Tutorial: https://github.com/FredHappyface/Android.EweSticker/blob/main/TUTORIAL.md
- Ayuda: https://github.com/FredHappyface/Android.EweSticker/blob/main/HELP.md
- Licencia: https://github.com/FredHappyface/Android.EweSticker/blob/main/LICENSE.md</string>
<!-- Interactive Messages -->
<string name="pref_000">Preferencias cambiadas.Actualizar el teclado para configurar para aplicar</string>

View File

@ -1,4 +1,4 @@
<resources>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="PluralsCandidate">
<!-- App info -->
<string name="app_name" translatable="false">EweSticker</string>
<string name="pack_icon">Pack icon</string>
@ -53,6 +53,7 @@ Copyright © Randy Zhou</string>
<string name="links_heading">Links</string>
<string name="links_text">"- Project: https://github.com/FredHappyface/Android.EweSticker
- Tutorial: https://github.com/FredHappyface/Android.EweSticker/blob/main/TUTORIAL.md
- Help: https://github.com/FredHappyface/Android.EweSticker/blob/main/HELP.md
- License: https://github.com/FredHappyface/Android.EweSticker/blob/main/LICENSE.md</string>
<!-- Interactive Messages -->
<string name="pref_000">Preferences changed. Reload the keyboard for settings to apply</string>