This commit is contained in:
Kieran BW 2021-08-10 20:50:24 +01:00
parent a0f84c3b0c
commit 8e93b09e05
17 changed files with 129 additions and 88 deletions

View File

@ -12,6 +12,6 @@
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2021-08-09T19:37:01.560785100Z" />
<timeTargetWasSelectedWithDropDown value="2021-08-10T19:32:45.536947900Z" />
</component>
</project>

3
.idea/misc.xml generated
View File

@ -5,6 +5,9 @@
<map>
<entry key="..\:/Users/Dell/Documents/GitHub/Android.EweSticker/app/src/main/res/drawable/ic_chevron_left.xml" value="0.14635416666666667" />
<entry key="..\:/Users/Dell/Documents/GitHub/Android.EweSticker/app/src/main/res/drawable/ic_clock.xml" value="0.14635416666666667" />
<entry key="..\:/Users/Dell/Documents/GitHub/Android.EweSticker/app/src/main/res/layout/image_container.xml" value="0.13020833333333334" />
<entry key="..\:/Users/Dell/Documents/GitHub/Android.EweSticker/app/src/main/res/layout/image_container_column.xml" value="0.13020833333333334" />
<entry key="..\:/Users/Dell/Documents/GitHub/Android.EweSticker/app/src/main/res/layout/keyboard_layout.xml" value="0.13020833333333334" />
</map>
</option>
</component>

View File

@ -3,15 +3,17 @@ All major and minor version changes will be documented in this file. Details of
patch-level version changes can be found in [commit messages](../../commits/master).
## (no version) - 2021/08/09
## 20210810 - 2021/08/10
- Code optimisations
- Code clean-up
- Removed APNG animation due to memory leak
- Linting fixes
- Added caching functionality
- to improve performance of fallback stickers
- to enable addition of recent list
- Updated gradle
- to improve switching packs performance
- Updated gradle and deps
- Add recent icon
- (In progress ...) testing, cleanup
## 20210723 - 2021/07/23

View File

