add import from url (1/3)
This commit is contained in:
12 files changed
+346
-279
No files matched your search
@@ -40,6 +40,10 @@
|
||||
android:label="@string/clash_import_from_file"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity android:name=".ImportUrlActivity"
|
||||
android:label="@string/clash_import_from_url"
|
||||
android:screenOrientation="portrait"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ClashStatusActivity"
|
||||
android:exported="false"
|
||||
|
||||
@@ -63,13 +63,22 @@ class CreateProfileActivity : BaseActivity() {
|
||||
adapter = Adapter(this@CreateProfileActivity)
|
||||
setOnItemClickListener { _, _, index, _ ->
|
||||
when (index) {
|
||||
0 ->
|
||||
0 -> {
|
||||
startActivity(
|
||||
Intent(
|
||||
this@CreateProfileActivity,
|
||||
ImportFileActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
1 -> {
|
||||
startActivity(
|
||||
Intent(
|
||||
this@CreateProfileActivity,
|
||||
ImportUrlActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
package com.github.kr328.clash
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.charleskorn.kaml.Yaml
|
||||
import com.charleskorn.kaml.YamlConfiguration
|
||||
import com.github.kr328.clash.adapter.FormAdapter
|
||||
import com.github.kr328.clash.model.ClashProfile
|
||||
import com.github.kr328.clash.service.data.ClashProfileEntity
|
||||
import com.github.kr328.clash.utils.FileUtils
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.activity_import_file.*
|
||||
import java.io.FileOutputStream
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class ImportFileActivity : BaseActivity() {
|
||||
companion object {
|
||||
const val REQUEST_FILE_CODE = 185
|
||||
}
|
||||
private val elements: List<FormAdapter.Type> = listOf(
|
||||
FormAdapter.TextType(R.drawable.ic_about,
|
||||
R.string.clash_profile_name,
|
||||
R.string.clash_profile_name_hint),
|
||||
FormAdapter.FilePickerType(R.drawable.ic_new_profile_file,
|
||||
R.string.clash_profile_file,
|
||||
R.string.clash_profile_file_hint)
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -30,93 +32,53 @@ class ImportFileActivity : BaseActivity() {
|
||||
|
||||
setSupportActionBar(activity_import_file_toolbar)
|
||||
|
||||
activity_import_file_form.also {
|
||||
it.layoutManager = LinearLayoutManager(this)
|
||||
it.adapter = FormAdapter(this, elements)
|
||||
}
|
||||
|
||||
activity_import_file_save.setOnClickListener {
|
||||
when {
|
||||
activity_import_file_name_text.text.isEmpty() -> {
|
||||
Snackbar.make(
|
||||
activity_import_file_root,
|
||||
R.string.clash_import_file_empty_name,
|
||||
Snackbar.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
activity_import_file_path_text.tag == null -> {
|
||||
Snackbar.make(
|
||||
activity_import_file_root,
|
||||
R.string.clash_import_file_empty_path,
|
||||
Snackbar.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
else -> {
|
||||
it.visibility = View.GONE
|
||||
activity_import_file_saving.visibility = View.VISIBLE
|
||||
activity_import_file_save.visibility = View.GONE
|
||||
activity_import_file_saving.visibility = View.VISIBLE
|
||||
|
||||
thread {
|
||||
checkAndInsert()
|
||||
|
||||
runOnUiThread {
|
||||
it.visibility = View.VISIBLE
|
||||
activity_import_file_saving.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
thread {
|
||||
checkAndInsert()
|
||||
}
|
||||
}
|
||||
|
||||
val name = activity_import_file_name_text
|
||||
|
||||
activity_import_file_name.setOnClickListener {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.clash_profile_name)
|
||||
.setView(R.layout.dialog_text_edit)
|
||||
.setPositiveButton(R.string.ok) { _, _ -> }
|
||||
.setNegativeButton(R.string.cancel) { _, _ -> }
|
||||
.create()
|
||||
.apply {
|
||||
setOnShowListener {
|
||||
val data = findViewById<EditText>(R.id.dialog_text_edit).also {
|
||||
it?.hint = name.hint
|
||||
it?.setText(name.text)
|
||||
}
|
||||
getButton(AlertDialog.BUTTON_POSITIVE)?.apply {
|
||||
setTextColor(getColor(R.color.colorAccent))
|
||||
setOnClickListener {
|
||||
name.text = data?.text ?: ""
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
getButton(AlertDialog.BUTTON_NEGATIVE)?.apply {
|
||||
setTextColor(getColor(R.color.colorAccent))
|
||||
}
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
activity_import_file_path.setOnClickListener {
|
||||
startActivityForResult(
|
||||
Intent(Intent.ACTION_GET_CONTENT)
|
||||
.setType(
|
||||
MimeTypeMap.getSingleton()
|
||||
.getMimeTypeFromExtension("yaml") ?: "*/*"
|
||||
),
|
||||
REQUEST_FILE_CODE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == REQUEST_FILE_CODE && resultCode == Activity.RESULT_OK) {
|
||||
activity_import_file_path_text.tag = data?.data
|
||||
activity_import_file_path_text.text = data?.data?.pathSegments?.last() ?: ""
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
if (data != null && (activity_import_file_form.adapter as FormAdapter).onActivityResult(requestCode, resultCode, data) )
|
||||
return
|
||||
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
private fun checkAndInsert() {
|
||||
try {
|
||||
val name = elements[0] as FormAdapter.TextType
|
||||
val file = elements[1] as FormAdapter.FilePickerType
|
||||
|
||||
if ( name.content.isEmpty() ) {
|
||||
runOnUiThread {
|
||||
activity_import_file_save.visibility = View.VISIBLE
|
||||
activity_import_file_saving.visibility = View.GONE
|
||||
Snackbar.make(activity_import_file_root, R.string.clash_import_file_empty_name, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if ( file.content == null || file.content == Uri.EMPTY ) {
|
||||
runOnUiThread {
|
||||
activity_import_file_save.visibility = View.VISIBLE
|
||||
activity_import_file_saving.visibility = View.GONE
|
||||
Snackbar.make(activity_import_file_root, R.string.clash_import_file_empty_path, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val data =
|
||||
contentResolver.openInputStream(activity_import_file_path_text.tag as Uri)?.use {
|
||||
contentResolver.openInputStream(file.content!!)?.use {
|
||||
it.readBytes().toString(Charsets.UTF_8)
|
||||
} ?: throw NullPointerException("Unable to open config file")
|
||||
|
||||
@@ -134,8 +96,8 @@ class ImportFileActivity : BaseActivity() {
|
||||
runClash {
|
||||
it.profileService.addProfile(
|
||||
ClashProfileEntity(
|
||||
name = activity_import_file_name_text.text.toString(),
|
||||
token = "file|" + activity_import_file_path_text.tag.toString(),
|
||||
name = name.content,
|
||||
token = "file|" + file.content!!.toString(),
|
||||
cache = cache.absolutePath,
|
||||
active = false,
|
||||
proxies = parsed.proxies.size,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.github.kr328.clash
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.kr328.clash.adapter.FormAdapter
|
||||
import kotlinx.android.synthetic.main.activity_import_url.*
|
||||
|
||||
class ImportUrlActivity : BaseActivity() {
|
||||
private val elements = listOf<FormAdapter.Type>(
|
||||
FormAdapter.TextType(
|
||||
R.drawable.ic_about,
|
||||
R.string.clash_profile_name,
|
||||
R.string.clash_profile_name_hint),
|
||||
FormAdapter.TextType(
|
||||
R.drawable.ic_link,
|
||||
R.string.clash_profile_url,
|
||||
R.string.clash_profile_url_hint
|
||||
)
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_import_url)
|
||||
|
||||
setSupportActionBar(activity_import_url_toolbar)
|
||||
|
||||
activity_import_url_form.also {
|
||||
it.layoutManager = LinearLayoutManager(this)
|
||||
it.adapter = FormAdapter(this, elements)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (data != null && (activity_import_url_form.adapter as FormAdapter)
|
||||
.onActivityResult(requestCode, resultCode, data))
|
||||
return
|
||||
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,116 @@
|
||||
package com.github.kr328.clash.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.app.Activity
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.kr328.clash.R
|
||||
import com.github.kr328.clash.fragment.FormFragment
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class FormAdapter(
|
||||
private val context: Context,
|
||||
private val elements: List<FormFragment.FormElement>
|
||||
private val activity: Activity,
|
||||
private val elements: List<Type>
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
companion object {
|
||||
const val BASE_REQUEST_CODE = 14654
|
||||
}
|
||||
|
||||
interface Type {
|
||||
val content: Any?
|
||||
}
|
||||
|
||||
data class TextType(
|
||||
val icon: Int,
|
||||
val title: Int,
|
||||
val hint: Int,
|
||||
override var content: String = ""
|
||||
) : Type
|
||||
|
||||
data class FilePickerType(
|
||||
val icon: Int,
|
||||
val title: Int,
|
||||
val hint: Int,
|
||||
override var content: Uri? = Uri.EMPTY
|
||||
) : Type
|
||||
|
||||
class TextHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val icon: ImageView = view.findViewById(R.id.adapter_from_text_icon)
|
||||
val edit: TextInputEditText = view.findViewById(R.id.adapter_from_text_text)
|
||||
val icon: ImageView = view.findViewById(R.id.adapter_form_text_icon)
|
||||
val title: TextView = view.findViewById(R.id.adapter_form_text_title)
|
||||
val text: TextView = view.findViewById(R.id.adapter_form_text_text)
|
||||
val clickable: View = view.findViewById(R.id.adapter_form_text_clickable)
|
||||
}
|
||||
|
||||
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Boolean {
|
||||
val position = requestCode - BASE_REQUEST_CODE
|
||||
|
||||
if (position in 0..elements.size) {
|
||||
val element = elements[position]
|
||||
|
||||
if (element is FilePickerType) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
element.content = data.data
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (val current = elements[position]) {
|
||||
is TextType -> {
|
||||
val castedHolder = holder as TextHolder
|
||||
|
||||
castedHolder.icon.setImageResource(current.icon)
|
||||
castedHolder.title.text = activity.getText(current.title)
|
||||
castedHolder.text.hint = activity.getText(current.hint)
|
||||
castedHolder.text.text = current.content
|
||||
castedHolder.clickable.setOnClickListener {
|
||||
showTextEditDialog(
|
||||
castedHolder.text.text.toString(),
|
||||
castedHolder.text.hint.toString()
|
||||
) {
|
||||
current.content = it
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
is FilePickerType -> {
|
||||
val castedHolder = holder as TextHolder
|
||||
|
||||
castedHolder.icon.setImageResource(current.icon)
|
||||
castedHolder.title.text = activity.getText(current.title)
|
||||
castedHolder.text.hint = activity.getText(current.hint)
|
||||
castedHolder.text.text = current.content?.pathSegments?.lastOrNull() ?: ""
|
||||
castedHolder.clickable.setOnClickListener {
|
||||
activity.startActivityForResult(
|
||||
Intent(Intent.ACTION_GET_CONTENT)
|
||||
.setType("*/*"),
|
||||
BASE_REQUEST_CODE + position
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
FormFragment.FormType.STRING.ordinal, FormFragment.FormType.FILE.ordinal, FormFragment.FormType.URL.ordinal ->
|
||||
TextType::class.java.hashCode(), FilePickerType::class.java.hashCode() ->
|
||||
TextHolder(
|
||||
LayoutInflater.from(context).inflate(
|
||||
R.layout.adapter_from_text,
|
||||
LayoutInflater.from(activity).inflate(
|
||||
R.layout.adapter_form_text,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
@@ -37,18 +123,35 @@ class FormAdapter(
|
||||
return elements.size
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val current = elements[position]
|
||||
|
||||
when (holder) {
|
||||
is TextHolder -> {
|
||||
holder.icon.setImageResource(current.icon)
|
||||
holder.edit.hint = context.getString(current.hint)
|
||||
}
|
||||
}
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return elements[position].javaClass.hashCode()
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return elements[position].type.ordinal
|
||||
private fun showTextEditDialog(initial: String, hint: String, callback: (String) -> Unit) {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.clash_profile_name)
|
||||
.setView(R.layout.dialog_text_edit)
|
||||
.setPositiveButton(R.string.ok) { _, _ -> }
|
||||
.setNegativeButton(R.string.cancel) { _, _ -> }
|
||||
.create()
|
||||
.apply {
|
||||
setOnShowListener {
|
||||
val data = findViewById<EditText>(R.id.dialog_text_edit).also {
|
||||
it?.hint = hint
|
||||
it?.setText(initial)
|
||||
}
|
||||
getButton(AlertDialog.BUTTON_POSITIVE)?.apply {
|
||||
setTextColor(context.getColor(R.color.colorAccent))
|
||||
setOnClickListener {
|
||||
callback(data?.text?.toString() ?: "")
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
getButton(AlertDialog.BUTTON_NEGATIVE)?.apply {
|
||||
setTextColor(context.getColor(R.color.colorAccent))
|
||||
}
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.github.kr328.clash.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.kr328.clash.adapter.FormAdapter
|
||||
|
||||
class FormFragment(private val elements: List<FormElement>) : Fragment() {
|
||||
enum class FormType {
|
||||
STRING, URL, FILE
|
||||
}
|
||||
|
||||
interface ResultDumper {
|
||||
class DumpException(message: String) : Exception(message)
|
||||
|
||||
fun dump(element: FormElement, data: Any)
|
||||
}
|
||||
|
||||
data class FormElement(
|
||||
val type: FormType,
|
||||
val icon: Int,
|
||||
val title: Int,
|
||||
val hint: Int,
|
||||
val result: ResultDumper
|
||||
)
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return RecyclerView(requireActivity()).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
layoutManager = LinearLayoutManager(requireActivity())
|
||||
adapter = FormAdapter(requireActivity(), elements)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9L7,15.1c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2L8,11v2zM17,7h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4L13,17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
|
||||
</vector>
|
||||
@@ -43,105 +43,8 @@
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/activity_import_file_form"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:src="@drawable/ic_about"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/activity_import_file_name"
|
||||
android:layout_marginStart="20dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:focusable="true"
|
||||
android:clickable="true">
|
||||
|
||||
<TextView
|
||||
android:text="@string/clash_profile_name"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_import_file_name_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableEnd="@drawable/ic_profile_edit"
|
||||
android:padding="5dp"
|
||||
android:lines="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:hint="@string/clash_profile_name_hint" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/colorAccent" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:src="@drawable/ic_new_profile_file"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/activity_import_file_path"
|
||||
android:layout_marginStart="20dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:focusable="true"
|
||||
android:clickable="true">
|
||||
|
||||
<TextView
|
||||
android:text="@string/clash_profile_file"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/activity_import_file_path_text"
|
||||
android:drawableEnd="@drawable/ic_profile_edit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:lines="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:hint="@string/clash_profile_file_hint" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/colorAccent" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/activity_import_url_root"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/activity_import_url_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:layout_gravity="end">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/activity_import_url_save"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_save"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:foreground="?attr/selectableItemBackground" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/activity_import_url_saving"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp" />
|
||||
</FrameLayout>
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/activity_import_url_form"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/adapter_form_text_icon"
|
||||
android:src="@drawable/ic_about"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/adapter_form_text_clickable"
|
||||
android:layout_marginStart="20dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:focusable="true"
|
||||
android:clickable="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/adapter_form_text_title"
|
||||
android:text="@string/clash_profile_name"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/adapter_form_text_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableEnd="@drawable/ic_profile_edit"
|
||||
android:padding="5dp"
|
||||
android:lines="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:hint="@string/clash_profile_name_hint" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/colorAccent" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:padding="15dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/adapter_from_text_icon"
|
||||
android:src="@drawable/ic_about"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/adapter_from_text_text"
|
||||
android:drawableEnd="@drawable/ic_about"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:inputType="none" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -28,6 +28,7 @@
|
||||
<string name="clash_new_profile_url_summary">From Internet</string>
|
||||
|
||||
<string name="clash_import_from_file">Import From File</string>
|
||||
<string name="clash_import_from_url">Import From URL</string>
|
||||
|
||||
<string name="clash_profile_name">Name</string>
|
||||
<string name="clash_profile_name_hint">Unlabeled</string>
|
||||
@@ -35,6 +36,9 @@
|
||||
<string name="clash_profile_file">File</string>
|
||||
<string name="clash_profile_file_hint">content://</string>
|
||||
|
||||
<string name="clash_profile_url">URL</string>
|
||||
<string name="clash_profile_url_hint">https://</string>
|
||||
|
||||
<string name="ok">OK</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
|
||||
|
||||
Reference in new issue
Block a user