diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 1e606d2..3fdeb0f 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/app/build.gradle b/app/build.gradle index 9546c4b..2b532b5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -122,4 +122,7 @@ dependencies { // Permissions implementation 'com.karumi:dexter:6.1.0' + + // Fonts + implementation 'com.github.firatkarababa:downloadable-font-list-library:1.0.2' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 12c4db4..fdc05c4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -32,6 +32,7 @@ + diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/global/Constants.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/global/Constants.kt index 14d6a91..dd39166 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/global/Constants.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/global/Constants.kt @@ -8,6 +8,9 @@ object Constants { const val RESULT_APP_PACKAGE = "RESULT_APP_PACKAGE" const val CUSTOM_FONT_GOOGLE_SANS = 1 + const val CUSTOM_FONT_DOWNLOADED = 2 + const val CUSTOM_FONT_DOWNLOAD_NEW = 3 + enum class ClockBottomMargin(val value: Int) { NONE(0), SMALL(1), diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/global/Preferences.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/global/Preferences.kt index a9ba5a3..29ca963 100755 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/global/Preferences.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/global/Preferences.kt @@ -92,7 +92,8 @@ object Preferences : KotprefModel() { var showOnlyBusyEvents by booleanPref(default = false) var secondRowInformation by intPref(key = "PREF_SECOND_ROW_INFORMATION", default = 0) var customFont by intPref(key = "PREF_CUSTOM_FONT", default = Constants.CUSTOM_FONT_GOOGLE_SANS) - var customFontFile by stringPref(key = "PREF_CUSTOM_FONT_FILE") + var customFontFile by stringPref(default = "") + var customFontName by stringPref(default = "") var showNextEvent by booleanPref(key = "PREF_SHOW_NEXT_EVENT", default = true) var showDividers by booleanPref(default = true) diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/SettingsStringHelper.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/SettingsStringHelper.kt index e9b1148..bb51343 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/SettingsStringHelper.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/SettingsStringHelper.kt @@ -54,11 +54,12 @@ object SettingsStringHelper { } } - fun getCustomFontLabel(shadow: Int): Int { - return when (shadow) { - 0 -> R.string.custom_font_subtitle_0 - 1 -> R.string.custom_font_subtitle_1 - else -> R.string.custom_font_subtitle_1 + fun getCustomFontLabel(context: Context, font: Int): String { + return when (font) { + 0 -> context.getString(R.string.custom_font_subtitle_0) + Constants.CUSTOM_FONT_GOOGLE_SANS -> context.getString(R.string.custom_font_subtitle_1) + Constants.CUSTOM_FONT_DOWNLOADED -> Preferences.customFontName + else -> context.getString(R.string.custom_font_subtitle_1) } } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WidgetHelper.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WidgetHelper.kt index 728b92e..d87f7cc 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WidgetHelper.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WidgetHelper.kt @@ -3,10 +3,20 @@ package com.tommasoberlose.anotherwidget.helpers import android.appwidget.AppWidgetManager import android.content.Context import android.content.res.Configuration.ORIENTATION_PORTRAIT +import android.graphics.Typeface +import android.os.Handler +import android.os.HandlerThread +import android.os.Looper import android.util.Log +import androidx.core.provider.FontRequest +import androidx.core.provider.FontsContractCompat import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.db.EventRepository import com.tommasoberlose.anotherwidget.global.Preferences +import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget +import com.tommasoberlose.anotherwidget.utils.toPixel +import kotlin.math.min object WidgetHelper { class WidgetSizeProvider( @@ -44,4 +54,37 @@ object WidgetHelper { width to second * factor } } + + fun runWithCustomTypeface(context: Context, function: (typeface: Typeface?) -> Unit) { + if (Preferences.customFontFile != "") { + val request = FontRequest( + "com.google.android.gms.fonts", + "com.google.android.gms", + Preferences.customFontFile, + R.array.com_google_android_gms_fonts_certs + ) + + val callback = object : FontsContractCompat.FontRequestCallback() { + override fun onTypefaceRetrieved(typeface: Typeface) { + function.invoke(typeface) + } + + override fun onTypefaceRequestFailed(reason: Int) { + function.invoke(null) + } + } + + val handlerThread = HandlerThread("generateView") + handlerThread.start() + if (Looper.myLooper() == null) { + Looper.prepare() + } + + Handler(handlerThread.looper).run { + FontsContractCompat.requestFont(context, request, callback, this) + } + } else { + function.invoke(null) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/CustomFontActivity.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/CustomFontActivity.kt new file mode 100644 index 0000000..d8762b7 --- /dev/null +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/CustomFontActivity.kt @@ -0,0 +1,167 @@ +package com.tommasoberlose.anotherwidget.ui.activities + +import android.app.Activity +import android.content.Intent +import android.graphics.Typeface +import android.os.Bundle +import android.os.Handler +import android.os.HandlerThread +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.core.provider.FontRequest +import androidx.core.provider.FontsContractCompat +import androidx.core.view.isVisible +import androidx.databinding.DataBindingUtil +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import com.chibatching.kotpref.blockingBulk +import com.koolio.library.Font +import com.tommasoberlose.anotherwidget.R +import com.tommasoberlose.anotherwidget.databinding.ActivityCustomFontBinding +import com.tommasoberlose.anotherwidget.global.Constants +import com.tommasoberlose.anotherwidget.global.Preferences +import com.tommasoberlose.anotherwidget.ui.viewmodels.CustomFontViewModel +import kotlinx.android.synthetic.main.activity_choose_application.* +import kotlinx.coroutines.* +import net.idik.lib.slimadapter.SlimAdapter +import net.idik.lib.slimadapter.diff.DefaultDiffCallback + + +class CustomFontActivity : AppCompatActivity() { + + private lateinit var adapter: SlimAdapter + private lateinit var viewModel: CustomFontViewModel + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + viewModel = ViewModelProvider(this).get(CustomFontViewModel::class.java) + val binding = DataBindingUtil.setContentView( + this, + R.layout.activity_custom_font + ) + + list_view.setHasFixedSize(true) + val mLayoutManager = LinearLayoutManager(this) + list_view.layoutManager = mLayoutManager + + adapter = SlimAdapter.create() + adapter.enableDiff(object: DefaultDiffCallback() { + override fun areItemsTheSame(oldItem: Any?, newItem: Any?): Boolean { + return oldItem is Font && newItem is Font && oldItem.fontFamily == newItem.fontFamily + } + + override fun areContentsTheSame(oldItem: Any?, newItem: Any?): Boolean { + return oldItem is Font && newItem is Font && oldItem.fontFamily == newItem.fontFamily + } + }) + adapter + .register(R.layout.list_item) { item, injector -> + injector + .text(R.id.text, item.fontFamily) + .with(R.id.text) { + val request = FontRequest( + "com.google.android.gms.fonts", + "com.google.android.gms", + item.queryString, + R.array.com_google_android_gms_fonts_certs + ) + + + val callback = object : FontsContractCompat.FontRequestCallback() { + override fun onTypefaceRetrieved(typeface: Typeface) { + it.typeface = typeface + it.isVisible = true + + it.measure( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + + override fun onTypefaceRequestFailed(reason: Int) { + it.isVisible = false + it.layoutParams = it.layoutParams.apply { + height = 0 + } + } + } + + val handlerThread = HandlerThread(item.fontFamily) + handlerThread.start() + val mHandler = Handler(handlerThread.looper) + FontsContractCompat.requestFont(this, request, callback, mHandler) + } + + injector.clicked(R.id.text) { + saveFont(item) + } + } + .attachTo(list_view) + + setupListener() + subscribeUi(binding, viewModel) + + search.requestFocus() + } + + private var filterJob: Job? = null + + private fun subscribeUi(binding: ActivityCustomFontBinding, viewModel: CustomFontViewModel) { + binding.viewModel = viewModel + + viewModel.fontList.observe(this, Observer { + updateList(list = it) + loader.visibility = View.INVISIBLE + }) + + viewModel.searchInput.observe(this, Observer { search -> + updateList(search = search) + }) + } + + private fun updateList( + list: ArrayList? = viewModel.fontList.value, + search: String? = viewModel.searchInput.value + ) { + loader.visibility = View.VISIBLE + filterJob?.cancel() + filterJob = lifecycleScope.launch(Dispatchers.IO) { + if (list != null && list.isNotEmpty()) { + delay(200) + val filteredList: List = if (search == null || search == "") { + list + } else { + list.filter { + it.fontFamily.contains(search, true) + } + }.distinctBy { it.fontFamily } + withContext(Dispatchers.Main) { + adapter.updateData(filteredList) + loader.visibility = View.INVISIBLE + } + } + } + } + + private fun setupListener() { + action_back.setOnClickListener { + onBackPressed() + } + } + + private fun saveFont(font: Font) { + val resultIntent = Intent() + Preferences.blockingBulk { + customFont = Constants.CUSTOM_FONT_DOWNLOADED + customFontName = font.fontFamily + customFontFile = font.queryString + } + setResult(Activity.RESULT_OK, resultIntent) + finish() + } +} diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/GeneralTabFragment.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/GeneralTabFragment.kt index b99d2b6..340def6 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/GeneralTabFragment.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/GeneralTabFragment.kt @@ -3,16 +3,24 @@ package com.tommasoberlose.anotherwidget.ui.fragments import android.annotation.SuppressLint import android.app.Activity import android.content.Intent +import android.graphics.Typeface import android.os.Bundle +import android.os.Handler +import android.os.HandlerThread +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast +import androidx.core.provider.FontRequest +import androidx.core.provider.FontsContractCompat import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.chibatching.kotpref.blockingBulk +import com.chibatching.kotpref.bulk import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker import com.tommasoberlose.anotherwidget.components.BottomSheetMenu @@ -25,9 +33,13 @@ import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toHexValue import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue import com.tommasoberlose.anotherwidget.helpers.DateHelper import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper +import com.tommasoberlose.anotherwidget.helpers.WidgetHelper +import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity import com.tommasoberlose.anotherwidget.ui.activities.CustomDateActivity +import com.tommasoberlose.anotherwidget.ui.activities.CustomFontActivity import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel +import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.utils.isDarkTheme import kotlinx.android.synthetic.main.fragment_clock_settings.* import kotlinx.android.synthetic.main.fragment_general_settings.* @@ -271,7 +283,15 @@ class GeneralTabFragment : Fragment() { viewModel.customFont.observe(viewLifecycleOwner, Observer { maintainScrollPosition { - custom_font_label?.text = getString(SettingsStringHelper.getCustomFontLabel(it)) + custom_font_label?.text = SettingsStringHelper.getCustomFontLabel(requireContext(), it) + MainWidget.updateWidget(requireContext()) + } + }) + + viewModel.customFontFile.observe(viewLifecycleOwner, Observer { + maintainScrollPosition { + custom_font_label?.text = SettingsStringHelper.getCustomFontLabel(requireContext(), Preferences.customFont) + MainWidget.updateWidget(requireContext()) } }) @@ -455,10 +475,25 @@ class GeneralTabFragment : Fragment() { action_custom_font.setOnClickListener { val dialog = BottomSheetMenu(requireContext(), header = getString(R.string.settings_custom_font_title)).setSelectedValue(Preferences.customFont) (0..1).forEach { - dialog.addItem(getString(SettingsStringHelper.getCustomFontLabel(it)), it) + dialog.addItem(SettingsStringHelper.getCustomFontLabel(requireContext(), it), it) } + if (Preferences.customFontFile != "") { + dialog.addItem(Preferences.customFontName, Constants.CUSTOM_FONT_DOWNLOADED) + } + dialog.addItem(getString(R.string.action_custom_font_to_search), Constants.CUSTOM_FONT_DOWNLOAD_NEW) dialog.addOnSelectItemListener { value -> - Preferences.customFont = value + if (value == Constants.CUSTOM_FONT_DOWNLOAD_NEW) { + startActivityForResult( + Intent(requireContext(), CustomFontActivity::class.java), + RequestCode.CUSTOM_FONT_CHOOSER_REQUEST_CODE.code + ) + } else if (value != Constants.CUSTOM_FONT_DOWNLOADED) { + Preferences.bulk { + customFont = value + customFontFile = "" + customFontName = "" + } + } }.show() /* @@ -487,15 +522,7 @@ class GeneralTabFragment : Fragment() { if (resultCode == Activity.RESULT_OK) { when (requestCode) { RequestCode.CUSTOM_FONT_CHOOSER_REQUEST_CODE.code -> { - /*val uri = data.data - Log.d("AW", "File Uri: " + uri.toString()) - val path = Util.getPath(this, uri) - Log.d("AW", "File Path: " + path) - SP.edit() - .putString(Constants.PREF_CUSTOM_FONT_FILE, path) - .commit() - sendBroadcast(Intent(Constants.ACTION_TIME_UPDATE)) - updateSettings()*/ + } } } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt index b7168f0..27e35ec 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt @@ -36,6 +36,7 @@ import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.helpers.BitmapHelper import com.tommasoberlose.anotherwidget.helpers.ColorHelper import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark +import com.tommasoberlose.anotherwidget.helpers.WidgetHelper import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.adapters.ViewPagerAdapter import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel @@ -147,133 +148,136 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList ColorHelper.getBackgroundColor(activity?.isDarkTheme() == true) ) ) - uiJob = lifecycleScope.launch(Dispatchers.IO) { - val generatedView = MainWidget.generateWidgetView(requireContext()) + WidgetHelper.runWithCustomTypeface(requireContext()) { typeface -> + uiJob = lifecycleScope.launch(Dispatchers.IO) { + val generatedView = MainWidget.generateWidgetView(requireContext(), typeface) - withContext(Dispatchers.Main) { - generatedView.measure(0, 0) - preview?.measure(0, 0) - } + withContext(Dispatchers.Main) { + generatedView.measure(0, 0) + preview?.measure(0, 0) + } - val bitmap = if (preview != null) { - BitmapHelper.getBitmapFromView( - generatedView, - if (preview.width > 0) preview.width else generatedView.measuredWidth, - generatedView.measuredHeight - ) - } else { - null - } - withContext(Dispatchers.Main) { - // Clock - time?.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true)) - time_am_pm?.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true)) - time?.setTextSize( - TypedValue.COMPLEX_UNIT_SP, - Preferences.clockTextSize.toPixel(requireContext()) - ) - time_am_pm?.setTextSize( - TypedValue.COMPLEX_UNIT_SP, - Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2 - ) - time_am_pm?.isVisible = Preferences.showAMPMIndicator + val bitmap = if (preview != null) { + BitmapHelper.getBitmapFromView( + generatedView, + if (preview.width > 0) preview.width else generatedView.measuredWidth, + generatedView.measuredHeight + ) + } else { + null + } + withContext(Dispatchers.Main) { + // Clock + time?.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true)) + time_am_pm?.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true)) + time?.setTextSize( + TypedValue.COMPLEX_UNIT_SP, + Preferences.clockTextSize.toPixel(requireContext()) + ) + time_am_pm?.setTextSize( + TypedValue.COMPLEX_UNIT_SP, + Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2 + ) + time_am_pm?.isVisible = Preferences.showAMPMIndicator - // Clock bottom margin - clock_bottom_margin_none?.isVisible = - Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.value - clock_bottom_margin_small?.isVisible = - Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.value - clock_bottom_margin_medium?.isVisible = - Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.value - clock_bottom_margin_large?.isVisible = - Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.value + // Clock bottom margin + clock_bottom_margin_none?.isVisible = + Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.value + clock_bottom_margin_small?.isVisible = + Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.value + clock_bottom_margin_medium?.isVisible = + Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.value + clock_bottom_margin_large?.isVisible = + Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.value - if ((Preferences.showClock && (time?.alpha ?: 1f < 1f)) || (!Preferences.showClock && (time?.alpha ?: 0f > 0f))) { - if (Preferences.showClock) { + if ((Preferences.showClock && (time?.alpha ?: 1f < 1f)) || (!Preferences.showClock && (time?.alpha ?: 0f > 0f))) { + if (Preferences.showClock) { + time_container?.layoutParams = time_container.layoutParams.apply { + height = RelativeLayout.LayoutParams.WRAP_CONTENT + } + time_container?.measure(0, 0) + } + val initialHeight = time_container?.measuredHeight ?: 0 + ValueAnimator.ofFloat( + if (Preferences.showClock) 0f else 1f, + if (Preferences.showClock) 1f else 0f + ).apply { + duration = 500L + addUpdateListener { + val animatedValue = animatedValue as Float + time_container?.layoutParams = + time_container.layoutParams.apply { + height = (initialHeight * animatedValue).toInt() + } + time?.alpha = animatedValue + } + addListener( + onStart = { + if (Preferences.showClock) { + time_container?.isVisible = true + } + }, + onEnd = { + if (!Preferences.showClock) { + time_container?.isVisible = false + } + } + ) + }.start() + + if (preview != null) { + ValueAnimator.ofInt( + preview.height, + PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel( + requireContext() + ) else 0 + ).apply { + duration = 500L + addUpdateListener { + if (preview != null) { + val animatedValue = animatedValue as Int + val layoutParams = preview.layoutParams + layoutParams.height = animatedValue + preview.layoutParams = layoutParams + } + } + }.start() + } + } else { time_container?.layoutParams = time_container.layoutParams.apply { height = RelativeLayout.LayoutParams.WRAP_CONTENT } time_container?.measure(0, 0) } - val initialHeight = time_container?.measuredHeight ?: 0 - ValueAnimator.ofFloat( - if (Preferences.showClock) 0f else 1f, - if (Preferences.showClock) 1f else 0f - ).apply { - duration = 500L - addUpdateListener { - val animatedValue = animatedValue as Float - time_container?.layoutParams = - time_container.layoutParams.apply { - height = (initialHeight * animatedValue).toInt() - } - time?.alpha = animatedValue - } - addListener( - onStart = { - if (Preferences.showClock) { - time_container?.isVisible = true - } - }, - onEnd = { - if (!Preferences.showClock) { - time_container?.isVisible = false - } - } - ) - }.start() - if (preview != null) { + if (preview != null && preview.height == 0) { ValueAnimator.ofInt( preview.height, PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel( requireContext() ) else 0 ).apply { - duration = 500L + duration = 300L addUpdateListener { if (preview != null) { val animatedValue = animatedValue as Int val layoutParams = preview.layoutParams layoutParams.height = animatedValue - preview.layoutParams = layoutParams + preview?.layoutParams = layoutParams } } }.start() } - } else { - time_container?.layoutParams = time_container.layoutParams.apply { - height = RelativeLayout.LayoutParams.WRAP_CONTENT + + widget_loader?.animate()?.scaleX(0f)?.scaleY(0f)?.alpha(0f) + ?.setDuration(200L)?.start() + bitmap_container?.apply { + setImageBitmap(bitmap) + scaleX = 0.9f + scaleY = 0.9f } - time_container?.measure(0, 0) + widget?.animate()?.alpha(1f)?.start() } - - if (preview != null && preview.height == 0) { - ValueAnimator.ofInt( - preview.height, - PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel( - requireContext() - ) else 0 - ).apply { - duration = 300L - addUpdateListener { - if (preview != null) { - val animatedValue = animatedValue as Int - val layoutParams = preview.layoutParams - layoutParams.height = animatedValue - preview?.layoutParams = layoutParams - } - } - }.start() - } - - widget_loader?.animate()?.scaleX(0f)?.scaleY(0f)?.alpha(0f)?.setDuration(200L)?.start() - bitmap_container?.apply { - setImageBitmap(bitmap) - scaleX = 0.9f - scaleY = 0.9f - } - widget?.animate()?.alpha(1f)?.start() } } } else { diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/viewmodels/CustomFontViewModel.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/viewmodels/CustomFontViewModel.kt new file mode 100644 index 0000000..b27fa27 --- /dev/null +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/viewmodels/CustomFontViewModel.kt @@ -0,0 +1,39 @@ +package com.tommasoberlose.anotherwidget.ui.viewmodels + +import android.app.Application +import android.content.Intent +import android.content.pm.ResolveInfo +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import com.koolio.library.DownloadableFontList +import com.koolio.library.Font +import com.koolio.library.FontList +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class CustomFontViewModel(application: Application) : AndroidViewModel(application) { + + val fontList: MutableLiveData> = MutableLiveData() + val searchInput: MutableLiveData = MutableLiveData("") + + init { + viewModelScope.launch(Dispatchers.IO) { + + + val fontListCallback: DownloadableFontList.FontListCallback = + object : DownloadableFontList.FontListCallback { + override fun onFontListRetrieved(downloadedList: FontList?) { + fontList.postValue(downloadedList?.fontArrayList) + } + + override fun onTypefaceRequestFailed(reason: Int) { + + } + } + + DownloadableFontList.requestDownloadableFontList(fontListCallback, "AIzaSyCT_v2Qw1zCWfmc_ywsovj_4poSXr7k5f0") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/viewmodels/MainViewModel.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/viewmodels/MainViewModel.kt index c6b7354..1944edc 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/viewmodels/MainViewModel.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/viewmodels/MainViewModel.kt @@ -24,6 +24,7 @@ class MainViewModel : ViewModel() { val textShadow = Preferences.asLiveData(Preferences::textShadow) val textShadowDark = Preferences.asLiveData(Preferences::textShadowDark) val customFont = Preferences.asLiveData(Preferences::customFont) + val customFontFile = Preferences.asLiveData(Preferences::customFontFile) val secondRowInformation = Preferences.asLiveData(Preferences::secondRowInformation) val showDividers = Preferences.asLiveData(Preferences::showDividers) val secondRowTopMargin = Preferences.asLiveData(Preferences::secondRowTopMargin) diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt index a90532e..d7ca1f6 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt @@ -11,12 +11,18 @@ import android.content.res.Resources import android.graphics.Color import android.graphics.Typeface import android.os.Bundle +import android.os.Handler +import android.os.HandlerThread +import android.os.Looper import android.text.format.DateUtils import android.util.Log import android.util.TypedValue import android.view.View +import android.view.ViewGroup import android.widget.* import androidx.core.content.ContextCompat +import androidx.core.provider.FontRequest +import androidx.core.provider.FontsContractCompat import androidx.core.view.isVisible import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.db.EventRepository @@ -90,10 +96,14 @@ class MainWidget : AppWidgetProvider() { val height = displayMetrics.heightPixels val dimensions = WidgetHelper.WidgetSizeProvider(context, appWidgetManager).getWidgetsSize(appWidgetId) - generateWidgetView(context, appWidgetId, appWidgetManager, min(dimensions.first - 8.toPixel(context), min(width, height) - 16.toPixel(context))) + + WidgetHelper.runWithCustomTypeface(context) { + generateWidgetView(context, appWidgetId, appWidgetManager, min(dimensions.first - 8.toPixel(context), min(width, height) - 16.toPixel(context)), it) + } } - private fun generateWidgetView(context: Context, appWidgetId: Int, appWidgetManager: AppWidgetManager, w: Int) { + private fun generateWidgetView(context: Context, appWidgetId: Int, appWidgetManager: AppWidgetManager, w: Int, typeface: Typeface? = null) { + var views = RemoteViews(context.packageName, R.layout.the_widget_sans) try { @@ -125,7 +135,8 @@ class MainWidget : AppWidgetProvider() { // Setup listener try { - val generatedView = generateWidgetView(context) + + val generatedView = generateWidgetView(context, typeface) views.setImageViewBitmap( R.id.bitmap_container, BitmapHelper.getBitmapFromView(generatedView, width = w) @@ -492,7 +503,7 @@ class MainWidget : AppWidgetProvider() { // Generates the widget bitmap from the view - fun generateWidgetView(context: Context): View { + fun generateWidgetView(context: Context, typeface: Typeface? = null): View { val eventRepository = EventRepository(context) val v = View.inflate(context, R.layout.the_widget, null) @@ -514,8 +525,10 @@ class MainWidget : AppWidgetProvider() { if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null) { // Multiple counter - v.action_next.isVisible = Preferences.showNextEvent && eventRepository.getEventsCount() > 1 - v.action_previous.isVisible = Preferences.showNextEvent && eventRepository.getEventsCount() > 1 + v.action_next.isVisible = + Preferences.showNextEvent && eventRepository.getEventsCount() > 1 + v.action_previous.isVisible = + Preferences.showNextEvent && eventRepository.getEventsCount() > 1 v.next_event.text = nextEvent.title @@ -528,7 +541,11 @@ class MainWidget : AppWidgetProvider() { ) .toLowerCase(Locale.getDefault()) } else { - SettingsStringHelper.getAllDayEventDifferenceText(context, now.timeInMillis, nextEvent.startDate).toLowerCase(Locale.getDefault()) + SettingsStringHelper.getAllDayEventDifferenceText( + context, + now.timeInMillis, + nextEvent.startDate + ).toLowerCase(Locale.getDefault()) } v.next_event_difference_time.visibility = View.VISIBLE } else { @@ -536,15 +553,30 @@ class MainWidget : AppWidgetProvider() { } if (nextEvent.address != "" && Preferences.secondRowInformation == 1) { - v.second_row_icon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.round_place)) + v.second_row_icon.setImageDrawable( + ContextCompat.getDrawable( + context, + R.drawable.round_place + ) + ) v.next_event_date.text = nextEvent.address } else { - v.second_row_icon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.round_today)) + v.second_row_icon.setImageDrawable( + ContextCompat.getDrawable( + context, + R.drawable.round_today + ) + ) if (!nextEvent.allDay) { - val startHour = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()).format(nextEvent.startDate) - val endHour = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()).format(nextEvent.endDate) + val startHour = + DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()) + .format(nextEvent.startDate) + val endHour = + DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()) + .format(nextEvent.endDate) - var dayDiff = TimeUnit.MILLISECONDS.toDays(nextEvent.endDate - nextEvent.startDate) + var dayDiff = + TimeUnit.MILLISECONDS.toDays(nextEvent.endDate - nextEvent.startDate) val startCal = Calendar.getInstance() startCal.timeInMillis = nextEvent.startDate @@ -554,31 +586,46 @@ class MainWidget : AppWidgetProvider() { if (startCal.get(Calendar.HOUR_OF_DAY) > endCal.get(Calendar.HOUR_OF_DAY)) { dayDiff++ - } else if (startCal.get(Calendar.HOUR_OF_DAY) == endCal.get(Calendar.HOUR_OF_DAY) && startCal.get(Calendar.MINUTE) >= endCal.get(Calendar.MINUTE)) { + } else if (startCal.get(Calendar.HOUR_OF_DAY) == endCal.get(Calendar.HOUR_OF_DAY) && startCal.get( + Calendar.MINUTE + ) >= endCal.get(Calendar.MINUTE) + ) { dayDiff++ } var multipleDay = "" if (dayDiff > 0) { - multipleDay = String.format(" (+%s%s)", dayDiff, context.getString(R.string.day_char)) + multipleDay = String.format( + " (+%s%s)", + dayDiff, + context.getString(R.string.day_char) + ) } - v.next_event_date.text = String.format("%s - %s%s", startHour, endHour, multipleDay) + v.next_event_date.text = + String.format("%s - %s%s", startHour, endHour, multipleDay) } else { - val flags: Int = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH - v.next_event_date.text = DateUtils.formatDateTime(context, nextEvent.startDate, flags) + val flags: Int = + DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH + v.next_event_date.text = + DateUtils.formatDateTime(context, nextEvent.startDate, flags) } } v.empty_layout.visibility = View.GONE v.calendar_layout.visibility = View.VISIBLE - v.second_row_top_margin_small.visibility = if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.value) View.VISIBLE else View.GONE - v.second_row_top_margin_medium.visibility = if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.value) View.VISIBLE else View.GONE - v.second_row_top_margin_large.visibility = if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE + v.second_row_top_margin_small.visibility = + if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.value) View.VISIBLE else View.GONE + v.second_row_top_margin_medium.visibility = + if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.value) View.VISIBLE else View.GONE + v.second_row_top_margin_large.visibility = + if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE } else if (GlanceProviderHelper.showGlanceProviders(context)) { v.second_row_icon.isVisible = true var showSomething = false - loop@ for (provider:Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) { + loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders( + context + )) { when (provider) { Constants.GlanceProviderId.PLAYING_SONG -> { if (MediaPlayerHelper.isSomeonePlaying(context)) { @@ -613,9 +660,13 @@ class MainWidget : AppWidgetProvider() { v.second_row_icon.isVisible = false val batteryLevel = BatteryHelper.getBatteryLevel(context) if (batteryLevel == 100) { - v.next_event_date.text = "%s - %d%%".format(context.getString(R.string.charging), batteryLevel) + v.next_event_date.text = "%s - %d%%".format( + context.getString(R.string.charging), + batteryLevel + ) } else { - v.next_event_date.text = context.getString(R.string.charging) + v.next_event_date.text = + context.getString(R.string.charging) } showSomething = true break@loop @@ -641,7 +692,9 @@ class MainWidget : AppWidgetProvider() { Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> { if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) { v.second_row_icon.isVisible = false - v.next_event_date.text = context.getString(R.string.daily_steps_counter).format(Preferences.googleFitSteps) + v.next_event_date.text = + context.getString(R.string.daily_steps_counter) + .format(Preferences.googleFitSteps) showSomething = true break@loop } @@ -654,9 +707,12 @@ class MainWidget : AppWidgetProvider() { v.empty_layout.visibility = View.GONE v.calendar_layout.visibility = View.VISIBLE - v.second_row_top_margin_small.visibility = if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.value) View.VISIBLE else View.GONE - v.second_row_top_margin_medium.visibility = if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.value) View.VISIBLE else View.GONE - v.second_row_top_margin_large.visibility = if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE + v.second_row_top_margin_small.visibility = + if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.value) View.VISIBLE else View.GONE + v.second_row_top_margin_medium.visibility = + if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.value) View.VISIBLE else View.GONE + v.second_row_top_margin_large.visibility = + if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE } else { v.second_row_icon.isVisible = false } @@ -664,17 +720,33 @@ class MainWidget : AppWidgetProvider() { // Color - listOf(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.divider3, v.special_temp).forEach { + listOf( + v.empty_date, + v.divider1, + v.temp, + v.next_event, + v.next_event_difference_time, + v.divider3, + v.special_temp + ).forEach { it.setTextColor(ColorHelper.getFontColor(context.applicationContext.isDarkTheme())) } if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.value) { listOf(v.action_next, v.action_previous) } else { - listOf(v.action_next, v.action_previous, v.empty_weather_icon, v.special_weather_icon) + listOf( + v.action_next, + v.action_previous, + v.empty_weather_icon, + v.special_weather_icon + ) }.forEach { it.setColorFilter(ColorHelper.getFontColorRgb(context.applicationContext.isDarkTheme())) - it.alpha = (if (context.isDarkTheme()) Preferences.textGlobalAlphaDark.toIntValue().toFloat() else Preferences.textGlobalAlpha.toIntValue().toFloat()) / 100 + it.alpha = + (if (context.isDarkTheme()) Preferences.textGlobalAlphaDark.toIntValue() + .toFloat() else Preferences.textGlobalAlpha.toIntValue() + .toFloat()) / 100 } listOf(v.next_event_date, v.divider2, v.calendar_temp).forEach { @@ -687,7 +759,10 @@ class MainWidget : AppWidgetProvider() { listOf(v.second_row_icon, v.weather_icon) }.forEach { it.setColorFilter(ColorHelper.getSecondaryFontColorRgb(context.applicationContext.isDarkTheme())) - it.alpha = (if (context.isDarkTheme()) Preferences.textSecondaryAlphaDark.toIntValue().toFloat() else Preferences.textSecondaryAlpha.toIntValue().toFloat()) / 100 + it.alpha = + (if (context.isDarkTheme()) Preferences.textSecondaryAlphaDark.toIntValue() + .toFloat() else Preferences.textSecondaryAlpha.toIntValue() + .toFloat()) / 100 } // Text Size @@ -727,35 +802,76 @@ class MainWidget : AppWidgetProvider() { // Shadows - val shadowRadius = when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { - 0 -> 0f - 1 -> 5f - 2 -> 5f - else -> 5f - } - val shadowColor = when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { - 0 -> Color.TRANSPARENT - 1 -> R.color.black_50 - 2 -> Color.BLACK - else -> R.color.black_50 - } - val shadowDy = when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { - 0 -> 0f - 1 -> 0f - 2 -> 1f - else -> 0f - } + val shadowRadius = + when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { + 0 -> 0f + 1 -> 5f + 2 -> 5f + else -> 5f + } + val shadowColor = + when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { + 0 -> Color.TRANSPARENT + 1 -> R.color.black_50 + 2 -> Color.BLACK + else -> R.color.black_50 + } + val shadowDy = + when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { + 0 -> 0f + 1 -> 0f + 2 -> 1f + else -> 0f + } - listOf(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp, v.divider3, v.special_temp).forEach { + listOf( + v.empty_date, + v.divider1, + v.temp, + v.next_event, + v.next_event_difference_time, + v.next_event_date, + v.divider2, + v.calendar_temp, + v.divider3, + v.special_temp + ).forEach { it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor) } // Custom Font if (Preferences.customFont == Constants.CUSTOM_FONT_GOOGLE_SANS) { - val googleSans: Typeface = Typeface.createFromAsset(context.assets, "fonts/google_sans_regular.ttf") - listOf(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp, v.divider3, v.special_temp).forEach { + val googleSans: Typeface = + Typeface.createFromAsset(context.assets, "fonts/google_sans_regular.ttf") + listOf( + v.empty_date, + v.divider1, + v.temp, + v.next_event, + v.next_event_difference_time, + v.next_event_date, + v.divider2, + v.calendar_temp, + v.divider3, + v.special_temp + ).forEach { it.typeface = googleSans } + } else if (Preferences.customFont == Constants.CUSTOM_FONT_DOWNLOADED && typeface != null) { + listOf( + v.empty_date, + v.divider1, + v.temp, + v.next_event, + v.next_event_difference_time, + v.next_event_date, + v.divider2, + v.calendar_temp, + v.divider3, + v.special_temp + ).forEach { + it.typeface = typeface + } } // Weather @@ -763,7 +879,12 @@ class MainWidget : AppWidgetProvider() { v.weather.visibility = View.VISIBLE v.calendar_weather.visibility = View.VISIBLE v.special_weather.visibility = View.VISIBLE - val currentTemp = String.format(Locale.getDefault(), "%d °%s", Preferences.weatherTemp.roundToInt(), Preferences.weatherRealTempUnit) + val currentTemp = String.format( + Locale.getDefault(), + "%d °%s", + Preferences.weatherTemp.roundToInt(), + Preferences.weatherRealTempUnit + ) val icon: String = Preferences.weatherIcon if (icon == "") { @@ -772,8 +893,16 @@ class MainWidget : AppWidgetProvider() { v.special_weather_icon.visibility = View.GONE } else { v.weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon)) - v.empty_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon)) - v.special_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon)) + v.empty_weather_icon.setImageResource( + WeatherHelper.getWeatherIconResource( + icon + ) + ) + v.special_weather_icon.setImageResource( + WeatherHelper.getWeatherIconResource( + icon + ) + ) v.weather_icon.visibility = View.VISIBLE v.empty_weather_icon.visibility = View.VISIBLE v.special_weather_icon.visibility = View.VISIBLE diff --git a/app/src/main/res/layout/activity_custom_font.xml b/app/src/main/res/layout/activity_custom_font.xml new file mode 100644 index 0000000..e014111 --- /dev/null +++ b/app/src/main/res/layout/activity_custom_font.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml new file mode 100644 index 0000000..d72b728 --- /dev/null +++ b/app/src/main/res/layout/list_item.xml @@ -0,0 +1,16 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index a87af76..465ed7d 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -27,6 +27,7 @@ Formato data Sfondo widget Margine + Cerca font... Calendario diff --git a/app/src/main/res/values/font_certs.xml b/app/src/main/res/values/font_certs.xml new file mode 100644 index 0000000..949ce9f --- /dev/null +++ b/app/src/main/res/values/font_certs.xml @@ -0,0 +1,29 @@ + + + + + @array/com_google_android_gms_fonts_certs_dev + @array/com_google_android_gms_fonts_certs_prod + + + + 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 + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1a51a4f..f2f2b5d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -29,6 +29,7 @@ Date format Widget background Margin + Custom font... Calendar