Commit 072df42f authored by Mygod's avatar Mygod

Support importing URL for tv

Also deprecates discovering from NFC/Android Beam due to deprecation in Android Q.
A follow-up commit will remove this feature completely.
parent d2af3d21
...@@ -55,6 +55,19 @@ ...@@ -55,6 +55,19 @@
android:exported="false"> android:exported="false">
</service> </service>
<activity
android:name="com.github.shadowsocks.UrlImportActivity"
android:theme="@style/Theme.AppCompat.Translucent"
android:excludeFromRecents="true"
android:taskAffinity="">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="ss"/>
</intent-filter>
</activity>
<activity <activity
android:name="com.github.shadowsocks.VpnRequestActivity" android:name="com.github.shadowsocks.VpnRequestActivity"
android:theme="@style/Theme.AppCompat.Translucent" android:theme="@style/Theme.AppCompat.Translucent"
......
/*******************************************************************************
* *
* Copyright (C) 2019 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2019 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
package com.github.shadowsocks
import android.content.DialogInterface
import android.os.Bundle
import android.os.Parcelable
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.github.shadowsocks.core.R
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.plugin.AlertDialogFragment
import com.github.shadowsocks.plugin.Empty
import kotlinx.android.parcel.Parcelize
class UrlImportActivity : AppCompatActivity() {
@Parcelize
data class ProfilesArg(val profiles: List<Profile>) : Parcelable
class ImportProfilesDialogFragment : AlertDialogFragment<ProfilesArg, Empty>() {
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
setTitle(R.string.add_profile_dialog)
setPositiveButton(R.string.yes, listener)
setNegativeButton(R.string.no, listener)
setMessage(arg.profiles.joinToString("\n"))
}
override fun onClick(dialog: DialogInterface?, which: Int) {
if (which == DialogInterface.BUTTON_POSITIVE) arg.profiles.forEach { ProfileManager.createProfile(it) }
requireActivity().finish()
}
override fun onDismiss(dialog: DialogInterface) {
requireActivity().finish()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
when (val dialog = handleShareIntent()) {
null -> {
Toast.makeText(this, R.string.profile_invalid_input, Toast.LENGTH_SHORT).show()
finish()
}
else -> dialog.show(supportFragmentManager, null)
}
}
private fun handleShareIntent() = intent.data?.toString()?.let { sharedStr ->
val profiles = Profile.findAllUrls(sharedStr, Core.currentProfile?.first).toList()
if (profiles.isEmpty()) null else ImportProfilesDialogFragment().withArg(ProfilesArg(profiles))
}
}
...@@ -30,17 +30,6 @@ ...@@ -30,17 +30,6 @@
<intent-filter> <intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" /> <action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="ss"/>
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="ss" />
</intent-filter>
<meta-data android:name="android.app.shortcuts" <meta-data android:name="android.app.shortcuts"
android:resource="@xml/shortcuts"/> android:resource="@xml/shortcuts"/>
</activity> </activity>
......
...@@ -23,20 +23,15 @@ package com.github.shadowsocks ...@@ -23,20 +23,15 @@ package com.github.shadowsocks
import android.app.Activity import android.app.Activity
import android.app.backup.BackupManager import android.app.backup.BackupManager
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.net.VpnService import android.net.VpnService
import android.nfc.NdefMessage
import android.nfc.NfcAdapter
import android.os.Bundle import android.os.Bundle
import android.os.DeadObjectException import android.os.DeadObjectException
import android.os.Handler import android.os.Handler
import android.os.Parcelable
import android.util.Log import android.util.Log
import android.view.KeyCharacterMap import android.view.KeyCharacterMap
import android.view.KeyEvent import android.view.KeyEvent
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
...@@ -51,10 +46,6 @@ import com.github.shadowsocks.aidl.IShadowsocksService ...@@ -51,10 +46,6 @@ import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.aidl.ShadowsocksConnection import com.github.shadowsocks.aidl.ShadowsocksConnection
import com.github.shadowsocks.aidl.TrafficStats import com.github.shadowsocks.aidl.TrafficStats
import com.github.shadowsocks.bg.BaseService import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.plugin.AlertDialogFragment
import com.github.shadowsocks.plugin.Empty
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
...@@ -63,7 +54,6 @@ import com.github.shadowsocks.widget.ServiceButton ...@@ -63,7 +54,6 @@ import com.github.shadowsocks.widget.ServiceButton
import com.github.shadowsocks.widget.StatsBar import com.github.shadowsocks.widget.StatsBar
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.parcel.Parcelize
class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPreferenceDataStoreChangeListener, class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPreferenceDataStoreChangeListener,
NavigationView.OnNavigationItemSelectedListener { NavigationView.OnNavigationItemSelectedListener {
...@@ -74,17 +64,6 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref ...@@ -74,17 +64,6 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
var stateListener: ((BaseService.State) -> Unit)? = null var stateListener: ((BaseService.State) -> Unit)? = null
} }
@Parcelize
data class ProfilesArg(val profiles: List<Profile>) : Parcelable
class ImportProfilesDialogFragment : AlertDialogFragment<ProfilesArg, Empty>() {
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
setTitle(R.string.add_profile_dialog)
setPositiveButton(R.string.yes) { _, _ -> arg.profiles.forEach { ProfileManager.createProfile(it) } }
setNegativeButton(R.string.no, null)
setMessage(arg.profiles.joinToString("\n"))
}
}
// UI // UI
private lateinit var fab: ServiceButton private lateinit var fab: ServiceButton
private lateinit var stats: StatsBar private lateinit var stats: StatsBar
...@@ -186,29 +165,6 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref ...@@ -186,29 +165,6 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
changeState(BaseService.State.Idle) // reset everything to init state changeState(BaseService.State.Idle) // reset everything to init state
connection.connect(this, this) connection.connect(this, this)
DataStore.publicStore.registerChangeListener(this) DataStore.publicStore.registerChangeListener(this)
val intent = this.intent
if (intent != null) handleShareIntent(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleShareIntent(intent)
}
private fun handleShareIntent(intent: Intent) {
val sharedStr = when (intent.action) {
Intent.ACTION_VIEW -> intent.data?.toString()
NfcAdapter.ACTION_NDEF_DISCOVERED -> {
val rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
if (rawMsgs != null && rawMsgs.isNotEmpty()) String((rawMsgs[0] as NdefMessage).records[0].payload)
else null
}
else -> null
}
if (sharedStr.isNullOrEmpty()) return
val profiles = Profile.findAllUrls(sharedStr, Core.currentProfile?.first).toList()
if (profiles.isEmpty()) snackbar().setText(R.string.profile_invalid_input).show()
else ImportProfilesDialogFragment().withArg(ProfilesArg(profiles)).show(supportFragmentManager, null)
} }
override fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String) { override fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment