Compare commits

..
21 Commits
Author SHA1 Message Date
Max Lv 5bce2ca8b6 bump version 2015-10-23 09:51:25 +08:00
Max Lv 58c613b365 use runner service to start the background service on Lolipop or above 2015-10-16 15:52:12 +08:00
Max Lv 446a71db93 make activity invisible again 2015-10-16 15:20:07 +08:00
Max Lv a3b2357d72 bump version 2015-10-16 14:45:58 +08:00
Max Lv 8d456e3d64 update plugin 2015-10-16 09:14:03 +08:00
Max Lv 4ff77701be update the default timeout 2015-10-13 15:40:16 +08:00
Max Lv 78e7175f7d update chnroute 2015-10-12 17:09:06 +08:00
Max Lv 0d03a09682 update plugin 2015-10-07 16:34:56 +08:00
Max Lv 62745de01b bump version 2015-10-05 20:49:44 +08:00
Max Lv bb81fe1339 fix close receiver 2015-10-05 20:47:48 +08:00
Max Lv 66d70f1545 update shadowsocks-libev 2015-10-05 16:56:52 +08:00
Max Lv ff0ddd5979 force VpnService as a foreground service 2015-10-05 16:53:22 +08:00
Max Lv bf3933bf2e Merge pull request #379 from Mygod/master
SAM (Scala 2.11.7)
2015-10-04 17:45:02 +08:00
Mygod d111bc8282 Using SAM 2015-10-04 15:13:16 +08:00
Mygod a57c06e304 Use SAM 2015-10-04 13:53:25 +08:00
Mygod 3605e18db2 Update dependencies 2015-10-04 13:43:08 +08:00
Max Lv 1ad8297278 fix #375 2015-10-03 21:06:16 +08:00
Max Lv 458f49d345 Merge pull request #376 from Mygod/master
Migrate to Android 6.0 (1)
2015-10-03 20:53:57 +08:00
Mygod 6b9b7b6749 Migrate to Android 6.0 (1) 2015-10-03 18:37:48 +08:00
Max Lv d542bafb7d bump version 2015-09-28 21:07:39 +08:00
Max Lv bbb62be458 fix a race condition 2015-09-28 21:05:41 +08:00
23 changed files with 732 additions and 588 deletions
+13 -15
View File
@@ -1,15 +1,19 @@
import android.Keys._
import android.Dependencies.{apklib,aar}
android.Plugin.androidBuild
platformTarget in Android := "android-21"
platformTarget in Android := "android-23"
name := "shadowsocks"
scalaVersion := "2.11.7"
compileOrder in Compile := CompileOrder.JavaThenScala
javacOptions ++= Seq("-source", "1.6", "-target", "1.6")
scalacOptions ++= Seq("-target:jvm-1.6", "-Xexperimental")
ndkJavah in Android := List()
ndkBuild in Android := List()
@@ -22,26 +26,20 @@ resolvers += "JRAF" at "http://JRAF.org/static/maven/2"
libraryDependencies ++= Seq(
"dnsjava" % "dnsjava" % "2.1.7",
"com.github.kevinsawicki" % "http-request" % "5.6",
"com.github.kevinsawicki" % "http-request" % "6.0",
"commons-net" % "commons-net" % "3.3",
"com.google.zxing" % "android-integration" % "3.1.0"
)
libraryDependencies ++= Seq(
"com.joanzapata.android" % "android-iconify" % "1.0.9",
"com.google.zxing" % "android-integration" % "3.1.0",
"com.joanzapata.android" % "android-iconify" % "1.0.11",
"net.glxn.qrgen" % "android" % "2.0",
"net.simonvt.menudrawer" % "menudrawer" % "3.0.6",
"com.google.android.gms" % "play-services-base" % "8.1.0",
"com.google.android.gms" % "play-services-ads" % "8.1.0",
"com.google.android.gms" % "play-services-analytics" % "8.1.0",
"com.android.support" % "support-v4" % "23.0.1"
)
libraryDependencies ++= Seq(
"com.android.support" % "support-v4" % "23.0.1",
"com.github.mrengineer13" % "snackbar" % "0.5.0",
"com.nostra13.universalimageloader" % "universal-image-loader" % "1.8.4",
"com.j256.ormlite" % "ormlite-core" % "4.47",
"com.j256.ormlite" % "ormlite-android" % "4.47"
"com.j256.ormlite" % "ormlite-core" % "4.48",
"com.j256.ormlite" % "ormlite-android" % "4.48"
)
proguardOptions in Android ++= Seq("-keep class android.support.v4.app.** { *; }",
+1 -1
View File
@@ -1,6 +1,6 @@
resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-snapshots"))(Resolver.ivyStylePatterns)
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.5.0")
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.5.6")
resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
+90 -85
View File
@@ -1,102 +1,107 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.shadowsocks"
android:versionCode="117"
android:versionName="2.8.0">
package="com.github.shadowsocks"
android:versionCode="120"
android:versionName="2.8.3">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false"/>
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false"/>
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="21"/>
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="23"/>
<application
android:allowBackup="true"
android:name=".ShadowsocksApplication"
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher"
android:backupAgent=".ShadowsocksBackupAgent"
android:label="@string/app_name">
<application
android:allowBackup="true"
android:name=".ShadowsocksApplication"
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher"
android:backupAgent=".ShadowsocksBackupAgent"
android:label="@string/app_name">
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg"/>
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg"/>
<activity
android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
<activity
android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
<activity
android:name=".Shadowsocks"
android:label="@string/app_name"
android:theme="@style/Theme.Shadowsocks"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".Shadowsocks"
android:label="@string/app_name"
android:theme="@style/Theme.Shadowsocks"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".ParserActivity"
android:theme="@style/PopupTheme"
android:launchMode="singleTask">
<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
android:name=".ParserActivity"
android:theme="@style/PopupTheme"
android:launchMode="singleTask">
<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
android:name=".ShadowsocksRunnerActivity"
android:theme="@android:style/Theme.NoDisplay"
android:launchMode="singleTask">
</activity>
<activity
android:name=".ShadowsocksRunnerActivity"
android:theme="@android:style/Theme.NoDisplay"
android:launchMode="singleTask">
</activity>
<activity
android:name=".AppManager"
android:theme="@style/Theme.AppManager"
android:label="@string/app_name"
android:parentActivityName=".Shadowsocks">
<intent-filter>
<action android:name="com.github.shadowsocks.AppManager"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name=".AppManager"
android:theme="@style/Theme.AppManager"
android:label="@string/app_name"
android:parentActivityName=".Shadowsocks">
<intent-filter>
<action android:name="com.github.shadowsocks.AppManager"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<service
android:name=".ShadowsocksNatService"
android:process=":nat"
android:exported="false">
</service>
<service
android:name=".ShadowsocksRunnerService"
android:exported="false">
</service>
<service
android:name=".ShadowsocksVpnService"
android:process=":vpn"
android:label="@string/app_name"
android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="false">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
<service
android:name=".ShadowsocksNatService"
android:process=":nat"
android:exported="false">
</service>
<receiver android:name=".ShadowsocksReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="com.github.shadowsocks.ACTION_UPDATE_STATE"/>
</intent-filter>
</receiver>
<service
android:name=".ShadowsocksVpnService"
android:process=":vpn"
android:label="@string/app_name"
android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="false">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
</application>
<receiver android:name=".ShadowsocksReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="com.github.shadowsocks.ACTION_UPDATE_STATE"/>
</intent-filter>
</receiver>
</application>
</manifest>
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.util;
/**
* Math routines similar to those found in {@link java.lang.Math}.
*
* <p>Historically these methods were faster than the equivalent double-based
* {@link java.lang.Math} methods. On versions of Android with a JIT they
* became slower and have since been re-implemented to wrap calls to
* {@link java.lang.Math}. {@link java.lang.Math} should be used in
* preference.
*
* @deprecated Use {@link java.lang.Math} instead.
*/
@Deprecated
public class FloatMath {
/** Prevents instantiation. */
private FloatMath() {}
/**
* Returns the float conversion of the most positive (i.e. closest to
* positive infinity) integer value which is less than the argument.
*
* @param value to be converted
* @return the floor of value
*/
public static float floor(float value) {
return (float) Math.floor(value);
}
/**
* Returns the float conversion of the most negative (i.e. closest to
* negative infinity) integer value which is greater than the argument.
*
* @param value to be converted
* @return the ceiling of value
*/
public static float ceil(float value) {
return (float) Math.ceil(value);
}
/**
* Returns the closest float approximation of the sine of the argument.
*
* @param angle to compute the cosine of, in radians
* @return the sine of angle
*/
public static float sin(float angle) {
return (float) Math.sin(angle);
}
/**
* Returns the closest float approximation of the cosine of the argument.
*
* @param angle to compute the cosine of, in radians
* @return the cosine of angle
*/
public static float cos(float angle) {
return (float) Math.cos(angle);
}
/**
* Returns the closest float approximation of the square root of the
* argument.
*
* @param value to compute sqrt of
* @return the square root of value
*/
public static float sqrt(float value) {
return (float) Math.sqrt(value);
}
/**
* Returns the closest float approximation of the raising "e" to the power
* of the argument.
*
* @param value to compute the exponential of
* @return the exponential of value
*/
public static float exp(float value) {
return (float) Math.exp(value);
}
/**
* Returns the closest float approximation of the result of raising {@code
* x} to the power of {@code y}.
*
* @param x the base of the operation.
* @param y the exponent of the operation.
* @return {@code x} to the power of {@code y}.
*/
public static float pow(float x, float y) {
return (float) Math.pow(x, y);
}
/**
* Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
* {@code y}</i><sup>{@code 2}</sup>{@code )}.
*
* @param x a float number
* @param y a float number
* @return the hypotenuse
*/
public static float hypot(float x, float y) {
return (float) Math.hypot(x, y);
}
}
+50
View File
@@ -388,6 +388,8 @@
<item>43.246.0.0/18</item>
<item>43.246.64.0/19</item>
<item>43.246.96.0/22</item>
<item>43.246.212.0/22</item>
<item>43.246.228.0/22</item>
<item>43.247.4.0/22</item>
<item>43.247.8.0/22</item>
<item>43.247.44.0/22</item>
@@ -655,6 +657,22 @@
<item>45.125.96.0/21</item>
<item>45.125.104.0/22</item>
<item>45.125.136.0/22</item>
<item>45.126.48.0/21</item>
<item>45.126.100.0/22</item>
<item>45.126.108.0/22</item>
<item>45.126.112.0/21</item>
<item>45.126.120.0/22</item>
<item>45.126.212.0/22</item>
<item>45.126.220.0/22</item>
<item>45.127.8.0/21</item>
<item>45.127.36.0/22</item>
<item>45.127.96.0/22</item>
<item>45.127.116.0/22</item>
<item>45.127.124.0/22</item>
<item>45.127.128.0/22</item>
<item>45.127.144.0/21</item>
<item>45.127.156.0/22</item>
<item>45.127.216.0/22</item>
<item>47.92.0.0/14</item>
<item>47.96.0.0/11</item>
<item>49.4.0.0/14</item>
@@ -699,6 +717,7 @@
<item>59.107.0.0/16</item>
<item>59.108.0.0/14</item>
<item>59.151.0.0/17</item>
<item>59.153.4.0/22</item>
<item>59.155.0.0/16</item>
<item>59.172.0.0/14</item>
<item>59.191.0.0/17</item>
@@ -844,6 +863,7 @@
<item>103.8.156.0/22</item>
<item>103.8.200.0/21</item>
<item>103.8.220.0/22</item>
<item>103.9.8.0/22</item>
<item>103.9.152.0/22</item>
<item>103.9.248.0/21</item>
<item>103.10.0.0/22</item>
@@ -971,6 +991,8 @@
<item>103.35.48.0/22</item>
<item>103.35.104.0/22</item>
<item>103.35.116.0/22</item>
<item>103.35.200.0/22</item>
<item>103.35.220.0/22</item>
<item>103.36.20.0/22</item>
<item>103.36.28.0/22</item>
<item>103.36.36.0/22</item>
@@ -1293,6 +1315,23 @@
<item>103.193.240.0/22</item>
<item>103.194.16.0/22</item>
<item>103.194.230.0/23</item>
<item>103.195.104.0/22</item>
<item>103.195.112.0/22</item>
<item>103.195.136.0/22</item>
<item>103.195.148.0/22</item>
<item>103.195.152.0/22</item>
<item>103.195.160.0/22</item>
<item>103.195.192.0/22</item>
<item>103.196.60.0/22</item>
<item>103.196.64.0/22</item>
<item>103.196.72.0/22</item>
<item>103.196.88.0/21</item>
<item>103.196.96.0/22</item>
<item>103.196.168.0/22</item>
<item>103.196.184.0/22</item>
<item>103.196.204.0/22</item>
<item>103.197.180.0/22</item>
<item>103.197.228.0/22</item>
<item>103.224.40.0/21</item>
<item>103.224.60.0/22</item>
<item>103.224.80.0/22</item>
@@ -1371,6 +1410,7 @@
<item>103.234.128.0/22</item>
<item>103.234.172.0/22</item>
<item>103.234.180.0/22</item>
<item>103.234.244.0/22</item>
<item>103.235.16.0/22</item>
<item>103.235.48.0/22</item>
<item>103.235.56.0/21</item>
@@ -1386,6 +1426,10 @@
<item>103.236.64.0/19</item>
<item>103.236.96.0/22</item>
<item>103.236.120.0/22</item>
<item>103.236.184.0/22</item>
<item>103.236.220.0/22</item>
<item>103.236.232.0/22</item>
<item>103.236.240.0/20</item>
<item>103.237.0.0/20</item>
<item>103.237.24.0/21</item>
<item>103.237.68.0/22</item>
@@ -2156,6 +2200,7 @@
<item>150.115.0.0/16</item>
<item>150.121.0.0/16</item>
<item>150.122.0.0/16</item>
<item>150.129.136.0/22</item>
<item>150.129.152.0/22</item>
<item>150.129.192.0/22</item>
<item>150.129.216.0/22</item>
@@ -2598,6 +2643,8 @@
<item>202.122.128.0/24</item>
<item>202.122.132.0/24</item>
<item>202.123.96.0/20</item>
<item>202.123.116.0/22</item>
<item>202.123.120.0/22</item>
<item>202.124.16.0/21</item>
<item>202.124.24.0/22</item>
<item>202.125.112.0/20</item>
@@ -2679,6 +2726,8 @@
<item>202.172.0.0/22</item>
<item>202.173.0.0/22</item>
<item>202.173.8.0/21</item>
<item>202.173.112.0/22</item>
<item>202.173.120.0/22</item>
<item>202.173.224.0/19</item>
<item>202.174.64.0/20</item>
<item>202.176.224.0/19</item>
@@ -3698,6 +3747,7 @@
<item>220.112.0.0/14</item>
<item>220.152.128.0/17</item>
<item>220.154.0.0/15</item>
<item>220.158.240.0/22</item>
<item>220.160.0.0/11</item>
<item>220.192.0.0/12</item>
<item>220.231.0.0/18</item>
@@ -65,7 +65,8 @@ import com.nostra13.universalimageloader.core.{DisplayImageOptions, ImageLoader,
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer
import com.github.shadowsocks.utils.{Utils, Scheme, Key}
import scala.concurrent.ops._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.language.implicitConversions
case class ProxiedApp(uid: Int, name: String, packageName: String, var proxied: Boolean)
@@ -267,9 +268,8 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
}
protected override def onCreateOptionsMenu(menu: Menu): Boolean = {
val inflater = getMenuInflater()
inflater.inflate(R.menu.app_manager_menu, menu)
return super.onCreateOptionsMenu(menu)
getMenuInflater.inflate(R.menu.app_manager_menu, menu)
super.onCreateOptionsMenu(menu)
}
protected override def onCreate(savedInstanceState: Bundle) {
@@ -277,7 +277,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
handler = new Handler()
val actionBar = getActionBar()
val actionBar = getActionBar
actionBar.setTitle(R.string.proxied_help)
actionBar.setDisplayHomeAsUpEnabled(true)
actionBar.setDisplayShowHomeEnabled(false)
@@ -297,11 +297,8 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
val bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch]
val prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext)
bypassSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener {
def onCheckedChanged(button: CompoundButton, checked: Boolean) {
prefs.edit().putBoolean(Key.isBypassApps, checked).commit()
}
})
bypassSwitch.setOnCheckedChangeListener((button: CompoundButton, checked: Boolean) =>
prefs.edit().putBoolean(Key.isBypassApps, checked).commit())
bypassSwitch.setChecked(prefs.getBoolean(Key.isBypassApps, false))
appListView = findViewById(R.id.applistview).asInstanceOf[ListView]
@@ -309,7 +306,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
protected override def onResume() {
super.onResume()
spawn {
Future {
handler.post(loadStartRunnable)
if (!appsLoaded) loadApps()
handler.post(loadFinishRunnable)
@@ -39,13 +39,11 @@
package com.github.shadowsocks
import android.os.{Handler, RemoteCallbackList}
import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback}
import com.github.shadowsocks.utils.{Path, State}
import java.io.{IOException, FileNotFoundException, FileReader, BufferedReader}
import android.util.Log
import android.app.Notification
import android.content.Context
import android.os.{Handler, RemoteCallbackList}
import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback}
import com.github.shadowsocks.utils.State
trait BaseService {
@@ -100,10 +98,10 @@ trait BaseService {
def getTag: String
def getContext: Context
def getCallbackCount(): Int = {
def getCallbackCount: Int = {
callbackCount
}
def getState(): Int = {
def getState: Int = {
state
}
def changeState(s: Int) {
@@ -112,23 +110,19 @@ trait BaseService {
protected def changeState(s: Int, msg: String) {
val handler = new Handler(getContext.getMainLooper)
handler.post(new Runnable {
override def run() {
if (state != s) {
if (callbackCount > 0) {
val n = callbacks.beginBroadcast()
for (i <- 0 to n - 1) {
try {
callbacks.getBroadcastItem(i).stateChanged(s, msg)
} catch {
case _: Exception => // Ignore
}
}
callbacks.finishBroadcast()
handler.post(() => if (state != s) {
if (callbackCount > 0) {
val n = callbacks.beginBroadcast()
for (i <- 0 to n - 1) {
try {
callbacks.getBroadcastItem(i).stateChanged(s, msg)
} catch {
case _: Exception => // Ignore
}
state = s
}
callbacks.finishBroadcast()
}
state = s
})
}
@@ -39,15 +39,15 @@
package com.github.shadowsocks
import android.os.{Message, Handler, Bundle}
import android.app.{ProgressDialog, AlertDialog, Activity}
import android.content.{Intent, DialogInterface}
import com.github.shadowsocks.database.{ProfileManager, Profile}
import com.github.shadowsocks.utils.{Parser, Action}
import android.preference.PreferenceManager
import android.view.WindowManager
import android.app.{Activity, AlertDialog, ProgressDialog}
import android.content.DialogInterface
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.{Bundle, Handler, Message}
import android.preference.PreferenceManager
import android.view.WindowManager
import com.github.shadowsocks.database.{Profile, ProfileManager}
import com.github.shadowsocks.utils.Parser
class ParserActivity extends Activity {
override def onCreate(savedInstanceState: Bundle) {
@@ -57,21 +57,17 @@ class ParserActivity extends Activity {
new AlertDialog.Builder(this)
.setTitle(R.string.add_profile_dialog)
.setCancelable(false)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, id: Int) {
.setPositiveButton(R.string.yes, ((dialog: DialogInterface, id: Int) => {
Parser.parse(data) match {
case Some(profile) => addProfile(profile)
case _ => // ignore
}
dialog.dismiss()
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, id: Int) {
}): DialogInterface.OnClickListener)
.setNegativeButton(R.string.no, ((dialog: DialogInterface, id: Int) => {
dialog.dismiss()
finish()
}
})
}): DialogInterface.OnClickListener)
.setMessage(data)
.create()
.show()
@@ -91,15 +87,13 @@ class ParserActivity extends Activity {
val h = showProgress(getString(R.string.loading))
h.postDelayed(new Runnable {
def run() {
val profileManager =
new ProfileManager(PreferenceManager.getDefaultSharedPreferences(getBaseContext),
getApplication.asInstanceOf[ShadowsocksApplication].dbHelper)
profileManager.createOrUpdateProfile(profile)
profileManager.reload(profile.id)
h.sendEmptyMessage(0)
}
h.postDelayed(() => {
val profileManager =
new ProfileManager(PreferenceManager.getDefaultSharedPreferences(getBaseContext),
getApplication.asInstanceOf[ShadowsocksApplication].dbHelper)
profileManager.createOrUpdateProfile(profile)
profileManager.reload(profile.id)
h.sendEmptyMessage(0)
}, 600)
}
@@ -38,10 +38,9 @@
*/
package com.github.shadowsocks
import java.io.{ByteArrayInputStream, ByteArrayOutputStream, FileOutputStream, IOException, InputStream, OutputStream}
import java.util
import java.io.{OutputStream, InputStream, ByteArrayInputStream, ByteArrayOutputStream, IOException, FileOutputStream}
import java.util.Locale
import java.lang.Math
import android.app.backup.BackupManager
import android.app.{Activity, AlertDialog, ProgressDialog}
@@ -52,8 +51,8 @@ import android.graphics.{Bitmap, Color, Typeface}
import android.net.{Uri, VpnService}
import android.os._
import android.preference._
import android.support.v4.content.ContextCompat
import android.util.{DisplayMetrics, Log}
import android.view.View.OnLongClickListener
import android.view._
import android.webkit.{WebView, WebViewClient}
import android.widget._
@@ -65,15 +64,15 @@ import com.github.shadowsocks.utils._
import com.google.android.gms.ads.{AdRequest, AdSize, AdView}
import com.google.android.gms.analytics.HitBuilders
import com.google.zxing.integration.android.IntentIntegrator
import com.nostra13.universalimageloader.core.download.BaseImageDownloader
import net.simonvt.menudrawer.MenuDrawer
import com.joanzapata.android.iconify.Iconify
import com.joanzapata.android.iconify.Iconify.IconValue
import com.joanzapata.android.iconify.IconDrawable
import com.joanzapata.android.iconify.Iconify.IconValue
import com.nostra13.universalimageloader.core.download.BaseImageDownloader
import net.glxn.qrgen.android.QRCode
import net.simonvt.menudrawer.MenuDrawer
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
import scala.concurrent.ops._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
class ProfileIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int)
extends BaseImageDownloader(context, connectTimeout, readTimeout) {
@@ -224,7 +223,7 @@ class Shadowsocks
if (!status.getBoolean(getVersionName, false)) {
status.edit.putBoolean(getVersionName, true).commit()
recovery();
recovery()
}
}
@@ -250,7 +249,7 @@ class Shadowsocks
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
var handler: Handler = null
var handler = new Handler()
def isSinglePane: Boolean = {
if (singlePane == -1) {
@@ -270,11 +269,7 @@ class Shadowsocks
switchButton.setChecked(checked)
if (switchButton.isEnabled) {
switchButton.setEnabled(false)
handler.postDelayed(new Runnable {
override def run() {
switchButton.setEnabled(true)
}
}, 1000)
handler.postDelayed(() => switchButton.setEnabled(true), 1000)
}
switchButton.setOnCheckedChangeListener(this)
}
@@ -396,7 +391,7 @@ class Shadowsocks
changeSwitch(checked = false)
}
def isReady(): Boolean = {
def isReady: Boolean = {
if (!checkText(Key.proxy)) return false
if (!checkText(Key.sitekey)) return false
if (!checkNumber(Key.localPort, low = false)) return false
@@ -407,7 +402,7 @@ class Shadowsocks
def prepareStartService() {
showProgress(R.string.connecting)
spawn {
Future {
if (isVpnEnabled) {
val intent = VpnService.prepare(this)
if (intent != null) {
@@ -434,11 +429,7 @@ class Shadowsocks
}
if (switchButton.isEnabled) {
switchButton.setEnabled(false)
handler.postDelayed(new Runnable {
override def run() {
switchButton.setEnabled(true)
}
}, 1000)
handler.postDelayed(() => switchButton.setEnabled(true), 1000)
}
}
}
@@ -479,8 +470,6 @@ class Shadowsocks
super.onCreate(savedInstanceState)
handler = new Handler()
addPreferencesFromResource(R.xml.pref_all)
// Update the profile
@@ -524,28 +513,26 @@ class Shadowsocks
getActionBar.setDisplayShowTitleEnabled(false)
getActionBar.setDisplayShowCustomEnabled(true)
if (Utils.isLollipopOrAbove) {
getWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
getWindow.setStatusBarColor(getResources().getColor(R.color.grey3));
getWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
getWindow.setStatusBarColor(ContextCompat.getColor(this, R.color.grey3))
getActionBar.setDisplayHomeAsUpEnabled(true)
getActionBar.setHomeAsUpIndicator(R.drawable.ic_drawer)
} else {
getActionBar.setIcon(R.drawable.ic_stat_shadowsocks)
}
title.setOnLongClickListener(new OnLongClickListener {
override def onLongClick(v: View): Boolean = {
if (Utils.isLollipopOrAbove && bgService != null
&& (bgService.getState == State.INIT || bgService.getState == State.STOPPED)) {
val natEnabled = status.getBoolean(Key.isNAT, false)
status.edit().putBoolean(Key.isNAT, !natEnabled).commit()
if (!natEnabled) {
Toast.makeText(getBaseContext, R.string.enable_nat, Toast.LENGTH_LONG).show()
} else {
Toast.makeText(getBaseContext, R.string.disable_nat, Toast.LENGTH_LONG).show()
}
true
title.setOnLongClickListener((v: View) => {
if (Utils.isLollipopOrAbove && bgService != null
&& (bgService.getState == State.INIT || bgService.getState == State.STOPPED)) {
val natEnabled = status.getBoolean(Key.isNAT, false)
status.edit().putBoolean(Key.isNAT, !natEnabled).commit()
if (!natEnabled) {
Toast.makeText(getBaseContext, R.string.enable_nat, Toast.LENGTH_LONG).show()
} else {
false
Toast.makeText(getBaseContext, R.string.disable_nat, Toast.LENGTH_LONG).show()
}
true
} else {
false
}
})
@@ -553,13 +540,11 @@ class Shadowsocks
registerReceiver(preferenceReceiver, new IntentFilter(Action.UPDATE_PREFS))
// Bind to the service
spawn {
Future {
val isRoot = (!Utils.isLollipopOrAbove || status.getBoolean(Key.isNAT, false)) && Console.isRoot
handler.post(new Runnable {
override def run() {
status.edit.putBoolean(Key.isRoot, isRoot).commit()
attachService()
}
handler.post(() => {
status.edit.putBoolean(Key.isRoot, isRoot).commit()
attachService()
})
}
}
@@ -615,29 +600,23 @@ class Shadowsocks
val builder = new AlertDialog.Builder(this)
builder
.setTitle(R.string.add_profile)
.setItems(R.array.add_profile_methods, new DialogInterface.OnClickListener() {
def onClick(dialog: DialogInterface, which: Int) {
which match {
case 0 =>
dialog.dismiss()
val h = showProgress(R.string.loading)
h.postDelayed(new Runnable() {
def run() {
val integrator = new IntentIntegrator(Shadowsocks.this)
val list = new java.util.ArrayList(IntentIntegrator.TARGET_ALL_KNOWN)
list.add("tw.com.quickmark")
integrator.setTargetApplications(list)
integrator.initiateScan()
h.sendEmptyMessage(0)
}
}, 600)
case 1 =>
dialog.dismiss()
addProfile(id)
case _ =>
}
}
})
.setItems(R.array.add_profile_methods, ((dialog: DialogInterface, which: Int) => which match {
case 0 =>
dialog.dismiss()
val h = showProgress(R.string.loading)
h.postDelayed(() => {
val integrator = new IntentIntegrator(Shadowsocks.this)
val list = new java.util.ArrayList(IntentIntegrator.TARGET_ALL_KNOWN)
list.add("tw.com.quickmark")
integrator.setTargetApplications(list)
integrator.initiateScan()
h.sendEmptyMessage(0)
}, 600)
case 1 =>
dialog.dismiss()
addProfile(id)
case _ =>
}): DialogInterface.OnClickListener)
builder.create().show()
}
@@ -646,17 +625,15 @@ class Shadowsocks
val h = showProgress(R.string.loading)
handler.postDelayed(new Runnable {
def run() {
currentProfile = {
profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile
}
menuAdapter.updateList(getMenuList, currentProfile.id)
updatePreferenceScreen()
h.sendEmptyMessage(0)
handler.postDelayed(() => {
currentProfile = {
profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile
}
menuAdapter.updateList(getMenuList, currentProfile.id)
updatePreferenceScreen()
h.sendEmptyMessage(0)
}, 600)
}
@@ -665,17 +642,15 @@ class Shadowsocks
val h = showProgress(R.string.loading)
handler.postDelayed(new Runnable {
def run() {
currentProfile = profile
profileManager.createOrUpdateProfile(currentProfile)
profileManager.reload(currentProfile.id)
menuAdapter.updateList(getMenuList, currentProfile.id)
handler.postDelayed(() => {
currentProfile = profile
profileManager.createOrUpdateProfile(currentProfile)
profileManager.reload(currentProfile.id)
menuAdapter.updateList(getMenuList, currentProfile.id)
updatePreferenceScreen()
updatePreferenceScreen()
h.sendEmptyMessage(0)
}
h.sendEmptyMessage(0)
}, 600)
}
@@ -684,16 +659,14 @@ class Shadowsocks
val h = showProgress(R.string.loading)
handler.postDelayed(new Runnable {
def run() {
currentProfile = profileManager.reload(id)
profileManager.save()
menuAdapter.updateList(getMenuList, currentProfile.id)
handler.postDelayed(() => {
currentProfile = profileManager.reload(id)
profileManager.save()
menuAdapter.updateList(getMenuList, currentProfile.id)
updatePreferenceScreen()
updatePreferenceScreen()
h.sendEmptyMessage(0)
}
h.sendEmptyMessage(0)
}, 600)
}
@@ -702,16 +675,14 @@ class Shadowsocks
val h = showProgress(R.string.loading)
handler.postDelayed(new Runnable {
def run() {
currentProfile = profileManager.reload(id)
menuAdapter.setActiveId(id)
menuAdapter.notifyDataSetChanged()
handler.postDelayed(() => {
currentProfile = profileManager.reload(id)
menuAdapter.setActiveId(id)
menuAdapter.notifyDataSetChanged()
updatePreferenceScreen()
updatePreferenceScreen()
h.sendEmptyMessage(0)
}
h.sendEmptyMessage(0)
}, 600)
}
@@ -720,20 +691,18 @@ class Shadowsocks
val profile = profileManager.getProfile(id)
if (!profile.isDefined) return false
if (profile.isEmpty) return false
new AlertDialog.Builder(this)
.setMessage(String.format(Locale.ENGLISH, getString(R.string.remove_profile), profile.get.name))
.setCancelable(false)
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, i: Int) = dialog.cancel()
})
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, i: Int) {
.setNegativeButton(R.string.no,
((dialog: DialogInterface, i: Int) => dialog.cancel()): DialogInterface.OnClickListener)
.setPositiveButton(R.string.yes, ((dialog: DialogInterface, i: Int) => {
profileManager.delProfile(id)
val profileId = {
val profiles = profileManager.getAllProfiles.getOrElse(List[Profile]())
if (profiles.isEmpty) -1 else profiles(0).id
if (profiles.isEmpty) -1 else profiles.head.id
}
currentProfile = profileManager.load(profileId)
menuAdapter.updateList(getMenuList, currentProfile.id)
@@ -741,8 +710,7 @@ class Shadowsocks
updatePreferenceScreen()
dialog.dismiss()
}
})
}): DialogInterface.OnClickListener)
.create()
.show()
@@ -901,10 +869,7 @@ class Shadowsocks
deattachService()
unregisterReceiver(preferenceReceiver)
new BackupManager(this).dataChanged()
if (handler != null) {
handler.removeCallbacksAndMessages(null)
handler = null
}
handler.removeCallbacksAndMessages(null)
}
def copyToSystem() {
@@ -939,16 +904,14 @@ class Shadowsocks
private def recovery() {
serviceStop()
val h = showProgress(R.string.recovering)
spawn {
Future {
reset()
h.sendEmptyMessage(0)
}
}
private def dp2px(dp: Int): Int = {
val displayMetrics = getBaseContext.getResources.getDisplayMetrics()
Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT))
}
private def dp2px(dp: Int): Int =
Math.round(dp * (getBaseContext.getResources.getDisplayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT))
private def showQrCode() {
val image = new ImageView(this)
@@ -960,11 +923,8 @@ class Shadowsocks
new AlertDialog.Builder(this)
.setCancelable(true)
.setNegativeButton(getString(R.string.close), new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, id: Int) {
dialog.cancel()
}
})
.setNegativeButton(getString(R.string.close),
((dialog: DialogInterface, id: Int) => dialog.cancel()): DialogInterface.OnClickListener)
.setView(image)
.create()
.show()
@@ -972,7 +932,7 @@ class Shadowsocks
private def flushDnsCache() {
val h = showProgress(R.string.flushing)
spawn {
Future {
Utils.toggleAirplaneMode(getBaseContext)
h.sendEmptyMessage(0)
}
@@ -1075,11 +1035,8 @@ class Shadowsocks
new AlertDialog.Builder(this)
.setTitle(getString(R.string.about_title).formatLocal(Locale.ENGLISH, versionName))
.setCancelable(false)
.setNegativeButton(getString(R.string.ok_iknow), new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, id: Int) {
dialog.cancel()
}
})
.setNegativeButton(getString(R.string.ok_iknow),
((dialog: DialogInterface, id: Int) => dialog.cancel()): DialogInterface.OnClickListener)
.setView(web)
.create()
.show()
@@ -1094,46 +1051,42 @@ class Shadowsocks
}
def onStateChanged(s: Int, m: String) {
handler.post(new Runnable {
override def run() {
if (state != s) {
state = s
state match {
case State.CONNECTING =>
if (progressDialog == null) {
progressDialog = ProgressDialog
.show(Shadowsocks.this, "", getString(R.string.connecting), true, true)
progressTag = R.string.connecting
}
setPreferenceEnabled(enabled = false)
case State.CONNECTED =>
if (progressTag == R.string.connecting) {
clearDialog()
}
changeSwitch(checked = true)
setPreferenceEnabled(enabled = false)
case State.STOPPED =>
if (progressTag == R.string.stopping || progressTag == R.string.connecting) {
clearDialog()
}
changeSwitch(checked = false)
if (m != null) {
new SnackBar.Builder(Shadowsocks.this)
.withMessage(getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m))
.withActionMessageId(R.string.error)
.withStyle(SnackBar.Style.ALERT)
.withDuration(SnackBar.LONG_SNACK)
.show()
}
setPreferenceEnabled(enabled = true)
case State.STOPPING =>
if (progressDialog == null) {
progressDialog = ProgressDialog
.show(Shadowsocks.this, "", getString(R.string.stopping), true, true)
progressTag = R.string.stopping
}
handler.post(() => if (state != s) {
state = s
state match {
case State.CONNECTING =>
if (progressDialog == null) {
progressDialog = ProgressDialog
.show(Shadowsocks.this, "", getString(R.string.connecting), true, true)
progressTag = R.string.connecting
}
setPreferenceEnabled(enabled = false)
case State.CONNECTED =>
if (progressTag == R.string.connecting) {
clearDialog()
}
changeSwitch(checked = true)
setPreferenceEnabled(enabled = false)
case State.STOPPED =>
if (progressTag == R.string.stopping || progressTag == R.string.connecting) {
clearDialog()
}
changeSwitch(checked = false)
if (m != null) {
new SnackBar.Builder(Shadowsocks.this)
.withMessage(getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m))
.withActionMessageId(R.string.error)
.withStyle(SnackBar.Style.ALERT)
.withDuration(SnackBar.LONG_SNACK)
.show()
}
setPreferenceEnabled(enabled = true)
case State.STOPPING =>
if (progressDialog == null) {
progressDialog = ProgressDialog
.show(Shadowsocks.this, "", getString(R.string.stopping), true, true)
progressTag = R.string.stopping
}
}
}
})
}
@@ -47,7 +47,6 @@ import com.github.shadowsocks.database.DBHelper
import com.github.shadowsocks.utils.Utils
import com.google.android.gms.analytics.GoogleAnalytics
import com.google.android.gms.common.api.ResultCallback
import com.google.android.gms.tagmanager.Container.FunctionCallMacroCallback
import com.google.android.gms.tagmanager.{ContainerHolder, TagManager}
class ShadowsocksApplication extends Application {
@@ -66,14 +65,13 @@ class ShadowsocksApplication extends Application {
}
containerHolder = holder
val container = holder.getContainer
container.registerFunctionCallMacroCallback(SIG_FUNC, new FunctionCallMacroCallback {
override def getValue(functionName: String, parameters: util.Map[String, AnyRef]): AnyRef = {
container.registerFunctionCallMacroCallback(SIG_FUNC,
(functionName: String, parameters: util.Map[String, AnyRef]) => {
if (functionName == SIG_FUNC) {
Utils.getSignature(getApplicationContext)
}
null
}
})
})
}
}
pending.setResultCallback(callback, 2, TimeUnit.SECONDS)
@@ -41,6 +41,7 @@ package com.github.shadowsocks
import java.io.File
import java.lang.reflect.{InvocationTargetException, Method}
import java.net.{Inet6Address, InetAddress}
import java.util.Locale
import android.app._
@@ -54,11 +55,11 @@ import android.widget.Toast
import com.github.shadowsocks.aidl.Config
import com.github.shadowsocks.utils._
import com.google.android.gms.analytics.HitBuilders
import org.apache.http.conn.util.InetAddressUtils
import scala.collection._
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ops._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
class ShadowsocksNatService extends Service with BaseService {
@@ -68,25 +69,13 @@ class ShadowsocksNatService extends Service with BaseService {
val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " +
"-j DNAT --to-destination 127.0.0.1:8123"
private val mStartForegroundSignature = Array[Class[_]](classOf[Int], classOf[Notification])
private val mStopForegroundSignature = Array[Class[_]](classOf[Boolean])
private val mSetForegroundSignature = Array[Class[_]](classOf[Boolean])
private val mSetForegroundArgs = new Array[AnyRef](1)
private val mStartForegroundArgs = new Array[AnyRef](2)
private val mStopForegroundArgs = new Array[AnyRef](1)
var lockReceiver: BroadcastReceiver = null
var closeReceiver: BroadcastReceiver = null
var connReceiver: BroadcastReceiver = null
var notificationManager: NotificationManager = null
var config: Config = null
var apps: Array[ProxiedApp] = null
val myUid = Process.myUid()
private var mSetForeground: Method = null
private var mStartForeground: Method = null
private var mStopForeground: Method = null
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
private val dnsAddressCache = new SparseArray[String]
@@ -172,11 +161,7 @@ class ShadowsocksNatService extends Service with BaseService {
def initConnectionReceiver() {
val filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
connReceiver = new BroadcastReceiver {
override def onReceive(context: Context, intent: Intent) = {
setupDns()
}
}
connReceiver = (context: Context, intent: Intent) => setupDns()
registerReceiver(connReceiver, filter)
}
@@ -193,7 +178,7 @@ class ShadowsocksNatService extends Service with BaseService {
val conf = ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort,
config.sitekey, config.encMethod, 10)
config.sitekey, config.encMethod, 600)
ConfigUtils.printToFile(new File(Path.BASE + "ss-local-nat.conf"))(p => {
p.println(conf)
})
@@ -324,17 +309,6 @@ class ShadowsocksNatService extends Service with BaseService {
true
}
def invokeMethod(method: Method, args: Array[AnyRef]) {
try {
method.invoke(this, mStartForegroundArgs: _*)
} catch {
case e: InvocationTargetException =>
Log.w(TAG, "Unable to invoke method", e)
case e: IllegalAccessException =>
Log.w(TAG, "Unable to invoke method", e)
}
}
def notifyForegroundAlert(title: String, info: String, visible: Boolean) {
val openIntent = new Intent(this, classOf[Shadowsocks])
openIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
@@ -358,7 +332,7 @@ class ShadowsocksNatService extends Service with BaseService {
else
builder.setPriority(NotificationCompat.PRIORITY_MIN)
startForegroundCompat(1, builder.build)
startForeground(1, builder.build)
}
def onBind(intent: Intent): IBinder = {
@@ -372,29 +346,7 @@ class ShadowsocksNatService extends Service with BaseService {
override def onCreate() {
super.onCreate()
ConfigUtils.refresh(this)
notificationManager = this
.getSystemService(Context.NOTIFICATION_SERVICE)
.asInstanceOf[NotificationManager]
try {
mStartForeground = getClass.getMethod("startForeground", mStartForegroundSignature: _*)
mStopForeground = getClass.getMethod("stopForeground", mStopForegroundSignature: _*)
} catch {
case e: NoSuchMethodException =>
mStartForeground = {
mStopForeground = null
mStopForeground
}
}
try {
mSetForeground = getClass.getMethod("setForeground", mSetForegroundSignature: _*)
} catch {
case e: NoSuchMethodException =>
throw new IllegalStateException(
"OS doesn't have Service.startForeground OR Service.setForeground!")
}
}
def killProcesses() {
@@ -430,7 +382,7 @@ class ShadowsocksNatService extends Service with BaseService {
init_sb.append(Utils.getIptables + " -t nat -F OUTPUT")
val cmd_bypass = Utils.getIptables + CMD_IPTABLES_RETURN
if (!InetAddressUtils.isIPv6Address(config.proxy.toUpperCase)) {
if (!InetAddress.getByName(config.proxy.toUpperCase).isInstanceOf[Inet6Address]) {
init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d " + config.proxy))
}
init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d 127.0.0.1"))
@@ -465,44 +417,6 @@ class ShadowsocksNatService extends Service with BaseService {
Console.runRootCommand(http_sb.toArray)
}
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
def startForegroundCompat(id: Int, notification: Notification) {
if (mStartForeground != null) {
mStartForegroundArgs(0) = int2Integer(id)
mStartForegroundArgs(1) = notification
invokeMethod(mStartForeground, mStartForegroundArgs)
return
}
mSetForegroundArgs(0) = boolean2Boolean(x = true)
invokeMethod(mSetForeground, mSetForegroundArgs)
notificationManager.notify(id, notification)
}
/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
def stopForegroundCompat(id: Int) {
if (mStopForeground != null) {
mStopForegroundArgs(0) = boolean2Boolean(x = true)
try {
mStopForeground.invoke(this, mStopForegroundArgs: _*)
} catch {
case e: InvocationTargetException =>
Log.w(TAG, "Unable to invoke stopForeground", e)
case e: IllegalAccessException =>
Log.w(TAG, "Unable to invoke stopForeground", e)
}
return
}
notificationManager.cancel(id)
mSetForegroundArgs(0) = boolean2Boolean(x = false)
invokeMethod(mSetForeground, mSetForegroundArgs)
}
override def startRunner(c: Config) {
config = c
@@ -511,11 +425,9 @@ class ShadowsocksNatService extends Service with BaseService {
val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN)
filter.addAction(Action.CLOSE)
closeReceiver = new BroadcastReceiver() {
def onReceive(context: Context, intent: Intent) {
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
stopRunner()
}
closeReceiver = (context: Context, intent: Intent) => {
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
stopRunner()
}
registerReceiver(closeReceiver, filter)
@@ -524,26 +436,22 @@ class ShadowsocksNatService extends Service with BaseService {
screenFilter.addAction(Intent.ACTION_SCREEN_ON)
screenFilter.addAction(Intent.ACTION_SCREEN_OFF)
screenFilter.addAction(Intent.ACTION_USER_PRESENT)
lockReceiver = new BroadcastReceiver() {
def onReceive(context: Context, intent: Intent) {
if (getState == State.CONNECTED) {
val action = intent.getAction
if (action == Intent.ACTION_SCREEN_OFF) {
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false)
} else if (action == Intent.ACTION_SCREEN_ON) {
val keyGuard = getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager]
if (!keyGuard.inKeyguardRestrictedInputMode) {
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true)
}
} else if (action == Intent.ACTION_USER_PRESENT) {
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true)
}
}
lockReceiver = (context: Context, intent: Intent) => if (getState == State.CONNECTED) {
val action = intent.getAction
if (action == Intent.ACTION_SCREEN_OFF) {
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false)
} else if (action == Intent.ACTION_SCREEN_ON) {
val keyGuard = getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager]
if (!keyGuard.inKeyguardRestrictedInputMode) {
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true)
}
} else if (action == Intent.ACTION_USER_PRESENT) {
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true)
}
}
registerReceiver(lockReceiver, screenFilter)
}
@@ -556,7 +464,7 @@ class ShadowsocksNatService extends Service with BaseService {
changeState(State.CONNECTING)
spawn {
Future {
if (config.proxy == "198.199.101.152") {
val holder = application.containerHolder
@@ -576,8 +484,7 @@ class ShadowsocksNatService extends Service with BaseService {
killProcesses()
var resolved: Boolean = false
if (!InetAddressUtils.isIPv4Address(config.proxy) &&
!InetAddressUtils.isIPv6Address(config.proxy)) {
if (!Utils.isNumeric(config.proxy)) {
Utils.resolve(config.proxy, enableIPv6 = true) match {
case Some(a) =>
config.proxy = a
@@ -637,7 +544,7 @@ class ShadowsocksNatService extends Service with BaseService {
stopSelf()
}
stopForegroundCompat(1)
stopForeground(true)
// change the state
changeState(State.STOPPED)
@@ -65,10 +65,15 @@ class ShadowsocksReceiver extends BroadcastReceiver {
val isAutoConnect: Boolean = settings.getBoolean(Key.isAutoConnect, false)
val isInstalled: Boolean = status.getBoolean(versionName, false)
if (isAutoConnect && isInstalled) {
val intent = new Intent(context, classOf[ShadowsocksRunnerActivity])
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
context.startActivity(intent)
if (Utils.isLollipopOrAbove) {
val intent = new Intent(context, classOf[ShadowsocksRunnerService])
context.startService(intent)
} else {
val intent = new Intent(context, classOf[ShadowsocksRunnerActivity])
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
context.startActivity(intent)
}
}
}
}
@@ -57,11 +57,7 @@ class ShadowsocksRunnerActivity extends Activity {
val connection = new ServiceConnection {
override def onServiceConnected(name: ComponentName, service: IBinder) {
bgService = IShadowsocksService.Stub.asInterface(service)
handler.postDelayed(new Runnable() {
override def run() {
if (bgService != null) startBackgroundService()
}
}, 1000)
handler.postDelayed(() => if (bgService != null) startBackgroundService(), 1000)
}
override def onServiceDisconnected(name: ComponentName) {
bgService = null
@@ -122,11 +118,9 @@ class ShadowsocksRunnerActivity extends Activity {
val locked = km.inKeyguardRestrictedInputMode
if (locked) {
val filter = new IntentFilter(Intent.ACTION_USER_PRESENT)
receiver = new BroadcastReceiver() {
override def onReceive(context: Context, intent: Intent) {
if (intent.getAction == Intent.ACTION_USER_PRESENT) {
attachService()
}
receiver = (context: Context, intent: Intent) => {
if (intent.getAction == Intent.ACTION_USER_PRESENT) {
attachService()
}
}
registerReceiver(receiver, filter)
@@ -0,0 +1,125 @@
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2014 <max.c.lv@gmail.com>
*
* 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/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (oo) //###############-
* -#################\\ / VV \ //#################-
* -###################\\/ \//###################-
* _#/|##########/\######( /\ )######/\##########|\#_
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
* ` ` ` ` / | | | | \ ' ' ' '
* ( | | | | )
* __\ | | | | /__
* (vvv(VVV)(VVV)vvv)
*
* HERE BE DRAGONS
*
*/
package com.github.shadowsocks
import android.app.Service
import android.content._
import android.net.VpnService
import android.os._
import android.preference.PreferenceManager
import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.utils._
class ShadowsocksRunnerService extends Service {
private lazy val settings = PreferenceManager.getDefaultSharedPreferences(this)
private lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE)
val handler = new Handler()
val connection = new ServiceConnection {
override def onServiceConnected(name: ComponentName, service: IBinder) {
bgService = IShadowsocksService.Stub.asInterface(service)
handler.postDelayed(() => if (bgService != null) startBackgroundService(), 1000)
}
override def onServiceDisconnected(name: ComponentName) {
bgService = null
}
}
// Variables
var vpnEnabled = -1
var bgService: IShadowsocksService = null
def isVpnEnabled: Boolean = {
if (vpnEnabled < 0) {
vpnEnabled = if ((!Utils.isLollipopOrAbove || status.getBoolean(Key.isNAT, false)) && Console.isRoot) {
0
} else {
1
}
}
if (vpnEnabled == 1) true else false
}
override def onBind(intent: Intent): IBinder = {
null
}
def startBackgroundService() {
if (isVpnEnabled) {
val intent = VpnService.prepare(ShadowsocksRunnerService.this)
if (intent == null) {
if (bgService != null) {
bgService.start(ConfigUtils.load(settings))
}
}
} else {
bgService.start(ConfigUtils.load(settings))
}
stopSelf()
}
def attachService() {
if (bgService == null) {
val s = if (!isVpnEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService]
val intent = new Intent(this, s)
intent.setAction(Action.SERVICE)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
startService(new Intent(this, s))
}
}
def deattachService() {
if (bgService != null) {
unbindService(connection)
bgService = null
}
}
override def onCreate() {
super.onCreate()
attachService()
}
override def onDestroy() {
super.onDestroy()
deattachService()
}
}
@@ -47,17 +47,18 @@ import android.content._
import android.content.pm.{PackageInfo, PackageManager}
import android.net.VpnService
import android.os._
import android.support.v4.app.NotificationCompat
import android.util.Log
import android.widget.Toast
import com.github.shadowsocks.aidl.Config
import com.github.shadowsocks.utils._
import com.google.android.gms.analytics.HitBuilders
import org.apache.commons.net.util.SubnetUtils
import org.apache.http.conn.util.InetAddressUtils
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ops._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
class ShadowsocksVpnService extends VpnService with BaseService {
@@ -67,11 +68,10 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val PRIVATE_VLAN = "26.26.26.%s"
val PRIVATE_VLAN6 = "fdfe:dcba:9876::%s"
var conn: ParcelFileDescriptor = null
var notificationManager: NotificationManager = null
var receiver: BroadcastReceiver = null
var apps: Array[ProxiedApp] = null
var config: Config = null
var vpnThread: ShadowsocksVpnThread = null
var closeReceiver: BroadcastReceiver = null
def isByass(net: SubnetUtils): Boolean = {
val info = net.getInfo
@@ -104,15 +104,34 @@ class ShadowsocksVpnService extends VpnService with BaseService {
null
}
def notifyForegroundAlert(title: String, info: String, visible: Boolean) {
val openIntent = new Intent(this, classOf[Shadowsocks])
val contentIntent = PendingIntent.getActivity(this, 0, openIntent, 0)
val closeIntent = new Intent(Action.CLOSE)
val actionIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0)
val builder = new NotificationCompat.Builder(this)
builder
.setWhen(0)
.setTicker(title)
.setContentTitle(getString(R.string.app_name))
.setContentText(info)
.setContentIntent(contentIntent)
.setSmallIcon(R.drawable.ic_stat_shadowsocks)
.addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop),
actionIntent)
if (visible)
builder.setPriority(NotificationCompat.PRIORITY_DEFAULT)
else
builder.setPriority(NotificationCompat.PRIORITY_MIN)
startForeground(1, builder.build)
}
override def onCreate() {
super.onCreate()
ConfigUtils.refresh(this)
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE)
.asInstanceOf[NotificationManager]
}
override def onRevoke() {
@@ -126,6 +145,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
vpnThread = null
}
stopForeground(true)
// channge the state
changeState(State.STOPPING)
@@ -150,10 +171,10 @@ class ShadowsocksVpnService extends VpnService with BaseService {
stopSelf()
}
// clean up the context
if (receiver != null) {
unregisterReceiver(receiver)
receiver = null
// clean up recevier
if (closeReceiver != null) {
unregisterReceiver(closeReceiver)
closeReceiver = null
}
// channge the state
@@ -190,6 +211,16 @@ class ShadowsocksVpnService extends VpnService with BaseService {
config = c
// register close receiver
val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN)
filter.addAction(Action.CLOSE)
closeReceiver = (context: Context, intent: Intent) => {
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
stopRunner()
}
registerReceiver(closeReceiver, filter)
// ensure the VPNService is prepared
if (VpnService.prepare(this) != null) {
val i = new Intent(this, classOf[ShadowsocksRunnerActivity])
@@ -205,20 +236,9 @@ class ShadowsocksVpnService extends VpnService with BaseService {
.setLabel(getVersionName)
.build())
// register close receiver
val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN)
receiver = new BroadcastReceiver {
def onReceive(p1: Context, p2: Intent) {
Toast.makeText(p1, R.string.stopping, Toast.LENGTH_SHORT)
stopRunner()
}
}
registerReceiver(receiver, filter)
changeState(State.CONNECTING)
spawn {
Future {
if (config.proxy == "198.199.101.152") {
val holder = getApplication.asInstanceOf[ShadowsocksApplication].containerHolder
try {
@@ -238,8 +258,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
// Resolve the server address
var resolved: Boolean = false
if (!InetAddressUtils.isIPv4Address(config.proxy) &&
!InetAddressUtils.isIPv6Address(config.proxy)) {
if (!Utils.isNumeric(config.proxy)) {
Utils.resolve(config.proxy, enableIPv6 = true) match {
case Some(addr) =>
config.proxy = addr
@@ -251,6 +270,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
if (resolved && handleConnection) {
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false)
changeState(State.CONNECTED)
} else {
changeState(State.STOPPED, getString(R.string.service_failed))
@@ -297,7 +318,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val conf = ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort,
config.sitekey, config.encMethod, 10)
config.sitekey, config.encMethod, 600)
ConfigUtils.printToFile(new File(Path.BASE + "ss-local-vpn.conf"))(p => {
p.println(conf)
})
@@ -454,7 +475,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
Console.runCommand(cmd)
return fd
fd
}
override def stopBackgroundService() {
@@ -93,35 +93,34 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
try {
val socket = serverSocket.accept()
pool.execute(new Runnable {
override def run() {
try {
val input = socket.getInputStream
val output = socket.getOutputStream
pool.execute(() => {
try {
val input = socket.getInputStream
val output = socket.getOutputStream
input.read()
input.read()
val fds = socket.getAncillaryFileDescriptors
val fds = socket.getAncillaryFileDescriptors
if (fds.length > 0) {
var ret = false
if (fds.nonEmpty) {
var ret = false
val getInt = classOf[FileDescriptor].getDeclaredMethod("getInt$")
val fd = getInt.invoke(fds(0)).asInstanceOf[Int]
ret = vpnService.protect(fd)
val getInt = classOf[FileDescriptor].getDeclaredMethod("getInt$")
val fd = getInt.invoke(fds(0)).asInstanceOf[Int]
ret = vpnService.protect(fd)
// Trick to close file decriptor
System.jniclose(fd)
// Trick to close file decriptor
System.jniclose(fd)
if (ret) {
output.write(0)
} else {
output.write(1)
}
input.close()
output.close()
if (ret) {
output.write(0)
} else {
output.write(1)
}
input.close()
output.close()
}
} catch {
case e: Exception =>
Log.e(TAG, "Error when protect socket", e)
@@ -134,12 +133,11 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
case _: Exception => // ignore
}
}})
})
} catch {
case e: IOException => {
case e: IOException =>
Log.e(TAG, "Error when accept socket", e)
return
}
}
}
}
@@ -40,14 +40,10 @@
package com.github.shadowsocks.database
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.TextView
import android.graphics.drawable.Drawable
import com.github.shadowsocks.{R}
import android.view.View.{OnLongClickListener, OnClickListener}
import android.view.{LayoutInflater, View, ViewGroup}
import android.widget.{BaseAdapter, TextView}
import com.github.shadowsocks.R
abstract class Item {
def id: Int
@@ -147,17 +143,13 @@ class MenuAdapter(context: Context, var items: List[Any]) extends BaseAdapter {
}
}
tv.setOnClickListener(new OnClickListener {
def onClick(view: View) {
val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
item.click(item.id)
}
tv.setOnClickListener((view: View) => {
val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
item.click(item.id)
})
tv.setOnLongClickListener(new OnLongClickListener {
def onLongClick(view: View): Boolean = {
val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
item.longClick(item.id)
}
tv.setOnLongClickListener((view: View) => {
val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
item.longClick(item.id)
})
v.setTag(R.id.mdItem, value)
@@ -39,9 +39,9 @@
package com.github.shadowsocks.database
import android.content.SharedPreferences
import android.util.Log
import com.github.shadowsocks._
import android.content.{SharedPreferences, Context}
import com.github.shadowsocks.utils.Key
class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
@@ -233,8 +233,8 @@ object ConfigUtils {
.readTimeout(2000)
.send("sig="+sig)
.body
val proxies = util.Random.shuffle(list.split('|').toSeq).toSeq
val proxy = proxies(0).split(':')
val proxies = util.Random.shuffle(list.split('|').toSeq)
val proxy = proxies.head.split(':')
val host = proxy(0).trim
val port = proxy(1).trim.toInt
@@ -39,10 +39,11 @@
package com.github.shadowsocks.utils
import eu.chainfire.libsuperuser.Shell
import eu.chainfire.libsuperuser.Shell.{OnCommandResultListener, SU, Builder}
import java.util
import eu.chainfire.libsuperuser.Shell
import eu.chainfire.libsuperuser.Shell.{Builder, SU}
object Console {
private def openShell(): Shell.Interactive = {
@@ -68,14 +69,8 @@ object Console {
def runCommand(commands: Array[String]) {
val shell = openShell()
shell.addCommand(commands, 0, new OnCommandResultListener {
override def onCommandResult(commandCode: Int, exitCode: Int,
output: util.List[String]) {
if (exitCode < 0) {
shell.close()
}
}
})
shell.addCommand(commands, 0, ((commandCode: Int, exitCode: Int, output: util.List[String]) =>
if (exitCode < 0) shell.close()): Shell.OnCommandResultListener)
shell.waitForIdle()
shell.close()
}
@@ -90,17 +85,14 @@ object Console {
}
val shell = openRootShell(context)
val sb = new StringBuilder
shell.addCommand(commands, 0, new OnCommandResultListener {
override def onCommandResult(commandCode: Int, exitCode: Int,
output: util.List[String]) {
if (exitCode < 0) {
shell.close()
} else {
import scala.collection.JavaConversions._
output.foreach(line => sb.append(line).append('\n'))
}
shell.addCommand(commands, 0, ((commandCode: Int, exitCode: Int, output: util.List[String]) => {
if (exitCode < 0) {
shell.close()
} else {
import scala.collection.JavaConversions._
output.foreach(line => sb.append(line).append('\n'))
}
})
}): Shell.OnCommandResultListener)
if (shell.waitForIdle()) {
shell.close()
sb.toString()
@@ -47,11 +47,8 @@ import android.util.{Log, Base64}
object Parser {
val TAG = "ShadowParser"
def generate (profile: Profile): String = {
val path = "%s:%s@%s:%d".formatLocal(Locale.ENGLISH,
profile.method, profile.password, profile.host, profile.remotePort)
return "ss://" + Base64.encodeToString(path.getBytes, Base64.NO_PADDING)
}
def generate (profile: Profile): String = "ss://" + Base64.encodeToString("%s:%s@%s:%d".formatLocal(Locale.ENGLISH,
profile.method, profile.password, profile.host, profile.remotePort).getBytes, Base64.NO_PADDING)
def parse (data: String): Option[Profile] = {
try {
@@ -38,22 +38,21 @@
*/
package com.github.shadowsocks.utils
import android.content.{Intent, Context}
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.{BitmapDrawable, Drawable}
import android.util.{Base64, Log}
import java.io._
import java.net.{UnknownHostException, InetAddress, NetworkInterface}
import org.apache.http.conn.util.InetAddressUtils
import org.xbill.DNS._
import android.graphics._
import java.net._
import java.security.MessageDigest
import android.app.ActivityManager
import android.content.pm.{ApplicationInfo, PackageManager}
import android.content.{Context, Intent}
import android.graphics._
import android.graphics.drawable.{BitmapDrawable, Drawable}
import android.os.Build
import android.provider.Settings
import scala.Some
import java.security.MessageDigest
import com.github.shadowsocks.{BuildConfig}
import android.support.v4.content.ContextCompat
import android.util.{Base64, Log}
import com.github.shadowsocks.BuildConfig
import org.xbill.DNS._
object Utils {
@@ -147,6 +146,7 @@ object Utils {
// + "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false\n")
}
//noinspection ScalaDeprecation
private def toggleBelowApiLevel17(context: Context) {
// Android 4.2 below
Settings.System.putInt(context.getContentResolver, Settings.System.AIRPLANE_MODE_ON, 1)
@@ -198,7 +198,7 @@ object Utils {
}
}
} catch {
case e: Exception => None
case e: Exception =>
}
None
}
@@ -233,6 +233,9 @@ object Utils {
None
}
private lazy val isNumericMethod = classOf[InetAddress].getMethod("isNumeric", classOf[String])
def isNumeric(address: String): Boolean = isNumericMethod.invoke(null, address).asInstanceOf[Boolean]
/**
* Get local IPv4 address
*/
@@ -245,9 +248,8 @@ object Utils {
while (addrs.hasMoreElements) {
val addr = addrs.nextElement()
if (!addr.isLoopbackAddress && !addr.isLinkLocalAddress) {
val sAddr = addr.getHostAddress.toUpperCase
if (InetAddressUtils.isIPv4Address(sAddr)) {
return Some(sAddr)
if (addr.isInstanceOf[Inet4Address]) {
return Some(addr.getHostAddress.toUpperCase)
}
}
}
@@ -271,8 +273,7 @@ object Utils {
while (addrs.hasMoreElements) {
val addr = addrs.nextElement()
if (!addr.isLoopbackAddress && !addr.isLinkLocalAddress) {
val sAddr = addr.getHostAddress.toUpperCase
if (InetAddressUtils.isIPv6Address(sAddr)) {
if (addr.isInstanceOf[Inet6Address]) {
if (BuildConfig.DEBUG) Log.d(TAG, "IPv6 address detected")
return true
}
@@ -338,7 +339,7 @@ object Utils {
def getAppIcon(c: Context, packageName: String): Drawable = {
val pm: PackageManager = c.getPackageManager
val icon: Drawable = c.getResources.getDrawable(android.R.drawable.sym_def_app_icon)
val icon: Drawable = ContextCompat.getDrawable(c, android.R.drawable.sym_def_app_icon)
try {
pm.getApplicationIcon(packageName)
} catch {
@@ -348,7 +349,7 @@ object Utils {
def getAppIcon(c: Context, uid: Int): Drawable = {
val pm: PackageManager = c.getPackageManager
val icon: Drawable = c.getResources.getDrawable(android.R.drawable.sym_def_app_icon)
val icon: Drawable = ContextCompat.getDrawable(c, android.R.drawable.sym_def_app_icon)
val packages: Array[String] = pm.getPackagesForUid(uid)
if (packages != null) {
if (packages.length >= 1) {