parent
5810596e41
commit
92b9a6e1cd
@ -1,15 +1,19 @@ |
|||||||
package io.legado.app.ui.config |
package io.legado.app.ui.association |
||||||
|
|
||||||
import android.content.Intent |
import android.content.Intent |
||||||
import android.os.Bundle |
import android.os.Bundle |
||||||
import io.legado.app.R |
import io.legado.app.R |
||||||
import io.legado.app.base.VMBaseActivity |
import io.legado.app.base.VMBaseActivity |
||||||
|
import io.legado.app.constant.Theme |
||||||
import io.legado.app.ui.main.MainActivity |
import io.legado.app.ui.main.MainActivity |
||||||
import io.legado.app.utils.getViewModel |
import io.legado.app.utils.getViewModel |
||||||
|
|
||||||
|
|
||||||
class FileAssociationActivity : |
class FileAssociationActivity : VMBaseActivity<FileAssociationViewModel>( |
||||||
VMBaseActivity<FileAssociationViewModel>(R.layout.activity_file_association) { |
R.layout.activity_translucence, |
||||||
|
theme = Theme.Transparent |
||||||
|
) { |
||||||
|
|
||||||
override val viewModel: FileAssociationViewModel |
override val viewModel: FileAssociationViewModel |
||||||
get() = getViewModel(FileAssociationViewModel::class.java) |
get() = getViewModel(FileAssociationViewModel::class.java) |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
package io.legado.app.ui.config |
package io.legado.app.ui.association |
||||||
|
|
||||||
import android.app.Application |
import android.app.Application |
||||||
import android.content.Intent |
import android.content.Intent |
@ -0,0 +1,80 @@ |
|||||||
|
package io.legado.app.ui.association |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import androidx.lifecycle.Observer |
||||||
|
import io.legado.app.R |
||||||
|
import io.legado.app.base.VMBaseActivity |
||||||
|
import io.legado.app.constant.Theme |
||||||
|
import io.legado.app.data.entities.BookSource |
||||||
|
import io.legado.app.help.SourceHelp |
||||||
|
import io.legado.app.lib.dialogs.alert |
||||||
|
import io.legado.app.lib.dialogs.noButton |
||||||
|
import io.legado.app.lib.dialogs.okButton |
||||||
|
import io.legado.app.utils.applyTint |
||||||
|
import io.legado.app.utils.getViewModel |
||||||
|
import org.jetbrains.anko.toast |
||||||
|
|
||||||
|
class ImportBookSourceActivity : VMBaseActivity<ImportBookSourceViewModel>( |
||||||
|
R.layout.activity_translucence, |
||||||
|
theme = Theme.Transparent |
||||||
|
) { |
||||||
|
|
||||||
|
override val viewModel: ImportBookSourceViewModel |
||||||
|
get() = getViewModel(ImportBookSourceViewModel::class.java) |
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) { |
||||||
|
viewModel.errorLiveData.observe(this, Observer { |
||||||
|
errorDialog(it) |
||||||
|
}) |
||||||
|
viewModel.successLiveData.observe(this, Observer { |
||||||
|
successDialog(it) |
||||||
|
}) |
||||||
|
initData() |
||||||
|
} |
||||||
|
|
||||||
|
private fun initData() { |
||||||
|
intent.getStringExtra("source")?.let { |
||||||
|
viewModel.importSource(it) |
||||||
|
return |
||||||
|
} |
||||||
|
intent.getStringExtra("filePath")?.let { |
||||||
|
viewModel.importSourceFromFilePath(it) |
||||||
|
return |
||||||
|
} |
||||||
|
intent.data?.let { |
||||||
|
when (it.path) { |
||||||
|
"/importonline" -> it.getQueryParameter("src")?.let { url -> |
||||||
|
if (url.startsWith("http", false)) { |
||||||
|
viewModel.importSource(url) |
||||||
|
} else { |
||||||
|
viewModel.importSourceFromFilePath(url) |
||||||
|
} |
||||||
|
} |
||||||
|
else -> { |
||||||
|
toast("格式不对") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun errorDialog(msg: String) { |
||||||
|
alert("导入出错", msg) { |
||||||
|
okButton { } |
||||||
|
}.show().applyTint().setOnDismissListener { |
||||||
|
finish() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun successDialog(allSource: ArrayList<BookSource>) { |
||||||
|
alert("解析结果", "共${allSource.size}个书源,是否确认导入?") { |
||||||
|
okButton { |
||||||
|
SourceHelp.insertBookSource(*allSource.toTypedArray()) |
||||||
|
} |
||||||
|
noButton { |
||||||
|
|
||||||
|
} |
||||||
|
}.show().applyTint().setOnDismissListener { |
||||||
|
finish() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
package io.legado.app.ui.association |
||||||
|
|
||||||
|
import android.app.Application |
||||||
|
import android.net.Uri |
||||||
|
import androidx.documentfile.provider.DocumentFile |
||||||
|
import androidx.lifecycle.MutableLiveData |
||||||
|
import com.jayway.jsonpath.JsonPath |
||||||
|
import io.legado.app.base.BaseViewModel |
||||||
|
import io.legado.app.data.entities.BookSource |
||||||
|
import io.legado.app.help.http.HttpHelper |
||||||
|
import io.legado.app.help.storage.OldRule |
||||||
|
import io.legado.app.help.storage.Restore |
||||||
|
import io.legado.app.utils.* |
||||||
|
import kotlinx.coroutines.Dispatchers |
||||||
|
import kotlinx.coroutines.withContext |
||||||
|
import java.io.File |
||||||
|
|
||||||
|
class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) { |
||||||
|
|
||||||
|
val errorLiveData = MutableLiveData<String>() |
||||||
|
val successLiveData = MutableLiveData<ArrayList<BookSource>>() |
||||||
|
|
||||||
|
private val allSources = arrayListOf<BookSource>() |
||||||
|
|
||||||
|
fun importSourceFromFilePath(path: String) { |
||||||
|
execute { |
||||||
|
val content = if (path.isContentPath()) { |
||||||
|
//在前面被解码了,如果不进行编码,中文会无法识别 |
||||||
|
val newPath = Uri.encode(path, ":/.") |
||||||
|
DocumentFile.fromSingleUri(context, Uri.parse(newPath))?.readText(context) |
||||||
|
} else { |
||||||
|
val file = File(path) |
||||||
|
if (file.exists()) { |
||||||
|
file.readText() |
||||||
|
} else { |
||||||
|
null |
||||||
|
} |
||||||
|
} |
||||||
|
if (content != null) { |
||||||
|
importSource(content) |
||||||
|
} else { |
||||||
|
withContext(Dispatchers.Main) { |
||||||
|
errorLiveData.postValue("打开文件出错") |
||||||
|
} |
||||||
|
} |
||||||
|
}.onError { |
||||||
|
errorLiveData.postValue(it.localizedMessage ?: "打开文件出错") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun importSource(text: String) { |
||||||
|
execute { |
||||||
|
val text1 = text.trim() |
||||||
|
when { |
||||||
|
text1.isJsonObject() -> { |
||||||
|
val json = JsonPath.parse(text1) |
||||||
|
val urls = json.read<List<String>>("$.sourceUrls") |
||||||
|
if (!urls.isNullOrEmpty()) { |
||||||
|
urls.forEach { |
||||||
|
importSourceUrl(it) |
||||||
|
} |
||||||
|
} else { |
||||||
|
OldRule.jsonToBookSource(text1)?.let { |
||||||
|
allSources.add(it) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
text1.isJsonArray() -> { |
||||||
|
val items: List<Map<String, Any>> = Restore.jsonPath.parse(text1).read("$") |
||||||
|
for (item in items) { |
||||||
|
val jsonItem = Restore.jsonPath.parse(item) |
||||||
|
OldRule.jsonToBookSource(jsonItem.jsonString())?.let { |
||||||
|
allSources.add(it) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
text1.isAbsUrl() -> { |
||||||
|
importSourceUrl(text1) |
||||||
|
} |
||||||
|
else -> throw Exception("格式不对") |
||||||
|
} |
||||||
|
}.onError { |
||||||
|
it.printStackTrace() |
||||||
|
errorLiveData.postValue(it.localizedMessage ?: "") |
||||||
|
}.onSuccess { |
||||||
|
successLiveData.postValue(allSources) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun importSourceUrl(url: String) { |
||||||
|
HttpHelper.simpleGet(url, "UTF-8").let { body -> |
||||||
|
if (body == null) { |
||||||
|
throw Exception("访问网站失败") |
||||||
|
} |
||||||
|
val items: List<Map<String, Any>> = Restore.jsonPath.parse(body).read("$") |
||||||
|
for (item in items) { |
||||||
|
val jsonItem = Restore.jsonPath.parse(item) |
||||||
|
OldRule.jsonToBookSource(jsonItem.jsonString())?.let { source -> |
||||||
|
allSources.add(source) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
package io.legado.app.ui.association |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import androidx.lifecycle.Observer |
||||||
|
import io.legado.app.App |
||||||
|
import io.legado.app.R |
||||||
|
import io.legado.app.base.VMBaseActivity |
||||||
|
import io.legado.app.constant.Theme |
||||||
|
import io.legado.app.data.entities.ReplaceRule |
||||||
|
import io.legado.app.lib.dialogs.alert |
||||||
|
import io.legado.app.lib.dialogs.noButton |
||||||
|
import io.legado.app.lib.dialogs.okButton |
||||||
|
import io.legado.app.utils.applyTint |
||||||
|
import io.legado.app.utils.getViewModel |
||||||
|
import org.jetbrains.anko.toast |
||||||
|
|
||||||
|
class ImportReplaceRuleActivity : VMBaseActivity<ImportReplaceRuleViewModel>( |
||||||
|
R.layout.activity_translucence, |
||||||
|
theme = Theme.Transparent |
||||||
|
) { |
||||||
|
|
||||||
|
override val viewModel: ImportReplaceRuleViewModel |
||||||
|
get() = getViewModel(ImportReplaceRuleViewModel::class.java) |
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) { |
||||||
|
viewModel.errorLiveData.observe(this, Observer { |
||||||
|
errorDialog(it) |
||||||
|
}) |
||||||
|
viewModel.successLiveData.observe(this, Observer { |
||||||
|
successDialog(it) |
||||||
|
}) |
||||||
|
initData() |
||||||
|
} |
||||||
|
|
||||||
|
private fun initData() { |
||||||
|
intent.getStringExtra("source")?.let { |
||||||
|
viewModel.import(it) |
||||||
|
return |
||||||
|
} |
||||||
|
intent.getStringExtra("filePath")?.let { |
||||||
|
viewModel.importFromFilePath(it) |
||||||
|
return |
||||||
|
} |
||||||
|
intent.data?.let { |
||||||
|
when (it.path) { |
||||||
|
"/importonline" -> it.getQueryParameter("src")?.let { url -> |
||||||
|
if (url.startsWith("http", false)) { |
||||||
|
viewModel.import(url) |
||||||
|
} else { |
||||||
|
viewModel.importFromFilePath(url) |
||||||
|
} |
||||||
|
} |
||||||
|
else -> { |
||||||
|
toast("格式不对") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun errorDialog(msg: String) { |
||||||
|
alert("导入出错", msg) { |
||||||
|
okButton { } |
||||||
|
}.show().applyTint().setOnDismissListener { |
||||||
|
finish() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun successDialog(allSource: ArrayList<ReplaceRule>) { |
||||||
|
alert("解析结果", "共${allSource.size}个替换规则,是否确认导入?") { |
||||||
|
okButton { |
||||||
|
App.db.replaceRuleDao().insert(*allSource.toTypedArray()) |
||||||
|
} |
||||||
|
noButton { |
||||||
|
|
||||||
|
} |
||||||
|
}.show().applyTint().setOnDismissListener { |
||||||
|
finish() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
package io.legado.app.ui.association |
||||||
|
|
||||||
|
import android.app.Application |
||||||
|
import android.net.Uri |
||||||
|
import androidx.documentfile.provider.DocumentFile |
||||||
|
import androidx.lifecycle.MutableLiveData |
||||||
|
import io.legado.app.R |
||||||
|
import io.legado.app.base.BaseViewModel |
||||||
|
import io.legado.app.data.entities.ReplaceRule |
||||||
|
import io.legado.app.help.http.HttpHelper |
||||||
|
import io.legado.app.help.storage.OldReplace |
||||||
|
import io.legado.app.utils.isAbsUrl |
||||||
|
import io.legado.app.utils.isContentPath |
||||||
|
import io.legado.app.utils.readText |
||||||
|
import java.io.File |
||||||
|
|
||||||
|
class ImportReplaceRuleViewModel(app: Application) : BaseViewModel(app) { |
||||||
|
val errorLiveData = MutableLiveData<String>() |
||||||
|
val successLiveData = MutableLiveData<ArrayList<ReplaceRule>>() |
||||||
|
|
||||||
|
private val allRules = arrayListOf<ReplaceRule>() |
||||||
|
|
||||||
|
fun importFromFilePath(path: String) { |
||||||
|
execute { |
||||||
|
val content = if (path.isContentPath()) { |
||||||
|
//在前面被解码了,如果不进行编码,中文会无法识别 |
||||||
|
val newPath = Uri.encode(path, ":/.") |
||||||
|
DocumentFile.fromSingleUri(context, Uri.parse(newPath))?.readText(context) |
||||||
|
} else { |
||||||
|
val file = File(path) |
||||||
|
if (file.exists()) { |
||||||
|
file.readText() |
||||||
|
} else { |
||||||
|
null |
||||||
|
} |
||||||
|
} |
||||||
|
if (content != null) { |
||||||
|
import(content) |
||||||
|
} else { |
||||||
|
errorLiveData.postValue("打开文件出错") |
||||||
|
} |
||||||
|
}.onError { |
||||||
|
errorLiveData.postValue(it.localizedMessage ?: "打开文件出错") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun import(text: String) { |
||||||
|
execute { |
||||||
|
if (text.isAbsUrl()) { |
||||||
|
HttpHelper.simpleGet(text)?.let { |
||||||
|
val rules = OldReplace.jsonToReplaceRules(it) |
||||||
|
allRules.addAll(rules) |
||||||
|
} |
||||||
|
} else { |
||||||
|
val rules = OldReplace.jsonToReplaceRules(text) |
||||||
|
allRules.addAll(rules) |
||||||
|
} |
||||||
|
}.onError { |
||||||
|
errorLiveData.postValue(it.localizedMessage ?: "ERROR") |
||||||
|
}.onSuccess { |
||||||
|
errorLiveData.postValue(context.getString(R.string.success)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
package io.legado.app.ui.association |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import androidx.lifecycle.Observer |
||||||
|
import io.legado.app.R |
||||||
|
import io.legado.app.base.VMBaseActivity |
||||||
|
import io.legado.app.constant.Theme |
||||||
|
import io.legado.app.data.entities.RssSource |
||||||
|
import io.legado.app.help.SourceHelp |
||||||
|
import io.legado.app.lib.dialogs.alert |
||||||
|
import io.legado.app.lib.dialogs.noButton |
||||||
|
import io.legado.app.lib.dialogs.okButton |
||||||
|
import io.legado.app.utils.applyTint |
||||||
|
import io.legado.app.utils.getViewModel |
||||||
|
import org.jetbrains.anko.toast |
||||||
|
|
||||||
|
class ImportRssSourceActivity : VMBaseActivity<ImportRssSourceViewModel>( |
||||||
|
R.layout.activity_translucence, |
||||||
|
theme = Theme.Transparent |
||||||
|
) { |
||||||
|
|
||||||
|
override val viewModel: ImportRssSourceViewModel |
||||||
|
get() = getViewModel(ImportRssSourceViewModel::class.java) |
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) { |
||||||
|
viewModel.errorLiveData.observe(this, Observer { |
||||||
|
errorDialog(it) |
||||||
|
}) |
||||||
|
viewModel.successLiveData.observe(this, Observer { |
||||||
|
successDialog(it) |
||||||
|
}) |
||||||
|
initData() |
||||||
|
} |
||||||
|
|
||||||
|
private fun initData() { |
||||||
|
intent.getStringExtra("source")?.let { |
||||||
|
viewModel.importSource(it) |
||||||
|
return |
||||||
|
} |
||||||
|
intent.getStringExtra("filePath")?.let { |
||||||
|
viewModel.importSourceFromFilePath(it) |
||||||
|
return |
||||||
|
} |
||||||
|
intent.data?.let { |
||||||
|
when (it.path) { |
||||||
|
"/importonline" -> it.getQueryParameter("src")?.let { url -> |
||||||
|
if (url.startsWith("http", false)) { |
||||||
|
viewModel.importSource(url) |
||||||
|
} else { |
||||||
|
viewModel.importSourceFromFilePath(url) |
||||||
|
} |
||||||
|
} |
||||||
|
else -> { |
||||||
|
toast("格式不对") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun errorDialog(msg: String) { |
||||||
|
alert("导入出错", msg) { |
||||||
|
okButton { } |
||||||
|
}.show().applyTint().setOnDismissListener { |
||||||
|
finish() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun successDialog(allSource: ArrayList<RssSource>) { |
||||||
|
alert("解析结果", "共${allSource.size}个订阅源,是否确认导入?") { |
||||||
|
okButton { |
||||||
|
SourceHelp.insertRssSource(*allSource.toTypedArray()) |
||||||
|
} |
||||||
|
noButton { |
||||||
|
|
||||||
|
} |
||||||
|
}.show().applyTint().setOnDismissListener { |
||||||
|
finish() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package io.legado.app.ui.association |
||||||
|
|
||||||
|
import android.app.Application |
||||||
|
import android.net.Uri |
||||||
|
import androidx.documentfile.provider.DocumentFile |
||||||
|
import androidx.lifecycle.MutableLiveData |
||||||
|
import com.jayway.jsonpath.JsonPath |
||||||
|
import io.legado.app.base.BaseViewModel |
||||||
|
import io.legado.app.data.entities.RssSource |
||||||
|
import io.legado.app.help.http.HttpHelper |
||||||
|
import io.legado.app.help.storage.Restore |
||||||
|
import io.legado.app.utils.* |
||||||
|
import java.io.File |
||||||
|
|
||||||
|
class ImportRssSourceViewModel(app: Application) : BaseViewModel(app) { |
||||||
|
|
||||||
|
val errorLiveData = MutableLiveData<String>() |
||||||
|
val successLiveData = MutableLiveData<ArrayList<RssSource>>() |
||||||
|
|
||||||
|
private val allSources = arrayListOf<RssSource>() |
||||||
|
|
||||||
|
fun importSourceFromFilePath(path: String) { |
||||||
|
execute { |
||||||
|
val content = if (path.isContentPath()) { |
||||||
|
//在前面被解码了,如果不进行编码,中文会无法识别 |
||||||
|
val newPath = Uri.encode(path, ":/.") |
||||||
|
DocumentFile.fromSingleUri(context, Uri.parse(newPath))?.readText(context) |
||||||
|
} else { |
||||||
|
val file = File(path) |
||||||
|
if (file.exists()) { |
||||||
|
file.readText() |
||||||
|
} else { |
||||||
|
null |
||||||
|
} |
||||||
|
} |
||||||
|
if (null != content) { |
||||||
|
GSON.fromJsonArray<RssSource>(content)?.let { |
||||||
|
allSources.addAll(it) |
||||||
|
} |
||||||
|
} |
||||||
|
}.onSuccess { |
||||||
|
successLiveData.postValue(allSources) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun importSource(text: String) { |
||||||
|
execute { |
||||||
|
val text1 = text.trim() |
||||||
|
when { |
||||||
|
text1.isJsonObject() -> { |
||||||
|
val json = JsonPath.parse(text1) |
||||||
|
val urls = json.read<List<String>>("$.sourceUrls") |
||||||
|
if (!urls.isNullOrEmpty()) { |
||||||
|
urls.forEach { |
||||||
|
importSourceUrl(it) |
||||||
|
} |
||||||
|
} else { |
||||||
|
GSON.fromJsonArray<RssSource>(text1)?.let { |
||||||
|
allSources.addAll(it) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
text1.isJsonArray() -> { |
||||||
|
val items: List<Map<String, Any>> = Restore.jsonPath.parse(text1).read("$") |
||||||
|
for (item in items) { |
||||||
|
val jsonItem = Restore.jsonPath.parse(item) |
||||||
|
GSON.fromJsonObject<RssSource>(jsonItem.jsonString())?.let { |
||||||
|
allSources.add(it) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
text1.isAbsUrl() -> { |
||||||
|
importSourceUrl(text1) |
||||||
|
} |
||||||
|
else -> throw Exception("格式不对") |
||||||
|
} |
||||||
|
}.onError { |
||||||
|
errorLiveData.postValue("ImportError:${it.localizedMessage}") |
||||||
|
}.onSuccess { |
||||||
|
successLiveData.postValue(allSources) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun importSourceUrl(url: String) { |
||||||
|
HttpHelper.simpleGet(url, "UTF-8")?.let { body -> |
||||||
|
val items: List<Map<String, Any>> = Restore.jsonPath.parse(body).read("$") |
||||||
|
for (item in items) { |
||||||
|
val jsonItem = Restore.jsonPath.parse(item) |
||||||
|
GSON.fromJsonObject<RssSource>(jsonItem.jsonString())?.let { source -> |
||||||
|
allSources.add(source) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,8 +1,7 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
xmlns:tools="http://schemas.android.com/tools" |
|
||||||
android:layout_width="match_parent" |
android:layout_width="match_parent" |
||||||
android:layout_height="match_parent" |
android:layout_height="match_parent" |
||||||
tools:context="io.legado.app.ui.config.FileAssociationActivity"> |
android:background="@color/transparent30"> |
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout> |
</androidx.constraintlayout.widget.ConstraintLayout> |
Loading…
Reference in new issue