diff --git a/build.gradle b/build.gradle index 71aed74..de96bef 100644 --- a/build.gradle +++ b/build.gradle @@ -14,8 +14,8 @@ buildscript { junitVersion = '4.13' androidTestVersion = '1.2.0' androidEspressoVersion = '3.2.0' - versionCode = 5000958 - versionName = '5.1.19-nightly' + versionCode = 5000968 + versionName = '5.1.20-nightly' resConfigs = ['ar', 'es', 'fa', 'fr', 'ja', 'ko', 'ru', 'tr', 'zh-rCN', 'zh-rTW'] } diff --git a/core/src/main/java/com/github/shadowsocks/Core.kt b/core/src/main/java/com/github/shadowsocks/Core.kt index 38bd6d0..ae7f8f7 100644 --- a/core/src/main/java/com/github/shadowsocks/Core.kt +++ b/core/src/main/java/com/github/shadowsocks/Core.kt @@ -22,6 +22,8 @@ package com.github.shadowsocks import SpeedUpVPN.VpnEncrypt import android.app.* import android.app.admin.DevicePolicyManager +import android.content.ClipData +import android.content.ClipboardManager import android.content.Context import android.content.DialogInterface import android.content.Intent @@ -77,6 +79,7 @@ object Core { val defaultDPreference by lazy { DPreference(app, app.packageName + "_preferences") } lateinit var configureIntent: (Context) -> PendingIntent val activity by lazy { app.getSystemService()!! } + val clipboard by lazy { app.getSystemService()!! } val connectivity by lazy { app.getSystemService()!! } val notification by lazy { app.getSystemService()!! } val packageInfo: PackageInfo by lazy { getPackageInfo(app.packageName) } @@ -228,6 +231,14 @@ object Core { if (Build.VERSION.SDK_INT >= 28) PackageManager.GET_SIGNING_CERTIFICATES else @Suppress("DEPRECATION") PackageManager.GET_SIGNATURES)!! + fun trySetPrimaryClip(clip: String) = try { + clipboard.setPrimaryClip(ClipData.newPlainText(null, clip)) + true + } catch (e: RuntimeException) { + printLog(e) + false + } + fun startService() = ContextCompat.startForegroundService(app, Intent(app, ShadowsocksConnection.serviceClass)) fun reloadService(oldProfileId:Long, connection : ShadowsocksConnection) { if (ProfileManager.getProfile(oldProfileId)?.profileType == ProfileManager.getProfile(DataStore.profileId)?.profileType ) diff --git a/gitupdate.bat b/gitupdate.bat index 2b785d4..172dc03 100644 --- a/gitupdate.bat +++ b/gitupdate.bat @@ -3,6 +3,6 @@ git pull origin master git add -A git commit -m "update" git push origin master -git tag -a v5.1.19 -m "release v5.1.19" +git tag -a v5.1.20 -m "release v5.1.20" git push origin --tags pause \ No newline at end of file diff --git a/mobile/src/main/java/com/github/shadowsocks/AppManager.kt b/mobile/src/main/java/com/github/shadowsocks/AppManager.kt index 083b755..0d844ce 100644 --- a/mobile/src/main/java/com/github/shadowsocks/AppManager.kt +++ b/mobile/src/main/java/com/github/shadowsocks/AppManager.kt @@ -25,8 +25,6 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.annotation.SuppressLint import android.content.BroadcastReceiver -import android.content.ClipData -import android.content.ClipboardManager import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo @@ -40,7 +38,6 @@ import android.widget.Filterable import android.widget.SearchView import androidx.annotation.UiThread import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.getSystemService import androidx.core.util.set import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.DefaultItemAnimator @@ -50,7 +47,6 @@ import com.github.shadowsocks.Core.app import com.github.shadowsocks.database.ProfileManager import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.utils.DirectBoot -import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.SingleInstanceActivity import com.github.shadowsocks.utils.listenForPackageChanges import com.github.shadowsocks.widget.ListHolderListener @@ -58,7 +54,10 @@ import com.github.shadowsocks.widget.ListListener import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.layout_apps.* import kotlinx.android.synthetic.main.layout_apps_item.view.* -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.withContext import me.zhanghai.android.fastscroll.FastScrollerBuilder import me.zhanghai.android.fastscroll.PopupTextProvider import kotlin.coroutines.coroutineContext @@ -177,7 +176,6 @@ class AppManager : AppCompatActivity() { } private val proxiedUids = SparseBooleanArray() - private val clipboard by lazy { getSystemService()!! } private var loader: Job? = null private var apps = emptyList() private val appsAdapter = AppsAdapter() @@ -267,26 +265,26 @@ class AppManager : AppCompatActivity() { when (item.itemId) { R.id.action_apply_all -> { val profiles = ProfileManager.getAllProfiles() - if (profiles != null) { + if (!profiles.isNullOrEmpty()) { val proxiedAppString = DataStore.individual profiles.forEach { it.individual = proxiedAppString it.bypass = DataStore.bypass + it.proxyApps = true ProfileManager.updateProfile(it) } if (DataStore.directBootAware) DirectBoot.update() Snackbar.make(list, R.string.action_apply_all, Snackbar.LENGTH_LONG).show() - } else Snackbar.make(list, R.string.action_export_err, Snackbar.LENGTH_LONG).show() + } return true } R.id.action_export_clipboard -> { - clipboard.setPrimaryClip(ClipData.newPlainText(Key.individual, - "${DataStore.bypass}\n${DataStore.individual}")) - Snackbar.make(list, R.string.action_export_msg, Snackbar.LENGTH_LONG).show() + Snackbar.make(list, if (Core.trySetPrimaryClip("${DataStore.bypass}\n${DataStore.individual}")) + R.string.action_export_msg else R.string.action_export_err, Snackbar.LENGTH_LONG).show() return true } R.id.action_import_clipboard -> { - val proxiedAppString = clipboard.primaryClip?.getItemAt(0)?.text?.toString() + val proxiedAppString = Core.clipboard.primaryClip?.getItemAt(0)?.text?.toString() if (!proxiedAppString.isNullOrEmpty()) { val i = proxiedAppString.indexOf('\n') try { diff --git a/mobile/src/main/java/com/github/shadowsocks/UdpFallbackProfileActivity.kt b/mobile/src/main/java/com/github/shadowsocks/UdpFallbackProfileActivity.kt index b27508f..04bb908 100644 --- a/mobile/src/main/java/com/github/shadowsocks/UdpFallbackProfileActivity.kt +++ b/mobile/src/main/java/com/github/shadowsocks/UdpFallbackProfileActivity.kt @@ -35,14 +35,18 @@ import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.ProfileManager import com.github.shadowsocks.plugin.PluginConfiguration import com.github.shadowsocks.preference.DataStore +import com.github.shadowsocks.utils.DirectBoot import com.github.shadowsocks.utils.SingleInstanceActivity import com.github.shadowsocks.utils.resolveResourceId import com.github.shadowsocks.widget.ListHolderListener import com.github.shadowsocks.widget.ListListener +import com.google.android.material.snackbar.Snackbar +import kotlinx.android.synthetic.main.layout_udp_fallback.* class UdpFallbackProfileActivity : AppCompatActivity() { inner class ProfileViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener { private var item: Profile? = null + private var pos: Int = 0 private val text = itemView.findViewById(android.R.id.text1) init { @@ -50,16 +54,20 @@ class UdpFallbackProfileActivity : AppCompatActivity() { itemView.setOnClickListener(this) } - fun bind(item: Profile?) { + fun bind(item: Profile?, position: Int) { this.item = item + this.pos = position if (item == null) text.setText(R.string.plugin_disabled) else text.text = item.formattedName text.isChecked = udpFallback == item?.id + if (text.isChecked) selected = position } override fun onClick(v: View?) { DataStore.udpFallback = item?.id DataStore.dirty = true - finish() + udpFallback = item?.id + profilesAdapter.notifyItemChanged(pos) + profilesAdapter.notifyItemChanged(selected) } } @@ -68,7 +76,8 @@ class UdpFallbackProfileActivity : AppCompatActivity() { .filter { it.id != editingId && PluginConfiguration(it.plugin ?: "").selected.isEmpty() } override fun onBindViewHolder(holder: ProfileViewHolder, position: Int) = - holder.bind(if (position == 0) null else profiles[position - 1]) + holder.bind(if (position == 0) null else profiles[position - 1], position) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileViewHolder = ProfileViewHolder( LayoutInflater.from(parent.context).inflate(Resources.getSystem() .getIdentifier("select_dialog_singlechoice_material", "layout", "android"), parent, false)) @@ -78,6 +87,7 @@ class UdpFallbackProfileActivity : AppCompatActivity() { private var editingId = DataStore.editingId private var udpFallback = DataStore.udpFallback private val profilesAdapter = ProfilesAdapter() + private var selected = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -93,6 +103,22 @@ class UdpFallbackProfileActivity : AppCompatActivity() { toolbar.setTitle(R.string.udp_fallback) toolbar.setNavigationIcon(R.drawable.ic_navigation_close) toolbar.setNavigationOnClickListener { finish() } + toolbar.inflateMenu(R.menu.udp_fallback_menu) + toolbar.setOnMenuItemClickListener { menuItem -> + if (menuItem.itemId == R.id.action_apply_all) { + val profiles = ProfileManager.getAllProfiles() + if (!profiles.isNullOrEmpty()) { + profiles.forEach { + if (it.id == DataStore.udpFallback) return@forEach + it.udpFallback = DataStore.udpFallback + ProfileManager.updateProfile(it) + } + if (DataStore.directBootAware) DirectBoot.update() + Snackbar.make(list, R.string.action_apply_all, Snackbar.LENGTH_LONG).show() + } + true + } else false + } findViewById(R.id.list).apply { setOnApplyWindowInsetsListener(ListListener)