@@ -1,7 +1,7 @@
|
||||
## Shadowsocks for Android
|
||||
|
||||
[](https://travis-ci.org/shadowsocks/shadowsocks-android)
|
||||
[](https://android-arsenal.com/api?level=19)
|
||||
[](https://android-arsenal.com/api?level=21)
|
||||
[](https://github.com/shadowsocks/shadowsocks-android/releases)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
kotlinVersion = '1.2.10'
|
||||
minSdkVersion = 19
|
||||
kotlinVersion = '1.2.20'
|
||||
minSdkVersion = 21
|
||||
sdkVersion = 27
|
||||
buildToolsVersion = '27.0.3'
|
||||
supportLibraryVersion = '27.0.2'
|
||||
|
||||
@@ -43,8 +43,6 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.system.ErrnoException;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
public class JniHelper {
|
||||
static {
|
||||
System.loadLibrary("jni-helper");
|
||||
@@ -54,8 +52,7 @@ public class JniHelper {
|
||||
public static void sigtermCompat(@NonNull Process process) throws Exception {
|
||||
if (Build.VERSION.SDK_INT >= 24) throw new UnsupportedOperationException("Never call this method in OpenJDK!");
|
||||
int errno = sigterm(process);
|
||||
if (errno != 0) throw Build.VERSION.SDK_INT >= 21
|
||||
? new ErrnoException("kill", errno) : new Exception("kill failed: " + errno);
|
||||
if (errno != 0) throw new ErrnoException("kill", errno);
|
||||
}
|
||||
|
||||
@Deprecated // only implemented for before API 24
|
||||
|
||||
@@ -12,7 +12,7 @@ TARGET=$DIR/bin
|
||||
DEPS=$DIR/.deps
|
||||
|
||||
ANDROID_ARM_TOOLCHAIN=$DEPS/android-toolchain-${MIN_API}-arm
|
||||
ANDROID_ARM64_TOOLCHAIN=$DEPS/android-toolchain-21-arm64
|
||||
ANDROID_ARM64_TOOLCHAIN=$DEPS/android-toolchain-${MIN_API}-arm64
|
||||
ANDROID_X86_TOOLCHAIN=$DEPS/android-toolchain-${MIN_API}-x86
|
||||
|
||||
ANDROID_ARM_CC=$ANDROID_ARM_TOOLCHAIN/bin/arm-linux-androideabi-clang
|
||||
@@ -35,7 +35,7 @@ fi
|
||||
if [ ! -f "$ANDROID_ARM64_CC" ]; then
|
||||
echo "Make standalone toolchain for ARM64 arch"
|
||||
$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --arch arm64 \
|
||||
--api 21 --install-dir $ANDROID_ARM64_TOOLCHAIN
|
||||
--api $MIN_API --install-dir $ANDROID_ARM64_TOOLCHAIN
|
||||
fi
|
||||
|
||||
if [ ! -f "$ANDROID_X86_CC" ]; then
|
||||
|
||||
@@ -11,7 +11,7 @@ SET TARGET=%DIR%\bin
|
||||
SET DEPS=%DIR%\.deps
|
||||
|
||||
SET ANDROID_ARM_TOOLCHAIN=%DEPS%\android-toolchain-%MIN_API%-arm
|
||||
SET ANDROID_ARM64_TOOLCHAIN=%DEPS%\android-toolchain-21-arm64
|
||||
SET ANDROID_ARM64_TOOLCHAIN=%DEPS%\android-toolchain-%MIN_API%-arm64
|
||||
SET ANDROID_X86_TOOLCHAIN=%DEPS%\android-toolchain-%MIN_API%-x86
|
||||
|
||||
SET ANDROID_ARM_CC=%ANDROID_ARM_TOOLCHAIN%\bin\arm-linux-androideabi-clang
|
||||
@@ -37,7 +37,7 @@ IF NOT EXIST %ANDROID_ARM_CC% (
|
||||
IF NOT EXIST %ANDROID_ARM64_CC% (
|
||||
ECHO "Make standalone toolchain for ARM64 arch"
|
||||
%ANDROID_NDK_HOME%\build\tools\make_standalone_toolchain.py --arch arm64 ^
|
||||
--api 21 --install-dir %ANDROID_ARM64_TOOLCHAIN%
|
||||
--api %MIN_API% --install-dir %ANDROID_ARM64_TOOLCHAIN%
|
||||
)
|
||||
|
||||
IF NOT EXIST %ANDROID_X86_CC% (
|
||||
|
||||
@@ -24,7 +24,6 @@ android {
|
||||
versionName "4.4.5"
|
||||
testApplicationId "com.github.shadowsocks.test"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary true
|
||||
resConfigs "fa", "fr", "ja", "ko", "ru", "zh-rCN", "zh-rTW"
|
||||
}
|
||||
buildTypes {
|
||||
@@ -48,17 +47,13 @@ dependencies {
|
||||
implementation "com.android.support:design:$supportLibraryVersion"
|
||||
implementation "com.android.support:gridlayout-v7:$supportLibraryVersion"
|
||||
implementation 'com.futuremind.recyclerfastscroll:fastscroll:0.2.5'
|
||||
implementation 'com.evernote:android-job:1.2.1'
|
||||
implementation 'com.evernote:android-job:1.2.2'
|
||||
implementation "com.google.android.gms:play-services-ads:$playServicesVersion"
|
||||
implementation "com.google.android.gms:play-services-analytics:$playServicesVersion"
|
||||
implementation "com.google.android.gms:play-services-gcm:$playServicesVersion"
|
||||
implementation "com.google.firebase:firebase-config:$playServicesVersion"
|
||||
implementation 'com.j256.ormlite:ormlite-android:5.0'
|
||||
implementation 'com.mikepenz:crossfader:1.5.1'
|
||||
implementation 'com.mikepenz:fastadapter:3.0.4'
|
||||
implementation 'com.mikepenz:iconics-core:3.0.0'
|
||||
implementation 'com.mikepenz:materialdrawer:6.0.2'
|
||||
implementation 'com.mikepenz:materialize:1.1.2'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
|
||||
implementation "com.takisoft.fix:preference-v7-simplemenu:$takisoftFixVersion"
|
||||
implementation 'com.twofortyfouram:android-plugin-api-for-locale:1.0.2'
|
||||
|
||||
@@ -171,9 +171,11 @@
|
||||
<service android:name="com.evernote.android.job.v14.PlatformAlarmService"
|
||||
android:process=":bg"
|
||||
android:directBootAware="true"/>
|
||||
<service android:name="com.evernote.android.job.gcm.PlatformGcmService"
|
||||
<service android:name="com.evernote.android.job.v14.PlatformAlarmServiceExact"
|
||||
android:process=":bg"
|
||||
android:directBootAware="true"/>
|
||||
<service android:name="com.evernote.android.job.gcm.PlatformGcmService"
|
||||
tools:node="remove"/>
|
||||
<service android:name="com.evernote.android.job.JobRescheduleService"
|
||||
android:process=":bg"
|
||||
android:directBootAware="true"/>
|
||||
|
||||
@@ -80,13 +80,11 @@ class ProfileConfigFragment : PreferenceFragmentCompatDividers(), Toolbar.OnMenu
|
||||
val serviceMode = DataStore.serviceMode
|
||||
findPreference(Key.remoteDns).isEnabled = serviceMode != Key.modeProxy
|
||||
isProxyApps = findPreference(Key.proxyApps) as SwitchPreference
|
||||
if (Build.VERSION.SDK_INT < 21) isProxyApps.parent!!.removePreference(isProxyApps) else {
|
||||
isProxyApps.isEnabled = serviceMode == Key.modeVpn
|
||||
isProxyApps.setOnPreferenceClickListener {
|
||||
startActivity(Intent(activity, AppManager::class.java))
|
||||
isProxyApps.isChecked = true
|
||||
false
|
||||
}
|
||||
isProxyApps.isEnabled = serviceMode == Key.modeVpn
|
||||
isProxyApps.setOnPreferenceClickListener {
|
||||
startActivity(Intent(activity, AppManager::class.java))
|
||||
isProxyApps.isChecked = true
|
||||
false
|
||||
}
|
||||
findPreference(Key.udpdns).isEnabled = serviceMode != Key.modeProxy
|
||||
plugin = findPreference(Key.plugin) as IconListPreference
|
||||
|
||||
@@ -20,49 +20,19 @@
|
||||
|
||||
package com.github.shadowsocks.bg
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.LinkProperties
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.github.shadowsocks.App.Companion.app
|
||||
import com.github.shadowsocks.BuildConfig
|
||||
import okhttp3.Dns as Okdns
|
||||
import org.xbill.DNS.*
|
||||
import java.net.Inet6Address
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
import java.net.UnknownHostException
|
||||
import java.util.*
|
||||
import okhttp3.Dns as Okdns
|
||||
|
||||
object Dns {
|
||||
private const val TAG = "Dns"
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getDnsResolver(context: Context): String {
|
||||
val dnsResolvers = getDnsResolvers(context)
|
||||
if (dnsResolvers.isEmpty()) throw Exception("Couldn't find an active DNS resolver")
|
||||
val dnsResolver = dnsResolvers.iterator().next().toString()
|
||||
if (dnsResolver.startsWith("/")) return dnsResolver.substring(1)
|
||||
return dnsResolver
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun getDnsResolvers(context: Context): Collection<InetAddress> {
|
||||
val addresses = ArrayList<InetAddress>()
|
||||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val classLinkProperties = Class.forName("android.net.LinkProperties")
|
||||
val getActiveLinkPropertiesMethod = ConnectivityManager::class.java.getMethod("getActiveLinkProperties")
|
||||
val linkProperties = getActiveLinkPropertiesMethod.invoke(connectivityManager)
|
||||
if (linkProperties != null) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
val dnses = classLinkProperties.getMethod("getDnses").invoke(linkProperties) as Collection<*>
|
||||
dnses.mapTo(addresses) { it as InetAddress }
|
||||
} else addresses.addAll((linkProperties as LinkProperties).dnsServers)
|
||||
}
|
||||
return addresses
|
||||
}
|
||||
|
||||
private val hasIPv6Support get() = try {
|
||||
val result = NetworkInterface.getNetworkInterfaces().asSequence().flatMap { it.inetAddresses.asSequence() }
|
||||
.count { it is Inet6Address && !it.isLoopbackAddress && !it.isLinkLocalAddress } > 0
|
||||
|
||||
@@ -40,8 +40,7 @@ import java.util.*
|
||||
|
||||
/**
|
||||
* Android < 8 VPN: always invisible because of VPN notification/icon
|
||||
* Android 4.x other: always visible
|
||||
* Android 5-7 other: only invisible in (possibly unsecure) lockscreen
|
||||
* Android < 8 other: only invisible in (possibly unsecure) lockscreen
|
||||
* Android 8+: always visible due to system limitations
|
||||
* (user can choose to hide the notification in secure lockscreen or anywhere)
|
||||
*/
|
||||
@@ -85,24 +84,22 @@ class ServiceNotification(private val service: BaseService.Interface, profileNam
|
||||
if (Build.VERSION.SDK_INT < 24) builder.addAction(R.drawable.ic_navigation_close,
|
||||
service.getString(R.string.stop), PendingIntent.getBroadcast(service, 0, Intent(Action.CLOSE), 0))
|
||||
val power = service.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
update(if (if (Build.VERSION.SDK_INT >= 20) power.isInteractive else @Suppress("DEPRECATION") power.isScreenOn)
|
||||
Intent.ACTION_SCREEN_ON else Intent.ACTION_SCREEN_OFF, true)
|
||||
update(if (power.isInteractive) Intent.ACTION_SCREEN_ON else Intent.ACTION_SCREEN_OFF, true)
|
||||
val screenFilter = IntentFilter()
|
||||
screenFilter.addAction(Intent.ACTION_SCREEN_ON)
|
||||
screenFilter.addAction(Intent.ACTION_SCREEN_OFF)
|
||||
if (visible && Build.VERSION.SDK_INT in 21 until 26) screenFilter.addAction(Intent.ACTION_USER_PRESENT)
|
||||
if (visible && Build.VERSION.SDK_INT < 26) screenFilter.addAction(Intent.ACTION_USER_PRESENT)
|
||||
service.registerReceiver(lockReceiver, screenFilter)
|
||||
}
|
||||
|
||||
private fun update(action: String, forceShow: Boolean = false) {
|
||||
if (forceShow || service.data.state == BaseService.CONNECTED) when (action) {
|
||||
Intent.ACTION_SCREEN_OFF -> {
|
||||
setVisible(visible && Build.VERSION.SDK_INT < 21, forceShow)
|
||||
setVisible(false, forceShow)
|
||||
unregisterCallback() // unregister callback to save battery
|
||||
}
|
||||
Intent.ACTION_SCREEN_ON -> {
|
||||
setVisible(visible && (Build.VERSION.SDK_INT < 21 || !keyGuard.inKeyguardRestrictedInputMode()),
|
||||
forceShow)
|
||||
setVisible(visible && !keyGuard.inKeyguardRestrictedInputMode(), forceShow)
|
||||
service.data.binder.registerCallback(callback)
|
||||
service.data.binder.startListeningForBandwidth(callback)
|
||||
callbackRegistered = true
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.LocalSocket
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Log
|
||||
@@ -140,7 +139,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
|
||||
builder.addRoute("::", 0)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21 && profile.proxyApps) {
|
||||
if (profile.proxyApps) {
|
||||
val me = packageName
|
||||
profile.individual.split('\n')
|
||||
.filter { it != me }
|
||||
|
||||
@@ -22,7 +22,6 @@ package com.github.shadowsocks.tasker
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.DefaultItemAnimator
|
||||
@@ -71,13 +70,12 @@ class ConfigActivity : AppCompatActivity() {
|
||||
|
||||
inner class ProfilesAdapter : RecyclerView.Adapter<ProfileViewHolder>() {
|
||||
internal val profiles = ProfileManager.getAllProfiles()?.toMutableList() ?: mutableListOf()
|
||||
private val name = "select_dialog_singlechoice_" + (if (Build.VERSION.SDK_INT >= 21) "material" else "holo")
|
||||
|
||||
override fun onBindViewHolder(holder: ProfileViewHolder, position: Int) =
|
||||
if (position == 0) holder.bindDefault() else holder.bind(profiles[position - 1])
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileViewHolder = ProfileViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(Resources.getSystem()
|
||||
.getIdentifier(name, "layout", "android"), parent, false))
|
||||
.getIdentifier("select_dialog_singlechoice_material", "layout", "android"), parent, false))
|
||||
override fun getItemCount(): Int = 1 + profiles.size
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 400 B |
|
Before Width: | Height: | Size: 156 B |
|
Before Width: | Height: | Size: 298 B |
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/material_primary_300" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<selector android:enterFadeDuration="@android:integer/config_mediumAnimTime"
|
||||
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/material_green_700" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<!-- android:start not available until API 23 -->
|
||||
<item android:left="8dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/white" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:drawable="?android:attr/selectableItemBackground"/>
|
||||
</layer-list>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<selector android:enterFadeDuration="@android:integer/config_mediumAnimTime"
|
||||
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
|
||||
<item android:state_selected="true">
|
||||
<color android:color="@color/material_primary_100"/>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<item android:drawable="?android:attr/selectableItemBackground"/>
|
||||
</layer-list>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?android:colorControlHighlight">
|
||||
<item>
|
||||
<color android:color="@color/material_primary_300"/>
|
||||
</item>
|
||||
</ripple>
|
||||
|
Before Width: | Height: | Size: 228 B |
|
Before Width: | Height: | Size: 510 B |
|
Before Width: | Height: | Size: 284 B |
|
Before Width: | Height: | Size: 748 B |
|
Before Width: | Height: | Size: 364 B |
@@ -15,18 +15,11 @@
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<item android:left="@dimen/profile_padding">
|
||||
<!-- android:start not available until API 23 -->
|
||||
<item android:left="8dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/white" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#12000000" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<item android:drawable="?android:attr/selectableItemBackground"/>
|
||||
</layer-list>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:enterFadeDuration="@android:integer/config_mediumAnimTime"
|
||||
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
|
||||
<item android:state_selected="true">
|
||||
<color android:color="@color/material_primary_100"/>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<selector android:enterFadeDuration="@android:integer/config_mediumAnimTime"
|
||||
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
|
||||
<item android:state_selected="true">
|
||||
<color android:color="@color/material_primary_100"/>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<item android:state_pressed="true">
|
||||
<color android:color="#12000000"/>
|
||||
</item>
|
||||
</selector>
|
||||
<item android:drawable="?android:attr/selectableItemBackground"/>
|
||||
</layer-list>
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/material_primary_300" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#12000000" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
<color android:color="@color/material_primary_300"/>
|
||||
</item>
|
||||
<item android:drawable="?android:attr/selectableItemBackground"/>
|
||||
</layer-list>
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="@dimen/fab_margin_top"
|
||||
android:layout_marginTop="-28dp"
|
||||
android:elevation="6dp"
|
||||
app:layout_anchor="@id/stat"
|
||||
app:layout_anchorGravity="top|end">
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="fab_margin_top">-28dp</dimen>
|
||||
</resources>
|
||||
@@ -4,5 +4,4 @@
|
||||
<dimen name="qr_code_size">250dp</dimen>
|
||||
<dimen name="profile_padding">8dp</dimen>
|
||||
<dimen name="bottom_sheet_padding">8dp</dimen>
|
||||
<dimen name="fab_margin_top">-44dp</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -24,7 +24,6 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api "com.android.support:preference-v7:$supportLibraryVersion"
|
||||
api "com.android.support:preference-v14:$supportLibraryVersion"
|
||||
api "com.takisoft.fix:preference-v7:$takisoftFixVersion"
|
||||
api "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVersion"
|
||||
|
||||