Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66d70f1545 | ||
|
|
ff0ddd5979 | ||
|
|
bf3933bf2e | ||
|
|
d111bc8282 | ||
|
|
a57c06e304 | ||
|
|
3605e18db2 | ||
|
|
1ad8297278 | ||
|
|
458f49d345 | ||
|
|
6b9b7b6749 | ||
|
|
d542bafb7d | ||
|
|
bbb62be458 |
@@ -1,15 +1,19 @@
|
|||||||
import android.Keys._
|
import android.Keys._
|
||||||
|
|
||||||
import android.Dependencies.{apklib,aar}
|
|
||||||
|
|
||||||
android.Plugin.androidBuild
|
android.Plugin.androidBuild
|
||||||
|
|
||||||
platformTarget in Android := "android-21"
|
platformTarget in Android := "android-23"
|
||||||
|
|
||||||
name := "shadowsocks"
|
name := "shadowsocks"
|
||||||
|
|
||||||
|
scalaVersion := "2.11.7"
|
||||||
|
|
||||||
compileOrder in Compile := CompileOrder.JavaThenScala
|
compileOrder in Compile := CompileOrder.JavaThenScala
|
||||||
|
|
||||||
|
javacOptions ++= Seq("-source", "1.6", "-target", "1.6")
|
||||||
|
|
||||||
|
scalacOptions ++= Seq("-target:jvm-1.6", "-Xexperimental")
|
||||||
|
|
||||||
ndkJavah in Android := List()
|
ndkJavah in Android := List()
|
||||||
|
|
||||||
ndkBuild in Android := List()
|
ndkBuild in Android := List()
|
||||||
@@ -22,26 +26,20 @@ resolvers += "JRAF" at "http://JRAF.org/static/maven/2"
|
|||||||
|
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"dnsjava" % "dnsjava" % "2.1.7",
|
"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",
|
"commons-net" % "commons-net" % "3.3",
|
||||||
"com.google.zxing" % "android-integration" % "3.1.0"
|
"com.google.zxing" % "android-integration" % "3.1.0",
|
||||||
)
|
"com.joanzapata.android" % "android-iconify" % "1.0.11",
|
||||||
|
|
||||||
libraryDependencies ++= Seq(
|
|
||||||
"com.joanzapata.android" % "android-iconify" % "1.0.9",
|
|
||||||
"net.glxn.qrgen" % "android" % "2.0",
|
"net.glxn.qrgen" % "android" % "2.0",
|
||||||
"net.simonvt.menudrawer" % "menudrawer" % "3.0.6",
|
"net.simonvt.menudrawer" % "menudrawer" % "3.0.6",
|
||||||
"com.google.android.gms" % "play-services-base" % "8.1.0",
|
"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-ads" % "8.1.0",
|
||||||
"com.google.android.gms" % "play-services-analytics" % "8.1.0",
|
"com.google.android.gms" % "play-services-analytics" % "8.1.0",
|
||||||
"com.android.support" % "support-v4" % "23.0.1"
|
"com.android.support" % "support-v4" % "23.0.1",
|
||||||
)
|
|
||||||
|
|
||||||
libraryDependencies ++= Seq(
|
|
||||||
"com.github.mrengineer13" % "snackbar" % "0.5.0",
|
"com.github.mrengineer13" % "snackbar" % "0.5.0",
|
||||||
"com.nostra13.universalimageloader" % "universal-image-loader" % "1.8.4",
|
"com.nostra13.universalimageloader" % "universal-image-loader" % "1.8.4",
|
||||||
"com.j256.ormlite" % "ormlite-core" % "4.47",
|
"com.j256.ormlite" % "ormlite-core" % "4.48",
|
||||||
"com.j256.ormlite" % "ormlite-android" % "4.47"
|
"com.j256.ormlite" % "ormlite-android" % "4.48"
|
||||||
)
|
)
|
||||||
|
|
||||||
proguardOptions in Android ++= Seq("-keep class android.support.v4.app.** { *; }",
|
proguardOptions in Android ++= Seq("-keep class android.support.v4.app.** { *; }",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-snapshots"))(Resolver.ivyStylePatterns)
|
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.1")
|
||||||
|
|
||||||
resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.github.shadowsocks"
|
package="com.github.shadowsocks"
|
||||||
android:versionCode="117"
|
android:versionCode="118"
|
||||||
android:versionName="2.8.0">
|
android:versionName="2.8.1">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="16"
|
android:minSdkVersion="16"
|
||||||
android:targetSdkVersion="21"/>
|
android:targetSdkVersion="23"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Submodule src/main/jni/shadowsocks-libev updated: ad4fdeb364...095aa7490c
@@ -65,7 +65,8 @@ import com.nostra13.universalimageloader.core.{DisplayImageOptions, ImageLoader,
|
|||||||
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer
|
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer
|
||||||
import com.github.shadowsocks.utils.{Utils, Scheme, Key}
|
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
|
import scala.language.implicitConversions
|
||||||
|
|
||||||
case class ProxiedApp(uid: Int, name: String, packageName: String, var proxied: Boolean)
|
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 = {
|
protected override def onCreateOptionsMenu(menu: Menu): Boolean = {
|
||||||
val inflater = getMenuInflater()
|
getMenuInflater.inflate(R.menu.app_manager_menu, menu)
|
||||||
inflater.inflate(R.menu.app_manager_menu, menu)
|
super.onCreateOptionsMenu(menu)
|
||||||
return super.onCreateOptionsMenu(menu)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override def onCreate(savedInstanceState: Bundle) {
|
protected override def onCreate(savedInstanceState: Bundle) {
|
||||||
@@ -277,7 +277,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
|
|||||||
|
|
||||||
handler = new Handler()
|
handler = new Handler()
|
||||||
|
|
||||||
val actionBar = getActionBar()
|
val actionBar = getActionBar
|
||||||
actionBar.setTitle(R.string.proxied_help)
|
actionBar.setTitle(R.string.proxied_help)
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true)
|
actionBar.setDisplayHomeAsUpEnabled(true)
|
||||||
actionBar.setDisplayShowHomeEnabled(false)
|
actionBar.setDisplayShowHomeEnabled(false)
|
||||||
@@ -297,11 +297,8 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
|
|||||||
|
|
||||||
val bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch]
|
val bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch]
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext)
|
||||||
bypassSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener {
|
bypassSwitch.setOnCheckedChangeListener((button: CompoundButton, checked: Boolean) =>
|
||||||
def onCheckedChanged(button: CompoundButton, checked: Boolean) {
|
prefs.edit().putBoolean(Key.isBypassApps, checked).commit())
|
||||||
prefs.edit().putBoolean(Key.isBypassApps, checked).commit()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
bypassSwitch.setChecked(prefs.getBoolean(Key.isBypassApps, false))
|
bypassSwitch.setChecked(prefs.getBoolean(Key.isBypassApps, false))
|
||||||
|
|
||||||
appListView = findViewById(R.id.applistview).asInstanceOf[ListView]
|
appListView = findViewById(R.id.applistview).asInstanceOf[ListView]
|
||||||
@@ -309,7 +306,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
|
|||||||
|
|
||||||
protected override def onResume() {
|
protected override def onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
spawn {
|
Future {
|
||||||
handler.post(loadStartRunnable)
|
handler.post(loadStartRunnable)
|
||||||
if (!appsLoaded) loadApps()
|
if (!appsLoaded) loadApps()
|
||||||
handler.post(loadFinishRunnable)
|
handler.post(loadFinishRunnable)
|
||||||
|
|||||||
@@ -39,13 +39,11 @@
|
|||||||
|
|
||||||
package com.github.shadowsocks
|
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.app.Notification
|
||||||
import android.content.Context
|
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 {
|
trait BaseService {
|
||||||
|
|
||||||
@@ -100,10 +98,10 @@ trait BaseService {
|
|||||||
def getTag: String
|
def getTag: String
|
||||||
def getContext: Context
|
def getContext: Context
|
||||||
|
|
||||||
def getCallbackCount(): Int = {
|
def getCallbackCount: Int = {
|
||||||
callbackCount
|
callbackCount
|
||||||
}
|
}
|
||||||
def getState(): Int = {
|
def getState: Int = {
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
def changeState(s: Int) {
|
def changeState(s: Int) {
|
||||||
@@ -112,23 +110,19 @@ trait BaseService {
|
|||||||
|
|
||||||
protected def changeState(s: Int, msg: String) {
|
protected def changeState(s: Int, msg: String) {
|
||||||
val handler = new Handler(getContext.getMainLooper)
|
val handler = new Handler(getContext.getMainLooper)
|
||||||
handler.post(new Runnable {
|
handler.post(() => if (state != s) {
|
||||||
override def run() {
|
if (callbackCount > 0) {
|
||||||
if (state != s) {
|
val n = callbacks.beginBroadcast()
|
||||||
if (callbackCount > 0) {
|
for (i <- 0 to n - 1) {
|
||||||
val n = callbacks.beginBroadcast()
|
try {
|
||||||
for (i <- 0 to n - 1) {
|
callbacks.getBroadcastItem(i).stateChanged(s, msg)
|
||||||
try {
|
} catch {
|
||||||
callbacks.getBroadcastItem(i).stateChanged(s, msg)
|
case _: Exception => // Ignore
|
||||||
} catch {
|
|
||||||
case _: Exception => // Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callbacks.finishBroadcast()
|
|
||||||
}
|
}
|
||||||
state = s
|
|
||||||
}
|
}
|
||||||
|
callbacks.finishBroadcast()
|
||||||
}
|
}
|
||||||
|
state = s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,15 +39,15 @@
|
|||||||
|
|
||||||
package com.github.shadowsocks
|
package com.github.shadowsocks
|
||||||
|
|
||||||
import android.os.{Message, Handler, Bundle}
|
import android.app.{Activity, AlertDialog, ProgressDialog}
|
||||||
import android.app.{ProgressDialog, AlertDialog, Activity}
|
import android.content.DialogInterface
|
||||||
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.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
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 {
|
class ParserActivity extends Activity {
|
||||||
override def onCreate(savedInstanceState: Bundle) {
|
override def onCreate(savedInstanceState: Bundle) {
|
||||||
@@ -57,21 +57,17 @@ class ParserActivity extends Activity {
|
|||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setTitle(R.string.add_profile_dialog)
|
.setTitle(R.string.add_profile_dialog)
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
.setPositiveButton(R.string.yes, ((dialog: DialogInterface, id: Int) => {
|
||||||
override def onClick(dialog: DialogInterface, id: Int) {
|
|
||||||
Parser.parse(data) match {
|
Parser.parse(data) match {
|
||||||
case Some(profile) => addProfile(profile)
|
case Some(profile) => addProfile(profile)
|
||||||
case _ => // ignore
|
case _ => // ignore
|
||||||
}
|
}
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
}
|
}): DialogInterface.OnClickListener)
|
||||||
})
|
.setNegativeButton(R.string.no, ((dialog: DialogInterface, id: Int) => {
|
||||||
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
|
|
||||||
override def onClick(dialog: DialogInterface, id: Int) {
|
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
finish()
|
finish()
|
||||||
}
|
}): DialogInterface.OnClickListener)
|
||||||
})
|
|
||||||
.setMessage(data)
|
.setMessage(data)
|
||||||
.create()
|
.create()
|
||||||
.show()
|
.show()
|
||||||
@@ -91,15 +87,13 @@ class ParserActivity extends Activity {
|
|||||||
|
|
||||||
val h = showProgress(getString(R.string.loading))
|
val h = showProgress(getString(R.string.loading))
|
||||||
|
|
||||||
h.postDelayed(new Runnable {
|
h.postDelayed(() => {
|
||||||
def run() {
|
val profileManager =
|
||||||
val profileManager =
|
new ProfileManager(PreferenceManager.getDefaultSharedPreferences(getBaseContext),
|
||||||
new ProfileManager(PreferenceManager.getDefaultSharedPreferences(getBaseContext),
|
getApplication.asInstanceOf[ShadowsocksApplication].dbHelper)
|
||||||
getApplication.asInstanceOf[ShadowsocksApplication].dbHelper)
|
profileManager.createOrUpdateProfile(profile)
|
||||||
profileManager.createOrUpdateProfile(profile)
|
profileManager.reload(profile.id)
|
||||||
profileManager.reload(profile.id)
|
h.sendEmptyMessage(0)
|
||||||
h.sendEmptyMessage(0)
|
|
||||||
}
|
|
||||||
}, 600)
|
}, 600)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,10 +38,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.github.shadowsocks
|
package com.github.shadowsocks
|
||||||
|
|
||||||
|
import java.io.{ByteArrayInputStream, ByteArrayOutputStream, FileOutputStream, IOException, InputStream, OutputStream}
|
||||||
import java.util
|
import java.util
|
||||||
import java.io.{OutputStream, InputStream, ByteArrayInputStream, ByteArrayOutputStream, IOException, FileOutputStream}
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.lang.Math
|
|
||||||
|
|
||||||
import android.app.backup.BackupManager
|
import android.app.backup.BackupManager
|
||||||
import android.app.{Activity, AlertDialog, ProgressDialog}
|
import android.app.{Activity, AlertDialog, ProgressDialog}
|
||||||
@@ -52,8 +51,8 @@ import android.graphics.{Bitmap, Color, Typeface}
|
|||||||
import android.net.{Uri, VpnService}
|
import android.net.{Uri, VpnService}
|
||||||
import android.os._
|
import android.os._
|
||||||
import android.preference._
|
import android.preference._
|
||||||
|
import android.support.v4.content.ContextCompat
|
||||||
import android.util.{DisplayMetrics, Log}
|
import android.util.{DisplayMetrics, Log}
|
||||||
import android.view.View.OnLongClickListener
|
|
||||||
import android.view._
|
import android.view._
|
||||||
import android.webkit.{WebView, WebViewClient}
|
import android.webkit.{WebView, WebViewClient}
|
||||||
import android.widget._
|
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.ads.{AdRequest, AdSize, AdView}
|
||||||
import com.google.android.gms.analytics.HitBuilders
|
import com.google.android.gms.analytics.HitBuilders
|
||||||
import com.google.zxing.integration.android.IntentIntegrator
|
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.IconDrawable
|
||||||
|
import com.joanzapata.android.iconify.Iconify.IconValue
|
||||||
|
import com.nostra13.universalimageloader.core.download.BaseImageDownloader
|
||||||
import net.glxn.qrgen.android.QRCode
|
import net.glxn.qrgen.android.QRCode
|
||||||
|
import net.simonvt.menudrawer.MenuDrawer
|
||||||
|
|
||||||
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
|
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)
|
class ProfileIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int)
|
||||||
extends BaseImageDownloader(context, connectTimeout, readTimeout) {
|
extends BaseImageDownloader(context, connectTimeout, readTimeout) {
|
||||||
@@ -224,7 +223,7 @@ class Shadowsocks
|
|||||||
|
|
||||||
if (!status.getBoolean(getVersionName, false)) {
|
if (!status.getBoolean(getVersionName, false)) {
|
||||||
status.edit.putBoolean(getVersionName, true).commit()
|
status.edit.putBoolean(getVersionName, true).commit()
|
||||||
recovery();
|
recovery()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,7 +249,7 @@ class Shadowsocks
|
|||||||
|
|
||||||
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
|
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
|
||||||
|
|
||||||
var handler: Handler = null
|
var handler = new Handler()
|
||||||
|
|
||||||
def isSinglePane: Boolean = {
|
def isSinglePane: Boolean = {
|
||||||
if (singlePane == -1) {
|
if (singlePane == -1) {
|
||||||
@@ -270,11 +269,7 @@ class Shadowsocks
|
|||||||
switchButton.setChecked(checked)
|
switchButton.setChecked(checked)
|
||||||
if (switchButton.isEnabled) {
|
if (switchButton.isEnabled) {
|
||||||
switchButton.setEnabled(false)
|
switchButton.setEnabled(false)
|
||||||
handler.postDelayed(new Runnable {
|
handler.postDelayed(() => switchButton.setEnabled(true), 1000)
|
||||||
override def run() {
|
|
||||||
switchButton.setEnabled(true)
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
}
|
}
|
||||||
switchButton.setOnCheckedChangeListener(this)
|
switchButton.setOnCheckedChangeListener(this)
|
||||||
}
|
}
|
||||||
@@ -396,7 +391,7 @@ class Shadowsocks
|
|||||||
changeSwitch(checked = false)
|
changeSwitch(checked = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
def isReady(): Boolean = {
|
def isReady: Boolean = {
|
||||||
if (!checkText(Key.proxy)) return false
|
if (!checkText(Key.proxy)) return false
|
||||||
if (!checkText(Key.sitekey)) return false
|
if (!checkText(Key.sitekey)) return false
|
||||||
if (!checkNumber(Key.localPort, low = false)) return false
|
if (!checkNumber(Key.localPort, low = false)) return false
|
||||||
@@ -407,7 +402,7 @@ class Shadowsocks
|
|||||||
|
|
||||||
def prepareStartService() {
|
def prepareStartService() {
|
||||||
showProgress(R.string.connecting)
|
showProgress(R.string.connecting)
|
||||||
spawn {
|
Future {
|
||||||
if (isVpnEnabled) {
|
if (isVpnEnabled) {
|
||||||
val intent = VpnService.prepare(this)
|
val intent = VpnService.prepare(this)
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
@@ -434,11 +429,7 @@ class Shadowsocks
|
|||||||
}
|
}
|
||||||
if (switchButton.isEnabled) {
|
if (switchButton.isEnabled) {
|
||||||
switchButton.setEnabled(false)
|
switchButton.setEnabled(false)
|
||||||
handler.postDelayed(new Runnable {
|
handler.postDelayed(() => switchButton.setEnabled(true), 1000)
|
||||||
override def run() {
|
|
||||||
switchButton.setEnabled(true)
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -479,8 +470,6 @@ class Shadowsocks
|
|||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
handler = new Handler()
|
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.pref_all)
|
addPreferencesFromResource(R.xml.pref_all)
|
||||||
|
|
||||||
// Update the profile
|
// Update the profile
|
||||||
@@ -524,28 +513,26 @@ class Shadowsocks
|
|||||||
getActionBar.setDisplayShowTitleEnabled(false)
|
getActionBar.setDisplayShowTitleEnabled(false)
|
||||||
getActionBar.setDisplayShowCustomEnabled(true)
|
getActionBar.setDisplayShowCustomEnabled(true)
|
||||||
if (Utils.isLollipopOrAbove) {
|
if (Utils.isLollipopOrAbove) {
|
||||||
getWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
getWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||||
getWindow.setStatusBarColor(getResources().getColor(R.color.grey3));
|
getWindow.setStatusBarColor(ContextCompat.getColor(this, R.color.grey3))
|
||||||
getActionBar.setDisplayHomeAsUpEnabled(true)
|
getActionBar.setDisplayHomeAsUpEnabled(true)
|
||||||
getActionBar.setHomeAsUpIndicator(R.drawable.ic_drawer)
|
getActionBar.setHomeAsUpIndicator(R.drawable.ic_drawer)
|
||||||
} else {
|
} else {
|
||||||
getActionBar.setIcon(R.drawable.ic_stat_shadowsocks)
|
getActionBar.setIcon(R.drawable.ic_stat_shadowsocks)
|
||||||
}
|
}
|
||||||
title.setOnLongClickListener(new OnLongClickListener {
|
title.setOnLongClickListener((v: View) => {
|
||||||
override def onLongClick(v: View): Boolean = {
|
if (Utils.isLollipopOrAbove && bgService != null
|
||||||
if (Utils.isLollipopOrAbove && bgService != null
|
&& (bgService.getState == State.INIT || bgService.getState == State.STOPPED)) {
|
||||||
&& (bgService.getState == State.INIT || bgService.getState == State.STOPPED)) {
|
val natEnabled = status.getBoolean(Key.isNAT, false)
|
||||||
val natEnabled = status.getBoolean(Key.isNAT, false)
|
status.edit().putBoolean(Key.isNAT, !natEnabled).commit()
|
||||||
status.edit().putBoolean(Key.isNAT, !natEnabled).commit()
|
if (!natEnabled) {
|
||||||
if (!natEnabled) {
|
Toast.makeText(getBaseContext, R.string.enable_nat, Toast.LENGTH_LONG).show()
|
||||||
Toast.makeText(getBaseContext, R.string.enable_nat, Toast.LENGTH_LONG).show()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(getBaseContext, R.string.disable_nat, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
true
|
|
||||||
} else {
|
} 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))
|
registerReceiver(preferenceReceiver, new IntentFilter(Action.UPDATE_PREFS))
|
||||||
|
|
||||||
// Bind to the service
|
// Bind to the service
|
||||||
spawn {
|
Future {
|
||||||
val isRoot = (!Utils.isLollipopOrAbove || status.getBoolean(Key.isNAT, false)) && Console.isRoot
|
val isRoot = (!Utils.isLollipopOrAbove || status.getBoolean(Key.isNAT, false)) && Console.isRoot
|
||||||
handler.post(new Runnable {
|
handler.post(() => {
|
||||||
override def run() {
|
status.edit.putBoolean(Key.isRoot, isRoot).commit()
|
||||||
status.edit.putBoolean(Key.isRoot, isRoot).commit()
|
attachService()
|
||||||
attachService()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -615,29 +600,23 @@ class Shadowsocks
|
|||||||
val builder = new AlertDialog.Builder(this)
|
val builder = new AlertDialog.Builder(this)
|
||||||
builder
|
builder
|
||||||
.setTitle(R.string.add_profile)
|
.setTitle(R.string.add_profile)
|
||||||
.setItems(R.array.add_profile_methods, new DialogInterface.OnClickListener() {
|
.setItems(R.array.add_profile_methods, ((dialog: DialogInterface, which: Int) => which match {
|
||||||
def onClick(dialog: DialogInterface, which: Int) {
|
case 0 =>
|
||||||
which match {
|
dialog.dismiss()
|
||||||
case 0 =>
|
val h = showProgress(R.string.loading)
|
||||||
dialog.dismiss()
|
h.postDelayed(() => {
|
||||||
val h = showProgress(R.string.loading)
|
val integrator = new IntentIntegrator(Shadowsocks.this)
|
||||||
h.postDelayed(new Runnable() {
|
val list = new java.util.ArrayList(IntentIntegrator.TARGET_ALL_KNOWN)
|
||||||
def run() {
|
list.add("tw.com.quickmark")
|
||||||
val integrator = new IntentIntegrator(Shadowsocks.this)
|
integrator.setTargetApplications(list)
|
||||||
val list = new java.util.ArrayList(IntentIntegrator.TARGET_ALL_KNOWN)
|
integrator.initiateScan()
|
||||||
list.add("tw.com.quickmark")
|
h.sendEmptyMessage(0)
|
||||||
integrator.setTargetApplications(list)
|
}, 600)
|
||||||
integrator.initiateScan()
|
case 1 =>
|
||||||
h.sendEmptyMessage(0)
|
dialog.dismiss()
|
||||||
}
|
addProfile(id)
|
||||||
}, 600)
|
case _ =>
|
||||||
case 1 =>
|
}): DialogInterface.OnClickListener)
|
||||||
dialog.dismiss()
|
|
||||||
addProfile(id)
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
builder.create().show()
|
builder.create().show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,17 +625,15 @@ class Shadowsocks
|
|||||||
|
|
||||||
val h = showProgress(R.string.loading)
|
val h = showProgress(R.string.loading)
|
||||||
|
|
||||||
handler.postDelayed(new Runnable {
|
handler.postDelayed(() => {
|
||||||
def run() {
|
currentProfile = {
|
||||||
currentProfile = {
|
profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile
|
||||||
profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile
|
|
||||||
}
|
|
||||||
menuAdapter.updateList(getMenuList, currentProfile.id)
|
|
||||||
|
|
||||||
updatePreferenceScreen()
|
|
||||||
|
|
||||||
h.sendEmptyMessage(0)
|
|
||||||
}
|
}
|
||||||
|
menuAdapter.updateList(getMenuList, currentProfile.id)
|
||||||
|
|
||||||
|
updatePreferenceScreen()
|
||||||
|
|
||||||
|
h.sendEmptyMessage(0)
|
||||||
}, 600)
|
}, 600)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,17 +642,15 @@ class Shadowsocks
|
|||||||
|
|
||||||
val h = showProgress(R.string.loading)
|
val h = showProgress(R.string.loading)
|
||||||
|
|
||||||
handler.postDelayed(new Runnable {
|
handler.postDelayed(() => {
|
||||||
def run() {
|
currentProfile = profile
|
||||||
currentProfile = profile
|
profileManager.createOrUpdateProfile(currentProfile)
|
||||||
profileManager.createOrUpdateProfile(currentProfile)
|
profileManager.reload(currentProfile.id)
|
||||||
profileManager.reload(currentProfile.id)
|
menuAdapter.updateList(getMenuList, currentProfile.id)
|
||||||
menuAdapter.updateList(getMenuList, currentProfile.id)
|
|
||||||
|
|
||||||
updatePreferenceScreen()
|
updatePreferenceScreen()
|
||||||
|
|
||||||
h.sendEmptyMessage(0)
|
h.sendEmptyMessage(0)
|
||||||
}
|
|
||||||
}, 600)
|
}, 600)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,16 +659,14 @@ class Shadowsocks
|
|||||||
|
|
||||||
val h = showProgress(R.string.loading)
|
val h = showProgress(R.string.loading)
|
||||||
|
|
||||||
handler.postDelayed(new Runnable {
|
handler.postDelayed(() => {
|
||||||
def run() {
|
currentProfile = profileManager.reload(id)
|
||||||
currentProfile = profileManager.reload(id)
|
profileManager.save()
|
||||||
profileManager.save()
|
menuAdapter.updateList(getMenuList, currentProfile.id)
|
||||||
menuAdapter.updateList(getMenuList, currentProfile.id)
|
|
||||||
|
|
||||||
updatePreferenceScreen()
|
updatePreferenceScreen()
|
||||||
|
|
||||||
h.sendEmptyMessage(0)
|
h.sendEmptyMessage(0)
|
||||||
}
|
|
||||||
}, 600)
|
}, 600)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,16 +675,14 @@ class Shadowsocks
|
|||||||
|
|
||||||
val h = showProgress(R.string.loading)
|
val h = showProgress(R.string.loading)
|
||||||
|
|
||||||
handler.postDelayed(new Runnable {
|
handler.postDelayed(() => {
|
||||||
def run() {
|
currentProfile = profileManager.reload(id)
|
||||||
currentProfile = profileManager.reload(id)
|
menuAdapter.setActiveId(id)
|
||||||
menuAdapter.setActiveId(id)
|
menuAdapter.notifyDataSetChanged()
|
||||||
menuAdapter.notifyDataSetChanged()
|
|
||||||
|
|
||||||
updatePreferenceScreen()
|
updatePreferenceScreen()
|
||||||
|
|
||||||
h.sendEmptyMessage(0)
|
h.sendEmptyMessage(0)
|
||||||
}
|
|
||||||
}, 600)
|
}, 600)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,20 +691,18 @@ class Shadowsocks
|
|||||||
|
|
||||||
val profile = profileManager.getProfile(id)
|
val profile = profileManager.getProfile(id)
|
||||||
|
|
||||||
if (!profile.isDefined) return false
|
if (profile.isEmpty) return false
|
||||||
|
|
||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setMessage(String.format(Locale.ENGLISH, getString(R.string.remove_profile), profile.get.name))
|
.setMessage(String.format(Locale.ENGLISH, getString(R.string.remove_profile), profile.get.name))
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
|
.setNegativeButton(R.string.no,
|
||||||
override def onClick(dialog: DialogInterface, i: Int) = dialog.cancel()
|
((dialog: DialogInterface, i: Int) => dialog.cancel()): DialogInterface.OnClickListener)
|
||||||
})
|
.setPositiveButton(R.string.yes, ((dialog: DialogInterface, i: Int) => {
|
||||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
|
||||||
override def onClick(dialog: DialogInterface, i: Int) {
|
|
||||||
profileManager.delProfile(id)
|
profileManager.delProfile(id)
|
||||||
val profileId = {
|
val profileId = {
|
||||||
val profiles = profileManager.getAllProfiles.getOrElse(List[Profile]())
|
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)
|
currentProfile = profileManager.load(profileId)
|
||||||
menuAdapter.updateList(getMenuList, currentProfile.id)
|
menuAdapter.updateList(getMenuList, currentProfile.id)
|
||||||
@@ -741,8 +710,7 @@ class Shadowsocks
|
|||||||
updatePreferenceScreen()
|
updatePreferenceScreen()
|
||||||
|
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
}
|
}): DialogInterface.OnClickListener)
|
||||||
})
|
|
||||||
.create()
|
.create()
|
||||||
.show()
|
.show()
|
||||||
|
|
||||||
@@ -901,10 +869,7 @@ class Shadowsocks
|
|||||||
deattachService()
|
deattachService()
|
||||||
unregisterReceiver(preferenceReceiver)
|
unregisterReceiver(preferenceReceiver)
|
||||||
new BackupManager(this).dataChanged()
|
new BackupManager(this).dataChanged()
|
||||||
if (handler != null) {
|
handler.removeCallbacksAndMessages(null)
|
||||||
handler.removeCallbacksAndMessages(null)
|
|
||||||
handler = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def copyToSystem() {
|
def copyToSystem() {
|
||||||
@@ -939,16 +904,14 @@ class Shadowsocks
|
|||||||
private def recovery() {
|
private def recovery() {
|
||||||
serviceStop()
|
serviceStop()
|
||||||
val h = showProgress(R.string.recovering)
|
val h = showProgress(R.string.recovering)
|
||||||
spawn {
|
Future {
|
||||||
reset()
|
reset()
|
||||||
h.sendEmptyMessage(0)
|
h.sendEmptyMessage(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def dp2px(dp: Int): Int = {
|
private def dp2px(dp: Int): Int =
|
||||||
val displayMetrics = getBaseContext.getResources.getDisplayMetrics()
|
Math.round(dp * (getBaseContext.getResources.getDisplayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT))
|
||||||
Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT))
|
|
||||||
}
|
|
||||||
|
|
||||||
private def showQrCode() {
|
private def showQrCode() {
|
||||||
val image = new ImageView(this)
|
val image = new ImageView(this)
|
||||||
@@ -960,11 +923,8 @@ class Shadowsocks
|
|||||||
|
|
||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setNegativeButton(getString(R.string.close), new DialogInterface.OnClickListener() {
|
.setNegativeButton(getString(R.string.close),
|
||||||
override def onClick(dialog: DialogInterface, id: Int) {
|
((dialog: DialogInterface, id: Int) => dialog.cancel()): DialogInterface.OnClickListener)
|
||||||
dialog.cancel()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setView(image)
|
.setView(image)
|
||||||
.create()
|
.create()
|
||||||
.show()
|
.show()
|
||||||
@@ -972,7 +932,7 @@ class Shadowsocks
|
|||||||
|
|
||||||
private def flushDnsCache() {
|
private def flushDnsCache() {
|
||||||
val h = showProgress(R.string.flushing)
|
val h = showProgress(R.string.flushing)
|
||||||
spawn {
|
Future {
|
||||||
Utils.toggleAirplaneMode(getBaseContext)
|
Utils.toggleAirplaneMode(getBaseContext)
|
||||||
h.sendEmptyMessage(0)
|
h.sendEmptyMessage(0)
|
||||||
}
|
}
|
||||||
@@ -1075,11 +1035,8 @@ class Shadowsocks
|
|||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setTitle(getString(R.string.about_title).formatLocal(Locale.ENGLISH, versionName))
|
.setTitle(getString(R.string.about_title).formatLocal(Locale.ENGLISH, versionName))
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.setNegativeButton(getString(R.string.ok_iknow), new DialogInterface.OnClickListener() {
|
.setNegativeButton(getString(R.string.ok_iknow),
|
||||||
override def onClick(dialog: DialogInterface, id: Int) {
|
((dialog: DialogInterface, id: Int) => dialog.cancel()): DialogInterface.OnClickListener)
|
||||||
dialog.cancel()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setView(web)
|
.setView(web)
|
||||||
.create()
|
.create()
|
||||||
.show()
|
.show()
|
||||||
@@ -1094,46 +1051,42 @@ class Shadowsocks
|
|||||||
}
|
}
|
||||||
|
|
||||||
def onStateChanged(s: Int, m: String) {
|
def onStateChanged(s: Int, m: String) {
|
||||||
handler.post(new Runnable {
|
handler.post(() => if (state != s) {
|
||||||
override def run() {
|
state = s
|
||||||
if (state != s) {
|
state match {
|
||||||
state = s
|
case State.CONNECTING =>
|
||||||
state match {
|
if (progressDialog == null) {
|
||||||
case State.CONNECTING =>
|
progressDialog = ProgressDialog
|
||||||
if (progressDialog == null) {
|
.show(Shadowsocks.this, "", getString(R.string.connecting), true, true)
|
||||||
progressDialog = ProgressDialog
|
progressTag = R.string.connecting
|
||||||
.show(Shadowsocks.this, "", getString(R.string.connecting), true, true)
|
}
|
||||||
progressTag = R.string.connecting
|
setPreferenceEnabled(enabled = false)
|
||||||
}
|
case State.CONNECTED =>
|
||||||
setPreferenceEnabled(enabled = false)
|
if (progressTag == R.string.connecting) {
|
||||||
case State.CONNECTED =>
|
clearDialog()
|
||||||
if (progressTag == R.string.connecting) {
|
}
|
||||||
clearDialog()
|
changeSwitch(checked = true)
|
||||||
}
|
setPreferenceEnabled(enabled = false)
|
||||||
changeSwitch(checked = true)
|
case State.STOPPED =>
|
||||||
setPreferenceEnabled(enabled = false)
|
if (progressTag == R.string.stopping || progressTag == R.string.connecting) {
|
||||||
case State.STOPPED =>
|
clearDialog()
|
||||||
if (progressTag == R.string.stopping || progressTag == R.string.connecting) {
|
}
|
||||||
clearDialog()
|
changeSwitch(checked = false)
|
||||||
}
|
if (m != null) {
|
||||||
changeSwitch(checked = false)
|
new SnackBar.Builder(Shadowsocks.this)
|
||||||
if (m != null) {
|
.withMessage(getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m))
|
||||||
new SnackBar.Builder(Shadowsocks.this)
|
.withActionMessageId(R.string.error)
|
||||||
.withMessage(getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m))
|
.withStyle(SnackBar.Style.ALERT)
|
||||||
.withActionMessageId(R.string.error)
|
.withDuration(SnackBar.LONG_SNACK)
|
||||||
.withStyle(SnackBar.Style.ALERT)
|
.show()
|
||||||
.withDuration(SnackBar.LONG_SNACK)
|
}
|
||||||
.show()
|
setPreferenceEnabled(enabled = true)
|
||||||
}
|
case State.STOPPING =>
|
||||||
setPreferenceEnabled(enabled = true)
|
if (progressDialog == null) {
|
||||||
case State.STOPPING =>
|
progressDialog = ProgressDialog
|
||||||
if (progressDialog == null) {
|
.show(Shadowsocks.this, "", getString(R.string.stopping), true, true)
|
||||||
progressDialog = ProgressDialog
|
progressTag = R.string.stopping
|
||||||
.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.github.shadowsocks.utils.Utils
|
||||||
import com.google.android.gms.analytics.GoogleAnalytics
|
import com.google.android.gms.analytics.GoogleAnalytics
|
||||||
import com.google.android.gms.common.api.ResultCallback
|
import com.google.android.gms.common.api.ResultCallback
|
||||||
import com.google.android.gms.tagmanager.Container.FunctionCallMacroCallback
|
|
||||||
import com.google.android.gms.tagmanager.{ContainerHolder, TagManager}
|
import com.google.android.gms.tagmanager.{ContainerHolder, TagManager}
|
||||||
|
|
||||||
class ShadowsocksApplication extends Application {
|
class ShadowsocksApplication extends Application {
|
||||||
@@ -66,14 +65,13 @@ class ShadowsocksApplication extends Application {
|
|||||||
}
|
}
|
||||||
containerHolder = holder
|
containerHolder = holder
|
||||||
val container = holder.getContainer
|
val container = holder.getContainer
|
||||||
container.registerFunctionCallMacroCallback(SIG_FUNC, new FunctionCallMacroCallback {
|
container.registerFunctionCallMacroCallback(SIG_FUNC,
|
||||||
override def getValue(functionName: String, parameters: util.Map[String, AnyRef]): AnyRef = {
|
(functionName: String, parameters: util.Map[String, AnyRef]) => {
|
||||||
if (functionName == SIG_FUNC) {
|
if (functionName == SIG_FUNC) {
|
||||||
Utils.getSignature(getApplicationContext)
|
Utils.getSignature(getApplicationContext)
|
||||||
}
|
}
|
||||||
null
|
null
|
||||||
}
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pending.setResultCallback(callback, 2, TimeUnit.SECONDS)
|
pending.setResultCallback(callback, 2, TimeUnit.SECONDS)
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ package com.github.shadowsocks
|
|||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.reflect.{InvocationTargetException, Method}
|
import java.lang.reflect.{InvocationTargetException, Method}
|
||||||
|
import java.net.{Inet6Address, InetAddress}
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
import android.app._
|
import android.app._
|
||||||
@@ -54,11 +55,11 @@ import android.widget.Toast
|
|||||||
import com.github.shadowsocks.aidl.Config
|
import com.github.shadowsocks.aidl.Config
|
||||||
import com.github.shadowsocks.utils._
|
import com.github.shadowsocks.utils._
|
||||||
import com.google.android.gms.analytics.HitBuilders
|
import com.google.android.gms.analytics.HitBuilders
|
||||||
import org.apache.http.conn.util.InetAddressUtils
|
|
||||||
|
|
||||||
import scala.collection._
|
import scala.collection._
|
||||||
import scala.collection.mutable.ArrayBuffer
|
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 {
|
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 " +
|
val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " +
|
||||||
"-j DNAT --to-destination 127.0.0.1:8123"
|
"-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 lockReceiver: BroadcastReceiver = null
|
||||||
var closeReceiver: BroadcastReceiver = null
|
var closeReceiver: BroadcastReceiver = null
|
||||||
var connReceiver: BroadcastReceiver = null
|
var connReceiver: BroadcastReceiver = null
|
||||||
var notificationManager: NotificationManager = null
|
|
||||||
var config: Config = null
|
var config: Config = null
|
||||||
var apps: Array[ProxiedApp] = null
|
var apps: Array[ProxiedApp] = null
|
||||||
val myUid = Process.myUid()
|
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 lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
|
||||||
|
|
||||||
private val dnsAddressCache = new SparseArray[String]
|
private val dnsAddressCache = new SparseArray[String]
|
||||||
@@ -172,11 +161,7 @@ class ShadowsocksNatService extends Service with BaseService {
|
|||||||
|
|
||||||
def initConnectionReceiver() {
|
def initConnectionReceiver() {
|
||||||
val filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
|
val filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
|
||||||
connReceiver = new BroadcastReceiver {
|
connReceiver = (context: Context, intent: Intent) => setupDns()
|
||||||
override def onReceive(context: Context, intent: Intent) = {
|
|
||||||
setupDns()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
registerReceiver(connReceiver, filter)
|
registerReceiver(connReceiver, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,17 +309,6 @@ class ShadowsocksNatService extends Service with BaseService {
|
|||||||
true
|
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) {
|
def notifyForegroundAlert(title: String, info: String, visible: Boolean) {
|
||||||
val openIntent = new Intent(this, classOf[Shadowsocks])
|
val openIntent = new Intent(this, classOf[Shadowsocks])
|
||||||
openIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
|
openIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
|
||||||
@@ -358,7 +332,7 @@ class ShadowsocksNatService extends Service with BaseService {
|
|||||||
else
|
else
|
||||||
builder.setPriority(NotificationCompat.PRIORITY_MIN)
|
builder.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||||
|
|
||||||
startForegroundCompat(1, builder.build)
|
startForeground(1, builder.build)
|
||||||
}
|
}
|
||||||
|
|
||||||
def onBind(intent: Intent): IBinder = {
|
def onBind(intent: Intent): IBinder = {
|
||||||
@@ -372,29 +346,7 @@ class ShadowsocksNatService extends Service with BaseService {
|
|||||||
|
|
||||||
override def onCreate() {
|
override def onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
ConfigUtils.refresh(this)
|
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() {
|
def killProcesses() {
|
||||||
@@ -430,7 +382,7 @@ class ShadowsocksNatService extends Service with BaseService {
|
|||||||
init_sb.append(Utils.getIptables + " -t nat -F OUTPUT")
|
init_sb.append(Utils.getIptables + " -t nat -F OUTPUT")
|
||||||
|
|
||||||
val cmd_bypass = Utils.getIptables + CMD_IPTABLES_RETURN
|
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 " + config.proxy))
|
||||||
}
|
}
|
||||||
init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d 127.0.0.1"))
|
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)
|
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) {
|
override def startRunner(c: Config) {
|
||||||
|
|
||||||
config = c
|
config = c
|
||||||
@@ -511,11 +425,9 @@ class ShadowsocksNatService extends Service with BaseService {
|
|||||||
val filter = new IntentFilter()
|
val filter = new IntentFilter()
|
||||||
filter.addAction(Intent.ACTION_SHUTDOWN)
|
filter.addAction(Intent.ACTION_SHUTDOWN)
|
||||||
filter.addAction(Action.CLOSE)
|
filter.addAction(Action.CLOSE)
|
||||||
closeReceiver = new BroadcastReceiver() {
|
closeReceiver = (context: Context, intent: Intent) => {
|
||||||
def onReceive(context: Context, intent: Intent) {
|
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
|
||||||
Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show()
|
stopRunner()
|
||||||
stopRunner()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
registerReceiver(closeReceiver, filter)
|
registerReceiver(closeReceiver, filter)
|
||||||
|
|
||||||
@@ -524,26 +436,22 @@ class ShadowsocksNatService extends Service with BaseService {
|
|||||||
screenFilter.addAction(Intent.ACTION_SCREEN_ON)
|
screenFilter.addAction(Intent.ACTION_SCREEN_ON)
|
||||||
screenFilter.addAction(Intent.ACTION_SCREEN_OFF)
|
screenFilter.addAction(Intent.ACTION_SCREEN_OFF)
|
||||||
screenFilter.addAction(Intent.ACTION_USER_PRESENT)
|
screenFilter.addAction(Intent.ACTION_USER_PRESENT)
|
||||||
lockReceiver = new BroadcastReceiver() {
|
lockReceiver = (context: Context, intent: Intent) => if (getState == State.CONNECTED) {
|
||||||
def onReceive(context: Context, intent: Intent) {
|
val action = intent.getAction
|
||||||
if (getState == State.CONNECTED) {
|
if (action == Intent.ACTION_SCREEN_OFF) {
|
||||||
val action = intent.getAction
|
notifyForegroundAlert(getString(R.string.forward_success),
|
||||||
if (action == Intent.ACTION_SCREEN_OFF) {
|
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false)
|
||||||
notifyForegroundAlert(getString(R.string.forward_success),
|
} else if (action == Intent.ACTION_SCREEN_ON) {
|
||||||
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false)
|
val keyGuard = getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager]
|
||||||
} else if (action == Intent.ACTION_SCREEN_ON) {
|
if (!keyGuard.inKeyguardRestrictedInputMode) {
|
||||||
val keyGuard = getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager]
|
notifyForegroundAlert(getString(R.string.forward_success),
|
||||||
if (!keyGuard.inKeyguardRestrictedInputMode) {
|
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} 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)
|
registerReceiver(lockReceiver, screenFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,7 +464,7 @@ class ShadowsocksNatService extends Service with BaseService {
|
|||||||
|
|
||||||
changeState(State.CONNECTING)
|
changeState(State.CONNECTING)
|
||||||
|
|
||||||
spawn {
|
Future {
|
||||||
|
|
||||||
if (config.proxy == "198.199.101.152") {
|
if (config.proxy == "198.199.101.152") {
|
||||||
val holder = application.containerHolder
|
val holder = application.containerHolder
|
||||||
@@ -576,8 +484,7 @@ class ShadowsocksNatService extends Service with BaseService {
|
|||||||
killProcesses()
|
killProcesses()
|
||||||
|
|
||||||
var resolved: Boolean = false
|
var resolved: Boolean = false
|
||||||
if (!InetAddressUtils.isIPv4Address(config.proxy) &&
|
if (!Utils.isNumeric(config.proxy)) {
|
||||||
!InetAddressUtils.isIPv6Address(config.proxy)) {
|
|
||||||
Utils.resolve(config.proxy, enableIPv6 = true) match {
|
Utils.resolve(config.proxy, enableIPv6 = true) match {
|
||||||
case Some(a) =>
|
case Some(a) =>
|
||||||
config.proxy = a
|
config.proxy = a
|
||||||
@@ -637,7 +544,7 @@ class ShadowsocksNatService extends Service with BaseService {
|
|||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
stopForegroundCompat(1)
|
stopForeground(true)
|
||||||
|
|
||||||
// change the state
|
// change the state
|
||||||
changeState(State.STOPPED)
|
changeState(State.STOPPED)
|
||||||
|
|||||||
@@ -57,11 +57,7 @@ class ShadowsocksRunnerActivity extends Activity {
|
|||||||
val connection = new ServiceConnection {
|
val connection = new ServiceConnection {
|
||||||
override def onServiceConnected(name: ComponentName, service: IBinder) {
|
override def onServiceConnected(name: ComponentName, service: IBinder) {
|
||||||
bgService = IShadowsocksService.Stub.asInterface(service)
|
bgService = IShadowsocksService.Stub.asInterface(service)
|
||||||
handler.postDelayed(new Runnable() {
|
handler.postDelayed(() => if (bgService != null) startBackgroundService(), 1000)
|
||||||
override def run() {
|
|
||||||
if (bgService != null) startBackgroundService()
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
}
|
}
|
||||||
override def onServiceDisconnected(name: ComponentName) {
|
override def onServiceDisconnected(name: ComponentName) {
|
||||||
bgService = null
|
bgService = null
|
||||||
@@ -122,11 +118,9 @@ class ShadowsocksRunnerActivity extends Activity {
|
|||||||
val locked = km.inKeyguardRestrictedInputMode
|
val locked = km.inKeyguardRestrictedInputMode
|
||||||
if (locked) {
|
if (locked) {
|
||||||
val filter = new IntentFilter(Intent.ACTION_USER_PRESENT)
|
val filter = new IntentFilter(Intent.ACTION_USER_PRESENT)
|
||||||
receiver = new BroadcastReceiver() {
|
receiver = (context: Context, intent: Intent) => {
|
||||||
override def onReceive(context: Context, intent: Intent) {
|
if (intent.getAction == Intent.ACTION_USER_PRESENT) {
|
||||||
if (intent.getAction == Intent.ACTION_USER_PRESENT) {
|
attachService()
|
||||||
attachService()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
registerReceiver(receiver, filter)
|
registerReceiver(receiver, filter)
|
||||||
|
|||||||
@@ -47,17 +47,18 @@ import android.content._
|
|||||||
import android.content.pm.{PackageInfo, PackageManager}
|
import android.content.pm.{PackageInfo, PackageManager}
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os._
|
import android.os._
|
||||||
|
import android.support.v4.app.NotificationCompat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.github.shadowsocks.aidl.Config
|
import com.github.shadowsocks.aidl.Config
|
||||||
import com.github.shadowsocks.utils._
|
import com.github.shadowsocks.utils._
|
||||||
import com.google.android.gms.analytics.HitBuilders
|
import com.google.android.gms.analytics.HitBuilders
|
||||||
import org.apache.commons.net.util.SubnetUtils
|
import org.apache.commons.net.util.SubnetUtils
|
||||||
import org.apache.http.conn.util.InetAddressUtils
|
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.collection.mutable.ArrayBuffer
|
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 {
|
class ShadowsocksVpnService extends VpnService with BaseService {
|
||||||
|
|
||||||
@@ -67,7 +68,6 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
val PRIVATE_VLAN = "26.26.26.%s"
|
val PRIVATE_VLAN = "26.26.26.%s"
|
||||||
val PRIVATE_VLAN6 = "fdfe:dcba:9876::%s"
|
val PRIVATE_VLAN6 = "fdfe:dcba:9876::%s"
|
||||||
var conn: ParcelFileDescriptor = null
|
var conn: ParcelFileDescriptor = null
|
||||||
var notificationManager: NotificationManager = null
|
|
||||||
var receiver: BroadcastReceiver = null
|
var receiver: BroadcastReceiver = null
|
||||||
var apps: Array[ProxiedApp] = null
|
var apps: Array[ProxiedApp] = null
|
||||||
var config: Config = null
|
var config: Config = null
|
||||||
@@ -104,15 +104,34 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
null
|
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() {
|
override def onCreate() {
|
||||||
|
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
ConfigUtils.refresh(this)
|
ConfigUtils.refresh(this)
|
||||||
|
|
||||||
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE)
|
|
||||||
.asInstanceOf[NotificationManager]
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def onRevoke() {
|
override def onRevoke() {
|
||||||
@@ -126,6 +145,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
vpnThread = null
|
vpnThread = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopForeground(true)
|
||||||
|
|
||||||
// channge the state
|
// channge the state
|
||||||
changeState(State.STOPPING)
|
changeState(State.STOPPING)
|
||||||
|
|
||||||
@@ -208,17 +229,15 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
// register close receiver
|
// register close receiver
|
||||||
val filter = new IntentFilter()
|
val filter = new IntentFilter()
|
||||||
filter.addAction(Intent.ACTION_SHUTDOWN)
|
filter.addAction(Intent.ACTION_SHUTDOWN)
|
||||||
receiver = new BroadcastReceiver {
|
receiver = (p1: Context, p2: Intent) => {
|
||||||
def onReceive(p1: Context, p2: Intent) {
|
Toast.makeText(p1, R.string.stopping, Toast.LENGTH_SHORT)
|
||||||
Toast.makeText(p1, R.string.stopping, Toast.LENGTH_SHORT)
|
stopRunner()
|
||||||
stopRunner()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
registerReceiver(receiver, filter)
|
registerReceiver(receiver, filter)
|
||||||
|
|
||||||
changeState(State.CONNECTING)
|
changeState(State.CONNECTING)
|
||||||
|
|
||||||
spawn {
|
Future {
|
||||||
if (config.proxy == "198.199.101.152") {
|
if (config.proxy == "198.199.101.152") {
|
||||||
val holder = getApplication.asInstanceOf[ShadowsocksApplication].containerHolder
|
val holder = getApplication.asInstanceOf[ShadowsocksApplication].containerHolder
|
||||||
try {
|
try {
|
||||||
@@ -238,8 +257,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
|
|
||||||
// Resolve the server address
|
// Resolve the server address
|
||||||
var resolved: Boolean = false
|
var resolved: Boolean = false
|
||||||
if (!InetAddressUtils.isIPv4Address(config.proxy) &&
|
if (!Utils.isNumeric(config.proxy)) {
|
||||||
!InetAddressUtils.isIPv6Address(config.proxy)) {
|
|
||||||
Utils.resolve(config.proxy, enableIPv6 = true) match {
|
Utils.resolve(config.proxy, enableIPv6 = true) match {
|
||||||
case Some(addr) =>
|
case Some(addr) =>
|
||||||
config.proxy = addr
|
config.proxy = addr
|
||||||
@@ -251,6 +269,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (resolved && handleConnection) {
|
if (resolved && handleConnection) {
|
||||||
|
notifyForegroundAlert(getString(R.string.forward_success),
|
||||||
|
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false)
|
||||||
changeState(State.CONNECTED)
|
changeState(State.CONNECTED)
|
||||||
} else {
|
} else {
|
||||||
changeState(State.STOPPED, getString(R.string.service_failed))
|
changeState(State.STOPPED, getString(R.string.service_failed))
|
||||||
@@ -454,7 +474,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
|
|
||||||
Console.runCommand(cmd)
|
Console.runCommand(cmd)
|
||||||
|
|
||||||
return fd
|
fd
|
||||||
}
|
}
|
||||||
|
|
||||||
override def stopBackgroundService() {
|
override def stopBackgroundService() {
|
||||||
|
|||||||
@@ -93,35 +93,34 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
|
|||||||
try {
|
try {
|
||||||
val socket = serverSocket.accept()
|
val socket = serverSocket.accept()
|
||||||
|
|
||||||
pool.execute(new Runnable {
|
pool.execute(() => {
|
||||||
override def run() {
|
try {
|
||||||
try {
|
val input = socket.getInputStream
|
||||||
val input = socket.getInputStream
|
val output = socket.getOutputStream
|
||||||
val output = socket.getOutputStream
|
|
||||||
|
|
||||||
input.read()
|
input.read()
|
||||||
|
|
||||||
val fds = socket.getAncillaryFileDescriptors
|
val fds = socket.getAncillaryFileDescriptors
|
||||||
|
|
||||||
if (fds.length > 0) {
|
if (fds.nonEmpty) {
|
||||||
var ret = false
|
var ret = false
|
||||||
|
|
||||||
val getInt = classOf[FileDescriptor].getDeclaredMethod("getInt$")
|
val getInt = classOf[FileDescriptor].getDeclaredMethod("getInt$")
|
||||||
val fd = getInt.invoke(fds(0)).asInstanceOf[Int]
|
val fd = getInt.invoke(fds(0)).asInstanceOf[Int]
|
||||||
ret = vpnService.protect(fd)
|
ret = vpnService.protect(fd)
|
||||||
|
|
||||||
// Trick to close file decriptor
|
// Trick to close file decriptor
|
||||||
System.jniclose(fd)
|
System.jniclose(fd)
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
output.write(0)
|
output.write(0)
|
||||||
} else {
|
} else {
|
||||||
output.write(1)
|
output.write(1)
|
||||||
}
|
|
||||||
|
|
||||||
input.close()
|
|
||||||
output.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.close()
|
||||||
|
output.close()
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case e: Exception =>
|
case e: Exception =>
|
||||||
Log.e(TAG, "Error when protect socket", e)
|
Log.e(TAG, "Error when protect socket", e)
|
||||||
@@ -134,12 +133,11 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
|
|||||||
case _: Exception => // ignore
|
case _: Exception => // ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
}})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException => {
|
case e: IOException =>
|
||||||
Log.e(TAG, "Error when accept socket", e)
|
Log.e(TAG, "Error when accept socket", e)
|
||||||
return
|
return
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,14 +40,10 @@
|
|||||||
package com.github.shadowsocks.database
|
package com.github.shadowsocks.database
|
||||||
|
|
||||||
import android.content.Context
|
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 android.graphics.drawable.Drawable
|
||||||
import com.github.shadowsocks.{R}
|
import android.view.{LayoutInflater, View, ViewGroup}
|
||||||
import android.view.View.{OnLongClickListener, OnClickListener}
|
import android.widget.{BaseAdapter, TextView}
|
||||||
|
import com.github.shadowsocks.R
|
||||||
|
|
||||||
abstract class Item {
|
abstract class Item {
|
||||||
def id: Int
|
def id: Int
|
||||||
@@ -147,17 +143,13 @@ class MenuAdapter(context: Context, var items: List[Any]) extends BaseAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tv.setOnClickListener(new OnClickListener {
|
tv.setOnClickListener((view: View) => {
|
||||||
def onClick(view: View) {
|
val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
|
||||||
val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
|
item.click(item.id)
|
||||||
item.click(item.id)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
tv.setOnLongClickListener(new OnLongClickListener {
|
tv.setOnLongClickListener((view: View) => {
|
||||||
def onLongClick(view: View): Boolean = {
|
val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
|
||||||
val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
|
item.longClick(item.id)
|
||||||
item.longClick(item.id)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
v.setTag(R.id.mdItem, value)
|
v.setTag(R.id.mdItem, value)
|
||||||
|
|||||||
@@ -39,9 +39,9 @@
|
|||||||
|
|
||||||
package com.github.shadowsocks.database
|
package com.github.shadowsocks.database
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.github.shadowsocks._
|
import com.github.shadowsocks._
|
||||||
import android.content.{SharedPreferences, Context}
|
|
||||||
import com.github.shadowsocks.utils.Key
|
import com.github.shadowsocks.utils.Key
|
||||||
|
|
||||||
class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
|
class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
|
||||||
|
|||||||
@@ -233,8 +233,8 @@ object ConfigUtils {
|
|||||||
.readTimeout(2000)
|
.readTimeout(2000)
|
||||||
.send("sig="+sig)
|
.send("sig="+sig)
|
||||||
.body
|
.body
|
||||||
val proxies = util.Random.shuffle(list.split('|').toSeq).toSeq
|
val proxies = util.Random.shuffle(list.split('|').toSeq)
|
||||||
val proxy = proxies(0).split(':')
|
val proxy = proxies.head.split(':')
|
||||||
|
|
||||||
val host = proxy(0).trim
|
val host = proxy(0).trim
|
||||||
val port = proxy(1).trim.toInt
|
val port = proxy(1).trim.toInt
|
||||||
|
|||||||
@@ -39,10 +39,11 @@
|
|||||||
|
|
||||||
package com.github.shadowsocks.utils
|
package com.github.shadowsocks.utils
|
||||||
|
|
||||||
import eu.chainfire.libsuperuser.Shell
|
|
||||||
import eu.chainfire.libsuperuser.Shell.{OnCommandResultListener, SU, Builder}
|
|
||||||
import java.util
|
import java.util
|
||||||
|
|
||||||
|
import eu.chainfire.libsuperuser.Shell
|
||||||
|
import eu.chainfire.libsuperuser.Shell.{Builder, SU}
|
||||||
|
|
||||||
object Console {
|
object Console {
|
||||||
|
|
||||||
private def openShell(): Shell.Interactive = {
|
private def openShell(): Shell.Interactive = {
|
||||||
@@ -68,14 +69,8 @@ object Console {
|
|||||||
|
|
||||||
def runCommand(commands: Array[String]) {
|
def runCommand(commands: Array[String]) {
|
||||||
val shell = openShell()
|
val shell = openShell()
|
||||||
shell.addCommand(commands, 0, new OnCommandResultListener {
|
shell.addCommand(commands, 0, ((commandCode: Int, exitCode: Int, output: util.List[String]) =>
|
||||||
override def onCommandResult(commandCode: Int, exitCode: Int,
|
if (exitCode < 0) shell.close()): Shell.OnCommandResultListener)
|
||||||
output: util.List[String]) {
|
|
||||||
if (exitCode < 0) {
|
|
||||||
shell.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
shell.waitForIdle()
|
shell.waitForIdle()
|
||||||
shell.close()
|
shell.close()
|
||||||
}
|
}
|
||||||
@@ -90,17 +85,14 @@ object Console {
|
|||||||
}
|
}
|
||||||
val shell = openRootShell(context)
|
val shell = openRootShell(context)
|
||||||
val sb = new StringBuilder
|
val sb = new StringBuilder
|
||||||
shell.addCommand(commands, 0, new OnCommandResultListener {
|
shell.addCommand(commands, 0, ((commandCode: Int, exitCode: Int, output: util.List[String]) => {
|
||||||
override def onCommandResult(commandCode: Int, exitCode: Int,
|
if (exitCode < 0) {
|
||||||
output: util.List[String]) {
|
shell.close()
|
||||||
if (exitCode < 0) {
|
} else {
|
||||||
shell.close()
|
import scala.collection.JavaConversions._
|
||||||
} else {
|
output.foreach(line => sb.append(line).append('\n'))
|
||||||
import scala.collection.JavaConversions._
|
|
||||||
output.foreach(line => sb.append(line).append('\n'))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}): Shell.OnCommandResultListener)
|
||||||
if (shell.waitForIdle()) {
|
if (shell.waitForIdle()) {
|
||||||
shell.close()
|
shell.close()
|
||||||
sb.toString()
|
sb.toString()
|
||||||
|
|||||||
@@ -47,11 +47,8 @@ import android.util.{Log, Base64}
|
|||||||
object Parser {
|
object Parser {
|
||||||
val TAG = "ShadowParser"
|
val TAG = "ShadowParser"
|
||||||
|
|
||||||
def generate (profile: Profile): String = {
|
def generate (profile: Profile): String = "ss://" + Base64.encodeToString("%s:%s@%s:%d".formatLocal(Locale.ENGLISH,
|
||||||
val path = "%s:%s@%s:%d".formatLocal(Locale.ENGLISH,
|
profile.method, profile.password, profile.host, profile.remotePort).getBytes, Base64.NO_PADDING)
|
||||||
profile.method, profile.password, profile.host, profile.remotePort)
|
|
||||||
return "ss://" + Base64.encodeToString(path.getBytes, Base64.NO_PADDING)
|
|
||||||
}
|
|
||||||
|
|
||||||
def parse (data: String): Option[Profile] = {
|
def parse (data: String): Option[Profile] = {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -38,22 +38,21 @@
|
|||||||
*/
|
*/
|
||||||
package com.github.shadowsocks.utils
|
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.io._
|
||||||
import java.net.{UnknownHostException, InetAddress, NetworkInterface}
|
import java.net._
|
||||||
import org.apache.http.conn.util.InetAddressUtils
|
import java.security.MessageDigest
|
||||||
import org.xbill.DNS._
|
|
||||||
import android.graphics._
|
|
||||||
import android.app.ActivityManager
|
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.os.Build
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import scala.Some
|
import android.support.v4.content.ContextCompat
|
||||||
import java.security.MessageDigest
|
import android.util.{Base64, Log}
|
||||||
import com.github.shadowsocks.{BuildConfig}
|
import com.github.shadowsocks.BuildConfig
|
||||||
|
import org.xbill.DNS._
|
||||||
|
|
||||||
|
|
||||||
object Utils {
|
object Utils {
|
||||||
@@ -147,6 +146,7 @@ object Utils {
|
|||||||
// + "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false\n")
|
// + "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//noinspection ScalaDeprecation
|
||||||
private def toggleBelowApiLevel17(context: Context) {
|
private def toggleBelowApiLevel17(context: Context) {
|
||||||
// Android 4.2 below
|
// Android 4.2 below
|
||||||
Settings.System.putInt(context.getContentResolver, Settings.System.AIRPLANE_MODE_ON, 1)
|
Settings.System.putInt(context.getContentResolver, Settings.System.AIRPLANE_MODE_ON, 1)
|
||||||
@@ -198,7 +198,7 @@ object Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case e: Exception => None
|
case e: Exception =>
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -233,6 +233,9 @@ object Utils {
|
|||||||
None
|
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
|
* Get local IPv4 address
|
||||||
*/
|
*/
|
||||||
@@ -245,9 +248,8 @@ object Utils {
|
|||||||
while (addrs.hasMoreElements) {
|
while (addrs.hasMoreElements) {
|
||||||
val addr = addrs.nextElement()
|
val addr = addrs.nextElement()
|
||||||
if (!addr.isLoopbackAddress && !addr.isLinkLocalAddress) {
|
if (!addr.isLoopbackAddress && !addr.isLinkLocalAddress) {
|
||||||
val sAddr = addr.getHostAddress.toUpperCase
|
if (addr.isInstanceOf[Inet4Address]) {
|
||||||
if (InetAddressUtils.isIPv4Address(sAddr)) {
|
return Some(addr.getHostAddress.toUpperCase)
|
||||||
return Some(sAddr)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,8 +273,7 @@ object Utils {
|
|||||||
while (addrs.hasMoreElements) {
|
while (addrs.hasMoreElements) {
|
||||||
val addr = addrs.nextElement()
|
val addr = addrs.nextElement()
|
||||||
if (!addr.isLoopbackAddress && !addr.isLinkLocalAddress) {
|
if (!addr.isLoopbackAddress && !addr.isLinkLocalAddress) {
|
||||||
val sAddr = addr.getHostAddress.toUpperCase
|
if (addr.isInstanceOf[Inet6Address]) {
|
||||||
if (InetAddressUtils.isIPv6Address(sAddr)) {
|
|
||||||
if (BuildConfig.DEBUG) Log.d(TAG, "IPv6 address detected")
|
if (BuildConfig.DEBUG) Log.d(TAG, "IPv6 address detected")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -338,7 +339,7 @@ object Utils {
|
|||||||
|
|
||||||
def getAppIcon(c: Context, packageName: String): Drawable = {
|
def getAppIcon(c: Context, packageName: String): Drawable = {
|
||||||
val pm: PackageManager = c.getPackageManager
|
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 {
|
try {
|
||||||
pm.getApplicationIcon(packageName)
|
pm.getApplicationIcon(packageName)
|
||||||
} catch {
|
} catch {
|
||||||
@@ -348,7 +349,7 @@ object Utils {
|
|||||||
|
|
||||||
def getAppIcon(c: Context, uid: Int): Drawable = {
|
def getAppIcon(c: Context, uid: Int): Drawable = {
|
||||||
val pm: PackageManager = c.getPackageManager
|
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)
|
val packages: Array[String] = pm.getPackagesForUid(uid)
|
||||||
if (packages != null) {
|
if (packages != null) {
|
||||||
if (packages.length >= 1) {
|
if (packages.length >= 1) {
|
||||||
|
|||||||
Reference in New Issue
Block a user