New emoji picker (#2395)
* Update to Emoji2 * Hopefully fix the emoji picker preference * Switch to released Filemojicompat version * Filemojicompat version as an own var * Remove an unused import * Small cleanup * Correct onDisplayPreferenceDialog; test TuskyApplication * Use TextViews instead of EmojiTextViews * Recreate the Main Activity if the emoji pack is updated * Enable coreLibraryDesugaring (for Java Streams); update Filemojicompat, downgrade Emoji2 * Update emoji font versions to 14 * Use FilemojiCompat 3.2.0-beta01 * Make ktLint happy again * Remove coreLibraryDesugaring and a FIXME * Use EmojiPickerPreference.get() * Disable emoji pack import * Update FilemojiCompat to Beta 2 * Update FilemojiCompat to Beta 3 * Update FilemojiCompat to Beta 3.2.0 final * Update FilemojiCompat to 3.2.1
This commit is contained in:
parent
2fcd6fdc14
commit
f15b3e61bb
32 changed files with 109 additions and 782 deletions
|
@ -96,6 +96,8 @@ ext.okhttpVersion = '4.9.3'
|
||||||
ext.glideVersion = '4.13.1'
|
ext.glideVersion = '4.13.1'
|
||||||
ext.daggerVersion = '2.41'
|
ext.daggerVersion = '2.41'
|
||||||
ext.materialdrawerVersion = '8.4.5'
|
ext.materialdrawerVersion = '8.4.5'
|
||||||
|
ext.emoji2_version = '1.1.0'
|
||||||
|
ext.filemojicompat_version = '3.2.1'
|
||||||
|
|
||||||
// if libraries are changed here, they should also be changed in LicenseActivity
|
// if libraries are changed here, they should also be changed in LicenseActivity
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -112,8 +114,9 @@ dependencies {
|
||||||
implementation "androidx.cardview:cardview:1.0.0"
|
implementation "androidx.cardview:cardview:1.0.0"
|
||||||
implementation "androidx.preference:preference-ktx:1.2.0"
|
implementation "androidx.preference:preference-ktx:1.2.0"
|
||||||
implementation "androidx.sharetarget:sharetarget:1.2.0-rc01"
|
implementation "androidx.sharetarget:sharetarget:1.2.0-rc01"
|
||||||
implementation "androidx.emoji:emoji:1.1.0"
|
implementation "androidx.emoji2:emoji2:$emoji2_version"
|
||||||
implementation "androidx.emoji:emoji-appcompat:1.1.0"
|
implementation "androidx.emoji2:emoji2-views:$emoji2_version"
|
||||||
|
implementation "androidx.emoji2:emoji2-views-helper:$emoji2_version"
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
|
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
|
||||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
|
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
|
||||||
|
@ -170,7 +173,9 @@ dependencies {
|
||||||
|
|
||||||
implementation "com.github.CanHub:Android-Image-Cropper:4.1.0"
|
implementation "com.github.CanHub:Android-Image-Cropper:4.1.0"
|
||||||
|
|
||||||
implementation "de.c1710:filemojicompat:1.0.18"
|
implementation "de.c1710:filemojicompat-ui:$filemojicompat_version"
|
||||||
|
implementation "de.c1710:filemojicompat:$filemojicompat_version"
|
||||||
|
implementation "de.c1710:filemojicompat-defaults:$filemojicompat_version"
|
||||||
|
|
||||||
testImplementation "androidx.test.ext:junit:1.1.3"
|
testImplementation "androidx.test.ext:junit:1.1.3"
|
||||||
testImplementation "org.robolectric:robolectric:4.4"
|
testImplementation "org.robolectric:robolectric:4.4"
|
||||||
|
|
|
@ -35,8 +35,8 @@ import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.pm.ShortcutManagerCompat
|
import androidx.core.content.pm.ShortcutManagerCompat
|
||||||
import androidx.emoji.text.EmojiCompat
|
import androidx.core.view.GravityCompat
|
||||||
import androidx.emoji.text.EmojiCompat.InitCallback
|
import androidx.emoji2.text.EmojiCompat
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
@ -114,6 +114,7 @@ import com.mikepenz.materialdrawer.util.updateBadge
|
||||||
import com.mikepenz.materialdrawer.widget.AccountHeaderView
|
import com.mikepenz.materialdrawer.widget.AccountHeaderView
|
||||||
import dagger.android.DispatchingAndroidInjector
|
import dagger.android.DispatchingAndroidInjector
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
|
import de.c1710.filemojicompat_ui.helpers.EMOJI_PREFERENCE
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -150,13 +151,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
|
|
||||||
private var accountLocked: Boolean = false
|
private var accountLocked: Boolean = false
|
||||||
|
|
||||||
private val emojiInitCallback = object : InitCallback() {
|
// We need to know if the emoji pack has been changed
|
||||||
override fun onInitialized() {
|
private var selectedEmojiPack: String? = null
|
||||||
if (!isDestroyed) {
|
|
||||||
updateProfiles()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -271,11 +267,31 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
// Flush old media that was cached for sharing
|
// Flush old media that was cached for sharing
|
||||||
deleteStaleCachedMedia(applicationContext.getExternalFilesDir("Tusky"))
|
deleteStaleCachedMedia(applicationContext.getExternalFilesDir("Tusky"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectedEmojiPack = preferences.getString(EMOJI_PREFERENCE, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
NotificationHelper.clearNotificationsForActiveAccount(this, accountManager)
|
NotificationHelper.clearNotificationsForActiveAccount(this, accountManager)
|
||||||
|
val currentEmojiPack = preferences.getString(EMOJI_PREFERENCE, "")
|
||||||
|
if (currentEmojiPack != selectedEmojiPack) {
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"onResume: EmojiPack has been changed from %s to %s"
|
||||||
|
.format(selectedEmojiPack, currentEmojiPack)
|
||||||
|
)
|
||||||
|
selectedEmojiPack = currentEmojiPack
|
||||||
|
recreate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
// For some reason the navigation drawer is opened when the activity is recreated
|
||||||
|
if (binding.mainDrawerLayout.isOpen) {
|
||||||
|
binding.mainDrawerLayout.closeDrawer(GravityCompat.START, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
|
@ -333,11 +349,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
EmojiCompat.get().unregisterInitCallback(emojiInitCallback)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun forwardShare(intent: Intent) {
|
private fun forwardShare(intent: Intent) {
|
||||||
val composeIntent = Intent(this, ComposeActivity::class.java)
|
val composeIntent = Intent(this, ComposeActivity::class.java)
|
||||||
composeIntent.action = intent.action
|
composeIntent.action = intent.action
|
||||||
|
@ -530,7 +541,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
EmojiCompat.get().registerInitCallback(emojiInitCallback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
@ -800,7 +810,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
private fun updateProfiles() {
|
private fun updateProfiles() {
|
||||||
val animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
val animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
val profiles: MutableList<IProfile> = accountManager.getAllAccountsOrderedByActive().map { acc ->
|
val profiles: MutableList<IProfile> = accountManager.getAllAccountsOrderedByActive().map { acc ->
|
||||||
val emojifiedName = EmojiCompat.get().process(acc.displayName.emojify(acc.emojis, header, animateEmojis))
|
val emojifiedName = EmojiCompat.get().process(acc.displayName.emojify(acc.emojis, header, animateEmojis))!!
|
||||||
|
|
||||||
ProfileDrawerItem().apply {
|
ProfileDrawerItem().apply {
|
||||||
isSelected = acc.isActive
|
isSelected = acc.isActive
|
||||||
|
|
|
@ -19,18 +19,18 @@ import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.emoji.text.EmojiCompat
|
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import autodispose2.AutoDisposePlugins
|
import autodispose2.AutoDisposePlugins
|
||||||
import com.keylesspalace.tusky.components.notifications.NotificationWorkerFactory
|
import com.keylesspalace.tusky.components.notifications.NotificationWorkerFactory
|
||||||
import com.keylesspalace.tusky.di.AppInjector
|
import com.keylesspalace.tusky.di.AppInjector
|
||||||
import com.keylesspalace.tusky.settings.PrefKeys
|
|
||||||
import com.keylesspalace.tusky.util.EmojiCompatFont
|
|
||||||
import com.keylesspalace.tusky.util.LocaleManager
|
import com.keylesspalace.tusky.util.LocaleManager
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils
|
import com.keylesspalace.tusky.util.ThemeUtils
|
||||||
import dagger.android.DispatchingAndroidInjector
|
import dagger.android.DispatchingAndroidInjector
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
|
import de.c1710.filemojicompat_defaults.DefaultEmojiPackList
|
||||||
|
import de.c1710.filemojicompat_ui.helpers.EmojiPackHelper
|
||||||
|
import de.c1710.filemojicompat_ui.helpers.EmojiPreference
|
||||||
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
||||||
import org.conscrypt.Conscrypt
|
import org.conscrypt.Conscrypt
|
||||||
import java.security.Security
|
import java.security.Security
|
||||||
|
@ -65,12 +65,10 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
||||||
|
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
|
||||||
// init the custom emoji fonts
|
// In this case, we want to have the emoji preferences merged with the other ones
|
||||||
val emojiSelection = preferences.getInt(PrefKeys.EMOJI, 0)
|
// Copied from PreferenceManager.getDefaultSharedPreferenceName
|
||||||
val emojiConfig = EmojiCompatFont.byId(emojiSelection)
|
EmojiPreference.sharedPreferenceName = packageName + "_preferences"
|
||||||
.getConfig(this)
|
EmojiPackHelper.init(this, DefaultEmojiPackList.get(this), allowPackImports = false)
|
||||||
.setReplaceAll(true)
|
|
||||||
EmojiCompat.init(emojiConfig)
|
|
||||||
|
|
||||||
// init night mode
|
// init night mode
|
||||||
val theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT)
|
val theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT)
|
||||||
|
|
|
@ -19,7 +19,7 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.emoji.text.EmojiCompat
|
import androidx.emoji2.text.EmojiCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.keylesspalace.tusky.R
|
import com.keylesspalace.tusky.R
|
||||||
import com.keylesspalace.tusky.databinding.ItemPollBinding
|
import com.keylesspalace.tusky.databinding.ItemPollBinding
|
||||||
|
|
|
@ -37,7 +37,7 @@ import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.emoji.text.EmojiCompat
|
import androidx.emoji2.text.EmojiCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.viewpager2.widget.MarginPageTransformer
|
import androidx.viewpager2.widget.MarginPageTransformer
|
||||||
|
|
|
@ -26,7 +26,7 @@ import androidx.core.view.OnReceiveContentListener
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.inputmethod.EditorInfoCompat
|
import androidx.core.view.inputmethod.EditorInfoCompat
|
||||||
import androidx.core.view.inputmethod.InputConnectionCompat
|
import androidx.core.view.inputmethod.InputConnectionCompat
|
||||||
import androidx.emoji.widget.EmojiEditTextHelper
|
import androidx.emoji2.viewsintegration.EmojiEditTextHelper
|
||||||
|
|
||||||
class EditTextTyped @JvmOverloads constructor(
|
class EditTextTyped @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
|
|
@ -1,240 +0,0 @@
|
||||||
package com.keylesspalace.tusky.components.preference
|
|
||||||
|
|
||||||
import android.app.AlarmManager
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Build
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.RadioButton
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.preference.Preference
|
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import com.keylesspalace.tusky.R
|
|
||||||
import com.keylesspalace.tusky.SplashActivity
|
|
||||||
import com.keylesspalace.tusky.components.notifications.NotificationHelper
|
|
||||||
import com.keylesspalace.tusky.databinding.DialogEmojicompatBinding
|
|
||||||
import com.keylesspalace.tusky.databinding.ItemEmojiPrefBinding
|
|
||||||
import com.keylesspalace.tusky.util.EmojiCompatFont
|
|
||||||
import com.keylesspalace.tusky.util.EmojiCompatFont.Companion.BLOBMOJI
|
|
||||||
import com.keylesspalace.tusky.util.EmojiCompatFont.Companion.FONTS
|
|
||||||
import com.keylesspalace.tusky.util.EmojiCompatFont.Companion.NOTOEMOJI
|
|
||||||
import com.keylesspalace.tusky.util.EmojiCompatFont.Companion.SYSTEM_DEFAULT
|
|
||||||
import com.keylesspalace.tusky.util.EmojiCompatFont.Companion.TWEMOJI
|
|
||||||
import com.keylesspalace.tusky.util.hide
|
|
||||||
import com.keylesspalace.tusky.util.show
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
|
||||||
import io.reactivex.rxjava3.disposables.Disposable
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This Preference lets the user select their preferred emoji font
|
|
||||||
*/
|
|
||||||
class EmojiPreference(
|
|
||||||
context: Context,
|
|
||||||
private val okHttpClient: OkHttpClient
|
|
||||||
) : Preference(context) {
|
|
||||||
|
|
||||||
private lateinit var selected: EmojiCompatFont
|
|
||||||
private lateinit var original: EmojiCompatFont
|
|
||||||
private val radioButtons = mutableListOf<RadioButton>()
|
|
||||||
private var updated = false
|
|
||||||
private var currentNeedsUpdate = false
|
|
||||||
|
|
||||||
private val downloadDisposables = MutableList<Disposable?>(FONTS.size) { null }
|
|
||||||
|
|
||||||
override fun onAttachedToHierarchy(preferenceManager: PreferenceManager) {
|
|
||||||
super.onAttachedToHierarchy(preferenceManager)
|
|
||||||
|
|
||||||
// Find out which font is currently active
|
|
||||||
selected = EmojiCompatFont.byId(
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(context).getInt(key, 0)
|
|
||||||
)
|
|
||||||
// We'll use this later to determine if anything has changed
|
|
||||||
original = selected
|
|
||||||
summary = selected.getDisplay(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onClick() {
|
|
||||||
val binding = DialogEmojicompatBinding.inflate(LayoutInflater.from(context))
|
|
||||||
|
|
||||||
setupItem(BLOBMOJI, binding.itemBlobmoji)
|
|
||||||
setupItem(TWEMOJI, binding.itemTwemoji)
|
|
||||||
setupItem(NOTOEMOJI, binding.itemNotoemoji)
|
|
||||||
setupItem(SYSTEM_DEFAULT, binding.itemNomoji)
|
|
||||||
|
|
||||||
AlertDialog.Builder(context)
|
|
||||||
.setView(binding.root)
|
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ -> onDialogOk() }
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupItem(font: EmojiCompatFont, binding: ItemEmojiPrefBinding) {
|
|
||||||
// Initialize all the views
|
|
||||||
binding.emojiName.text = font.getDisplay(context)
|
|
||||||
binding.emojiCaption.setText(font.caption)
|
|
||||||
binding.emojiThumbnail.setImageResource(font.img)
|
|
||||||
|
|
||||||
// There needs to be a list of all the radio buttons in order to uncheck them when one is selected
|
|
||||||
radioButtons.add(binding.emojiRadioButton)
|
|
||||||
updateItem(font, binding)
|
|
||||||
|
|
||||||
// Set actions
|
|
||||||
binding.emojiDownload.setOnClickListener { startDownload(font, binding) }
|
|
||||||
binding.emojiDownloadCancel.setOnClickListener { cancelDownload(font, binding) }
|
|
||||||
binding.emojiRadioButton.setOnClickListener { radioButton: View -> select(font, radioButton as RadioButton) }
|
|
||||||
binding.root.setOnClickListener {
|
|
||||||
select(font, binding.emojiRadioButton)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startDownload(font: EmojiCompatFont, binding: ItemEmojiPrefBinding) {
|
|
||||||
// Switch to downloading style
|
|
||||||
binding.emojiDownload.hide()
|
|
||||||
binding.emojiCaption.visibility = View.INVISIBLE
|
|
||||||
binding.emojiProgress.show()
|
|
||||||
binding.emojiProgress.progress = 0
|
|
||||||
binding.emojiDownloadCancel.show()
|
|
||||||
font.downloadFontFile(context, okHttpClient)
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(
|
|
||||||
{ progress ->
|
|
||||||
// The progress is returned as a float between 0 and 1, or -1 if it could not determined
|
|
||||||
if (progress >= 0) {
|
|
||||||
binding.emojiProgress.isIndeterminate = false
|
|
||||||
val max = binding.emojiProgress.max.toFloat()
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
binding.emojiProgress.setProgress((max * progress).toInt(), true)
|
|
||||||
} else {
|
|
||||||
binding.emojiProgress.progress = (max * progress).toInt()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
binding.emojiProgress.isIndeterminate = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Toast.makeText(context, R.string.download_failed, Toast.LENGTH_SHORT).show()
|
|
||||||
updateItem(font, binding)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
finishDownload(font, binding)
|
|
||||||
}
|
|
||||||
).also { downloadDisposables[font.id] = it }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun cancelDownload(font: EmojiCompatFont, binding: ItemEmojiPrefBinding) {
|
|
||||||
font.deleteDownloadedFile(context)
|
|
||||||
downloadDisposables[font.id]?.dispose()
|
|
||||||
downloadDisposables[font.id] = null
|
|
||||||
updateItem(font, binding)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun finishDownload(font: EmojiCompatFont, binding: ItemEmojiPrefBinding) {
|
|
||||||
select(font, binding.emojiRadioButton)
|
|
||||||
updateItem(font, binding)
|
|
||||||
// Set the flag to restart the app (because an update has been downloaded)
|
|
||||||
if (selected === original && currentNeedsUpdate) {
|
|
||||||
updated = true
|
|
||||||
currentNeedsUpdate = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select a font both visually and logically
|
|
||||||
*
|
|
||||||
* @param font The font to be selected
|
|
||||||
* @param radio The radio button associated with it's visual item
|
|
||||||
*/
|
|
||||||
private fun select(font: EmojiCompatFont, radio: RadioButton) {
|
|
||||||
selected = font
|
|
||||||
radioButtons.forEach { radioButton ->
|
|
||||||
radioButton.isChecked = radioButton == radio
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a "consistent" state is reached, i.e. it's not downloading the font
|
|
||||||
*
|
|
||||||
* @param font The font to be displayed
|
|
||||||
* @param binding The ItemEmojiPrefBinding to show the item in
|
|
||||||
*/
|
|
||||||
private fun updateItem(font: EmojiCompatFont, binding: ItemEmojiPrefBinding) {
|
|
||||||
// There's no download going on
|
|
||||||
binding.emojiProgress.hide()
|
|
||||||
binding.emojiDownloadCancel.hide()
|
|
||||||
binding.emojiCaption.show()
|
|
||||||
if (font.isDownloaded(context)) {
|
|
||||||
// Make it selectable
|
|
||||||
binding.emojiDownload.hide()
|
|
||||||
binding.emojiRadioButton.show()
|
|
||||||
binding.root.isClickable = true
|
|
||||||
} else {
|
|
||||||
// Make it downloadable
|
|
||||||
binding.emojiDownload.show()
|
|
||||||
binding.emojiRadioButton.hide()
|
|
||||||
binding.root.isClickable = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select it if necessary
|
|
||||||
if (font === selected) {
|
|
||||||
binding.emojiRadioButton.isChecked = true
|
|
||||||
// Update available
|
|
||||||
if (!font.isDownloaded(context)) {
|
|
||||||
currentNeedsUpdate = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
binding.emojiRadioButton.isChecked = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun saveSelectedFont() {
|
|
||||||
val index = selected.id
|
|
||||||
Log.i(TAG, "saveSelectedFont: Font ID: $index")
|
|
||||||
PreferenceManager
|
|
||||||
.getDefaultSharedPreferences(context)
|
|
||||||
.edit()
|
|
||||||
.putInt(key, index)
|
|
||||||
.apply()
|
|
||||||
summary = selected.getDisplay(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked ok -> save the selected font and offer to restart the app if something changed
|
|
||||||
*/
|
|
||||||
private fun onDialogOk() {
|
|
||||||
saveSelectedFont()
|
|
||||||
if (selected !== original || updated) {
|
|
||||||
AlertDialog.Builder(context)
|
|
||||||
.setTitle(R.string.restart_required)
|
|
||||||
.setMessage(R.string.restart_emoji)
|
|
||||||
.setNegativeButton(R.string.later, null)
|
|
||||||
.setPositiveButton(R.string.restart) { _, _ ->
|
|
||||||
// Restart the app
|
|
||||||
// From https://stackoverflow.com/a/17166729/5070653
|
|
||||||
val launchIntent = Intent(context, SplashActivity::class.java)
|
|
||||||
val mPendingIntent = PendingIntent.getActivity(
|
|
||||||
context,
|
|
||||||
0x1f973, // This is the codepoint of the party face emoji :D
|
|
||||||
launchIntent,
|
|
||||||
NotificationHelper.pendingIntentFlags(false)
|
|
||||||
)
|
|
||||||
val mgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
|
||||||
mgr.set(
|
|
||||||
AlarmManager.RTC,
|
|
||||||
System.currentTimeMillis() + 100,
|
|
||||||
mPendingIntent
|
|
||||||
)
|
|
||||||
exitProcess(0)
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val TAG = "EmojiPreference"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -38,14 +38,11 @@ import com.mikepenz.iconics.IconicsDrawable
|
||||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||||
import com.mikepenz.iconics.utils.colorInt
|
import com.mikepenz.iconics.utils.colorInt
|
||||||
import com.mikepenz.iconics.utils.sizePx
|
import com.mikepenz.iconics.utils.sizePx
|
||||||
import okhttp3.OkHttpClient
|
import de.c1710.filemojicompat_ui.views.picker.preference.EmojiPickerPreference
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var okhttpclient: OkHttpClient
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var accountManager: AccountManager
|
lateinit var accountManager: AccountManager
|
||||||
|
|
||||||
|
@ -65,11 +62,7 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
icon = makeIcon(GoogleMaterial.Icon.gmd_palette)
|
icon = makeIcon(GoogleMaterial.Icon.gmd_palette)
|
||||||
}
|
}
|
||||||
|
|
||||||
emojiPreference(okhttpclient) {
|
emojiPreference(requireActivity()) {
|
||||||
setDefaultValue("system_default")
|
|
||||||
setIcon(R.drawable.ic_emoji_24dp)
|
|
||||||
key = PrefKeys.EMOJI
|
|
||||||
setSummary(R.string.system_default)
|
|
||||||
setTitle(R.string.emoji_style)
|
setTitle(R.string.emoji_style)
|
||||||
icon = makeIcon(GoogleMaterial.Icon.gmd_sentiment_satisfied)
|
icon = makeIcon(GoogleMaterial.Icon.gmd_sentiment_satisfied)
|
||||||
}
|
}
|
||||||
|
@ -300,6 +293,12 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDisplayPreferenceDialog(preference: Preference) {
|
||||||
|
if (!EmojiPickerPreference.onDisplayPreferenceDialog(this, preference)) {
|
||||||
|
super.onDisplayPreferenceDialog(preference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun newInstance(): PreferencesFragment {
|
fun newInstance(): PreferencesFragment {
|
||||||
return PreferencesFragment()
|
return PreferencesFragment()
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package com.keylesspalace.tusky.settings
|
package com.keylesspalace.tusky.settings
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.activity.result.ActivityResultRegistryOwner
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.preference.CheckBoxPreference
|
import androidx.preference.CheckBoxPreference
|
||||||
import androidx.preference.EditTextPreference
|
import androidx.preference.EditTextPreference
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
|
@ -10,8 +12,7 @@ import androidx.preference.PreferenceCategory
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import androidx.preference.SwitchPreference
|
import androidx.preference.SwitchPreference
|
||||||
import com.keylesspalace.tusky.components.preference.EmojiPreference
|
import de.c1710.filemojicompat_ui.views.picker.preference.EmojiPickerPreference
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
|
|
||||||
class PreferenceParent(
|
class PreferenceParent(
|
||||||
val context: Context,
|
val context: Context,
|
||||||
|
@ -32,8 +33,9 @@ inline fun PreferenceParent.listPreference(builder: ListPreference.() -> Unit):
|
||||||
return pref
|
return pref
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun PreferenceParent.emojiPreference(okHttpClient: OkHttpClient, builder: EmojiPreference.() -> Unit): EmojiPreference {
|
inline fun <A> PreferenceParent.emojiPreference(activity: A, builder: EmojiPickerPreference.() -> Unit): EmojiPickerPreference
|
||||||
val pref = EmojiPreference(context, okHttpClient)
|
where A : Context, A : ActivityResultRegistryOwner, A : LifecycleOwner {
|
||||||
|
val pref = EmojiPickerPreference.get(activity)
|
||||||
builder(pref)
|
builder(pref)
|
||||||
addPref(pref)
|
addPref(pref)
|
||||||
return pref
|
return pref
|
||||||
|
|
|
@ -1,364 +0,0 @@
|
||||||
package com.keylesspalace.tusky.util
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.Log
|
|
||||||
import android.util.Pair
|
|
||||||
import androidx.annotation.DrawableRes
|
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.annotation.VisibleForTesting
|
|
||||||
import com.keylesspalace.tusky.R
|
|
||||||
import de.c1710.filemojicompat.FileEmojiCompatConfig
|
|
||||||
import io.reactivex.rxjava3.core.Observable
|
|
||||||
import io.reactivex.rxjava3.core.ObservableEmitter
|
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import okhttp3.ResponseBody
|
|
||||||
import okhttp3.internal.toLongOrDefault
|
|
||||||
import okio.Source
|
|
||||||
import okio.buffer
|
|
||||||
import okio.sink
|
|
||||||
import java.io.EOFException
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FilenameFilter
|
|
||||||
import java.io.IOException
|
|
||||||
import kotlin.math.max
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class bundles information about an emoji font as well as many convenient actions.
|
|
||||||
*/
|
|
||||||
class EmojiCompatFont(
|
|
||||||
val name: String,
|
|
||||||
private val display: String,
|
|
||||||
@StringRes val caption: Int,
|
|
||||||
@DrawableRes val img: Int,
|
|
||||||
val url: String,
|
|
||||||
// The version is stored as a String in the x.xx.xx format (to be able to compare versions)
|
|
||||||
val version: String
|
|
||||||
) {
|
|
||||||
|
|
||||||
private val versionCode = getVersionCode(version)
|
|
||||||
|
|
||||||
// A list of all available font files and whether they are older than the current version or not
|
|
||||||
// They are ordered by their version codes in ascending order
|
|
||||||
private var existingFontFileCache: List<Pair<File, List<Int>>>? = null
|
|
||||||
|
|
||||||
val id: Int
|
|
||||||
get() = FONTS.indexOf(this)
|
|
||||||
|
|
||||||
fun getDisplay(context: Context): String {
|
|
||||||
return if (this !== SYSTEM_DEFAULT) display else context.getString(R.string.system_default)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method will return the actual font file (regardless of its existence) for
|
|
||||||
* the current version (not necessarily the latest!).
|
|
||||||
*
|
|
||||||
* @return The font (TTF) file or null if called on SYSTEM_FONT
|
|
||||||
*/
|
|
||||||
private fun getFontFile(context: Context): File? {
|
|
||||||
return if (this !== SYSTEM_DEFAULT) {
|
|
||||||
val directory = File(context.getExternalFilesDir(null), DIRECTORY)
|
|
||||||
File(directory, "$name$version.ttf")
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getConfig(context: Context): FileEmojiCompatConfig {
|
|
||||||
return FileEmojiCompatConfig(context, getLatestFontFile(context))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isDownloaded(context: Context): Boolean {
|
|
||||||
return this === SYSTEM_DEFAULT || getFontFile(context)?.exists() == true || fontFileExists(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether there is already a font version that satisfies the current version, i.e. it
|
|
||||||
* has a higher or equal version code.
|
|
||||||
*
|
|
||||||
* @param context The Context
|
|
||||||
* @return Whether there is a font file with a higher or equal version code to the current
|
|
||||||
*/
|
|
||||||
private fun fontFileExists(context: Context): Boolean {
|
|
||||||
val existingFontFiles = getExistingFontFiles(context)
|
|
||||||
return if (existingFontFiles.isNotEmpty()) {
|
|
||||||
compareVersions(existingFontFiles.last().second, versionCode) >= 0
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes any older version of a font
|
|
||||||
*
|
|
||||||
* @param context The current Context
|
|
||||||
*/
|
|
||||||
private fun deleteOldVersions(context: Context) {
|
|
||||||
val existingFontFiles = getExistingFontFiles(context)
|
|
||||||
Log.d(TAG, "deleting old versions...")
|
|
||||||
Log.d(TAG, String.format("deleteOldVersions: Found %d other font files", existingFontFiles.size))
|
|
||||||
for (fileExists in existingFontFiles) {
|
|
||||||
if (compareVersions(fileExists.second, versionCode) < 0) {
|
|
||||||
val file = fileExists.first
|
|
||||||
// Uses side effects!
|
|
||||||
Log.d(
|
|
||||||
TAG,
|
|
||||||
String.format(
|
|
||||||
"Deleted %s successfully: %s", file.absolutePath,
|
|
||||||
file.delete()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads all font files that are inside the files directory into an ArrayList with the information
|
|
||||||
* on whether they are older than the currently available version or not.
|
|
||||||
*
|
|
||||||
* @param context The Context
|
|
||||||
*/
|
|
||||||
private fun getExistingFontFiles(context: Context): List<Pair<File, List<Int>>> {
|
|
||||||
// Only load it once
|
|
||||||
existingFontFileCache?.let {
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
// If we call this on the system default font, just return nothing...
|
|
||||||
if (this === SYSTEM_DEFAULT) {
|
|
||||||
existingFontFileCache = emptyList()
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val directory = File(context.getExternalFilesDir(null), DIRECTORY)
|
|
||||||
// It will search for old versions using a regex that matches the font's name plus
|
|
||||||
// (if present) a version code. No version code will be regarded as version 0.
|
|
||||||
val fontRegex = "$name(\\d+(\\.\\d+)*)?\\.ttf".toPattern()
|
|
||||||
val ttfFilter = FilenameFilter { _, name: String -> name.endsWith(".ttf") }
|
|
||||||
val foundFontFiles = directory.listFiles(ttfFilter).orEmpty()
|
|
||||||
Log.d(
|
|
||||||
TAG,
|
|
||||||
String.format(
|
|
||||||
"loadExistingFontFiles: %d other font files found",
|
|
||||||
foundFontFiles.size
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return foundFontFiles.map { file ->
|
|
||||||
val matcher = fontRegex.matcher(file.name)
|
|
||||||
val versionCode = if (matcher.matches()) {
|
|
||||||
val version = matcher.group(1)
|
|
||||||
getVersionCode(version)
|
|
||||||
} else {
|
|
||||||
listOf(0)
|
|
||||||
}
|
|
||||||
Pair(file, versionCode)
|
|
||||||
}.sortedWith { a, b ->
|
|
||||||
compareVersions(a.second, b.second)
|
|
||||||
}.also {
|
|
||||||
existingFontFileCache = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current or latest version of this font file (if there is any)
|
|
||||||
*
|
|
||||||
* @param context The Context
|
|
||||||
* @return The file for this font with the current or (if not existent) highest version code or null if there is no file for this font.
|
|
||||||
*/
|
|
||||||
private fun getLatestFontFile(context: Context): File? {
|
|
||||||
val current = getFontFile(context)
|
|
||||||
if (current != null && current.exists()) return current
|
|
||||||
val existingFontFiles = getExistingFontFiles(context)
|
|
||||||
return existingFontFiles.firstOrNull()?.first
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getVersionCode(version: String?): List<Int> {
|
|
||||||
if (version == null) return listOf(0)
|
|
||||||
return version.split(".").map {
|
|
||||||
it.toIntOrNull() ?: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun downloadFontFile(
|
|
||||||
context: Context,
|
|
||||||
okHttpClient: OkHttpClient
|
|
||||||
): Observable<Float> {
|
|
||||||
return Observable.create { emitter: ObservableEmitter<Float> ->
|
|
||||||
// It is possible (and very likely) that the file does not exist yet
|
|
||||||
val downloadFile = getFontFile(context)!!
|
|
||||||
if (!downloadFile.exists()) {
|
|
||||||
downloadFile.parentFile?.mkdirs()
|
|
||||||
downloadFile.createNewFile()
|
|
||||||
}
|
|
||||||
val request = Request.Builder().url(url)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val sink = downloadFile.sink().buffer()
|
|
||||||
var source: Source? = null
|
|
||||||
try {
|
|
||||||
// Download!
|
|
||||||
val response = okHttpClient.newCall(request).execute()
|
|
||||||
|
|
||||||
val responseBody = response.body
|
|
||||||
if (response.isSuccessful && responseBody != null) {
|
|
||||||
val size = response.length()
|
|
||||||
var progress = 0f
|
|
||||||
source = responseBody.source()
|
|
||||||
try {
|
|
||||||
while (!emitter.isDisposed) {
|
|
||||||
sink.write(source, CHUNK_SIZE)
|
|
||||||
progress += CHUNK_SIZE.toFloat()
|
|
||||||
if (size > 0) {
|
|
||||||
emitter.onNext(progress / size)
|
|
||||||
} else {
|
|
||||||
emitter.onNext(-1f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (ex: EOFException) {
|
|
||||||
/*
|
|
||||||
This means we've finished downloading the file since sink.write
|
|
||||||
will throw an EOFException when the file to be read is empty.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Downloading $url failed. Status code: ${response.code}")
|
|
||||||
emitter.tryOnError(Exception())
|
|
||||||
}
|
|
||||||
} catch (ex: IOException) {
|
|
||||||
Log.e(TAG, "Downloading $url failed.", ex)
|
|
||||||
downloadFile.deleteIfExists()
|
|
||||||
emitter.tryOnError(ex)
|
|
||||||
} finally {
|
|
||||||
source?.close()
|
|
||||||
sink.close()
|
|
||||||
if (emitter.isDisposed) {
|
|
||||||
downloadFile.deleteIfExists()
|
|
||||||
} else {
|
|
||||||
deleteOldVersions(context)
|
|
||||||
emitter.onComplete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the downloaded file, if it exists. Should be called when a download gets cancelled.
|
|
||||||
*/
|
|
||||||
fun deleteDownloadedFile(context: Context) {
|
|
||||||
getFontFile(context)?.deleteIfExists()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return display
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val TAG = "EmojiCompatFont"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This String represents the sub-directory the fonts are stored in.
|
|
||||||
*/
|
|
||||||
private const val DIRECTORY = "emoji"
|
|
||||||
|
|
||||||
private const val CHUNK_SIZE = 4096L
|
|
||||||
|
|
||||||
// The system font gets some special behavior...
|
|
||||||
val SYSTEM_DEFAULT = EmojiCompatFont(
|
|
||||||
"system-default",
|
|
||||||
"System Default",
|
|
||||||
R.string.caption_systememoji,
|
|
||||||
R.drawable.ic_emoji_34dp,
|
|
||||||
"",
|
|
||||||
"0"
|
|
||||||
)
|
|
||||||
val BLOBMOJI = EmojiCompatFont(
|
|
||||||
"Blobmoji",
|
|
||||||
"Blobmoji",
|
|
||||||
R.string.caption_blobmoji,
|
|
||||||
R.drawable.ic_blobmoji,
|
|
||||||
"https://tusky.app/hosted/emoji/BlobmojiCompat.ttf",
|
|
||||||
"14.0.1"
|
|
||||||
)
|
|
||||||
val TWEMOJI = EmojiCompatFont(
|
|
||||||
"Twemoji",
|
|
||||||
"Twemoji",
|
|
||||||
R.string.caption_twemoji,
|
|
||||||
R.drawable.ic_twemoji,
|
|
||||||
"https://tusky.app/hosted/emoji/TwemojiCompat.ttf",
|
|
||||||
"14.0.0"
|
|
||||||
)
|
|
||||||
val NOTOEMOJI = EmojiCompatFont(
|
|
||||||
"NotoEmoji",
|
|
||||||
"Noto Emoji",
|
|
||||||
R.string.caption_notoemoji,
|
|
||||||
R.drawable.ic_notoemoji,
|
|
||||||
"https://tusky.app/hosted/emoji/NotoEmojiCompat.ttf",
|
|
||||||
"14.0.0"
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This array stores all available EmojiCompat fonts.
|
|
||||||
* References to them can simply be saved by saving their indices
|
|
||||||
*/
|
|
||||||
val FONTS = listOf(SYSTEM_DEFAULT, BLOBMOJI, TWEMOJI, NOTOEMOJI)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the Emoji font associated with this ID
|
|
||||||
*
|
|
||||||
* @param id the ID of this font
|
|
||||||
* @return the corresponding font. Will default to SYSTEM_DEFAULT if not in range.
|
|
||||||
*/
|
|
||||||
fun byId(id: Int): EmojiCompatFont = FONTS.getOrElse(id) { SYSTEM_DEFAULT }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares two version codes to each other
|
|
||||||
*
|
|
||||||
* @param versionA The first version
|
|
||||||
* @param versionB The second version
|
|
||||||
* @return -1 if versionA < versionB, 1 if versionA > versionB and 0 otherwise
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
fun compareVersions(versionA: List<Int>, versionB: List<Int>): Int {
|
|
||||||
val len = max(versionB.size, versionA.size)
|
|
||||||
for (i in 0 until len) {
|
|
||||||
|
|
||||||
val vA = versionA.getOrElse(i) { 0 }
|
|
||||||
val vB = versionB.getOrElse(i) { 0 }
|
|
||||||
|
|
||||||
// It needs to be decided on the next level
|
|
||||||
if (vA == vB) continue
|
|
||||||
// Okay, is version B newer or version A?
|
|
||||||
return vA.compareTo(vB)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The versions are equal
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is needed because when transparent compression is used OkHttp reports
|
|
||||||
* [ResponseBody.contentLength] as -1. We try to get the header which server sent
|
|
||||||
* us manually here.
|
|
||||||
*
|
|
||||||
* @see [OkHttp issue 259](https://github.com/square/okhttp/issues/259)
|
|
||||||
*/
|
|
||||||
private fun Response.length(): Long {
|
|
||||||
networkResponse?.let {
|
|
||||||
val header = it.header("Content-Length") ?: return -1
|
|
||||||
return header.toLongOrDefault(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case it's a fully cached response
|
|
||||||
return body?.contentLength() ?: -1
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun File.deleteIfExists() {
|
|
||||||
if (exists() && !delete()) {
|
|
||||||
Log.e(TAG, "Could not delete file $this")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -112,7 +112,7 @@
|
||||||
app:layout_constraintStart_toStartOf="@id/guideAvatar"
|
app:layout_constraintStart_toStartOf="@id/guideAvatar"
|
||||||
app:layout_constraintTop_toTopOf="@+id/accountFollowButton" />
|
app:layout_constraintTop_toTopOf="@+id/accountFollowButton" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/accountDisplayNameTextView"
|
android:id="@+id/accountDisplayNameTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -215,7 +215,7 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountNoteTextInputLayout" />
|
app:layout_constraintTop_toBottomOf="@id/accountNoteTextInputLayout" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/accountNoteTextView"
|
android:id="@+id/accountNoteTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -257,7 +257,7 @@
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountRemoveView"
|
app:layout_constraintTop_toBottomOf="@id/accountRemoveView"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/accountMovedText"
|
android:id="@+id/accountMovedText"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -283,7 +283,7 @@
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountMovedText"
|
app:layout_constraintTop_toBottomOf="@id/accountMovedText"
|
||||||
tools:src="@drawable/avatar_default" />
|
tools:src="@drawable/avatar_default" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/accountMovedDisplayName"
|
android:id="@+id/accountMovedDisplayName"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
tools:text="Reply to @username"
|
tools:text="Reply to @username"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/composeReplyContentView"
|
android:id="@+id/composeReplyContentView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiEditText
|
<androidx.emoji2.widget.EmojiEditText
|
||||||
android:id="@+id/composeContentWarningField"
|
android:id="@+id/composeContentWarningField"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingTop="16dp">
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/item_blobmoji"
|
|
||||||
layout="@layout/item_emoji_pref" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/item_twemoji"
|
|
||||||
layout="@layout/item_emoji_pref" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/item_notoemoji"
|
|
||||||
layout="@layout/item_emoji_pref" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/item_nomoji"
|
|
||||||
layout="@layout/item_emoji_pref" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/emoji_download_label"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:lineSpacingMultiplier="1.1"
|
|
||||||
android:paddingStart="24dp"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingEnd="24dp"
|
|
||||||
android:paddingBottom="8dp"
|
|
||||||
android:text="@string/download_fonts"
|
|
||||||
android:textColor="?android:attr/textColorSecondary" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
|
@ -32,7 +32,7 @@
|
||||||
tools:src="#000"
|
tools:src="#000"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/account_display_name"
|
android:id="@+id/account_display_name"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
android:paddingTop="4dp">
|
android:paddingTop="4dp">
|
||||||
|
|
||||||
<!-- 30% width for the field name, 70% for the value -->
|
<!-- 30% width for the field name, 70% for the value -->
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/accountFieldName"
|
android:id="@+id/accountFieldName"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
app:layout_constraintWidth_percent=".3"
|
app:layout_constraintWidth_percent=".3"
|
||||||
tools:text="Field title" />
|
tools:text="Field title" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/accountFieldValue"
|
android:id="@+id/accountFieldValue"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/display_name"
|
android:id="@+id/display_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/blocked_user_display_name"
|
android:id="@+id/blocked_user_display_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
android:paddingStart="12dp"
|
android:paddingStart="12dp"
|
||||||
android:paddingEnd="14dp">
|
android:paddingEnd="14dp">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/conversation_name"
|
android:id="@+id/conversation_name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
tools:src="#000"
|
tools:src="#000"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_display_name"
|
android:id="@+id/status_display_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -122,7 +122,7 @@
|
||||||
app:layout_constraintTop_toTopOf="@id/status_display_name"
|
app:layout_constraintTop_toTopOf="@id/status_display_name"
|
||||||
tools:text="13:37" />
|
tools:text="13:37" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_content_warning_description"
|
android:id="@+id/status_content_warning_description"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -157,7 +157,7 @@
|
||||||
tools:text="@string/post_content_warning_show_more"
|
tools:text="@string/post_content_warning_show_more"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_content"
|
android:id="@+id/status_content"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/contentWarning"
|
android:id="@+id/contentWarning"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
app:layout_constraintTop_toBottomOf="@id/draftSendingInfo"
|
app:layout_constraintTop_toBottomOf="@id/draftSendingInfo"
|
||||||
tools:text="Some content warning" />
|
tools:text="Some content warning" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/content"
|
android:id="@+id/content"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:paddingBottom="8dp">
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiEditText
|
<androidx.emoji2.widget.EmojiEditText
|
||||||
android:id="@+id/accountFieldName"
|
android:id="@+id/accountFieldName"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
android:textColorHint="?android:attr/textColorTertiary"
|
android:textColorHint="?android:attr/textColorTertiary"
|
||||||
android:textSize="?attr/status_text_medium" />
|
android:textSize="?attr/status_text_medium" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiEditText
|
<androidx.emoji2.widget.EmojiEditText
|
||||||
android:id="@+id/accountFieldValue"
|
android:id="@+id/accountFieldValue"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
android:paddingRight="14dp"
|
android:paddingRight="14dp"
|
||||||
android:paddingBottom="10dp">
|
android:paddingBottom="10dp">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/notification_text"
|
android:id="@+id/notification_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
app:layout_constraintTop_toTopOf="@id/notification_display_name"
|
app:layout_constraintTop_toTopOf="@id/notification_display_name"
|
||||||
tools:src="@drawable/avatar_default" />
|
tools:src="@drawable/avatar_default" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/notification_display_name"
|
android:id="@+id/notification_display_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
android:paddingRight="16dp"
|
android:paddingRight="16dp"
|
||||||
android:paddingBottom="10dp">
|
android:paddingBottom="10dp">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/notificationTextView"
|
android:id="@+id/notificationTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/notificationTextView" />
|
app:layout_constraintTop_toBottomOf="@id/notificationTextView" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/displayNameTextView"
|
android:id="@+id/displayNameTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/muted_user_display_name"
|
android:id="@+id/muted_user_display_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_poll_option_result"
|
android:id="@+id/status_poll_option_result"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
app:layout_constraintGuide_begin="8dp" />
|
app:layout_constraintGuide_begin="8dp" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/statusContentWarningDescription"
|
android:id="@+id/statusContentWarningDescription"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
tools:text="@string/post_content_warning_show_more"
|
tools:text="@string/post_content_warning_show_more"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/statusContent"
|
android:id="@+id/statusContent"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -237,7 +237,7 @@
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_poll_option_result_0"
|
android:id="@+id/status_poll_option_result_0"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -257,7 +257,7 @@
|
||||||
app:layout_constraintTop_toBottomOf="@id/status_media_preview_container"
|
app:layout_constraintTop_toBottomOf="@id/status_media_preview_container"
|
||||||
tools:text="40%" />
|
tools:text="40%" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_poll_option_result_1"
|
android:id="@+id/status_poll_option_result_1"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -277,7 +277,7 @@
|
||||||
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_0"
|
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_0"
|
||||||
tools:text="10%" />
|
tools:text="10%" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_poll_option_result_2"
|
android:id="@+id/status_poll_option_result_2"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -297,7 +297,7 @@
|
||||||
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_1"
|
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_1"
|
||||||
tools:text="20%" />
|
tools:text="20%" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_poll_option_result_3"
|
android:id="@+id/status_poll_option_result_3"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
android:paddingLeft="14dp"
|
android:paddingLeft="14dp"
|
||||||
android:paddingRight="14dp">
|
android:paddingRight="14dp">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_info"
|
android:id="@+id/status_info"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
tools:src="#000"
|
tools:src="#000"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_display_name"
|
android:id="@+id/status_display_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -101,7 +101,7 @@
|
||||||
app:layout_constraintTop_toTopOf="@id/status_display_name"
|
app:layout_constraintTop_toTopOf="@id/status_display_name"
|
||||||
tools:text="13:37" />
|
tools:text="13:37" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_content_warning_description"
|
android:id="@+id/status_content_warning_description"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -141,7 +141,7 @@
|
||||||
tools:text="@string/post_content_warning_show_more"
|
tools:text="@string/post_content_warning_show_more"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_content"
|
android:id="@+id/status_content"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -191,7 +191,7 @@
|
||||||
android:paddingRight="6dp"
|
android:paddingRight="6dp"
|
||||||
android:paddingBottom="6dp">
|
android:paddingBottom="6dp">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/card_title"
|
android:id="@+id/card_title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -202,7 +202,7 @@
|
||||||
android:textColor="?android:textColorSecondary"
|
android:textColor="?android:textColorSecondary"
|
||||||
android:textSize="?attr/status_text_medium" />
|
android:textSize="?attr/status_text_medium" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/card_description"
|
android:id="@+id/card_description"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
tools:src="#000"
|
tools:src="#000"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_display_name"
|
android:id="@+id/status_display_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
app:layout_constraintTop_toBottomOf="@id/status_display_name"
|
app:layout_constraintTop_toBottomOf="@id/status_display_name"
|
||||||
tools:text="\@ConnyDuck\@mastodon.social" />
|
tools:text="\@ConnyDuck\@mastodon.social" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_content_warning_description"
|
android:id="@+id/status_content_warning_description"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -113,7 +113,7 @@
|
||||||
app:layout_constraintTop_toBottomOf="@+id/status_content_warning_description"
|
app:layout_constraintTop_toBottomOf="@+id/status_content_warning_description"
|
||||||
tools:text="@string/post_content_warning_show_more" />
|
tools:text="@string/post_content_warning_show_more" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_content"
|
android:id="@+id/status_content"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -162,7 +162,7 @@
|
||||||
android:paddingRight="6dp"
|
android:paddingRight="6dp"
|
||||||
android:paddingBottom="6dp">
|
android:paddingBottom="6dp">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/card_title"
|
android:id="@+id/card_title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -173,7 +173,7 @@
|
||||||
android:textColor="?android:textColorSecondary"
|
android:textColor="?android:textColorSecondary"
|
||||||
android:textSize="?attr/status_text_medium" />
|
android:textSize="?attr/status_text_medium" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/card_description"
|
android:id="@+id/card_description"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
android:paddingLeft="14dp"
|
android:paddingLeft="14dp"
|
||||||
android:paddingRight="14dp">
|
android:paddingRight="14dp">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/notification_top_text"
|
android:id="@+id/notification_top_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
android:layout_toEndOf="@+id/notification_status_avatar"
|
android:layout_toEndOf="@+id/notification_status_avatar"
|
||||||
android:paddingBottom="4dp">
|
android:paddingBottom="4dp">
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/status_display_name"
|
android:id="@+id/status_display_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/notification_content_warning_description"
|
android:id="@+id/notification_content_warning_description"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
style="@style/TuskyButton.Outlined"
|
style="@style/TuskyButton.Outlined"
|
||||||
android:textSize="?attr/status_text_medium" />
|
android:textSize="?attr/status_text_medium" />
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<TextView
|
||||||
android:id="@+id/notification_content"
|
android:id="@+id/notification_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -18,16 +18,16 @@ package com.keylesspalace.tusky
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.emoji.text.EmojiCompat
|
|
||||||
import com.keylesspalace.tusky.util.LocaleManager
|
import com.keylesspalace.tusky.util.LocaleManager
|
||||||
import de.c1710.filemojicompat.FileEmojiCompatConfig
|
import de.c1710.filemojicompat_defaults.DefaultEmojiPackList
|
||||||
|
import de.c1710.filemojicompat_ui.helpers.EmojiPackHelper
|
||||||
|
|
||||||
// override TuskyApplication for Robolectric tests, only initialize the necessary stuff
|
// override TuskyApplication for Robolectric tests, only initialize the necessary stuff
|
||||||
class TuskyApplication : Application() {
|
class TuskyApplication : Application() {
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
EmojiCompat.init(FileEmojiCompatConfig(this, ""))
|
EmojiPackHelper.init(this, DefaultEmojiPackList.get(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun attachBaseContext(base: Context) {
|
override fun attachBaseContext(base: Context) {
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
package com.keylesspalace.tusky.util
|
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
class EmojiCompatFontTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCompareVersions() {
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
-1,
|
|
||||||
EmojiCompatFont.compareVersions(
|
|
||||||
listOf(0),
|
|
||||||
listOf(1, 2, 3)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
assertEquals(
|
|
||||||
1,
|
|
||||||
EmojiCompatFont.compareVersions(
|
|
||||||
listOf(1, 2, 3),
|
|
||||||
listOf(0, 0, 0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
assertEquals(
|
|
||||||
-1,
|
|
||||||
EmojiCompatFont.compareVersions(
|
|
||||||
listOf(1, 0, 1),
|
|
||||||
listOf(1, 1, 0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
assertEquals(
|
|
||||||
0,
|
|
||||||
EmojiCompatFont.compareVersions(
|
|
||||||
listOf(4, 5, 6),
|
|
||||||
listOf(4, 5, 6)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
assertEquals(
|
|
||||||
0,
|
|
||||||
EmojiCompatFont.compareVersions(
|
|
||||||
listOf(0, 0),
|
|
||||||
listOf(0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue