Drop AppCompat

This commit is contained in:
kitsunyan
2020-06-25 23:02:28 +03:00
parent 41d5544bc8
commit b646259f5d
36 changed files with 496 additions and 240 deletions
+2 -2
View File
@@ -116,8 +116,8 @@ repositories {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:' + versions.kotlin
implementation 'androidx.preference:preference:1.1.1'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.fragment:fragment:1.2.5'
implementation 'androidx.viewpager2:viewpager2:1.0.0'
implementation 'com.squareup.okhttp3:okhttp:4.7.2'
implementation 'io.reactivex.rxjava3:rxjava:3.0.4'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
@@ -2,7 +2,6 @@ package nya.kitsunyan.foxydroid.content
import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.subjects.PublishSubject
import nya.kitsunyan.foxydroid.R
@@ -17,7 +16,7 @@ object Preferences {
Key.Theme, Key.UpdateNotify, Key.UpdateUnstable).map { Pair(it.name, it) }.toMap()
fun init(context: Context) {
preferences = PreferenceManager.getDefaultSharedPreferences(context)
preferences = context.getSharedPreferences("${context.packageName}_preferences", Context.MODE_PRIVATE)
preferences.registerOnSharedPreferenceChangeListener { _, keyString -> keys[keyString]?.let(subject::onNext) }
}
@@ -1,5 +1,6 @@
package nya.kitsunyan.foxydroid.screen
import android.app.AlertDialog
import android.content.ClipboardManager
import android.content.Context
import android.graphics.PorterDuff
@@ -17,8 +18,7 @@ import android.view.ViewGroup
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import android.widget.Toolbar
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
@@ -1,11 +1,11 @@
package nya.kitsunyan.foxydroid.screen
import android.app.AlertDialog
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Parcel
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import nya.kitsunyan.foxydroid.R
@@ -1,38 +1,56 @@
package nya.kitsunyan.foxydroid.screen
import android.app.AlertDialog
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.text.InputFilter
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.EditText
import android.widget.FrameLayout
import androidx.appcompat.widget.Toolbar
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceGroup
import androidx.preference.SwitchPreference
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.Switch
import android.widget.TextView
import android.widget.Toolbar
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import io.reactivex.rxjava3.disposables.Disposable
import nya.kitsunyan.foxydroid.R
import nya.kitsunyan.foxydroid.content.Preferences
import nya.kitsunyan.foxydroid.utility.extension.resources.*
class PreferencesFragment: PreferenceFragmentCompat() {
class PreferencesFragment: Fragment() {
private val preferences = mutableMapOf<Preferences.Key<*>, Preference<*>>()
private var disposable: Disposable? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val view = inflater.inflate(R.layout.fragment, container, false)
val content = view.findViewById<FrameLayout>(R.id.fragment_content)
val child = super.onCreateView(LayoutInflater.from(content.context), content, savedInstanceState)
content.addView(child, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
return view
return inflater.inflate(R.layout.fragment, container, false)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceScreen = preferenceManager.createPreferenceScreen(requireContext())
preferenceScreen.addCategory(getString(R.string.updates)).apply {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
screenActivity.onFragmentViewCreated(toolbar)
toolbar.setTitle(R.string.preferences)
val content = view.findViewById<FrameLayout>(R.id.fragment_content)
val scroll = ScrollView(content.context)
scroll.id = R.id.preferences_list
scroll.isFillViewport = true
content.addView(scroll, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
val scrollLayout = FrameLayout(content.context)
scroll.addView(scrollLayout, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val preferences = LinearLayout(scrollLayout.context)
preferences.orientation = LinearLayout.VERTICAL
scrollLayout.addView(preferences, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
preferences.addCategory(getString(R.string.updates)) {
addEnumeration(Preferences.Key.AutoSync, getString(R.string.sync_repositories_automatically)) {
when (it) {
Preferences.AutoSync.Never -> getString(R.string.never)
@@ -45,7 +63,7 @@ class PreferencesFragment: PreferenceFragmentCompat() {
addSwitch(Preferences.Key.UpdateUnstable, getString(R.string.unstable_updates),
getString(R.string.unstable_updates_summary))
}
preferenceScreen.addCategory(getString(R.string.proxy)).apply {
preferences.addCategory(getString(R.string.proxy)) {
addEnumeration(Preferences.Key.ProxyType, getString(R.string.proxy_type)) {
when (it) {
is Preferences.ProxyType.Direct -> getString(R.string.no_proxy)
@@ -56,7 +74,7 @@ class PreferencesFragment: PreferenceFragmentCompat() {
addEditString(Preferences.Key.ProxyHost, getString(R.string.proxy_host))
addEditInt(Preferences.Key.ProxyPort, getString(R.string.proxy_port), 1 .. 65535)
}
preferenceScreen.addCategory(getString(R.string.other)).apply {
preferences.addCategory(getString(R.string.other)) {
addEnumeration(Preferences.Key.Theme, getString(R.string.theme)) {
when (it) {
is Preferences.Theme.Light -> getString(R.string.light)
@@ -66,14 +84,6 @@ class PreferencesFragment: PreferenceFragmentCompat() {
addSwitch(Preferences.Key.IncompatibleVersions, getString(R.string.incompatible_versions),
getString(R.string.incompatible_versions_summary))
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
screenActivity.onFragmentViewCreated(toolbar)
toolbar.setTitle(R.string.preferences)
disposable = Preferences.observable.subscribe(this::updatePreference)
updatePreference(null)
@@ -82,75 +92,107 @@ class PreferencesFragment: PreferenceFragmentCompat() {
override fun onDestroyView() {
super.onDestroyView()
preferences.clear()
disposable?.dispose()
disposable = null
}
private fun updatePreference(key: Preferences.Key<*>?) {
if (key != null) {
preferences[key]?.update()
}
if (key == null || key == Preferences.Key.ProxyType) {
val enabled = when (Preferences[Preferences.Key.ProxyType]) {
is Preferences.ProxyType.Direct -> false
is Preferences.ProxyType.Http, is Preferences.ProxyType.Socks -> true
}
findPreference<Preference>(Preferences.Key.ProxyHost.name)!!.isEnabled = enabled
findPreference<Preference>(Preferences.Key.ProxyPort.name)!!.isEnabled = enabled
preferences[Preferences.Key.ProxyHost]?.setEnabled(enabled)
preferences[Preferences.Key.ProxyPort]?.setEnabled(enabled)
}
if (key == Preferences.Key.Theme) {
requireActivity().recreate()
}
}
private fun PreferenceGroup.addCategory(title: String): PreferenceCategory {
val preference = PreferenceCategory(context)
preference.isIconSpaceReserved = false
preference.title = title
addPreference(preference)
private fun LinearLayout.addDivider(full: Boolean): View {
val divider = View(context)
divider.background = context.getDrawableFromAttr(android.R.attr.listDivider)
addView(divider, LinearLayout.LayoutParams.MATCH_PARENT, divider.background.intrinsicHeight)
if (!full) {
(divider.layoutParams as LinearLayout.LayoutParams).apply {
marginStart = divider.resources.sizeScaled(16)
}
}
return divider
}
private fun LinearLayout.addCategory(title: String, callback: LinearLayout.() -> Unit) {
val text = TextView(context)
text.typeface = TypefaceExtra.medium
text.setTextSizeScaled(14)
text.setTextColor(text.context.getColorFromAttr(android.R.attr.colorAccent))
text.text = title
resources.sizeScaled(16).let { text.setPadding(it, it, it, 0) }
addView(text, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
callback()
val divider = addDivider(true)
// Negative margin for last divider
(layoutParams as ViewGroup.MarginLayoutParams).bottomMargin = -divider.layoutParams.height
}
private fun <T> LinearLayout.addPreference(key: Preferences.Key<T>, title: String,
summaryProvider: () -> String, dialogProvider: ((Context) -> AlertDialog)?): Preference<T> {
if (childCount > 0 && getChildAt(childCount - 1) !is TextView) {
addDivider(false)
}
val preference = Preference(key, this@PreferencesFragment, this, title, summaryProvider, dialogProvider)
preferences[key] = preference
return preference
}
private fun PreferenceGroup.addSwitch(key: Preferences.Key<Boolean>, title: String, summary: String?) {
val preference = SwitchPreference(context)
preference.isIconSpaceReserved = false
preference.title = title
preference.summary = summary
preference.key = key.name
preference.setDefaultValue(key.default.value)
addPreference(preference)
private fun LinearLayout.addSwitch(key: Preferences.Key<Boolean>, title: String, summary: String) {
val preference = addPreference(key, title, { summary }, null)
preference.check.visibility = View.VISIBLE
preference.view.setOnClickListener { Preferences[key] = !Preferences[key] }
preference.setCallback { preference.check.isChecked = Preferences[key] }
}
private fun PreferenceGroup.addEditString(key: Preferences.Key<String>, title: String) {
val preference = EditTextPreference(context)
preference.isIconSpaceReserved = false
preference.title = title
preference.dialogTitle = title
preference.summaryProvider = Preference.SummaryProvider<EditTextPreference> { it.text }
preference.key = key.name
preference.setDefaultValue(key.default.value)
addPreference(preference)
}
private fun PreferenceGroup.addEditInt(key: Preferences.Key<Int>, title: String, range: IntRange?) {
val preference = object: EditTextPreference(context) {
override fun persistString(value: String?): Boolean {
val intValue = value.orEmpty().toIntOrNull() ?: key.default.value
val result = persistInt(intValue)
if (intValue.toString() != value) {
text = intValue.toString()
private fun <T> LinearLayout.addEdit(key: Preferences.Key<T>, title: String, valueToString: (T) -> String,
stringToValue: (String) -> T?, configureEdit: (EditText) -> Unit) {
addPreference(key, title, { valueToString(Preferences[key]) }) {
val scroll = ScrollView(it)
scroll.resources.sizeScaled(20).let { scroll.setPadding(it, 0, it, 0) }
val edit = EditText(it)
configureEdit(edit)
edit.id = android.R.id.edit
edit.setTextSizeScaled(16)
edit.resources.sizeScaled(16).let { edit.setPadding(edit.paddingLeft, it, edit.paddingRight, it) }
edit.setText(valueToString(Preferences[key]))
edit.hint = edit.text.toString()
edit.setSelection(edit.text.length)
edit.requestFocus()
scroll.addView(edit, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
AlertDialog.Builder(it)
.setTitle(title)
.setView(scroll)
.setPositiveButton(R.string.ok) { _, _ ->
val value = stringToValue(edit.text.toString()) ?: key.default.value
post { Preferences[key] = value }
}
.setNegativeButton(R.string.cancel, null)
.create()
.apply {
window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
}
return result
}
override fun onSetInitialValue(defaultValue: Any?) {
text = getPersistedInt((defaultValue as? Int) ?: key.default.value).toString()
}
}
preference.isIconSpaceReserved = false
preference.title = title
preference.dialogTitle = title
preference.summaryProvider = Preference.SummaryProvider<EditTextPreference> { it.text }
preference.key = key.name
preference.setDefaultValue(key.default.value)
preference.setOnBindEditTextListener {
}
private fun LinearLayout.addEditString(key: Preferences.Key<String>, title: String) {
addEdit(key, title, { it }, { it }, { })
}
private fun LinearLayout.addEditInt(key: Preferences.Key<Int>, title: String, range: IntRange?) {
addEdit(key, title, { it.toString() }, { it.toIntOrNull() }) {
it.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
if (range != null) {
it.filters = arrayOf(InputFilter { source, start, end, dest, dstart, dend ->
@@ -160,23 +202,84 @@ class PreferencesFragment: PreferenceFragmentCompat() {
})
}
}
addPreference(preference)
}
private fun <T: Preferences.Enumeration<T>> PreferenceGroup
.addEnumeration(key: Preferences.Key<T>, title: String, valueString: (T) -> String) {
val preference = ListPreference(context)
preference.isIconSpaceReserved = false
preference.title = title
preference.dialogTitle = title
preference.summaryProvider = Preference.SummaryProvider<ListPreference> { p ->
val index = p.entryValues.indexOfFirst { it == p.value }
if (index >= 0) p.entries[index] else valueString(key.default.value)
private fun <T: Preferences.Enumeration<T>> LinearLayout
.addEnumeration(key: Preferences.Key<T>, title: String, valueToString: (T) -> String) {
addPreference(key, title, { valueToString(Preferences[key]) }) {
val values = key.default.value.values
AlertDialog.Builder(it)
.setTitle(title)
.setSingleChoiceItems(values.map(valueToString).toTypedArray(),
values.indexOf(Preferences[key])) { dialog, which ->
dialog.dismiss()
post { Preferences[key] = values[which] }
}
.setNegativeButton(R.string.cancel, null)
.create()
}
}
private class Preference<T>(private val key: Preferences.Key<T>,
fragment: Fragment, parent: ViewGroup, titleText: String,
private val summaryProvider: () -> String, private val dialogProvider: ((Context) -> AlertDialog)?) {
val view = parent.inflate(R.layout.preference_item)
val title = view.findViewById<TextView>(R.id.title)!!
val summary = view.findViewById<TextView>(R.id.summary)!!
val check = view.findViewById<Switch>(R.id.check)!!
private var callback: (() -> Unit)? = null
init {
title.text = titleText
parent.addView(view)
if (dialogProvider != null) {
view.setOnClickListener { PreferenceDialog(key.name)
.show(fragment.childFragmentManager, "${PreferenceDialog::class.java.name}.${key.name}") }
}
update()
}
fun setCallback(callback: () -> Unit) {
this.callback = callback
update()
}
fun setEnabled(enabled: Boolean) {
view.isEnabled = enabled
title.isEnabled = enabled
summary.isEnabled = enabled
check.isEnabled = enabled
}
fun update() {
summary.text = summaryProvider()
summary.visibility = if (summary.text.isNotEmpty()) View.VISIBLE else View.GONE
callback?.invoke()
}
fun createDialog(context: Context): AlertDialog {
return dialogProvider!!(context)
}
}
class PreferenceDialog(): DialogFragment() {
companion object {
private const val EXTRA_KEY = "key"
}
constructor(key: String): this() {
arguments = Bundle().apply {
putString(EXTRA_KEY, key)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val preferences = (parentFragment as PreferencesFragment).preferences
val key = requireArguments().getString(EXTRA_KEY)!!
.let { name -> preferences.keys.find { it.name == name }!! }
val preference = preferences[key]!!
return preference.createDialog(requireContext())
}
preference.key = key.name
preference.setDefaultValue(key.default.value.valueString)
preference.entryValues = key.default.value.values.map { it.valueString }.toTypedArray()
preference.entries = key.default.value.values.map(valueString).toTypedArray()
addPreference(preference)
}
}
@@ -33,11 +33,9 @@ import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.Switch
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.AppCompatTextView
import androidx.appcompat.widget.SwitchCompat
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.text.util.LinkifyCompat
@@ -106,12 +104,12 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
private enum class SectionType(val titleResId: Int, val colorAttrResId: Int) {
ANTI_FEATURES(R.string.anti_features, R.attr.colorError),
WHATS_NEW(R.string.whats_new, R.attr.colorAccent),
LINKS(R.string.links, R.attr.colorAccent),
DONATE(R.string.donate, R.attr.colorAccent),
PERMISSIONS(R.string.permissions, R.attr.colorAccent),
SCREENSHOTS(R.string.screenshots, R.attr.colorAccent),
RELEASES(R.string.releases, R.attr.colorAccent)
WHATS_NEW(R.string.whats_new, android.R.attr.colorAccent),
LINKS(R.string.links, android.R.attr.colorAccent),
DONATE(R.string.donate, android.R.attr.colorAccent),
PERMISSIONS(R.string.permissions, android.R.attr.colorAccent),
SCREENSHOTS(R.string.screenshots, android.R.attr.colorAccent),
RELEASES(R.string.releases, android.R.attr.colorAccent)
}
internal enum class ExpandType { NOTHING, DESCRIPTION, WHATS_NEW,
@@ -300,10 +298,13 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
val progressIcon: Drawable
val defaultIcon: Drawable
val actionTintNormal = action.context.getColorFromAttr(R.attr.colorAccent)
val actionTintNormal = action.context.getColorFromAttr(android.R.attr.colorAccent)
val actionTintCancel = action.context.getColorFromAttr(R.attr.colorError)
init {
if (Android.sdk(22)) {
action.setTextColor(action.context.getColorFromAttr(android.R.attr.colorBackground))
}
val (progressIcon, defaultIcon) = Utils.getDefaultApplicationIcons(icon.context)
this.progressIcon = progressIcon
this.defaultIcon = defaultIcon
@@ -312,7 +313,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
private class SwitchViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val title = itemView.findViewById<TextView>(R.id.title)!!
val enabled = itemView.findViewById<SwitchCompat>(R.id.enabled)!!
val enabled = itemView.findViewById<Switch>(R.id.enabled)!!
val statefulViews: Sequence<View>
get() = sequenceOf(itemView, title, enabled)
@@ -323,7 +324,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
val icon = itemView.findViewById<ImageView>(R.id.icon)!!
}
private class ExpandViewHolder(context: Context): RecyclerView.ViewHolder(AppCompatTextView(context)) {
private class ExpandViewHolder(context: Context): RecyclerView.ViewHolder(TextView(context)) {
val text: TextView
get() = itemView as TextView
@@ -331,8 +332,8 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
itemView as TextView
itemView.typeface = TypefaceExtra.medium
itemView.setTextSizeScaled(14)
itemView.setTextColor(itemView.context.getColorFromAttr(R.attr.colorAccent))
itemView.background = itemView.context.getDrawableFromAttr(R.attr.selectableItemBackground)
itemView.setTextColor(itemView.context.getColorFromAttr(android.R.attr.colorAccent))
itemView.background = itemView.context.getDrawableFromAttr(android.R.attr.selectableItemBackground)
itemView.gravity = Gravity.CENTER
itemView.isAllCaps = true
itemView.layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT,
@@ -340,7 +341,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
}
}
private class TextViewHolder(context: Context): RecyclerView.ViewHolder(AppCompatTextView(context)) {
private class TextViewHolder(context: Context): RecyclerView.ViewHolder(TextView(context)) {
val text: TextView
get() = itemView as TextView
@@ -423,12 +424,12 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
init {
itemView as FrameLayout
itemView.foreground = itemView.context.getDrawableFromAttr(R.attr.selectableItemBackground)
itemView.foreground = itemView.context.getDrawableFromAttr(android.R.attr.selectableItemBackground)
val backgroundColor = itemView.context.getColorFromAttr(android.R.attr.colorBackground).defaultColor
val accentColor = itemView.context.getColorFromAttr(R.attr.colorAccent).defaultColor
val accentColor = itemView.context.getColorFromAttr(android.R.attr.colorAccent).defaultColor
val primaryColor = itemView.context.getColorFromAttr(android.R.attr.textColorPrimary).defaultColor
image = object: AppCompatImageView(context) {
image = object: ImageView(context) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(measuredWidth, measuredWidth)
@@ -465,7 +466,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
init {
status.background = GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, null).apply {
setStatusActive = { active -> color = itemView.context.getColorFromAttr(if (active)
R.attr.colorAccent else android.R.attr.textColorSecondary) }
android.R.attr.colorAccent else android.R.attr.textColorSecondary) }
cornerRadius = itemView.resources.sizeScaled(2).toFloat()
}
}
@@ -479,14 +480,14 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
itemView.orientation = LinearLayout.VERTICAL
itemView.gravity = Gravity.CENTER
itemView.resources.sizeScaled(20).let { itemView.setPadding(it, it, it, it) }
val title = AppCompatTextView(itemView.context)
val title = TextView(itemView.context)
title.gravity = Gravity.CENTER
title.typeface = TypefaceExtra.light
title.setTextColor(context.getColorFromAttr(android.R.attr.textColorPrimary))
title.setTextSizeScaled(20)
title.setText(R.string.application_not_found)
itemView.addView(title, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
val packageName = AppCompatTextView(itemView.context)
val packageName = TextView(itemView.context)
packageName.gravity = Gravity.CENTER
packageName.setTextColor(context.getColorFromAttr(android.R.attr.textColorPrimary))
packageName.setTextSizeScaled(16)
@@ -974,8 +975,10 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
if (action != null) {
holder.action.setText(action.titleResId)
}
holder.action.backgroundTintList = if (action == Action.CANCEL)
holder.actionTintCancel else holder.actionTintNormal
if (Android.sdk(22)) {
holder.action.backgroundTintList = if (action == Action.CANCEL)
holder.actionTintCancel else holder.actionTintNormal
}
holder.statusLayout.visibility = if (status != null) View.VISIBLE else View.GONE
if (status != null) {
when (status) {
@@ -12,7 +12,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.appcompat.widget.Toolbar
import android.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
@@ -10,7 +10,6 @@ import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.widget.AppCompatTextView
import androidx.recyclerview.widget.RecyclerView
import coil.api.clear
import coil.api.load
@@ -55,7 +54,7 @@ class ProductsAdapter(private val onClick: (ProductItem) -> Unit):
}
}
private class EmptyViewHolder(context: Context): RecyclerView.ViewHolder(AppCompatTextView(context)) {
private class EmptyViewHolder(context: Context): RecyclerView.ViewHolder(TextView(context)) {
val text: TextView
get() = itemView as TextView
@@ -143,7 +142,7 @@ class ProductsAdapter(private val onClick: (ProductItem) -> Unit):
resources.sizeScaled(4).let { setPadding(it, 0, it, 0) }
setTextColor(holder.status.context.getColorFromAttr(android.R.attr.colorBackground))
background = GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, null).apply {
color = holder.status.context.getColorFromAttr(R.attr.colorAccent)
color = holder.status.context.getColorFromAttr(android.R.attr.colorAccent)
cornerRadius = holder.status.resources.sizeScaled(2).toFloat()
}
}
@@ -73,7 +73,7 @@ class ProductsFragment(): Fragment(), CursorOwner.Callback {
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return RecyclerView(container!!.context).apply {
return RecyclerView(requireContext()).apply {
id = android.R.id.list
layoutManager = LinearLayoutManager(context)
isMotionEventSplittingEnabled = false
@@ -2,8 +2,8 @@ package nya.kitsunyan.foxydroid.screen
import android.view.View
import android.view.ViewGroup
import android.widget.Switch
import android.widget.TextView
import androidx.appcompat.widget.SwitchCompat
import androidx.recyclerview.widget.RecyclerView
import nya.kitsunyan.foxydroid.R
import nya.kitsunyan.foxydroid.database.Database
@@ -18,7 +18,7 @@ class RepositoriesAdapter(private val onClick: (Repository) -> Unit,
private class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val name = itemView.findViewById<TextView>(R.id.name)!!
val enabled = itemView.findViewById<SwitchCompat>(R.id.enabled)!!
val enabled = itemView.findViewById<Switch>(R.id.enabled)!!
var listenSwitch = true
}
@@ -7,7 +7,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.appcompat.widget.Toolbar
import android.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -13,7 +13,7 @@ import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import android.widget.Toolbar
import androidx.fragment.app.Fragment
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
@@ -9,9 +9,9 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import android.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import nya.kitsunyan.foxydroid.R
import nya.kitsunyan.foxydroid.content.Cache
import nya.kitsunyan.foxydroid.content.Preferences
@@ -23,7 +23,7 @@ import nya.kitsunyan.foxydroid.utility.extension.resources.*
import nya.kitsunyan.foxydroid.utility.extension.text.*
import java.lang.ref.WeakReference
abstract class ScreenActivity: AppCompatActivity() {
abstract class ScreenActivity: FragmentActivity() {
companion object {
private const val STATE_FRAGMENT_STACK = "fragmentStack"
}
@@ -162,7 +162,7 @@ abstract class ScreenActivity: AppCompatActivity() {
internal fun onFragmentViewCreated(toolbar: Toolbar?) {
this.toolbar = toolbar?.let(::WeakReference)
if (fragmentStack.isNotEmpty() && toolbar != null) {
toolbar.navigationIcon = toolbar.context.getDrawableFromAttr(R.attr.homeAsUpIndicator)
toolbar.navigationIcon = toolbar.context.getDrawableFromAttr(android.R.attr.homeAsUpIndicator)
toolbar.setNavigationOnClickListener { onBackPressed() }
}
}
@@ -8,7 +8,6 @@ import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.fragment.app.DialogFragment
@@ -75,7 +74,7 @@ class ScreenshotsFragment(): DialogFragment() {
format = PixelFormat.TRANSLUCENT
windowAnimations = run {
val typedArray = dialog.context.obtainStyledAttributes(null,
intArrayOf(android.R.attr.windowAnimationStyle), R.attr.dialogTheme, 0)
intArrayOf(android.R.attr.windowAnimationStyle), android.R.attr.dialogTheme, 0)
try {
typedArray.getResourceId(0, 0)
} finally {
@@ -156,7 +155,7 @@ class ScreenshotsFragment(): DialogFragment() {
StableRecyclerAdapter<Adapter.ViewType, RecyclerView.ViewHolder>() {
enum class ViewType { SCREENSHOT }
private class ViewHolder(context: Context): RecyclerView.ViewHolder(AppCompatImageView(context)) {
private class ViewHolder(context: Context): RecyclerView.ViewHolder(ImageView(context)) {
val image: ImageView
get() = itemView as ImageView
@@ -2,6 +2,12 @@ package nya.kitsunyan.foxydroid.screen
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.Paint
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
@@ -12,17 +18,15 @@ import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.SearchView
import android.widget.TextView
import androidx.appcompat.widget.AppCompatTextView
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar
import androidx.core.view.MenuCompat
import android.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import com.google.android.material.tabs.TabLayout
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.Disposable
@@ -34,6 +38,7 @@ import nya.kitsunyan.foxydroid.service.Connection
import nya.kitsunyan.foxydroid.service.SyncService
import nya.kitsunyan.foxydroid.utility.RxUtils
import nya.kitsunyan.foxydroid.utility.Utils
import nya.kitsunyan.foxydroid.utility.extension.android.*
import nya.kitsunyan.foxydroid.utility.extension.resources.*
import nya.kitsunyan.foxydroid.utility.extension.text.*
import nya.kitsunyan.foxydroid.widget.EnumRecyclerAdapter
@@ -49,7 +54,7 @@ class TabsFragment: Fragment() {
}
private class Layout(view: View) {
val tabLayout = view.findViewById<TabLayout>(R.id.tabs)!!
val tabs = view.findViewById<LinearLayout>(R.id.tabs)!!
val categoryLayout = view.findViewById<ViewGroup>(R.id.category_layout)!!
val categoryChange = view.findViewById<View>(R.id.category_change)!!
val categoryName = view.findViewById<TextView>(R.id.category_name)!!
@@ -57,17 +62,18 @@ class TabsFragment: Fragment() {
}
private var sortOrderMenu: Pair<MenuItem, List<MenuItem>>? = null
private var syncRepositoriesMenuItem: MenuItem? = null
private var layout: Layout? = null
private var categoriesList: RecyclerView? = null
private var viewPager: ViewPager? = null
private var viewPager: ViewPager2? = null
private var showCategories = false
set(value) {
if (field != value) {
field = value
val layout = layout
layout?.tabLayout?.let { (0 until it.tabCount)
.forEach { index -> it.getTabAt(index)!!.view.isEnabled = !value } }
layout?.tabs?.let { (0 until it.childCount)
.forEach { index -> it.getChildAt(index)!!.isEnabled = !value } }
layout?.categoryIcon?.scaleY = if (value) -1f else 1f
if ((categoriesList?.parent as? View)?.height ?: 0 > 0) {
animateCategoriesList()
@@ -127,7 +133,9 @@ class TabsFragment: Fragment() {
})
toolbar.menu.apply {
MenuCompat.setGroupDividerEnabled(this, true)
if (Android.sdk(28)) {
setGroupDividerEnabled(true)
}
add(0, R.id.toolbar_search, 0, R.string.search)
.setIcon(Utils.getToolbarIcon(toolbar.context, R.drawable.ic_search))
@@ -138,21 +146,22 @@ class TabsFragment: Fragment() {
.setIcon(Utils.getToolbarIcon(toolbar.context, R.drawable.ic_sort))
.let { menu ->
menu.item.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
val items = ProductItem.Order.values().map { order -> menu
.add(order.titleResId)
.setOnMenuItemClickListener { item ->
this@TabsFragment.order = order
item.isChecked = true
productFragments.forEach { it.setOrder(order) }
true
} }
val items = ProductItem.Order.values().map { order ->
menu
.add(order.titleResId)
.setOnMenuItemClickListener { item ->
this@TabsFragment.order = order
item.isChecked = true
productFragments.forEach { it.setOrder(order) }
true
}
}
menu.setGroupCheckable(0, true, true)
Pair(menu.item, items)
}
add(0, 0, 0, R.string.sync_repositories)
syncRepositoriesMenuItem = add(0, 0, 0, R.string.sync_repositories)
.setIcon(Utils.getToolbarIcon(toolbar.context, R.drawable.ic_sync))
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM)
.setOnMenuItemClickListener {
syncConnection.binder?.sync(SyncService.SyncRequest.MANUAL)
true
@@ -179,16 +188,27 @@ class TabsFragment: Fragment() {
val layout = Layout(view)
this.layout = layout
ProductsFragment.Source.values().forEach { layout.tabLayout
.addTab(layout.tabLayout.newTab().setText(it.titleResId)) }
layout.tabLayout.addOnTabSelectedListener(object: TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
viewPager!!.currentItem = tab.position
layout.tabs.background = TabsBackgroundDrawable(layout.tabs.context,
layout.tabs.layoutDirection == View.LAYOUT_DIRECTION_RTL)
ProductsFragment.Source.values().forEach {
val tab = TextView(layout.tabs.context)
val selectedColor = tab.context.getColorFromAttr(android.R.attr.textColorPrimary).defaultColor
val normalColor = tab.context.getColorFromAttr(android.R.attr.textColorSecondary).defaultColor
tab.gravity = Gravity.CENTER
tab.typeface = TypefaceExtra.medium
tab.setTextColor(ColorStateList(arrayOf(intArrayOf(android.R.attr.state_selected), intArrayOf()),
intArrayOf(selectedColor, normalColor)))
tab.setTextSizeScaled(14)
tab.isAllCaps = true
tab.text = getString(it.titleResId)
tab.background = tab.context.getDrawableFromAttr(android.R.attr.selectableItemBackground)
tab.setOnClickListener { _ ->
setSelectedTab(it)
viewPager!!.currentItem = it.ordinal
}
override fun onTabUnselected(tab: TabLayout.Tab) = Unit
override fun onTabReselected(tab: TabLayout.Tab) = Unit
})
layout.tabs.addView(tab, 0, LinearLayout.LayoutParams.MATCH_PARENT)
(tab.layoutParams as LinearLayout.LayoutParams).weight = 1f
}
showCategories = savedInstanceState?.getByte(STATE_SHOW_CATEGORIES)?.toInt() ?: 0 != 0
categories = savedInstanceState?.getStringArrayList(STATE_CATEGORIES).orEmpty()
@@ -201,14 +221,16 @@ class TabsFragment: Fragment() {
val content = view.findViewById<FrameLayout>(R.id.fragment_content)
viewPager = ViewPager(content.context).apply {
viewPager = ViewPager2(content.context).apply {
id = R.id.fragment_pager
adapter = object: FragmentStatePagerAdapter(childFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int): Fragment = ProductsFragment(ProductsFragment.Source.values()[position])
override fun getCount(): Int = ProductsFragment.Source.values().size
adapter = object: FragmentStateAdapter(this@TabsFragment) {
override fun getItemCount(): Int = ProductsFragment.Source.values().size
override fun createFragment(position: Int): Fragment = ProductsFragment(ProductsFragment
.Source.values()[position])
}
content.addView(this, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
addOnPageChangeListener(pageChangeListener)
registerOnPageChangeCallback(pageChangeCallback)
offscreenPageLimit = 1
}
categoriesDisposable = Observable.just(Unit)
@@ -238,7 +260,7 @@ class TabsFragment: Fragment() {
updateCategory()
}
}
setBackgroundColor(context.getColorFromAttr(R.attr.colorPrimaryDark).defaultColor)
setBackgroundColor(context.getColorFromAttr(android.R.attr.colorPrimaryDark).defaultColor)
elevation = resources.sizeScaled(4).toFloat()
content.addView(this, FrameLayout.LayoutParams.MATCH_PARENT, 0)
visibility = View.GONE
@@ -268,6 +290,7 @@ class TabsFragment: Fragment() {
super.onDestroyView()
sortOrderMenu = null
syncRepositoriesMenuItem = null
layout = null
categoriesList = null
viewPager = null
@@ -294,7 +317,7 @@ class TabsFragment: Fragment() {
if (needSelectUpdates) {
needSelectUpdates = false
selectUpdates()
selectUpdatesInternal(false)
}
}
@@ -308,11 +331,19 @@ class TabsFragment: Fragment() {
}
}
internal fun selectUpdates() {
if (view == null) {
needSelectUpdates = true
private fun setSelectedTab(source: ProductsFragment.Source) {
val layout = layout!!
(0 until layout.tabs.childCount).forEach { layout.tabs.getChildAt(it).isSelected = it == source.ordinal }
}
internal fun selectUpdates() = selectUpdatesInternal(true)
private fun selectUpdatesInternal(allowSmooth: Boolean) {
if (view != null) {
val viewPager = viewPager
viewPager?.setCurrentItem(ProductsFragment.Source.UPDATES.ordinal, allowSmooth && viewPager.isLaidOut)
} else {
layout?.tabLayout?.getTabAt(ProductsFragment.Source.UPDATES.ordinal)!!.select()
needSelectUpdates = true
}
}
@@ -369,7 +400,7 @@ class TabsFragment: Fragment() {
}
}
private val pageChangeListener = object: ViewPager.OnPageChangeListener {
private val pageChangeCallback = object: ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
val layout = layout!!
val fromCategories = ProductsFragment.Source.values()[position].categories
@@ -380,14 +411,12 @@ class TabsFragment: Fragment() {
} else {
if (fromCategories) 1f else 0f
}
if (layout.categoryLayout.childCount != 1) {
throw RuntimeException()
}
(layout.tabs.background as TabsBackgroundDrawable)
.update(position + positionOffset, layout.tabs.childCount)
assert(layout.categoryLayout.childCount == 1)
val child = layout.categoryLayout.getChildAt(0)
val height = child.layoutParams.height
if (height <= 0) {
throw RuntimeException()
}
assert(height > 0)
val currentHeight = (offset * height).roundToInt()
if (layout.categoryLayout.layoutParams.height != currentHeight) {
layout.categoryLayout.layoutParams.height = currentHeight
@@ -396,27 +425,63 @@ class TabsFragment: Fragment() {
}
override fun onPageSelected(position: Int) {
val layout = layout!!
val source = ProductsFragment.Source.values()[position]
updateUpdateNotificationBlocker(source)
sortOrderMenu!!.first.isVisible = source.order
layout.tabLayout.selectTab(layout.tabLayout.getTabAt(source.ordinal))
syncRepositoriesMenuItem!!.setShowAsActionFlags(if (!source.order ||
resources.configuration.screenWidthDp >= 480) MenuItem.SHOW_AS_ACTION_ALWAYS else 0)
setSelectedTab(source)
if (showCategories && !source.categories) {
showCategories = false
}
}
override fun onPageScrollStateChanged(state: Int) {
layout!!.categoryChange.isEnabled = state != ViewPager.SCROLL_STATE_DRAGGING &&
layout!!.categoryChange.isEnabled = state != ViewPager2.SCROLL_STATE_DRAGGING &&
ProductsFragment.Source.values()[viewPager!!.currentItem].categories
}
}
private class TabsBackgroundDrawable(context: Context, private val rtl: Boolean): Drawable() {
private val height = context.resources.sizeScaled(2)
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = context.getColorFromAttr(android.R.attr.colorAccent).defaultColor
}
private var position = 0f
private var total = 0
fun update(position: Float, total: Int) {
this.position = position
this.total = total
invalidateSelf()
}
override fun draw(canvas: Canvas) {
if (total > 0) {
val bounds = bounds
val width = bounds.width() / total.toFloat()
val x = width * position
if (rtl) {
canvas.drawRect(bounds.right - width - x, (bounds.bottom - height).toFloat(),
bounds.right - x, bounds.bottom.toFloat(), paint)
} else {
canvas.drawRect(bounds.left + x, (bounds.bottom - height).toFloat(),
bounds.left + x + width, bounds.bottom.toFloat(), paint)
}
}
}
override fun setAlpha(alpha: Int) = Unit
override fun setColorFilter(colorFilter: ColorFilter?) = Unit
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
}
private class CategoriesAdapter(private val categories: () -> List<String>, private val onClick: (String) -> Unit):
EnumRecyclerAdapter<CategoriesAdapter.ViewType, RecyclerView.ViewHolder>() {
enum class ViewType { CATEGORY }
private class CategoryViewHolder(context: Context): RecyclerView.ViewHolder(AppCompatTextView(context)) {
private class CategoryViewHolder(context: Context): RecyclerView.ViewHolder(TextView(context)) {
val title: TextView
get() = itemView as TextView
@@ -426,7 +491,7 @@ class TabsFragment: Fragment() {
itemView.resources.sizeScaled(16).let { itemView.setPadding(it, 0, it, 0) }
itemView.setTextColor(context.getColorFromAttr(android.R.attr.textColorPrimary))
itemView.setTextSizeScaled(16)
itemView.background = context.getDrawableFromAttr(R.attr.selectableItemBackground)
itemView.background = context.getDrawableFromAttr(android.R.attr.selectableItemBackground)
itemView.layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT,
itemView.resources.sizeScaled(48))
}
@@ -3,7 +3,7 @@ package nya.kitsunyan.foxydroid.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
import nya.kitsunyan.foxydroid.utility.extension.android.Android
import nya.kitsunyan.foxydroid.utility.extension.android.*
abstract class ConnectionService<T: IBinder>: Service() {
abstract override fun onBind(intent: Intent): T
@@ -7,7 +7,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.appcompat.view.ContextThemeWrapper
import android.view.ContextThemeWrapper
import androidx.core.app.NotificationCompat
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
@@ -188,7 +188,7 @@ class DownloadService: ConnectionService<DownloadService.Binder>() {
.setAutoCancel(true)
.setSmallIcon(android.R.drawable.stat_sys_warning)
.setColor(ContextThemeWrapper(this, R.style.Theme_Main_Light)
.getColorFromAttr(R.attr.colorAccent).defaultColor)
.getColorFromAttr(android.R.attr.colorAccent).defaultColor)
.setContentIntent(PendingIntent.getBroadcast(this, 0, Intent(this, Receiver::class.java)
.setAction("$ACTION_OPEN.${task.packageName}"), PendingIntent.FLAG_UPDATE_CURRENT))
.apply {
@@ -223,7 +223,7 @@ class DownloadService: ConnectionService<DownloadService.Binder>() {
.setAutoCancel(true)
.setSmallIcon(android.R.drawable.stat_sys_download_done)
.setColor(ContextThemeWrapper(this, R.style.Theme_Main_Light)
.getColorFromAttr(R.attr.colorAccent).defaultColor)
.getColorFromAttr(android.R.attr.colorAccent).defaultColor)
.setContentIntent(PendingIntent.getBroadcast(this, 0, Intent(this, Receiver::class.java)
.setAction("$ACTION_INSTALL.${task.packageName}")
.putExtra(EXTRA_CACHE_FILE_NAME, task.release.cacheFileName), PendingIntent.FLAG_UPDATE_CURRENT))
@@ -286,7 +286,7 @@ class DownloadService: ConnectionService<DownloadService.Binder>() {
.Builder(this, Common.NOTIFICATION_CHANNEL_DOWNLOADING)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setColor(ContextThemeWrapper(this, R.style.Theme_Main_Light)
.getColorFromAttr(R.attr.colorAccent).defaultColor)
.getColorFromAttr(android.R.attr.colorAccent).defaultColor)
.addAction(0, getString(R.string.cancel), PendingIntent.getService(this, 0,
Intent(this, this::class.java).setAction(ACTION_CANCEL), PendingIntent.FLAG_UPDATE_CURRENT)) }
@@ -9,7 +9,7 @@ import android.content.Intent
import android.graphics.Color
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import androidx.appcompat.view.ContextThemeWrapper
import android.view.ContextThemeWrapper
import androidx.core.app.NotificationCompat
import androidx.fragment.app.Fragment
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
@@ -200,7 +200,7 @@ class SyncService: ConnectionService<SyncService.Binder>() {
.Builder(this, Common.NOTIFICATION_CHANNEL_SYNCING)
.setSmallIcon(android.R.drawable.stat_sys_warning)
.setColor(ContextThemeWrapper(this, R.style.Theme_Main_Light)
.getColorFromAttr(R.attr.colorAccent).defaultColor)
.getColorFromAttr(android.R.attr.colorAccent).defaultColor)
.setContentTitle(getString(R.string.error_syncing_format, repository.name))
.setContentText(getString(when (exception) {
is RepositoryUpdater.UpdateException -> when (exception.errorType) {
@@ -218,7 +218,7 @@ class SyncService: ConnectionService<SyncService.Binder>() {
.Builder(this, Common.NOTIFICATION_CHANNEL_SYNCING)
.setSmallIcon(R.drawable.ic_sync)
.setColor(ContextThemeWrapper(this, R.style.Theme_Main_Light)
.getColorFromAttr(R.attr.colorAccent).defaultColor)
.getColorFromAttr(android.R.attr.colorAccent).defaultColor)
.addAction(0, getString(R.string.cancel), PendingIntent.getService(this, 0,
Intent(this, this::class.java).setAction(ACTION_CANCEL), PendingIntent.FLAG_UPDATE_CURRENT)) }
@@ -353,7 +353,7 @@ class SyncService: ConnectionService<SyncService.Binder>() {
.setContentText(resources.getQuantityString(R.plurals.new_updates_description_format,
productItems.size, productItems.size))
.setColor(ContextThemeWrapper(this, R.style.Theme_Main_Light)
.getColorFromAttr(R.attr.colorAccent).defaultColor)
.getColorFromAttr(android.R.attr.colorAccent).defaultColor)
.setContentIntent(PendingIntent.getActivity(this, 0, Intent(this, MainActivity::class.java)
.setAction(MainActivity.ACTION_UPDATES), PendingIntent.FLAG_UPDATE_CURRENT))
.setStyle(NotificationCompat.InboxStyle().applyHack {
@@ -24,7 +24,7 @@ object Utils {
fun getDefaultApplicationIcons(context: Context): Pair<Drawable, Drawable> {
val progressIcon: Drawable = createDefaultApplicationIcon(context, android.R.attr.textColorSecondary)
val defaultIcon: Drawable = createDefaultApplicationIcon(context, R.attr.colorAccent)
val defaultIcon: Drawable = createDefaultApplicationIcon(context, android.R.attr.colorAccent)
return Pair(progressIcon, defaultIcon)
}
@@ -5,6 +5,7 @@ import android.graphics.Canvas
import android.graphics.Rect
import android.os.SystemClock
import android.view.MotionEvent
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import nya.kitsunyan.foxydroid.utility.extension.resources.*
@@ -23,7 +24,7 @@ class RecyclerFastScroller(private val recyclerView: RecyclerView) {
private val thumbDrawable = recyclerView.context.getDrawableFromAttr(android.R.attr.fastScrollThumbDrawable)
private val trackDrawable = recyclerView.context.getDrawableFromAttr(android.R.attr.fastScrollTrackDrawable)
private val minTrackSize = recyclerView.resources.sizeScaled(32)
private val minTrackSize = recyclerView.resources.sizeScaled(16)
private data class FastScrolling(val startAtThumbOffset: Float?, val startY: Float, val currentY: Float)
@@ -134,6 +135,7 @@ class RecyclerFastScroller(private val recyclerView: RecyclerView) {
val atThumbVertical = if (rtl) event.x <= trackWidth else event.x >= recyclerView.width - trackWidth
atThumbVertical && run {
withScroll { itemHeight, thumbHeight, range ->
(recyclerView.parent as? ViewGroup)?.requestDisallowInterceptTouchEvent(true)
val offset = currentOffset(itemHeight, range)
val thumbY = ((recyclerView.height - thumbHeight) * offset).roundToInt()
val atThumb = event.y >= thumbY && event.y <= thumbY + thumbHeight
@@ -153,6 +155,7 @@ class RecyclerFastScroller(private val recyclerView: RecyclerView) {
}
val cancel = event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL
if (!success || cancel) {
(recyclerView.parent as? ViewGroup)?.requestDisallowInterceptTouchEvent(false)
updateState(scrolling, null)
recyclerView.invalidate()
}
@@ -0,0 +1,33 @@
package nya.kitsunyan.foxydroid.widget
import android.content.Context
import android.util.AttributeSet
import android.widget.Toolbar
class Toolbar: Toolbar {
constructor(context: Context): super(context)
constructor(context: Context, attrs: AttributeSet?): super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int,
defStyleRes: Int): super(context, attrs, defStyleAttr, defStyleRes)
private var initalized = false
private var layoutDirectionChanged: Int? = null
init {
initalized = true
val layoutDirection = layoutDirectionChanged
layoutDirectionChanged = null
if (layoutDirection != null) {
onRtlPropertiesChanged(layoutDirection)
}
}
override fun onRtlPropertiesChanged(layoutDirection: Int) {
if (initalized) {
super.onRtlPropertiesChanged(layoutDirection)
} else {
layoutDirectionChanged = layoutDirection
}
}
}
+4 -3
View File
@@ -55,7 +55,8 @@
android:scaleType="center"
android:src="@drawable/ic_arrow_down"
android:tint="?android:attr/textColorSecondary"
android:background="?attr/actionBarItemBackground"
android:tintMode="src_in"
android:background="?android:attr/actionBarItemBackground"
android:visibility="gone"
tools:ignore="ContentDescription" />
@@ -91,7 +92,7 @@
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:gravity="top"
android:typeface="monospace"
android:fontFamily="monospace"
android:textSize="16sp"
android:inputType="textNoSuggestions|textMultiLine"
tools:ignore="Autofill,LabelFor" />
@@ -208,7 +209,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
style="@style/Widget.AppCompat.Button.Borderless.Colored"
style="@android:style/Widget.Material.Button.Borderless.Colored"
android:text="@string/skip" />
</LinearLayout>
+8 -6
View File
@@ -1,28 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<nya.kitsunyan.foxydroid.widget.FragmentLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme">
android:orientation="vertical"
android:theme="?android:attr/actionBarTheme"
android:background="?android:attr/colorPrimary"
android:elevation="4dp">
<androidx.appcompat.widget.Toolbar
<nya.kitsunyan.foxydroid.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:popupTheme="@style/Theme.Toolbar.Popup" />
android:popupTheme="@style/Theme.Toolbar.Popup" />
<FrameLayout
android:id="@+id/toolbar_extra"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/fragment_content"
+2 -1
View File
@@ -7,7 +7,7 @@
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:background="?attr/selectableItemBackground">
android:background="?android:attr/selectableItemBackground">
<ImageView
android:id="@+id/icon"
@@ -15,6 +15,7 @@
android:layout_height="24dp"
android:scaleType="centerInside"
android:tint="?android:attr/textColorSecondary"
android:tintMode="src_in"
tools:ignore="ContentDescription" />
<LinearLayout
+1 -1
View File
@@ -7,7 +7,7 @@
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:background="?attr/selectableItemBackground">
android:background="?android:attr/selectableItemBackground">
<ImageView
android:id="@+id/icon"
+42
View File
@@ -0,0 +1,42 @@
<?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="wrap_content"
android:orientation="horizontal"
android:background="?android:attr/selectableItemBackground">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:singleLine="true" />
<TextView
android:id="@+id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp" />
</LinearLayout>
<Switch
android:id="@+id/check"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="-16dp"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:visibility="gone" />
</LinearLayout>
+2 -2
View File
@@ -66,7 +66,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_gravity="bottom"
style="@style/Widget.AppCompat.Button.Colored" />
style="@android:style/Widget.Material.Button" />
</LinearLayout>
@@ -94,7 +94,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginBottom="2dp"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" />
style="@android:style/Widget.Material.ProgressBar.Horizontal" />
</LinearLayout>
+1 -1
View File
@@ -8,7 +8,7 @@
android:paddingStart="14dp"
android:paddingEnd="16dp"
android:gravity="center_vertical"
android:background="?attr/selectableItemBackground">
android:background="?android:attr/selectableItemBackground">
<ImageView
android:id="@+id/icon"
+1 -1
View File
@@ -8,7 +8,7 @@
android:gravity="center_vertical"
android:baselineAligned="false"
android:padding="16dp"
android:background="?attr/selectableItemBackground">
android:background="?android:attr/selectableItemBackground">
<LinearLayout
android:layout_width="match_parent"
+2 -2
View File
@@ -5,7 +5,7 @@
android:layout_height="54dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="?attr/selectableItemBackground">
android:background="?android:attr/selectableItemBackground">
<TextView
android:id="@+id/name"
@@ -17,7 +17,7 @@
android:textSize="16sp"
android:singleLine="true" />
<androidx.appcompat.widget.SwitchCompat
<Switch
android:id="@+id/enabled"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+2 -1
View File
@@ -6,7 +6,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="?attr/selectableItemBackground">
android:background="?android:attr/selectableItemBackground">
<TextView
android:id="@+id/title"
@@ -25,6 +25,7 @@
android:scaleType="center"
android:src="@drawable/ic_arrow_down"
android:tint="?android:attr/textColorPrimary"
android:tintMode="src_in"
tools:ignore="ContentDescription" />
</LinearLayout>
+2 -2
View File
@@ -5,7 +5,7 @@
android:layout_height="48dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="?attr/selectableItemBackground">
android:background="?android:attr/selectableItemBackground">
<TextView
android:id="@+id/title"
@@ -17,7 +17,7 @@
android:textSize="16sp"
android:singleLine="true" />
<androidx.appcompat.widget.SwitchCompat
<Switch
android:id="@+id/enabled"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+4 -6
View File
@@ -1,18 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
<LinearLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="fill"
app:tabMaxWidth="0dp" />
android:layout_height="48dp"
android:orientation="horizontal" />
<FrameLayout
android:id="@+id/category_layout"
@@ -27,7 +25,7 @@
android:gravity="center_vertical"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:background="?attr/selectableItemBackground"
android:background="?android:attr/selectableItemBackground"
tools:ignore="UselessParent">
<TextView
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="colorError" format="color" />
</resources>
+1
View File
@@ -5,6 +5,7 @@
<item type="id" name="divider_configuration" />
<item type="id" name="fragment_pager" />
<item type="id" name="main_content" />
<item type="id" name="preferences_list" />
<item type="id" name="toolbar_search" />
</resources>
+15 -15
View File
@@ -1,30 +1,30 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<style name="Theme.Main.Light" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<style name="Theme.Main.Light" parent="@android:style/Theme.Material.Light.DarkActionBar">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:colorBackground">#fffafafa</item>
<item name="colorPrimary">@color/primary_light</item>
<item name="colorPrimaryDark">@color/primary_dark_light</item>
<item name="colorAccent">@color/accent_light</item>
<item name="colorControlActivated">@color/accent_default_light</item>
<item name="android:colorPrimary">@color/primary_light</item>
<item name="android:colorPrimaryDark">@color/primary_dark_light</item>
<item name="android:colorAccent">@color/accent_light</item>
<item name="android:colorControlActivated">@color/accent_default_light</item>
<item name="colorError">@color/error_light</item>
</style>
<style name="Theme.Main.Dark" parent="Theme.AppCompat">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<style name="Theme.Main.Dark" parent="@android:style/Theme.Material">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:colorBackground">#ff111111</item>
<item name="colorPrimary">@color/primary_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_dark</item>
<item name="colorAccent">@color/accent_dark</item>
<item name="colorControlActivated">@color/accent_default_dark</item>
<item name="android:colorPrimary">@color/primary_dark</item>
<item name="android:colorPrimaryDark">@color/primary_dark_dark</item>
<item name="android:colorAccent">@color/accent_dark</item>
<item name="android:colorControlActivated">@color/accent_default_dark</item>
<item name="colorError">@color/error_dark</item>
</style>
<style name="Theme.Toolbar.Popup" parent="Theme.Main.Dark">
<item name="android:colorBackground">?attr/colorPrimaryDark</item>
<item name="android:colorBackground">?android:attr/colorPrimaryDark</item>
</style>
</resources>