@ -11,8 +11,8 @@ android {
applicationId "com.fredhappyface.ewesticker"
minSdkVersion 28
targetSdkVersion 30
versionCode 20210723
versionName "2021.07.23"
versionCode 20210810
versionName "2021.08.10"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -39,14 +39,15 @@ android {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.5.21"
implementation 'androidx.core:core-ktx:1.6.0'
implementation "androidx.fragment:fragment-ktx"
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.preference:preference-ktx:1.1.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation "com.github.penfeizhou.android.animation:apng:2.10.0"
//implementation "com.github.penfeizhou.android.animation:apng:2.11.0"
}

View File

@ -8,10 +8,9 @@ import java.util.*
* an element is removed from the start
*
*/
class Cache(size: Int = 30) {
class Cache(private val size: Int = 30) {
private var data: LinkedList<String> = LinkedList()
private val size = size
/**
* Logic to add an element
@ -31,14 +30,6 @@ class Cache(size: Int = 30) {
}
/**
* Remove an element
*
* @param elem
*/
fun remove(elem: String) {
data.remove(elem)
}
/**
* Get an element

View File

@ -7,13 +7,11 @@ import android.graphics.ImageDecoder
import android.graphics.drawable.AnimatedImageDrawable
import android.graphics.drawable.Drawable
import android.inputmethodservice.InputMethodService
import android.util.Log
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.Toast
import androidx.cardview.widget.CardView
import androidx.core.content.FileProvider
@ -22,23 +20,24 @@ import androidx.core.view.inputmethod.EditorInfoCompat
import androidx.core.view.inputmethod.InputConnectionCompat
import androidx.core.view.inputmethod.InputContentInfoCompat
import androidx.preference.PreferenceManager
import com.github.penfeizhou.animation.apng.APNGDrawable
//import com.github.penfeizhou.animation.apng.APNGDrawable
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.*
import kotlin.collections.HashMap
class ImageKeyboard : InputMethodService() {
// Attributes
private lateinit var supportedMimes: MutableMap<String, String>
private var loadedPacks = HashMap<String, StickerPack>()
private var imageContainer: LinearLayout? = null
private var packContainer: LinearLayout? = null
private lateinit var imageContainer: LinearLayout
private lateinit var packContainer: LinearLayout
private lateinit var internalDir: File
// SharedPref
private lateinit var sharedPreferences: SharedPreferences
private var iconsPerRow = 0
private var iconsPerColumn = 0
private var iconSize = 0
private var disableAnimations = false
@ -46,6 +45,9 @@ class ImageKeyboard : InputMethodService() {
private var compatCache = Cache()
private var recentCache = Cache()
// Cache for image container
private var imageContainerCache = HashMap<Int, LinearLayout>()
/**
* Adds a back button as a PackCard to keyboard that shows the InputMethodPicker
*/
@ -59,7 +61,7 @@ class ImageKeyboard : InputMethodService() {
.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showInputMethodPicker()
}
packContainer!!.addView(packCard)
packContainer.addView(packCard)
}
/**
@ -70,11 +72,11 @@ class ImageKeyboard : InputMethodService() {
val recentButton = packCard.findViewById<ImageButton>(R.id.ib3)
val icon = ResourcesCompat.getDrawable(resources, R.drawable.ic_clock, null)
recentButton.setImageDrawable(icon)
recentButton.setOnClickListener { view: View ->
imageContainer!!.removeAllViewsInLayout()
recreateImageContainer(recentCache.toFiles())
recentButton.setOnClickListener {
imageContainer.removeAllViewsInLayout()
imageContainer.addView(createImageContainer(recentCache.toFiles()))
}
packContainer!!.addView(packCard)
packContainer.addView(packCard)
}
@ -89,10 +91,9 @@ class ImageKeyboard : InputMethodService() {
setStickerButtonImage(pack.thumbSticker, packButton)
packButton.tag = pack
packButton.setOnClickListener { view: View ->
imageContainer!!.removeAllViewsInLayout()
recreateImageContainer((view.tag as StickerPack).stickerList)
switchImageContainer((view.tag as StickerPack).stickerList)
}
packContainer!!.addView(packCard)
packContainer.addView(packCard)
}
/**
@ -170,19 +171,19 @@ class ImageKeyboard : InputMethodService() {
drawable = ImageDecoder.decodeDrawable(ImageDecoder.createSource(sticker))
} catch (ignore: IOException) {
}
if (sName.contains(".png") || sName.contains(".apng")) {
drawable = APNGDrawable.fromFile(sticker.absolutePath)
drawable!!.setAutoPlay(false)
drawable.start()
}
//if (sName.contains(".png") || sName.contains(".apng")) {
// drawable = APNGDrawable.fromFile(sticker.absolutePath)
// drawable!!.setAutoPlay(false)
// drawable.start()
//}
// Disable animations?
if (drawable is AnimatedImageDrawable && !disableAnimations
) {
drawable.start()
}
if (drawable is APNGDrawable && disableAnimations) {
drawable.stop()
}
//if (drawable is APNGDrawable && disableAnimations) {
// drawable.stop()
//}
// Apply
btn.setImageDrawable(drawable)
}
@ -215,14 +216,14 @@ class ImageKeyboard : InputMethodService() {
}
/**
* When the activity is crated, grab the number of icons per row and the configured icon size
* When the activity is crated, grab the number of icons per column and the configured icon size
* before reloading the packs
*
*/
override fun onCreate() {
super.onCreate()
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(baseContext)
iconsPerRow = sharedPreferences.getInt("iconsPerRow", 3)
iconsPerColumn = sharedPreferences.getInt("iconsPerColumn", 3)
iconSize = sharedPreferences.getInt("iconSize", 160)
disableAnimations = sharedPreferences.getBoolean(
"disableAnimations",
@ -234,19 +235,28 @@ class ImageKeyboard : InputMethodService() {
reloadPacks()
}
/**
* Called when the keyboard is first drawn
*
* @return
*/
override fun onCreateInputView(): View {
val keyboardLayout =
layoutInflater.inflate(R.layout.keyboard_layout, null) as RelativeLayout
View.inflate(applicationContext, R.layout.keyboard_layout, null)
packContainer = keyboardLayout.findViewById(R.id.packContainer)
imageContainer = keyboardLayout.findViewById(R.id.imageContainer)
imageContainer?.layoutParams?.height = (iconSize * iconsPerRow * 1.4).toInt()
imageContainer.layoutParams?.height = (iconSize * iconsPerColumn * 1.4).toInt()
recreatePackContainer()
return keyboardLayout
}
/**
* In full-screen mode the inserted content is likely to be hidden by the IME. Hence in this
* sample we simply disable full-screen mode.
*
* @return
*/
override fun onEvaluateFullscreenMode(): Boolean {
// In full-screen mode the inserted content is likely to be hidden by the IME. Hence in this
// sample we simply disable full-screen mode.
return false
}
@ -267,27 +277,45 @@ class ImageKeyboard : InputMethodService() {
}
}
/**
* Swap the image container every time a new pack is selected. If already cached use that otherwise create
*
* @param stickers
*/
private fun switchImageContainer(stickers: Array<File>) {
// Check the cache
val imageContainerHash = stickers.hashCode()
lateinit var imageContainerLayout: LinearLayout
if (imageContainerHash !in imageContainerCache.keys) {
imageContainerLayout = createImageContainer(stickers)
imageContainerCache[imageContainerHash] = createImageContainer(stickers)
} else {
imageContainerLayout = imageContainerCache[imageContainerHash]!!
}
// Swap the image container
imageContainer.removeAllViews()
imageContainer.addView(imageContainerLayout)
}
/**
* Recreate the image container every time a new pack is selected
*
* @param pack
* @param stickers
*/
private fun recreateImageContainer(stickers: Array<File>) {
// Clear the image view
imageContainer!!.removeAllViewsInLayout()
// And rebuild...
var imageContainerColumn = layoutInflater.inflate(
R.layout.image_container_column,
imageContainer,
false
) as LinearLayout
private fun createImageContainer(stickers: Array<File>): LinearLayout {
val tempImageContainer =
View.inflate(applicationContext, R.layout.image_container, null) as LinearLayout
lateinit var imageContainerColumn: LinearLayout
for (i in stickers.indices) {
if (i % iconsPerRow == 0) {
// Add a new column
if (i % iconsPerColumn == 0) {
imageContainerColumn = layoutInflater.inflate(
R.layout.image_container_column,
imageContainer,
tempImageContainer,
false
) as LinearLayout
tempImageContainer.addView(imageContainerColumn)
}
val imageCard = layoutInflater.inflate(
R.layout.sticker_card,
@ -302,7 +330,6 @@ class ImageKeyboard : InputMethodService() {
imgButton.setOnClickListener { view: View ->
val file = view.tag as File
recentCache.add(file.absolutePath)
Log.e("qwerty", recentCache.toSharedPref() )
val stickerType = supportedMimes[Utils.getFileExtension(file.name)]
if (stickerType == null) {
// Sticker is unsupported by input
@ -312,10 +339,8 @@ class ImageKeyboard : InputMethodService() {
doCommitContent(file.name, stickerType, file)
}
imageContainerColumn.addView(imageCard)
if (i % iconsPerRow == 0) {
imageContainer!!.addView(imageContainerColumn)
}
}
return tempImageContainer
}
/**
@ -323,7 +348,7 @@ class ImageKeyboard : InputMethodService() {
*
*/
private fun recreatePackContainer() {
packContainer!!.removeAllViewsInLayout()
packContainer.removeAllViewsInLayout()
// Back button
if (sharedPreferences.getBoolean("showBackButton", false)) {
addBackButtonToContainer()
@ -338,7 +363,7 @@ class ImageKeyboard : InputMethodService() {
addPackToContainer(loadedPacks[sortedPackName]!!)
}
if (sortedPackNames.isNotEmpty()) {
recreateImageContainer(loadedPacks[sortedPackNames[0]]!!.stickerList)
switchImageContainer(loadedPacks[sortedPackNames[0]]!!.stickerList)
}
}
@ -386,4 +411,4 @@ class ImageKeyboard : InputMethodService() {
// Constants
private const val AUTHORITY = "com.fredhappyface.ewesticker.inputcontent"
}
}
}

View File

@ -189,18 +189,18 @@ class MainActivity : AppCompatActivity() {
editor.putBoolean("disableAnimations", isChecked)
editor.apply()
}
val iconsPerRowSeekBar = findViewById<SeekBar>(R.id.iconsPerRowSeekBar)
iconsPerRowSeekBar.progress = sharedPreferences.getInt("iconsPerRow", 3)
iconsPerRowSeekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
var iconsPerRow = 3
val iconsPerColumnSeekBar = findViewById<SeekBar>(R.id.iconsPerColumnSeekBar)
iconsPerColumnSeekBar.progress = sharedPreferences.getInt("iconsPerColumn", 3)
iconsPerColumnSeekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
var iconsPerColumn = 3
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
iconsPerRow = progress
iconsPerColumn = progress
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {
val editor = sharedPreferences.edit()
editor.putInt("iconsPerRow", iconsPerRow)
editor.putInt("iconsPerColumn", iconsPerColumn)
editor.apply()
refreshKeyboardConfig()
showChangedPrefText()
@ -229,9 +229,9 @@ class MainActivity : AppCompatActivity() {
* Refreshes config from preferences
*/
fun refreshKeyboardConfig() {
val iconsPerRow = sharedPreferences.getInt("iconsPerRow", 3)
val iconsPerRowValue = findViewById<TextView>(R.id.iconsPerRowValue)
iconsPerRowValue.text = iconsPerRow.toString()
val iconsPerColumn = sharedPreferences.getInt("iconsPerColumn", 3)
val iconsPerColumnValue = findViewById<TextView>(R.id.iconsPerColumnValue)
iconsPerColumnValue.text = iconsPerColumn.toString()
val iconSize = sharedPreferences.getInt("iconSize", 160)
val iconSizeValue = findViewById<TextView>(R.id.iconSizeValue)
iconSizeValue.text = String.format("%dpx", iconSize)

View File

@ -102,14 +102,14 @@
android:text="@string/icons_per_row_status" />
<TextView
android:id="@+id/iconsPerRowValue"
android:id="@+id/iconsPerColumnValue"
style="@style/body_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="false" />
<SeekBar
android:id="@+id/iconsPerRowSeekBar"
android:id="@+id/iconsPerColumnSeekBar"
style="@style/body_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -187,6 +187,3 @@
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:orientation="horizontal" />

View File

@ -13,7 +13,7 @@
<string name="sub_options">&#xf423; Options</string>
<string name="show_back_button">Show back button in navbar?</string>
<string name="disable_animations">Disable Animations?</string>
<string name="icons_per_row_status">"Icons per row: "</string>
<string name="icons_per_row_status">"Icons per column: "</string>
<string name="icon_size_status">"Icon size: "</string>
<!-- About -->

View File

@ -1,13 +1,12 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.5.10"
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -0,0 +1,16 @@
<ul>
<li>Code optimisations
<ul>
<li>Code clean-up</li>
<li>Removed APNG animation due to memory leak</li>
<li>Linting fixes</li>
</ul></li>
<li>Added caching functionality
<ul>
<li>to improve performance of fallback stickers</li>
<li>to enable addition of recent list</li>
<li>to improve switching packs performance</li>
</ul></li>
<li>Updated gradle and deps</li>
<li>Add recent icon</li>
</ul>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 191 KiB