Add support for import remote profile

This commit is contained in:
世界
2023-07-27 12:14:03 +08:00
parent 805d99e297
commit cb9799936b
8 changed files with 92 additions and 7 deletions

View File

@@ -9,14 +9,14 @@ fun Context.errorDialogBuilder(@StringRes messageId: Int): MaterialAlertDialogBu
return MaterialAlertDialogBuilder(this)
.setTitle(R.string.error_title)
.setMessage(messageId)
.setPositiveButton(resources.getString(android.R.string.ok), null)
.setPositiveButton(android.R.string.ok, null)
}
fun Context.errorDialogBuilder(message: String): MaterialAlertDialogBuilder {
return MaterialAlertDialogBuilder(this)
.setTitle(R.string.error_title)
.setMessage(message)
.setPositiveButton(resources.getString(android.R.string.ok), null)
.setPositiveButton(android.R.string.ok, null)
}
fun Context.errorDialogBuilder(exception: Throwable): MaterialAlertDialogBuilder {

View File

@@ -26,6 +26,7 @@ import com.microsoft.appcenter.distribute.DistributeListener
import com.microsoft.appcenter.distribute.ReleaseDetails
import com.microsoft.appcenter.distribute.UpdateAction
import com.microsoft.appcenter.utils.AppNameHelper
import io.nekohasekai.libbox.Libbox
import io.nekohasekai.sfa.Application
import io.nekohasekai.sfa.BuildConfig
import io.nekohasekai.sfa.R
@@ -37,6 +38,7 @@ import io.nekohasekai.sfa.constant.Status
import io.nekohasekai.sfa.database.Settings
import io.nekohasekai.sfa.databinding.ActivityMainBinding
import io.nekohasekai.sfa.ktx.errorDialogBuilder
import io.nekohasekai.sfa.ui.profile.NewProfileActivity
import io.nekohasekai.sfa.ui.shared.AbstractActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@@ -80,6 +82,31 @@ class MainActivity : AbstractActivity(), ServiceConnection.Callback, DistributeL
startAnalysis()
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
val uri = intent.data ?: return
if (uri.scheme != "sing-box" || uri.host != "import-remote-profile") {
return
}
val profile = try {
Libbox.parseRemoteProfileImportLink(uri.toString())
} catch (e: Exception) {
errorDialogBuilder(e).show()
return
}
MaterialAlertDialogBuilder(this)
.setTitle(R.string.import_remote_profile)
.setMessage(getString(R.string.import_remote_profile_message, profile.name, profile.host))
.setPositiveButton(android.R.string.ok) { _,_ ->
startActivity(Intent(this, NewProfileActivity::class.java).apply {
putExtra("importName", profile.name)
putExtra("importURL", profile.url)
})
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
fun reconnect() {
connection.reconnect()
}
@@ -104,7 +131,7 @@ class MainActivity : AbstractActivity(), ServiceConnection.Callback, DistributeL
val builder = MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.analytics_title))
.setMessage(getString(R.string.analytics_message))
.setPositiveButton(getString(R.string.ok)) { _, _ ->
.setPositiveButton(android.R.string.ok) { _, _ ->
lifecycleScope.launch(Dispatchers.IO) {
Settings.analyticsAllowed = Settings.ANALYSIS_ALLOWED
startAnalysisInternal()
@@ -258,7 +285,7 @@ class MainActivity : AbstractActivity(), ServiceConnection.Callback, DistributeL
override fun onServiceAlert(type: Alert, message: String?) {
val builder = MaterialAlertDialogBuilder(this)
builder.setPositiveButton(resources.getString(android.R.string.ok), null)
builder.setPositiveButton(android.R.string.ok, null)
when (type) {
Alert.RequestVPNPermission -> {
builder.setMessage(getString(R.string.service_error_missing_permission))

View File

@@ -12,11 +12,15 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.nekohasekai.libbox.Libbox
import io.nekohasekai.sfa.R
import io.nekohasekai.sfa.database.Profile
import io.nekohasekai.sfa.database.Profiles
import io.nekohasekai.sfa.database.TypedProfile
import io.nekohasekai.sfa.databinding.FragmentConfigurationBinding
import io.nekohasekai.sfa.databinding.ViewConfigutationItemBinding
import io.nekohasekai.sfa.ktx.errorDialogBuilder
import io.nekohasekai.sfa.ui.MainActivity
import io.nekohasekai.sfa.ui.profile.EditProfileActivity
import io.nekohasekai.sfa.ui.profile.NewProfileActivity
import kotlinx.coroutines.CoroutineScope
@@ -152,12 +156,32 @@ class ConfigurationFragment : Fragment() {
intent.putExtra("profile_id", profile.id)
it.context.startActivity(intent)
}
binding.moreButton.setOnClickListener { it ->
val popup = PopupMenu(it.context, it)
binding.moreButton.setOnClickListener { button ->
val popup = PopupMenu(button.context, button)
popup.setForceShowIcon(true)
popup.menuInflater.inflate(R.menu.profile_menu, popup.menu)
if (profile.typed.type != TypedProfile.Type.Remote) {
popup.menu.removeItem(R.id.action_share)
}
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.action_share -> {
try {
val link = Libbox.generateRemoteProfileImportLink(
profile.name,
profile.typed.remoteURL
)
button.context.startActivity(Intent.createChooser(Intent(android.content.Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_SUBJECT, "Share profile ${profile.name}")
putExtra(Intent.EXTRA_TEXT, link)
}, "Share"))
} catch (e: Exception) {
button.context.errorDialogBuilder(e).show()
}
true
}
R.id.action_delete -> {
adapter.items.remove(profile)
adapter.notifyItemRemoved(adapterPosition)

View File

@@ -82,6 +82,13 @@ class NewProfileActivity : AbstractActivity() {
startFilesForResult(importFile, "application/json")
}
binding.createProfile.setOnClickListener(this::createProfile)
intent.getStringExtra("importName")?.also { importName ->
intent.getStringExtra("importURL") ?.also { importURL ->
binding.name.editText?.setText(importName)
binding.type.text = TypedProfile.Type.Remote.name
binding.remoteURL.editText?.setText(importURL)
}
}
}
private fun createProfile(view: View) {