@@ -2,7 +2,10 @@ package org.fossify.commons.activities
22
33import android.content.ContentValues
44import android.graphics.Color
5+ import android.graphics.Typeface
6+ import android.net.Uri
57import android.os.Bundle
8+ import androidx.activity.result.contract.ActivityResultContracts
69import org.fossify.commons.R
710import org.fossify.commons.databinding.ActivityCustomizationBinding
811import org.fossify.commons.dialogs.ColorPickerDialog
@@ -11,16 +14,19 @@ import org.fossify.commons.dialogs.ConfirmationDialog
1114import org.fossify.commons.dialogs.LineColorPickerDialog
1215import org.fossify.commons.dialogs.PurchaseThankYouDialog
1316import org.fossify.commons.dialogs.RadioGroupDialog
17+ import org.fossify.commons.extensions.applyFontToViewRecursively
1418import org.fossify.commons.extensions.baseConfig
1519import org.fossify.commons.extensions.beVisibleIf
1620import org.fossify.commons.extensions.canAccessGlobalConfig
1721import org.fossify.commons.extensions.checkAppIconColor
1822import org.fossify.commons.extensions.getColoredMaterialStatusBarColor
1923import org.fossify.commons.extensions.getContrastColor
24+ import org.fossify.commons.extensions.getFilenameFromUri
2025import org.fossify.commons.extensions.getProperPrimaryColor
2126import org.fossify.commons.extensions.getProperTextColor
2227import org.fossify.commons.extensions.getThemeId
2328import org.fossify.commons.extensions.isDynamicTheme
29+ import org.fossify.commons.extensions.isFontFile
2430import org.fossify.commons.extensions.isSystemInDarkMode
2531import org.fossify.commons.extensions.isThankYouInstalled
2632import org.fossify.commons.extensions.setFillWithStroke
@@ -32,9 +38,16 @@ import org.fossify.commons.extensions.withGlobalConfig
3238import org.fossify.commons.helpers.APP_ICON_IDS
3339import org.fossify.commons.helpers.APP_LAUNCHER_NAME
3440import org.fossify.commons.helpers.DARK_GREY
41+ import org.fossify.commons.helpers.FONT_TYPE_CUSTOM
42+ import org.fossify.commons.helpers.FONT_TYPE_MONOSPACE
43+ import org.fossify.commons.helpers.FONT_TYPE_SYSTEM_DEFAULT
44+ import org.fossify.commons.helpers.FontHelper
3545import org.fossify.commons.helpers.MyContentProvider.COL_ACCENT_COLOR
3646import org.fossify.commons.helpers.MyContentProvider.COL_APP_ICON_COLOR
3747import org.fossify.commons.helpers.MyContentProvider.COL_BACKGROUND_COLOR
48+ import org.fossify.commons.helpers.MyContentProvider.COL_FONT_DATA
49+ import org.fossify.commons.helpers.MyContentProvider.COL_FONT_NAME
50+ import org.fossify.commons.helpers.MyContentProvider.COL_FONT_TYPE
3851import org.fossify.commons.helpers.MyContentProvider.COL_PRIMARY_COLOR
3952import org.fossify.commons.helpers.MyContentProvider.COL_TEXT_COLOR
4053import org.fossify.commons.helpers.MyContentProvider.COL_THEME_TYPE
@@ -48,6 +61,7 @@ import org.fossify.commons.models.GlobalConfig
4861import org.fossify.commons.models.MyTheme
4962import org.fossify.commons.models.RadioItem
5063import org.fossify.commons.models.isGlobalThemingEnabled
64+ import java.io.File
5165import kotlin.math.abs
5266
5367class CustomizationActivity : BaseSimpleActivity () {
@@ -69,12 +83,19 @@ class CustomizationActivity : BaseSimpleActivity() {
6983 private var curAppIconColor = 0
7084 private var curSelectedThemeId = 0
7185 private var originalAppIconColor = 0
86+ private var curFontType = 0
87+ private var curFontFileName = " "
7288 private var lastSavePromptTS = 0L
7389 private var hasUnsavedChanges = false
7490 private val predefinedThemes = LinkedHashMap <Int , MyTheme >()
7591 private var curPrimaryLineColorPicker: LineColorPickerDialog ? = null
7692 private var globalConfig: GlobalConfig ? = null
7793
94+ private val fontFilePicker =
95+ registerForActivityResult(ActivityResultContracts .OpenDocument ()) { uri ->
96+ uri?.let { handleFontFileSelected(it) }
97+ }
98+
7899 override fun getAppIconIDs () = intent.getIntegerArrayListExtra(APP_ICON_IDS ) ? : ArrayList ()
79100
80101 override fun getAppLauncherName () = intent.getStringExtra(APP_LAUNCHER_NAME ) ? : " "
@@ -437,12 +458,16 @@ class CustomizationActivity : BaseSimpleActivity() {
437458 primaryColor = curPrimaryColor
438459 accentColor = curAccentColor
439460 appIconColor = curAppIconColor
461+ fontType = curFontType
462+ fontName = curFontFileName
440463 }
441464
442465 if (didAppIconColorChange) {
443466 checkAppIconColor()
444467 }
445468
469+ FontHelper .clearCache()
470+
446471 baseConfig.isGlobalThemeEnabled = binding.applyToAllSwitch.isChecked
447472 baseConfig.isSystemThemeEnabled = curSelectedThemeId == THEME_SYSTEM
448473
@@ -461,6 +486,13 @@ class CustomizationActivity : BaseSimpleActivity() {
461486 put(COL_PRIMARY_COLOR , curPrimaryColor)
462487 put(COL_ACCENT_COLOR , curAccentColor)
463488 put(COL_APP_ICON_COLOR , curAppIconColor)
489+ put(COL_FONT_TYPE , curFontType)
490+ put(COL_FONT_NAME , curFontFileName)
491+ if (curFontType == FONT_TYPE_CUSTOM && curFontFileName.isNotEmpty()) {
492+ FontHelper .getFontData(this @CustomizationActivity, curFontFileName)?.let {
493+ put(COL_FONT_DATA , it)
494+ }
495+ }
464496 }
465497 )
466498 }
@@ -490,6 +522,8 @@ class CustomizationActivity : BaseSimpleActivity() {
490522 curPrimaryColor = baseConfig.primaryColor
491523 curAccentColor = baseConfig.accentColor
492524 curAppIconColor = baseConfig.appIconColor
525+ curFontType = baseConfig.fontType
526+ curFontFileName = baseConfig.fontName
493527 }
494528
495529 private fun setupColorsPickers () {
@@ -526,6 +560,8 @@ class CustomizationActivity : BaseSimpleActivity() {
526560 }
527561 }
528562 }
563+
564+ setupFontPicker()
529565 }
530566
531567 private fun hasColorChanged (old : Int , new : Int ) = abs(old - new) > 1
@@ -536,6 +572,100 @@ class CustomizationActivity : BaseSimpleActivity() {
536572 refreshMenuItems()
537573 }
538574
575+ private fun setupFontPicker () {
576+ updateFontDisplay()
577+ binding.customizationFontHolder.setOnClickListener {
578+ fontPickerClicked()
579+ }
580+ }
581+
582+ private fun updateFontDisplay () {
583+ binding.customizationFont.text = when (curFontType) {
584+ FONT_TYPE_MONOSPACE -> getString(R .string.font_monospace)
585+ FONT_TYPE_CUSTOM -> curFontFileName.ifEmpty { getString(R .string.select_font_file) }
586+ else -> getString(R .string.font_system_default)
587+ }
588+ }
589+
590+ private fun fontPickerClicked () {
591+ val items = arrayListOf (
592+ RadioItem (FONT_TYPE_SYSTEM_DEFAULT , getString(R .string.font_system_default)),
593+ RadioItem (FONT_TYPE_MONOSPACE , getString(R .string.font_monospace)),
594+ RadioItem (FONT_TYPE_CUSTOM , getString(R .string.select_font_file))
595+ )
596+
597+ RadioGroupDialog (this , items, curFontType) { selected ->
598+ val selectedType = selected as Int
599+ if (selectedType == FONT_TYPE_CUSTOM ) {
600+ openFontFilePicker()
601+ } else {
602+ curFontType = selectedType
603+ curFontFileName = " "
604+ fontChanged()
605+ }
606+ }
607+ }
608+
609+ private fun openFontFilePicker () {
610+ try {
611+ fontFilePicker.launch(
612+ arrayOf(
613+ " font/ttf" ,
614+ " font/otf" ,
615+ " application/x-font-ttf" ,
616+ " application/x-font-otf" ,
617+ " */*"
618+ )
619+ )
620+ } catch (e: Exception ) {
621+ toast(R .string.system_service_disabled)
622+ }
623+ }
624+
625+ private fun handleFontFileSelected (uri : Uri ) {
626+ try {
627+ val fileName = getFilenameFromUri(uri)
628+ if (fileName.isEmpty() || ! fileName.isFontFile()) {
629+ toast(R .string.invalid_font_file)
630+ return
631+ }
632+
633+ val fontData = contentResolver.openInputStream(uri)?.use { it.readBytes() }
634+ if (fontData == null ) {
635+ toast(R .string.invalid_font_file)
636+ return
637+ }
638+
639+ val tempFile = File (cacheDir, fileName)
640+ tempFile.writeBytes(fontData)
641+ try {
642+ Typeface .createFromFile(tempFile)
643+ } catch (_: Exception ) {
644+ tempFile.delete()
645+ toast(R .string.invalid_font_file)
646+ return
647+ }
648+ tempFile.delete()
649+
650+ if (FontHelper .saveFontData(this , fontData, fileName)) {
651+ curFontType = FONT_TYPE_CUSTOM
652+ curFontFileName = fileName
653+ fontChanged()
654+ } else {
655+ toast(R .string.invalid_font_file)
656+ }
657+ } catch (_: Exception ) {
658+ toast(R .string.invalid_font_file)
659+ }
660+ }
661+
662+ private fun fontChanged () {
663+ hasUnsavedChanges = true
664+ updateFontDisplay()
665+ applyFontToViewRecursively(window.decorView)
666+ refreshMenuItems()
667+ }
668+
539669 private fun setCurrentTextColor (color : Int ) {
540670 curTextColor = color
541671 updateLabelColors(color)
@@ -707,6 +837,8 @@ class CustomizationActivity : BaseSimpleActivity() {
707837 binding.customizationPrimaryColorLabel,
708838 binding.customizationAccentColorLabel,
709839 binding.customizationAppIconColorLabel,
840+ binding.customizationFontLabel,
841+ binding.customizationFont,
710842 binding.applyToAllLabel,
711843 binding.applyToAllNote
712844 ).forEach {
@@ -717,6 +849,7 @@ class CustomizationActivity : BaseSimpleActivity() {
717849 private fun updateHeaderColors (primaryColor : Int = getProperPrimaryColor()) {
718850 arrayListOf (
719851 binding.settingsThemeAndColorsLabel,
852+ binding.settingsFontLabel,
720853 binding.settingsAllFossifyAppsLabel
721854 ).forEach {
722855 it.setTextColor(primaryColor)
0 commit comments