Compare commits

...
33 Commits
Author SHA1 Message Date
Max Lv b9517f0703 Bump version 2018-04-23 19:58:48 -07:00
Mygod 72f4586acb Revert "Update play services"
This reverts commit 635105ca05.

Fixes #1764.
2018-04-21 22:49:44 -07:00
ewrfdv 3d6daccbb1 Fix windows building issues (#1768)
* Update make.bat

* Update make.bat
2018-04-21 09:55:35 -07:00
Mygod 33a8967f30 Revert "make connection test url configurable" (#1767) 2018-04-19 22:24:44 -07:00
Max Lv 762da4c62f Merge pull request #1762 from V-E-O/master
make connection test url configurable
2018-04-20 12:56:42 +08:00
V.E.O 8e83272fa7 remove test URL for ChinaList 2018-04-19 23:53:19 +08:00
VYSE V.E.O d17d791b3b make connection test url configurable 2018-04-18 16:27:14 +08:00
Max Lv 63ae014581 Fix #1754 2018-04-16 23:56:17 -07:00
Mygod 95e1eee59f Add #1184 to FAQ 2018-04-16 20:54:15 -07:00
Mygod 98df31c258 Use Formatter.formatFileSize
Fix #1751.
2018-04-16 20:37:56 -07:00
Mygod 04092bf8e0 Disable night mode for now 2018-04-16 20:26:32 -07:00
Mygod 5e56bfec89 Fix dialog padding 2018-04-15 09:09:12 -07:00
Mygod 635105ca05 Update play services 2018-04-15 09:04:48 -07:00
Max Lv d5fd487ff9 Bump version 2018-04-13 01:35:24 -07:00
Max Lv 89c5d94275 Merge pull request #1747 from Mygod/guarded-process-pool
Manage all processes with GuardedProcessPool
2018-04-12 16:56:31 +08:00
Mygod 0b98a2d4e7 Fix #1716 #1718 2018-04-10 13:20:37 -07:00
Mygod 1bc851dac0 Fix cache inconsistency 2018-04-10 11:54:44 -07:00
Max Lv 37ea3d3f02 Ensure the plugin process is killed. 2018-04-10 01:01:01 -07:00
Mygod 518e25d205 Refine plugin lib 2018-04-10 00:32:43 -07:00
Mygod 43e2731604 Update plugin lib version 2018-04-09 22:20:30 -07:00
Mygod 84c12e705f Update dependencies 2018-04-09 22:17:32 -07:00
Mygod 77ea376b46 Update build.gradle 2018-04-09 22:14:32 -07:00
Mygod 68f3f8b20d Manage all processes with GuardedProcessPool
This way we can destroy the processes and wait for them to exit in parallel, and ensure the process is destroyed so that we won't encounter strange port being used issue at the same time.
2018-04-09 21:54:11 -07:00
Mygod 7226f8aab4 Update shadowsocks-libev 2018-04-09 21:52:55 -07:00
Mygod 36b46d6f32 Refine code style 2018-04-08 20:49:31 -07:00
Max Lv 7687a19cc1 Update GFW List 2018-04-08 18:03:11 -07:00
Max Lv 246e436bbf Bump version 2018-04-05 21:09:40 -07:00
initrider 9b7d2c387e Update translation of FAQ (#1733)
* Update faq.ru.md

* Add some new Yota-related tips for Russian users
2018-04-05 11:40:01 -07:00
Mygod 0f9ca2c5b5 Update strings.xml (POEditor.com)
Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)

Update strings.xml (POEditor.com)
2018-04-04 20:16:03 -07:00
Mygod a338395030 Fix all routing not working 2018-04-04 19:08:32 -07:00
Mygod 6718c4bc83 Update CONTRIBUTING.md 2018-04-04 16:32:15 -07:00
Mygod 89e09d4682 Update build section
Fixes #1728.
2018-04-04 16:20:58 -07:00
Max Lv 91f4919b21 Fix #1727 2018-04-04 23:45:28 +08:00
41 changed files with 213 additions and 428 deletions
+1
View File
@@ -36,6 +36,7 @@ The exclamation mark in the Wi-Fi/cellular icon appears because the system fails
* Samsung and/or Brevent: [#1410](https://github.com/shadowsocks/shadowsocks-android/issues/1410)
* Another Samsung: [#1712](https://github.com/shadowsocks/shadowsocks-android/issues/1712)
* Don't install this app on SD card because of permission issues: [#1124 (comment)](https://github.com/shadowsocks/shadowsocks-android/issues/1124#issuecomment-307556453)
* `INTERACT_ACROSS_USERS` permission missing: [#1184](https://github.com/shadowsocks/shadowsocks-android/issues/1184)
### How to pause Shadowsocks service?
+7 -1
View File
@@ -34,6 +34,7 @@
* Иправления для Huawei: [#1091 (комментарий)](https://github.com/shadowsocks/shadowsocks-android/issues/1091#issuecomment-276949836)
* Вызванные из-за Xposed: [#1414](https://github.com/shadowsocks/shadowsocks-android/issues/1414)
* Samsung и/или Brevent: [#1410](https://github.com/shadowsocks/shadowsocks-android/issues/1410)
* Ещё Samsung: [#1712](https://github.com/shadowsocks/shadowsocks-android/issues/1712)
* Не устанавливайте это приложение на SD карту, из-за проблем с разрешениями: [#1124 (комментарий)](https://github.com/shadowsocks/shadowsocks-android/issues/1124#issuecomment-307556453)
### Как приостановить службу Shadowsocks?
@@ -54,7 +55,12 @@
Разрешите этому приложению передачу данных в фоновом режиме в настройках приложения.
Если у вас оперератор Yota, то измените точку доступа APN с `yota.ru` на `client.yota.ru`
Если у вас оперератор Yota, попробуйте следующее:
* измените точку доступа APN с `yota.ru` на `client.yota.ru`
* погоняйте программы: Speedtest, Telegram (скачивание файлов)
Я уже уведомил оператора о проблеме и вместе мы стараемся решить её.
### Почему требуется разрешение камере на устройствах младше Android 6.0?
+4 -1
View File
@@ -1 +1,4 @@
Please check the [FAQ](https://github.com/shadowsocks/shadowsocks-android/wiki/FAQ) before submitting new issues.
Please check [FAQ](.github/faq.md) before submitting new issues.
And just in case you don't know,
[don't ask questions on issues](https://medium.com/@methane/why-you-must-not-ask-questions-on-github-issues-51d741d83fde).
+5 -1
View File
@@ -20,10 +20,14 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Kotlin.
### BUILD
You can check whether the latest commit builds under UNIX environment by checking Travis status.
Building on Windows is also possible since [#1570](https://github.com/shadowsocks/shadowsocks-android/pull/1570),
but probably painful. Further contributions regarding building on Windows are also welcome.
* Set environment variable `ANDROID_HOME` to `/path/to/android-sdk`
* (optional) Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk` (default: `$ANDROID_HOME/ndk-bundle`)
* Set environment variable `GOROOT_BOOTSTRAP` to `/path/to/go`
* Fetch submodules using `$ git submodule update --init --recursive`
* Clone the repo using `git clone --recurse-submodules <repo>` or update submodules using `git submodule update --init --recursive`
* Build it using Android Studio or gradle script
### TRANSLATE
+3 -3
View File
@@ -8,8 +8,8 @@ buildscript {
minSdkVersion = 21
sdkVersion = 27
buildToolsVersion = '27.0.3'
supportLibraryVersion = '27.1.0'
takisoftFixVersion = '27.1.0.0'
supportLibraryVersion = '27.1.1'
takisoftFixVersion = '27.1.1.0'
playServicesVersion = '12.0.1'
junitVersion = '4.12'
androidTestVersion = '1.0.1'
@@ -22,7 +22,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.0'
classpath 'com.android.tools.build:gradle:3.1.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.17.0'
classpath 'com.google.gms:google-services:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
+12 -12
View File
@@ -30,18 +30,6 @@ MKDIR %TARGET%\arm64-v8a>nul 2>nul
SET CC=%ANDROID_ARM_TOOLCHAIN%\bin\arm-linux-androideabi-gcc.exe
REM Check environment availability
IF NOT EXIST %CC% (
ECHO "gcc not found"
EXIT 1
)
WHERE python.exe
IF "%ERRORLEVEL%" == 1 (
ECHO "python not found"
EXIT 1
)
IF NOT EXIST %ANDROID_ARM_CC% (
ECHO "Make standalone toolchain for ARM arch"
python.exe %ANDROID_NDK_HOME%\build\tools\make_standalone_toolchain.py --arch arm ^
@@ -60,6 +48,18 @@ IF NOT EXIST %ANDROID_X86_CC% (
--api %MIN_API% --install-dir %ANDROID_X86_TOOLCHAIN%
)
REM Check environment availability
IF NOT EXIST %CC% (
ECHO "gcc not found"
EXIT 1
)
WHERE python.exe
IF "%ERRORLEVEL%" == 1 (
ECHO "python not found"
EXIT 1
)
IF NOT EXIST %DIR%\go\bin\go.exe (
ECHO "Build the custom go"
+3 -3
View File
@@ -21,8 +21,8 @@ android {
applicationId "com.github.shadowsocks"
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.sdkVersion
versionCode 4050400
versionName "4.5.4"
versionCode 4050700
versionName "4.5.7"
testApplicationId "com.github.shadowsocks.test"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
resConfigs "fa", "fr", "ja", "ko", "ru", "zh-rCN", "zh-rTW"
@@ -64,7 +64,7 @@ dependencies {
implementation "com.google.android.gms:play-services-vision:$playServicesVersion"
implementation "com.google.firebase:firebase-config:$playServicesVersion"
implementation 'com.j256.ormlite:ormlite-android:5.1'
implementation 'com.mikepenz:materialdrawer:6.0.6'
implementation 'com.mikepenz:materialdrawer:6.0.7'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation "com.takisoft.fix:preference-v7-simplemenu:$takisoftFixVersion"
implementation 'com.twofortyfouram:android-plugin-api-for-locale:1.0.2'
+1 -1
View File
@@ -72,7 +72,7 @@
<activity
android:name=".VpnRequestActivity"
android:theme="@style/Theme.Translucent"
android:theme="@style/Theme.AppCompat.Translucent"
android:excludeFromRecents="true"
android:taskAffinity=""
android:launchMode="singleTask"/>
+3 -58
View File
@@ -5634,9 +5634,7 @@
(^|\.)blogs\.libraryinformationtechnology\.com$
(^|\.)blogs\.tampabay\.com$
(^|\.)blogs\.yahoo\.co\.jp$
(^|\.)blogspot\.com$
(^|\.)blogspot\.hk$
(^|\.)blogspot\.jp$
(^|\.)blogspot(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)blogtd\.net$
(^|\.)blogtd\.org$
(^|\.)bloodshed\.net$
@@ -6875,61 +6873,8 @@
(^|\.)goodtv\.com\.tw$
(^|\.)goodtv\.tv$
(^|\.)goofind\.com$
(^|\.)google\.ae$
(^|\.)google\.am$
(^|\.)google\.as$
(^|\.)google\.at$
(^|\.)google\.az$
(^|\.)google\.ba$
(^|\.)google\.be$
(^|\.)google\.bg$
(^|\.)google\.ca$
(^|\.)google\.calstate\.edu$
(^|\.)google\.cd$
(^|\.)google\.ci$
(^|\.)google\.co\.id$
(^|\.)google\.co\.jp$
(^|\.)google\.co\.kr$
(^|\.)google\.co\.ma$
(^|\.)google\.co\.uk$
(^|\.)google\.com$
(^|\.)google\.de$
(^|\.)google\.dj$
(^|\.)google\.dk$
(^|\.)google\.es$
(^|\.)google\.fi$
(^|\.)google\.fm$
(^|\.)google\.fr$
(^|\.)google\.gg$
(^|\.)google\.gl$
(^|\.)google\.gr$
(^|\.)google\.ie$
(^|\.)google\.is$
(^|\.)google\.it$
(^|\.)google\.jo$
(^|\.)google\.kz$
(^|\.)google\.lv$
(^|\.)google\.mn$
(^|\.)google\.ms$
(^|\.)google\.nl$
(^|\.)google\.no$
(^|\.)google\.nu$
(^|\.)google\.ro$
(^|\.)google\.ru$
(^|\.)google\.rw$
(^|\.)google\.sc$
(^|\.)google\.sh$
(^|\.)google\.sk$
(^|\.)google\.sm$
(^|\.)google\.sn$
(^|\.)google\.tk$
(^|\.)google\.tm$
(^|\.)google\.to$
(^|\.)google\.tt$
(^|\.)google\.vu$
(^|\.)google\.ws$
(^|\.)googleapis\.cn$
(^|\.)googleapis\.com$
(^|\.)google(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)googleapis(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)googleapps\.com$
(^|\.)googlearth\.com$
(^|\.)googleartproject\.com$
@@ -5648,9 +5648,7 @@
(^|\.)blogs\.libraryinformationtechnology\.com$
(^|\.)blogs\.tampabay\.com$
(^|\.)blogs\.yahoo\.co\.jp$
(^|\.)blogspot\.com$
(^|\.)blogspot\.hk$
(^|\.)blogspot\.jp$
(^|\.)blogspot(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)blogtd\.net$
(^|\.)blogtd\.org$
(^|\.)bloodshed\.net$
@@ -6889,61 +6887,8 @@
(^|\.)goodtv\.com\.tw$
(^|\.)goodtv\.tv$
(^|\.)goofind\.com$
(^|\.)google\.ae$
(^|\.)google\.am$
(^|\.)google\.as$
(^|\.)google\.at$
(^|\.)google\.az$
(^|\.)google\.ba$
(^|\.)google\.be$
(^|\.)google\.bg$
(^|\.)google\.ca$
(^|\.)google\.calstate\.edu$
(^|\.)google\.cd$
(^|\.)google\.ci$
(^|\.)google\.co\.id$
(^|\.)google\.co\.jp$
(^|\.)google\.co\.kr$
(^|\.)google\.co\.ma$
(^|\.)google\.co\.uk$
(^|\.)google\.com$
(^|\.)google\.de$
(^|\.)google\.dj$
(^|\.)google\.dk$
(^|\.)google\.es$
(^|\.)google\.fi$
(^|\.)google\.fm$
(^|\.)google\.fr$
(^|\.)google\.gg$
(^|\.)google\.gl$
(^|\.)google\.gr$
(^|\.)google\.ie$
(^|\.)google\.is$
(^|\.)google\.it$
(^|\.)google\.jo$
(^|\.)google\.kz$
(^|\.)google\.lv$
(^|\.)google\.mn$
(^|\.)google\.ms$
(^|\.)google\.nl$
(^|\.)google\.no$
(^|\.)google\.nu$
(^|\.)google\.ro$
(^|\.)google\.ru$
(^|\.)google\.rw$
(^|\.)google\.sc$
(^|\.)google\.sh$
(^|\.)google\.sk$
(^|\.)google\.sm$
(^|\.)google\.sn$
(^|\.)google\.tk$
(^|\.)google\.tm$
(^|\.)google\.to$
(^|\.)google\.tt$
(^|\.)google\.vu$
(^|\.)google\.ws$
(^|\.)googleapis\.cn$
(^|\.)googleapis\.com$
(^|\.)google(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)googleapis(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)googleapps\.com$
(^|\.)googlearth\.com$
(^|\.)googleartproject\.com$
+3 -58
View File
@@ -561,9 +561,7 @@
(^|\.)blogs\.libraryinformationtechnology\.com$
(^|\.)blogs\.tampabay\.com$
(^|\.)blogs\.yahoo\.co\.jp$
(^|\.)blogspot\.com$
(^|\.)blogspot\.hk$
(^|\.)blogspot\.jp$
(^|\.)blogspot(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)blogtd\.net$
(^|\.)blogtd\.org$
(^|\.)bloodshed\.net$
@@ -1802,61 +1800,8 @@
(^|\.)goodtv\.com\.tw$
(^|\.)goodtv\.tv$
(^|\.)goofind\.com$
(^|\.)google\.ae$
(^|\.)google\.am$
(^|\.)google\.as$
(^|\.)google\.at$
(^|\.)google\.az$
(^|\.)google\.ba$
(^|\.)google\.be$
(^|\.)google\.bg$
(^|\.)google\.ca$
(^|\.)google\.calstate\.edu$
(^|\.)google\.cd$
(^|\.)google\.ci$
(^|\.)google\.co\.id$
(^|\.)google\.co\.jp$
(^|\.)google\.co\.kr$
(^|\.)google\.co\.ma$
(^|\.)google\.co\.uk$
(^|\.)google\.com$
(^|\.)google\.de$
(^|\.)google\.dj$
(^|\.)google\.dk$
(^|\.)google\.es$
(^|\.)google\.fi$
(^|\.)google\.fm$
(^|\.)google\.fr$
(^|\.)google\.gg$
(^|\.)google\.gl$
(^|\.)google\.gr$
(^|\.)google\.ie$
(^|\.)google\.is$
(^|\.)google\.it$
(^|\.)google\.jo$
(^|\.)google\.kz$
(^|\.)google\.lv$
(^|\.)google\.mn$
(^|\.)google\.ms$
(^|\.)google\.nl$
(^|\.)google\.no$
(^|\.)google\.nu$
(^|\.)google\.ro$
(^|\.)google\.ru$
(^|\.)google\.rw$
(^|\.)google\.sc$
(^|\.)google\.sh$
(^|\.)google\.sk$
(^|\.)google\.sm$
(^|\.)google\.sn$
(^|\.)google\.tk$
(^|\.)google\.tm$
(^|\.)google\.to$
(^|\.)google\.tt$
(^|\.)google\.vu$
(^|\.)google\.ws$
(^|\.)googleapis\.cn$
(^|\.)googleapis\.com$
(^|\.)google(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)googleapis(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)googleapps\.com$
(^|\.)googlearth\.com$
(^|\.)googleartproject\.com$
@@ -1,17 +1,3 @@
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.0.0/29
192.0.2.0/24
192.88.99.0/24
192.168.0.0/16
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
224.0.0.0/3
1.0.1.0/24
1.0.2.0/23
1.0.8.0/21
@@ -118,6 +118,7 @@ class App : Application() {
app = this
if (!BuildConfig.DEBUG) System.setProperty(LocalLog.LOCAL_LOG_LEVEL_PROPERTY, "ERROR")
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
PreferenceFragmentCompat.registerPreferenceFragment(IconListPreference::class.java,
BottomSheetPreferenceDialogFragment::class.java)
@@ -39,6 +39,7 @@ import android.support.v7.app.AlertDialog
import android.support.v7.app.AppCompatActivity
import android.support.v7.content.res.AppCompatResources
import android.support.v7.preference.PreferenceDataStore
import android.text.format.Formatter
import android.util.Log
import android.view.View
import android.widget.TextView
@@ -49,7 +50,6 @@ import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.bg.Executable
import com.github.shadowsocks.bg.TrafficMonitor
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.preference.DataStore
@@ -149,10 +149,10 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
stateListener?.invoke(state)
}
fun updateTraffic(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) {
txText.text = TrafficMonitor.formatTraffic(txTotal)
rxText.text = TrafficMonitor.formatTraffic(rxTotal)
txRateText.text = getString(R.string.speed, TrafficMonitor.formatTraffic(txRate))
rxRateText.text = getString(R.string.speed, TrafficMonitor.formatTraffic(rxRate))
txText.text = Formatter.formatFileSize(this, txTotal)
rxText.text = Formatter.formatFileSize(this, rxTotal)
txRateText.text = getString(R.string.speed, Formatter.formatFileSize(this, txRate))
rxRateText.text = getString(R.string.speed, Formatter.formatFileSize(this, rxRate))
val child = supportFragmentManager.findFragmentById(R.id.fragment_holder) as ToolbarFragment?
if (state != BaseService.STOPPING)
child?.onTrafficUpdated(profileId, txRate, rxRate, txTotal, rxTotal)
@@ -33,13 +33,13 @@ import android.support.design.widget.Snackbar
import android.support.v4.app.DialogFragment
import android.support.v7.widget.*
import android.support.v7.widget.helper.ItemTouchHelper
import android.text.format.Formatter
import android.view.*
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.github.shadowsocks.App.Companion.app
import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.bg.TrafficMonitor
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.plugin.PluginConfiguration
@@ -123,7 +123,10 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
private var adView: AdView? = null
init {
edit.setOnClickListener { startConfig(item) }
edit.setOnClickListener {
item = ProfileManager.getProfile(item.id)!!
startConfig(item)
}
TooltipCompat.setTooltipText(edit, edit.contentDescription)
itemView.setOnClickListener(this)
val share = itemView.findViewById<View>(R.id.share)
@@ -156,10 +159,11 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
text2.visibility = View.VISIBLE
text2.text = t2.joinToString("\n")
}
val context = requireContext()
if (tx <= 0 && rx <= 0) traffic.visibility = View.GONE else {
traffic.visibility = View.VISIBLE
traffic.text = getString(R.string.traffic,
TrafficMonitor.formatTraffic(tx), TrafficMonitor.formatTraffic(rx))
Formatter.formatFileSize(context, tx), Formatter.formatFileSize(context, rx))
}
if (item.id == DataStore.profileId) {
@@ -176,7 +180,6 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
params.gravity = Gravity.CENTER_HORIZONTAL
val context = requireContext()
adView = AdView(context)
adView.layoutParams = params
adView.adUnitId = "ca-app-pub-9097031975646651/7760346322"
@@ -38,13 +38,13 @@ class AclSyncJob(private val route: String) : Job() {
return when (parts[0]) {
TAG -> AclSyncJob(parts[1])
else -> {
Log.w(TAG, "Unknown job tag: " + tag)
Log.w(TAG, "Unknown job tag: $tag")
null
}
}
}
fun schedule(route: String): Int = JobRequest.Builder(TAG + ':' + route)
fun schedule(route: String): Int = JobRequest.Builder("$TAG:$route")
.setExecutionWindow(TimeUnit.SECONDS.toMillis(10), TimeUnit.DAYS.toMillis(28))
.setRequirementsEnforced(true)
.setRequiredNetworkType(JobRequest.NetworkType.UNMETERED)
@@ -76,7 +76,7 @@ object BaseService {
@Volatile var state = STOPPED
@Volatile var plugin = PluginOptions()
@Volatile var pluginPath: String? = null
var sslocalProcess: GuardedProcess? = null
val processes = GuardedProcessPool()
var timer: Timer? = null
var trafficMonitorThread: TrafficMonitorThread? = null
@@ -274,7 +274,7 @@ object BaseService {
if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
data.sslocalProcess = GuardedProcess(cmd).start()
data.processes.start(cmd)
}
fun createNotification(profileName: String): ServiceNotification
@@ -285,11 +285,7 @@ object BaseService {
else startService(Intent(this, javaClass))
}
fun killProcesses() {
val data = data
data.sslocalProcess?.destroy()
data.sslocalProcess = null
}
fun killProcesses() = data.processes.killAll()
fun stopRunner(stopService: Boolean, msg: String? = null) {
// channge the state
@@ -31,31 +31,34 @@ import com.github.shadowsocks.utils.thread
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.util.concurrent.Semaphore
import java.util.*
import java.util.concurrent.ArrayBlockingQueue
class GuardedProcess(private val cmd: List<String>) {
class GuardedProcessPool {
companion object {
private const val TAG = "GuardedProcess"
private const val TAG = "GuardedProcessPool"
private val dummy = IOException()
}
private lateinit var guardThread: Thread
@Volatile
private var isDestroyed = false
@Volatile
private lateinit var process: Process
private val name = File(cmd.first()).nameWithoutExtension
private inner class Guard(private val cmd: List<String>, private val onRestartCallback: (() -> Unit)?) {
val cmdName = File(cmd.first()).nameWithoutExtension
val excQueue = ArrayBlockingQueue<IOException>(1) // ArrayBlockingQueue doesn't want null
private var pushed = false
private fun streamLogger(input: InputStream, logger: (String, String) -> Int) = thread("StreamLogger-$name") {
try {
input.bufferedReader().useLines { it.forEach { logger(TAG, it) } }
} catch (_: IOException) { } // ignore
}
private fun streamLogger(input: InputStream, logger: (String, String) -> Int) =
thread("StreamLogger-$cmdName") {
try {
input.bufferedReader().useLines { it.forEach { logger(TAG, it) } }
} catch (_: IOException) { } // ignore
}
private fun pushException(ioException: IOException?) {
if (pushed) return
excQueue.put(ioException ?: dummy)
pushed = true
}
fun start(onRestartCallback: (() -> Unit)? = null): GuardedProcess {
val semaphore = Semaphore(1)
semaphore.acquire()
var ioException: IOException? = null
guardThread = thread("GuardThread-$name") {
fun looper() {
var process: Process? = null
try {
var callback: (() -> Unit)? = null
while (!isDestroyed) {
@@ -72,44 +75,54 @@ class GuardedProcess(private val cmd: List<String>) {
if (callback == null) callback = onRestartCallback else callback()
semaphore.release()
pushException(null)
process.waitFor()
synchronized(this) {
if (SystemClock.elapsedRealtime() - startTime < 1000) {
Log.w(TAG, "process exit too fast, stop guard: " + Commandline.toString(cmd))
Log.w(TAG, "process exit too fast, stop guard: $cmdName")
isDestroyed = true
}
}
}
} catch (_: InterruptedException) {
if (BuildConfig.DEBUG) Log.d(TAG, "thread interrupt, destroy process: " + Commandline.toString(cmd))
destroyProcess()
if (BuildConfig.DEBUG) Log.d(TAG, "thread interrupt, destroy process: $cmdName")
} catch (e: IOException) {
ioException = e
pushException(e)
} finally {
semaphore.release()
if (process != null) {
if (Build.VERSION.SDK_INT < 24) @Suppress("DEPRECATION") {
JniHelper.sigtermCompat(process)
JniHelper.waitForCompat(process, 500)
}
process.destroy()
process.waitFor() // ensure the process is destroyed
}
pushException(null)
}
}
semaphore.acquire()
if (ioException != null) throw ioException!!
}
private val guardThreads = HashSet<Thread>()
@Volatile
private var isDestroyed = false
fun start(cmd: List<String>, onRestartCallback: (() -> Unit)? = null): GuardedProcessPool {
val guard = Guard(cmd, onRestartCallback)
guardThreads.add(thread("GuardThread-${guard.cmdName}", block = guard::looper))
val ioException = guard.excQueue.take()
if (ioException !== dummy) throw ioException
return this
}
fun destroy() {
fun killAll() {
isDestroyed = true
guardThread.interrupt()
destroyProcess()
guardThreads.forEach { it.interrupt() }
try {
guardThread.join()
guardThreads.forEach { it.join() }
} catch (_: InterruptedException) { }
}
private fun destroyProcess() {
if (Build.VERSION.SDK_INT < 24) @Suppress("DEPRECATION") {
JniHelper.sigtermCompat(process)
JniHelper.waitForCompat(process, 500)
}
process.destroy()
guardThreads.clear()
isDestroyed = false
}
}
@@ -28,19 +28,9 @@ import org.json.JSONArray
import org.json.JSONObject
import java.io.File
import java.net.Inet6Address
import java.util.*
/**
* This object also uses WeakMap to simulate the effects of multi-inheritance, but more lightweight.
*/
object LocalDnsService {
interface Interface : BaseService.Interface {
var overtureProcess: GuardedProcess?
get() = overtureProcesses[this]
set(value) {
if (value == null) overtureProcesses.remove(this) else overtureProcesses[this] = value
}
override fun startNativeProcesses() {
super.startNativeProcesses()
val data = data
@@ -96,18 +86,10 @@ object LocalDnsService {
return file
}
if (!profile.udpdns) overtureProcess = GuardedProcess(buildAdditionalArguments(arrayListOf(
if (!profile.udpdns) data.processes.start(buildAdditionalArguments(arrayListOf(
File(app.applicationInfo.nativeLibraryDir, Executable.OVERTURE).absolutePath,
"-c", buildOvertureConfig("overture.conf")
))).start()
}
override fun killProcesses() {
super.killProcesses()
overtureProcess?.destroy()
overtureProcess = null
)))
}
}
private val overtureProcesses = WeakHashMap<Interface, GuardedProcess>()
}
@@ -31,6 +31,7 @@ import android.os.Build
import android.os.PowerManager
import android.support.v4.app.NotificationCompat
import android.support.v4.content.ContextCompat
import android.text.format.Formatter
import com.github.shadowsocks.MainActivity
import com.github.shadowsocks.R
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
@@ -55,11 +56,11 @@ class ServiceNotification(private val service: BaseService.Interface, profileNam
override fun stateChanged(state: Int, profileName: String?, msg: String?) { } // ignore
override fun trafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) {
service as Context
val txr = service.getString(R.string.speed, TrafficMonitor.formatTraffic(txRate))
val rxr = service.getString(R.string.speed, TrafficMonitor.formatTraffic(rxRate))
val txr = service.getString(R.string.speed, Formatter.formatFileSize(service, txRate))
val rxr = service.getString(R.string.speed, Formatter.formatFileSize(service, rxRate))
builder.setContentText("$txr↑\t$rxr↓")
style.bigText(service.getString(R.string.stat_summary).format(Locale.ENGLISH, txr, rxr,
TrafficMonitor.formatTraffic(txTotal), TrafficMonitor.formatTraffic(rxTotal)))
Formatter.formatFileSize(service, txTotal), Formatter.formatFileSize(service, rxTotal)))
show()
}
override fun trafficPersisted(profileId: Int) { }
@@ -21,9 +21,6 @@
package com.github.shadowsocks.bg
import android.os.SystemClock
import com.github.shadowsocks.App.Companion.app
import com.github.shadowsocks.R
import java.text.DecimalFormat
object TrafficMonitor {
// Bytes per second
@@ -41,19 +38,6 @@ object TrafficMonitor {
@Volatile
private var dirty = true
private val units = arrayOf("KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "BB", "NB", "DB", "CB")
private val numberFormat = DecimalFormat("@@@")
fun formatTraffic(size: Long): String {
var n = size.toDouble()
var i = -1
while (n >= 999.5) {
n /= 1024
++i
}
return if (i < 0) "$size ${app.resources.getQuantityString(R.plurals.bytes, size.toInt())}"
else "${numberFormat.format(n)} ${units[i]}"
}
fun updateRate(): Boolean {
val now = SystemClock.elapsedRealtime()
val delta = now - timestampLast
@@ -40,18 +40,14 @@ class TransproxyService : Service(), LocalDnsService.Interface {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int =
super<LocalDnsService.Interface>.onStartCommand(intent, flags, startId)
private var sstunnelProcess: GuardedProcess? = null
private var redsocksProcess: GuardedProcess? = null
private fun startDNSTunnel() {
val cmd = arrayListOf(File(applicationInfo.nativeLibraryDir, Executable.SS_TUNNEL).absolutePath,
data.processes.start(listOf(File(applicationInfo.nativeLibraryDir, Executable.SS_TUNNEL).absolutePath,
"-t", "10",
"-b", "127.0.0.1",
"-u",
"-l", DataStore.portLocalDns.toString(), // ss-tunnel listens on the same port as overture
"-L", data.profile!!.remoteDns.split(",").first().trim() + ":53",
"-c", data.shadowsocksConfigFile!!.absolutePath) // config is already built by BaseService.Interface
sstunnelProcess = GuardedProcess(cmd).start()
"-c", data.shadowsocksConfigFile!!.absolutePath)) // config is already built by BaseService.Interface
}
private fun startRedsocksDaemon() {
@@ -70,10 +66,8 @@ redsocks {
type = socks5;
}
""")
redsocksProcess = GuardedProcess(arrayListOf(
File(applicationInfo.nativeLibraryDir, Executable.REDSOCKS).absolutePath,
"-c", "redsocks.conf")
).start()
data.processes.start(listOf(
File(applicationInfo.nativeLibraryDir, Executable.REDSOCKS).absolutePath, "-c", "redsocks.conf"))
}
override fun startNativeProcesses() {
@@ -81,12 +75,4 @@ redsocks {
super.startNativeProcesses()
if (data.profile!!.udpdns) startDNSTunnel()
}
override fun killProcesses() {
super.killProcesses()
sstunnelProcess?.destroy()
sstunnelProcess = null
redsocksProcess?.destroy()
redsocksProcess = null
}
}
@@ -116,7 +116,6 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
private var conn: ParcelFileDescriptor? = null
private var worker: ProtectWorker? = null
private var tun2socksProcess: GuardedProcess? = null
private var underlyingNetwork: Network? = null
@TargetApi(28)
set(value) {
@@ -155,8 +154,6 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
worker?.stopThread()
worker = null
super.killProcesses()
tun2socksProcess?.destroy()
tun2socksProcess = null
conn?.close()
conn = null
}
@@ -256,7 +253,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
cmd += "--dnsgw"
cmd += "127.0.0.1:${DataStore.portLocalDns}"
}
tun2socksProcess = GuardedProcess(cmd).start { sendFd(fd) }
data.processes.start(cmd) { sendFd(fd) }
return fd
}
@@ -3,7 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:padding="16dp">
<Spinner android:id="@+id/template_selector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+1 -1
View File
@@ -41,7 +41,6 @@
<string name="delete_confirm_prompt">"آیا مطمئن هستید که می‌خواهید این پروفایل را حذف کنید؟"</string>
<string name="share_qr_nfc">"NFC یا QR Code"</string>
<string name="add_profile_methods_scan_qr_code">"اسکن‌کردن QR Code"</string>
<string name="add_profile_methods_manual_settings">"تنظیمات دستی"</string>
<!-- tasker -->
<string name="toggle_service_state">"فعال‌کردن سرویس"</string>
@@ -55,4 +54,5 @@
<string name="vpn_connected">"وصل شد. برای بررسی اتصال ضربه (Tap) بزنید."</string>
<string name="not_connected">"اتصال برقرار نیست!"</string>
<string name="route_entry_all">"همه"</string>
<string name="add_profile_methods_manual_settings">"تنظیمات دستی"</string>
</resources>
+50
View File
@@ -1,35 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="quick_toggle">"Basculer"</string>
<!-- misc -->
<string name="profile">"Profil"</string>
<string name="profile_summary">"Passer à un autre profil ou ajouter de nouveaux profils"</string>
<string name="remote_dns">"DNS Distant"</string>
<string name="stat_summary">"Envoyé : \t%3$s\t↑\t%1$s
Reçu : \t\t\t%4$s\t↓\t%2$s"</string>
<string name="connection_test_testing">"Essai..."</string>
<string name="connection_test_error">"Impossible de détecter la connexion Internet :%s"</string>
<string name="connection_test_fail">"Internet Indisponible"</string>
<string name="connection_test_error_status_code">"Code de l'Erreur : #%d"</string>
<!-- proxy category -->
<string name="profile_name">"Nom du Profil"</string>
<string name="proxy">"Serveur"</string>
<string name="remote_port">"Port Distant"</string>
<string name="sitekey">"Mot de passe"</string>
<string name="enc_method">"Méthode d'Encryption"</string>
<string name="auto_connect_summary">"Activer Shadowsocks au démarrage"</string>
<string name="tcp_fastopen_summary_unsupported">"Version du noyau non supportée : %s &lt;3.7.1"</string>
<!-- notification category -->
<string name="forward_success">"Shadowsocks a démarré"</string>
<string name="invalid_server">"Nom de serveur invalide"</string>
<string name="service_failed">"Échec de la connexion au serveur distant"</string>
<string name="reboot_required">"Impossible de démarrer le service VPN. Vous devrez peut-être redémarrer votre appareil."</string>
<!-- alert category -->
<string name="close">"Fermer"</string>
<string name="profile_empty">"Veuillez sélectionner un profil"</string>
<string name="connect">"Connexion"</string>
<string name="remove_profile">"Supprimer ce profil %s ?"</string>
<!-- menu category -->
<string name="profiles">"Profils"</string>
<string name="settings">"Paramètres"</string>
<string name="about">"À propos"</string>
<string name="edit">"Éditer"</string>
<string name="share">"Partager"</string>
<string name="add_profile">"Ajouter un Profil"</string>
<string name="action_apply_all">"Appliquer les Paramètres à tous les Profils"</string>
<string name="action_export">"Exporter vers le presse-papiers"</string>
<string name="action_import">"Importer depuis le presse-papiers"</string>
<string name="action_export_msg">"Exporter avec succès !"</string>
<string name="action_export_err">"Échec de l'exportation"</string>
<string name="action_import_msg">"Importer avec succès !"</string>
<string name="action_import_err">"Échec de l’importation."</string>
<string name="delete">"Supprimer"</string>
<string name="delete_confirm_prompt">"Êtes-vous sûr de vouloir supprimer ce profil?"</string>
<string name="share_qr_nfc">"QR Code/NFC"</string>
<string name="add_profile_dialog">"Ajouter ce profil Shadowsocks?"</string>
<string name="add_profile_methods_scan_qr_code">"Scanner le QR Code"</string>
<plurals name="removed">
<item quantity="one">"Supprimé"</item>
<item quantity="other">"%d éléments supprimés"</item>
</plurals>
<string name="undo">"Annuler"</string>
<!-- tasker -->
<string name="toggle_service_state">"Démarrer le service"</string>
<string name="profile_default">"Utiliser le profil actuel"</string>
<!-- status -->
<string name="sent">"Envoyé :"</string>
<string name="received">"Reçu :"</string>
<string name="connecting">"Connexion..."</string>
<string name="not_connected">"Non connecté"</string>
<!-- acl -->
<string name="custom_rules">"Règles personnalisées"</string>
<string name="action_add_rule">"Ajouter une/des règle(s)"</string>
<string name="edit_rule">"Modifier une règle"</string>
<string name="route_entry_all">"Toutes"</string>
<string name="plugin_configure">"Configurer..."</string>
<string name="plugin_disabled">"Désactivé"</string>
<string name="plugin_unknown">"Plugin inconnu %s"</string>
<string name="plugin_untrusted">"Attention : ce plugin ne semble pas provenir d'une source fiable reconnue."</string>
<string name="profile_plugin">"Plugin : %s"</string>
<string name="advanced">"Avancé"</string>
<string name="auto_connect_summary_v24">"Activer Shadowsocks au démarrage. Il est plutôt recommandé d'utiliser le VPN en permanence"</string>
<string name="direct_boot_aware_summary">"Vos informations de profil sélectionnées seront moins protégées"</string>
</resources>
+2 -14
View File
@@ -15,9 +15,6 @@
<string name="connection_test_fail">"インターネット利用不可"</string>
<string name="connection_test_error_status_code">"ステータスコード無効: #%d"</string>
<!-- proxy category -->
<string name="proxy_cat">"サーバー設定"</string>
<!-- proxy category -->
<string name="profile_name">"サーバー名"</string>
<string name="proxy">"サーバーアドレス"</string>
@@ -25,9 +22,6 @@
<string name="sitekey">"パスワード"</string>
<string name="enc_method">"暗号化方式"</string>
<!-- feature category -->
<string name="feature_cat">"ファンクション設定"</string>
<!-- feature category -->
<string name="ipv6">"IPv6 プロキシ"</string>
<string name="ipv6_summary">"リモートサーバーに IPv6 パケットを転送"</string>
@@ -55,10 +49,6 @@
<string name="reboot_required">"VPN サービスの起動に失敗しました、デバイスの再起動を試みて下さい"</string>
<string name="profile_invalid_input">"有効なプロファイルが見つかりません"</string>
<!-- alert category -->
<string name="yes">"はい"</string>
<string name="no">"いいえ"</string>
<!-- alert category -->
<string name="close">"閉じる"</string>
<string name="profile_empty">"プロファイルを選択して下さい"</string>
@@ -84,8 +74,6 @@
<!-- profile -->
<string name="profile_config">"プロファイル編集"</string>
<string name="unsaved_changes_prompt">"変更は適応されておりません、保存しますか?"</string>
<string name="apply">"適用"</string>
<string name="delete">"削除"</string>
<string name="delete_confirm_prompt">"このプロファイルを削除しますか"</string>
<string name="share_qr_nfc">"QR コード / NFC"</string>
@@ -147,7 +135,7 @@
<string name="service_transproxy">"トランスプロキシサービス"</string>
<string name="vpn_permission_denied">"VPNサービス作成のアクセス許可が拒否されました"</string>
<string name="auto_connect_summary_v24">"起動時にShadowsockを有効。 VPN常時接続の使用をお勧めします"</string>
<string name="direct_boot_aware">"ダイレクトブート"</string>
<string name="direct_boot_aware_summary">"デバイスがロック解除される前にShadowsocksの自動起動を許可(選択されたプロファイル情報はより少ない保護を受けることになります)"</string>
<string name="direct_boot_aware_summary">"選択されたプロファイル情報はより少ない保護を受けることになります"</string>
<string name="acl_rule_online_config">"オンライン設定のURL"</string>
<string name="action_import_file">"ファイルからのインポート"</string>
</resources>
-12
View File
@@ -18,9 +18,6 @@
<string name="connection_test_fail">"인터넷에 연결할 수 없습니다"</string>
<string name="connection_test_error_status_code">"오류 코드: #%d"</string>
<!-- proxy category -->
<string name="proxy_cat">"서버 설정"</string>
<!-- proxy category -->
<string name="profile_name">"프로필 이름"</string>
<string name="proxy">"서버 주소"</string>
@@ -28,9 +25,6 @@
<string name="sitekey">"비밀번호"</string>
<string name="enc_method">"암호화 방법"</string>
<!-- feature category -->
<string name="feature_cat">"기능 설정"</string>
<!-- feature category -->
<string name="ipv6">"IPv6 라우팅"</string>
<string name="ipv6_summary">"IPv6 트래픽도 원격으로 리다이렉트 합니다"</string>
@@ -58,10 +52,6 @@
<string name="reboot_required">"VPN 서비스를 시작하는 데 실패했습니다. 장치를 재시작해 보세요."</string>
<string name="profile_invalid_input">"올바른 프로필 데이터를 찾을 수 없습니다"</string>
<!-- alert category -->
<string name="yes">"예"</string>
<string name="no">"아니오"</string>
<!-- alert category -->
<string name="close">"닫기"</string>
<string name="profile_empty">"프로필을 선택해 주세요"</string>
@@ -86,8 +76,6 @@
<!-- profile -->
<string name="profile_config">"프로필 설정"</string>
<string name="unsaved_changes_prompt">"변경 사항이 저장되지 않았습니다. 저장하시겠습니까?"</string>
<string name="apply">"적용"</string>
<string name="delete">"삭제"</string>
<string name="delete_confirm_prompt">"정말 이 프로필을 삭제하시겠습니까?"</string>
<string name="share_qr_nfc">"QR 코드/NFC"</string>
+2 -14
View File
@@ -21,9 +21,6 @@
<string name="connection_test_fail">"Интернет недоступен"</string>
<string name="connection_test_error_status_code">"Код ошибки: #%d"</string>
<!-- proxy category -->
<string name="proxy_cat">"Настройки Сервера"</string>
<!-- proxy category -->
<string name="profile_name">"Имя профиля"</string>
<string name="proxy">"Сервер"</string>
@@ -31,9 +28,6 @@
<string name="sitekey">"Пароль"</string>
<string name="enc_method">"Метод шифрования"</string>
<!-- feature category -->
<string name="feature_cat">"Дополнительные Настройки"</string>
<!-- feature category -->
<string name="ipv6">"IPv6 Маршрут"</string>
<string name="ipv6_summary">"Перенаправлять трафик IPv6 на удалённый сервер"</string>
@@ -60,10 +54,6 @@
<string name="reboot_required">"Не удалось запустить службу VPN. Возможно, требуется перезагрузить ваше устройство."</string>
<string name="profile_invalid_input">"Не найдено действительных данных профиля."</string>
<!-- alert category -->
<string name="yes">"Да"</string>
<string name="no">"Нет"</string>
<!-- alert category -->
<string name="close">"Закрыть"</string>
<string name="profile_empty">"Пожалуйста, выберите профиль"</string>
@@ -91,8 +81,6 @@
<!-- profile -->
<string name="profile_config">"Настройка профиля"</string>
<string name="unsaved_changes_prompt">"Изменения не сохранены. Сохранить?"</string>
<string name="apply">"Применить"</string>
<string name="delete">"Удалить"</string>
<string name="delete_confirm_prompt">"Вы уверены, что хотите удалить этот профиль?"</string>
<string name="share_qr_nfc">"QR-код/NFC"</string>
@@ -157,7 +145,7 @@
<string name="vpn_permission_denied">"Нет разрешения на создание VPN соединения"</string>
<string name="auto_connect_summary_v24">"Включить Shadowsocks во время запуска. Лучше использовать режим постоянной VPN."</string>
<string name="speed">"%s/с"</string>
<string name="direct_boot_aware">"Непосредственная загрузка"</string>
<string name="direct_boot_aware_summary">"Позволить Shadowsocks запускаться до того, как устройство будет разблокировано (данные выбранного профиля будут хуже защищены)"</string>
<string name="direct_boot_aware_summary">"данные выбранного профиля будут хуже защищены"</string>
<string name="acl_rule_online_config">"URL конфигурации"</string>
<string name="action_import_file">"Импортировать из файла..."</string>
</resources>
+3 -15
View File
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">"影梭"</string>
<string name="quick_toggle">"开关"</string>
<!-- misc -->
@@ -16,9 +15,6 @@
<string name="connection_test_fail">"无互联网连接"</string>
<string name="connection_test_error_status_code">"状态码无效(#%d)"</string>
<!-- proxy category -->
<string name="proxy_cat">"服务器设置"</string>
<!-- proxy category -->
<string name="profile_name">"配置名称"</string>
<string name="proxy">"服务器"</string>
@@ -26,9 +22,6 @@
<string name="sitekey">"密码"</string>
<string name="enc_method">"加密方式"</string>
<!-- feature category -->
<string name="feature_cat">"功能设置"</string>
<!-- feature category -->
<string name="ipv6">"IPv6 路由"</string>
<string name="ipv6_summary">"转发 IPv6 流量到远程服务器"</string>
@@ -56,10 +49,6 @@
<string name="reboot_required">"VPN 服务启动失败。你可能需要重启设备。"</string>
<string name="profile_invalid_input">"未找到有效的配置文件。"</string>
<!-- alert category -->
<string name="yes">"是"</string>
<string name="no">"否"</string>
<!-- alert category -->
<string name="close">"关闭"</string>
<string name="profile_empty">"请选择配置文件"</string>
@@ -86,8 +75,6 @@
<!-- profile -->
<string name="profile_config">"配置文件设置"</string>
<string name="unsaved_changes_prompt">"保存修改吗?"</string>
<string name="apply">"应用"</string>
<string name="delete">"删除"</string>
<string name="delete_confirm_prompt">"您确定要删除此配置文件?"</string>
<string name="share_qr_nfc">"二维码 / NFC"</string>
@@ -148,7 +135,8 @@
<string name="service_transproxy">"透明代理模式"</string>
<string name="vpn_permission_denied">"创建 VPN 服务权限不足"</string>
<string name="auto_connect_summary_v24">"允许 Shadowsocks 随系统启动,建议使用始终开启的 VPN"</string>
<string name="direct_boot_aware">"直接启动"</string>
<string name="direct_boot_aware_summary">"允许在设备解锁前启动服务(选中的配置信息会不那么安全)"</string>
<string name="direct_boot_aware">"允许锁屏下切换"</string>
<string name="direct_boot_aware_summary">"选中的配置信息会不那么安全"</string>
<string name="acl_rule_online_config">"在线规则文件 URL"</string>
<string name="action_import_file">"从文件导入…"</string>
</resources>
@@ -19,9 +19,6 @@
<string name="connection_test_fail">"無法使用網際網路"</string>
<string name="connection_test_error_status_code">"錯誤碼: (#%d)"</string>
<!-- proxy category -->
<string name="proxy_cat">"伺服器設定"</string>
<!-- proxy category -->
<string name="profile_name">"設定檔名稱"</string>
<string name="proxy">"伺服器"</string>
@@ -29,9 +26,6 @@
<string name="sitekey">"密碼"</string>
<string name="enc_method">"加密方法"</string>
<!-- feature category -->
<string name="feature_cat">"功能設定"</string>
<!-- feature category -->
<string name="ipv6">"IPv6 路由"</string>
<string name="ipv6_summary">"向遠端重新導向 IPv6 流量"</string>
@@ -58,10 +52,6 @@
<string name="reboot_required">"VPN 服務啟動失敗。您或許需要重新啟動您的裝置。"</string>
<string name="profile_invalid_input">"未找到有效的設定檔資料。"</string>
<!-- alert category -->
<string name="yes">"是"</string>
<string name="no">"否"</string>
<!-- alert category -->
<string name="close">"關閉"</string>
<string name="profile_empty">"請選擇設定檔"</string>
@@ -88,8 +78,6 @@
<!-- profile -->
<string name="profile_config">"設定檔設定"</string>
<string name="unsaved_changes_prompt">"要儲存變更嗎?"</string>
<string name="apply">"套用"</string>
<string name="delete">"刪除"</string>
<string name="delete_confirm_prompt">"您確定要移除這個設定檔嗎?"</string>
<string name="share_qr_nfc">"QR 碼 / NFC"</string>
-4
View File
@@ -20,10 +20,6 @@
<string name="traffic">%s↑\t%s↓</string>
<string name="stat_summary">Sent: \t\t\t\t\t%3$s\t↑\t%1$s\nReceived: \t%4$s\t↓\t%2$s</string>
<string name="speed">%s/s</string>
<plurals name="bytes">
<item quantity="one">Byte</item>
<item quantity="other">Bytes</item>
</plurals>
<string name="connection_test_pending">Check Connectivity</string>
<string name="connection_test_testing">Testing…</string>
<string name="connection_test_available">Success: %dms latency</string>
-10
View File
@@ -1,15 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Translucent" parent="Theme.AppCompat.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowCloseOnTouchOutside">false</item>
</style>
<style name="StyledScrollerTextAppearance" parent="@android:style/TextAppearance">
<item name="android:textSize">24sp</item>
<item name="android:textStyle">bold</item>
+5
View File
@@ -1,3 +1,8 @@
* 0.1.1:
* Rewritten in Kotlin;
* Fix assert not working;
* Min API 21;
* Update support library version to 27.1.1.
* 0.0.4:
* Enlarge text size of number pickers;
* Update support library version to 26.0.0.
+2 -2
View File
@@ -10,8 +10,8 @@ android {
defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.sdkVersion
versionCode 5
versionName "0.1.0"
versionCode 6
versionName "0.1.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
+2 -1
View File
@@ -153,7 +153,8 @@ This corresponds to `com.github.shadowsocks.plugin.NativePluginProvider` in the
...
<provider android:name=".BinaryProvider"
android:exported="true"
android:authorities="$FULLY_QUALIFIED_NAME_OF_YOUR_CONTENTPROVIDER">
android:authorities="$FULLY_QUALIFIED_NAME_OF_YOUR_CONTENTPROVIDER"
tools:ignore="ExportedContentProvider">
<intent-filter>
<action android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"/>
</intent-filter>
@@ -1,3 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="proxy_cat">"Paramètres du Serveur"</string>
<string name="feature_cat">"Paramètres des Fonctionnalités"</string>
<string name="yes">"Oui"</string>
<string name="no">"Non"</string>
<string name="apply">"Appliquer"</string>
</resources>
+1 -7
View File
@@ -1,15 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- proxy category -->
<string name="proxy_cat">"서버 설정"</string>
<!-- feature category -->
<string name="feature_cat">"기능 설정"</string>
<!-- alert category -->
<string name="unsaved_changes_prompt">"변경 사항이 저장되지 않았습니다. 저장하시겠습니까?"</string>
<string name="yes">"예"</string>
<string name="no">"아니오"</string>
<string name="unsaved_changes_prompt">"변경 사항이 저장되지 않았습니다. 저장하시겠습니까?"</string>
<string name="apply">"적용"</string>
</resources>
@@ -1,15 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- proxy category -->
<string name="proxy_cat">"伺服器設定"</string>
<!-- feature category -->
<string name="feature_cat">"功能設定"</string>
<!-- alert category -->
<string name="unsaved_changes_prompt">"要儲存變更嗎?"</string>
<string name="yes">"是"</string>
<string name="no">"否"</string>
<string name="unsaved_changes_prompt">"要儲存變更嗎?"</string>
<string name="apply">"套用"</string>
</resources>
+11
View File
@@ -11,4 +11,15 @@
<item name="preferenceCategory_marginBottom">8dp</item>
<item name="windowActionModeOverlay">true</item>
</style>
<style name="Theme.AppCompat.Translucent" parent="Theme.AppCompat.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowCloseOnTouchOutside">false</item>
<item name="windowNoTitle">true</item>
</style>
</resources>