Layout Changes (#25)
Co-authored-by: fgerber <frederic.gerber@mydoli.ch> Co-authored-by: soraefir <soraefir+git@pm.me> Reviewed-on: helcel/beendroid#25 Co-authored-by: fgerber <fred@mydoli.ch> Co-committed-by: fgerber <fred@mydoli.ch>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
package net.helcel.beendroid.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import net.helcel.beendroid.databinding.FragmentAboutBinding
|
||||
|
||||
class AboutFragment: Fragment() {
|
||||
private var _binding: FragmentAboutBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and
|
||||
// onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentAboutBinding.inflate(inflater, container, false)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
@@ -1,17 +1,20 @@
|
||||
package net.helcel.beendroid.activity
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.checkbox.MaterialCheckBox
|
||||
import net.helcel.beendroid.R
|
||||
import net.helcel.beendroid.countries.GeoLoc
|
||||
import net.helcel.beendroid.countries.LocType
|
||||
import net.helcel.beendroid.countries.Visited
|
||||
import java.util.*
|
||||
|
||||
@@ -36,10 +39,7 @@ class FoldingListAdapter(
|
||||
|
||||
override fun onBindViewHolder(holder: FoldingListViewHolder, position: Int) {
|
||||
val el = cg.toList()[position]
|
||||
holder.bind(el) {
|
||||
notifyItemChanged(position)
|
||||
parentLambda()
|
||||
}
|
||||
holder.bind(el) { parentLambda() }
|
||||
|
||||
holder.addListeners( {
|
||||
if (!el.first.isEnd) {
|
||||
@@ -60,46 +60,52 @@ class FoldingListAdapter(
|
||||
class FoldingListViewHolder(private val ctx: Context, itemView: View,
|
||||
private val visited: Visited,
|
||||
) : RecyclerView.ViewHolder(itemView) {
|
||||
private val textView: TextView
|
||||
private val expand: ImageView
|
||||
private val checkBox: MaterialCheckBox
|
||||
private val subItemView: View
|
||||
private val list: RecyclerView
|
||||
|
||||
private val textView: TextView = itemView.findViewById(R.id.textView)
|
||||
private val checkBox: MaterialCheckBox = itemView.findViewById(R.id.checkBox)
|
||||
private val subItemView: View = itemView.findViewById(R.id.sub_item)
|
||||
private val list: RecyclerView = itemView.findViewById(R.id.list_list)
|
||||
init {
|
||||
textView = itemView.findViewById(R.id.textView)
|
||||
expand = itemView.findViewById(R.id.expand)
|
||||
expand.setImageDrawable(AppCompatResources.getDrawable(ctx,R.drawable.chevron_right_solid))
|
||||
checkBox = itemView.findViewById(R.id.checkBox)
|
||||
subItemView = itemView.findViewById(R.id.sub_item)
|
||||
list = itemView.findViewById(R.id.list_list)
|
||||
list.layoutManager = LinearLayoutManager(ctx, RecyclerView.VERTICAL, false)
|
||||
}
|
||||
|
||||
fun bind(el: Pair<GeoLoc, Boolean>, parentLambda: () -> Unit) {
|
||||
expand.rotation = if(el.second) 90f else 0f
|
||||
subItemView.visibility = if (el.second) View.VISIBLE else View.GONE
|
||||
expand.visibility = if(!el.first.isEnd) View.VISIBLE else View.GONE
|
||||
|
||||
textView.text = el.first.fullName
|
||||
if (el.first.type == LocType.GROUP) {
|
||||
textView.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
val colorGrayTyped = TypedValue()
|
||||
ctx.theme.resolveAttribute(android.R.attr.panelColorBackground, colorGrayTyped, true)
|
||||
val color = Color.valueOf(colorGrayTyped.data)
|
||||
textView.setBackgroundColor(Color.valueOf(color.red(), color.green(), color.blue(), 0.5f).toArgb())
|
||||
list.adapter = FoldingListAdapter(ctx, el.first.children,visited, parentLambda)
|
||||
textView.parent.parent.requestChildFocus(textView,textView)
|
||||
|
||||
} else {
|
||||
val colorBackgroundTyped = TypedValue()
|
||||
ctx.theme.resolveAttribute(android.R.attr.colorBackground, colorBackgroundTyped, true)
|
||||
textView.backgroundTintList = null
|
||||
textView.background = ColorDrawable(colorBackgroundTyped.data)
|
||||
textView.isActivated = false
|
||||
|
||||
val layoutParam = checkBox.layoutParams
|
||||
layoutParam.width = 125
|
||||
checkBox.layoutParams = layoutParam
|
||||
checkBox.visibility = View.VISIBLE
|
||||
}
|
||||
checkBox.checkedState =
|
||||
if(visited.visited(el.first)) MaterialCheckBox.STATE_CHECKED
|
||||
if (visited.visited(el.first)) MaterialCheckBox.STATE_CHECKED
|
||||
else if (el.first.children.any { visited.visited(it) }) MaterialCheckBox.STATE_INDETERMINATE
|
||||
else MaterialCheckBox.STATE_UNCHECKED
|
||||
|
||||
textView.parent.parent.requestChildFocus(textView,textView)
|
||||
list.adapter = FoldingListAdapter(ctx, el.first.children,visited, parentLambda)
|
||||
}
|
||||
|
||||
fun addListeners(expandLambda: ()->Boolean, visitedLambda: (Boolean)->Unit) {
|
||||
|
||||
textView.setOnClickListener { checkBox.toggle() }
|
||||
textView.setOnClickListener { expandLambda() }
|
||||
checkBox.addOnCheckedStateChangedListener { _, e ->
|
||||
visitedLambda(e == MaterialCheckBox.STATE_CHECKED)
|
||||
}
|
||||
|
||||
textView.setOnLongClickListener{ expandLambda() }
|
||||
expand.setOnClickListener { expandLambda() }
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,41 @@
|
||||
package net.helcel.beendroid.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import net.helcel.beendroid.R
|
||||
import net.helcel.beendroid.databinding.FragmentLicenseBinding
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
|
||||
class LicenseFragment: Fragment() {
|
||||
private var _binding: FragmentLicenseBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and
|
||||
// onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentLicenseBinding.inflate(inflater, container, false)
|
||||
|
||||
val librariesFragment = LibsBuilder()
|
||||
.withLicenseShown(true)
|
||||
.supportFragment()
|
||||
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.license_fragment_view, librariesFragment)
|
||||
.commit()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
@@ -1,9 +1,19 @@
|
||||
package net.helcel.beendroid.activity
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.caverock.androidsvg.RenderOptions
|
||||
@@ -17,6 +27,8 @@ import net.helcel.beendroid.svg.PSVGWrapper
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
private lateinit var map : SVGImageView
|
||||
private lateinit var list : RecyclerView
|
||||
|
||||
@@ -24,32 +36,103 @@ class MainActivity : AppCompatActivity() {
|
||||
private lateinit var psvg : PSVGWrapper
|
||||
private lateinit var css : CSSWrapper
|
||||
|
||||
private var processor: ImageProcessor = ImageProcessor({ refreshMapCompute() },{ refreshMapDisplay(it) })
|
||||
|
||||
private val bitmap: Bitmap = Bitmap.createBitmap(1200,900, Bitmap.Config.ARGB_8888)
|
||||
private val canvas = Canvas(bitmap)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
canvas.drawRGB(255, 255, 255)
|
||||
// Create action bar
|
||||
val colorPrimaryTyped = TypedValue()
|
||||
theme.resolveAttribute(android.R.attr.colorPrimary, colorPrimaryTyped, true)
|
||||
supportActionBar?.setBackgroundDrawable(ColorDrawable(colorPrimaryTyped.data))
|
||||
|
||||
// Fetch shared preferences to restore app theme upon startup
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
SettingsFragment.setTheme(this, sharedPreferences.getString(getString(R.string.key_theme), getString(R.string.system)))
|
||||
|
||||
// Create menu in action bar
|
||||
addMenuProvider(object : MenuProvider {
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.menu_main, menu)
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_edit -> {
|
||||
// TODO: Enable editing selected countries
|
||||
true
|
||||
}
|
||||
R.id.action_stats -> {
|
||||
// TODO: Write stats activity
|
||||
true
|
||||
}
|
||||
R.id.action_settings -> {
|
||||
// Open settings
|
||||
startActivity(Intent(this@MainActivity, SettingsActivity::class.java))
|
||||
true
|
||||
}
|
||||
else -> {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// Restore visited countries
|
||||
visited = Visited(this)
|
||||
visited.load()
|
||||
|
||||
// Wrap lists of countries
|
||||
psvg = PSVGWrapper(this)
|
||||
css = CSSWrapper(visited)
|
||||
|
||||
// Populate map from list of countries
|
||||
setContentView(R.layout.activity_main)
|
||||
map = findViewById(R.id.map)
|
||||
map.setImageBitmap(bitmap)
|
||||
refreshMapDisplay(refreshMapCompute())
|
||||
|
||||
refreshMap()
|
||||
|
||||
// Populate list below the map
|
||||
list = findViewById(R.id.list)
|
||||
list.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
|
||||
list.adapter = FoldingListAdapter(this, World.WWW.children, visited) { refreshMap() }
|
||||
list.adapter = FoldingListAdapter(this, World.WWW.children, visited) { processor.process() }
|
||||
}
|
||||
|
||||
private fun refreshMap(){
|
||||
psvg.get().renderToCanvas(canvas,RenderOptions.create().css(css.get()))
|
||||
private fun refreshMapDisplay(css_value: String){
|
||||
// Set or reset background (replaces canvas.drawColor(0, 0, 0))
|
||||
val colorBackgroundTyped = TypedValue()
|
||||
theme.resolveAttribute(android.R.attr.colorBackground, colorBackgroundTyped, true)
|
||||
canvas.drawColor(colorBackgroundTyped.data)
|
||||
|
||||
// Render all countries and visited ones
|
||||
psvg.getFill().renderToCanvas(canvas, RenderOptions.create().css(css_value))
|
||||
|
||||
// Render all contours in the same color as the background to make them much clearer
|
||||
psvg.getDraw().renderToCanvas(canvas)
|
||||
}
|
||||
|
||||
private fun refreshMapCompute() : String {
|
||||
return css.get()
|
||||
}
|
||||
|
||||
|
||||
|
||||
class ImageProcessor(private val refreshMapCompute: ()->String, private val refreshMapDisplay: (String)->Unit) {
|
||||
|
||||
private var currentJob : Job? = null
|
||||
fun process() {
|
||||
currentJob?.cancel()
|
||||
currentJob = CoroutineScope(Dispatchers.Main).launch {
|
||||
try {
|
||||
refreshMapDisplay(refreshMapCompute())
|
||||
} catch (_: CancellationException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
package net.helcel.beendroid.activity
|
||||
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import net.helcel.beendroid.R
|
||||
|
||||
class SettingsActivity: AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Bind activity to XML layout with fragment view
|
||||
setContentView(R.layout.activity_settings)
|
||||
|
||||
// Create action bar
|
||||
val colorPrimaryTyped = TypedValue()
|
||||
theme.resolveAttribute(android.R.attr.colorPrimary, colorPrimaryTyped, true)
|
||||
supportActionBar?.setBackgroundDrawable(ColorDrawable(colorPrimaryTyped.data))
|
||||
supportActionBar?.title = getString(R.string.action_settings)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
// Populate activity with settings fragment
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_view, SettingsFragment(), getString(R.string.action_settings))
|
||||
.commit()
|
||||
|
||||
// Change title in action bar according to current fragment
|
||||
supportFragmentManager.addFragmentOnAttachListener { _, _ ->
|
||||
supportActionBar?.title = supportFragmentManager.findFragmentById(R.id.fragment_view).let {
|
||||
when (it) {
|
||||
is LicenseFragment -> getString(R.string.licenses)
|
||||
is AboutFragment -> getString(R.string.about)
|
||||
else -> getString(R.string.action_settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
// Configure on back pressed
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_view).let {
|
||||
when (it) {
|
||||
is LicenseFragment, is AboutFragment -> {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.remove(it)
|
||||
.commit()
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_view, SettingsFragment(), getString(R.string.action_settings))
|
||||
.commit()
|
||||
supportActionBar?.title = getString(R.string.action_settings)
|
||||
}
|
||||
else -> {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
package net.helcel.beendroid.activity
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import net.helcel.beendroid.R
|
||||
|
||||
|
||||
class SettingsFragment: PreferenceFragmentCompat() {
|
||||
private lateinit var themePreference: ListPreference
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.fragment_settings, rootKey)
|
||||
|
||||
// Select Light/Dark/System Mode
|
||||
themePreference = findPreference(getString(R.string.key_theme))!!
|
||||
themePreference.setOnPreferenceChangeListener { _, key ->
|
||||
setTheme(requireContext(), key as String)
|
||||
}
|
||||
|
||||
val aboutPreference = findPreference<Preference>(getString(R.string.about))
|
||||
aboutPreference?.setOnPreferenceClickListener {
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_view, AboutFragment(), getString(R.string.about))
|
||||
.commit()
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
val licensesPreference = findPreference<Preference>(getString(R.string.licenses))
|
||||
licensesPreference?.setOnPreferenceClickListener {
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_view, LicenseFragment(), getString(R.string.licenses))
|
||||
.commit()
|
||||
true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun setTheme(context: Context, key: String?): Boolean {
|
||||
when (key) {
|
||||
context.getString(R.string.system) -> {
|
||||
// Set SYSTEM Theme
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
return true
|
||||
}
|
||||
|
||||
context.getString(R.string.light) -> {
|
||||
// Set LIGHT Theme
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||
return true
|
||||
}
|
||||
|
||||
context.getString(R.string.dark) -> {
|
||||
// Set DARK Theme
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||
return true
|
||||
}
|
||||
|
||||
else -> {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,13 +10,13 @@ class Visited(ctx: Context) {
|
||||
|
||||
fun load() {
|
||||
|
||||
Group.values().forEach {
|
||||
Group.entries.forEach {
|
||||
locs[it] = pref.getBoolean(it.code, false)
|
||||
}
|
||||
Country.values().forEach {
|
||||
Country.entries.forEach {
|
||||
locs[it] = pref.getBoolean(it.code, false)
|
||||
}
|
||||
State.values().forEach {
|
||||
State.entries.forEach {
|
||||
locs[it] = pref.getBoolean(it.code, false)
|
||||
}
|
||||
editor.apply()
|
||||
|
@@ -5,16 +5,18 @@ import net.helcel.beendroid.countries.World
|
||||
|
||||
class CSSWrapper(private val visited: Visited) {
|
||||
|
||||
private val colorPrimary = "#0187FF"
|
||||
|
||||
fun get() : String {
|
||||
return listOf(World.WWW.children
|
||||
.filter { visited.visited(it)}
|
||||
.map { ".${it.code}{fill:blue;}"}
|
||||
.map { ".${it.code}{fill:$colorPrimary;}"}
|
||||
.fold(""){acc, s-> acc + s},
|
||||
World.WWW.children
|
||||
.filter { !visited.visited(it) }
|
||||
.map { cg -> cg.children
|
||||
.filter { visited.visited(it) }
|
||||
.map { ".${it.code}{fill:blue;}"}
|
||||
.map { ".${it.code}{fill:$colorPrimary;}"}
|
||||
.fold(""){acc, s-> acc + s}
|
||||
}.fold(""){acc,s->acc+s},
|
||||
).fold(""){acc,s-> acc+s}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package net.helcel.beendroid.svg
|
||||
|
||||
import android.content.Context
|
||||
import android.util.TypedValue
|
||||
import com.caverock.androidsvg.SVG
|
||||
import net.helcel.beendroid.countries.Country
|
||||
import net.helcel.beendroid.countries.GeoLoc
|
||||
@@ -11,8 +12,20 @@ class PSVGWrapper(ctx: Context) {
|
||||
private val cm = HashMap<GeoLoc, PSVGLoader>()
|
||||
private var fm = ""
|
||||
|
||||
private val colorForeground: String
|
||||
private val colorBackground: String
|
||||
|
||||
init {
|
||||
Country.values().forEach {
|
||||
val colorSecondaryTyped = TypedValue()
|
||||
ctx.theme.resolveAttribute(android.R.attr.panelColorBackground, colorSecondaryTyped, true)
|
||||
colorForeground = "\"#${Integer.toHexString(colorSecondaryTyped.data).subSequence(2, 8)}\""
|
||||
|
||||
val colorBackgroundTyped = TypedValue()
|
||||
ctx.theme.resolveAttribute(android.R.attr.colorBackground, colorBackgroundTyped, true)
|
||||
colorBackground = "\"#${Integer.toHexString(colorBackgroundTyped.data).subSequence(2, 8)}\""
|
||||
|
||||
|
||||
Country.entries.forEach {
|
||||
cm[it] = PSVGLoader(ctx, it, Level.ZERO).load()
|
||||
}
|
||||
build()
|
||||
@@ -31,12 +44,12 @@ class PSVGWrapper(ctx: Context) {
|
||||
}.fold("") { acc, e -> acc + e }
|
||||
}
|
||||
|
||||
fun get(): SVG {
|
||||
return SVG.getFromString("<svg id=\"map\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"1200\" x=\"0\" y=\"0\" >$fm</svg>")
|
||||
fun getFill(): SVG {
|
||||
return SVG.getFromString("<svg id=\"map\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"1200\" x=\"0\" y=\"0\" fill=${colorForeground}>$fm</svg>")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fun getDraw(): SVG {
|
||||
return SVG.getFromString("<svg id=\"map\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"1200\" x=\"0\" y=\"0\" fill-opacity=\"0\" stroke=${colorBackground}>$fm</svg>")
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user