Commit f345c7d7 authored by Max Lv's avatar Max Lv

Merge pull request #433 from Mygod/master

Fix #432
parents 5ac7f2b9 9adf1171
......@@ -32,10 +32,10 @@ libraryDependencies ++= Seq(
"commons-net" % "commons-net" % "3.3",
"com.google.zxing" % "android-integration" % "3.2.1",
"net.glxn.qrgen" % "android" % "2.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-analytics" % "8.1.0",
"com.android.support" % "design" % "23.1.0",
"com.google.android.gms" % "play-services-base" % "8.3.0",
"com.google.android.gms" % "play-services-ads" % "8.3.0",
"com.google.android.gms" % "play-services-analytics" % "8.3.0",
"com.android.support" % "design" % "23.1.1",
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01",
"com.j256.ormlite" % "ormlite-core" % "4.48",
"com.j256.ormlite" % "ormlite-android" % "4.48"
......
......@@ -2,39 +2,7 @@
<resources>
<style name="Toolbar.Logo" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
<item name="android:textSize">24sp</item>
</style>
<style name="Theme.Material" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:alertDialogTheme">@style/Theme.Material.Dialog.Alert</item>
<item name="android:dialogTheme">@style/Theme.Material.Dialog</item>
<item name="android:navigationBarColor">@color/material_accent_700</item>
<item name="colorAccent">@color/material_accent_500</item>
<item name="colorButtonNormal">@color/material_accent_500</item>
<item name="colorPrimary">@color/material_accent_500</item>
<item name="colorPrimaryDark">@color/material_accent_700</item>
</style>
<style name="Theme.Material.Dialog" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/material_accent_500</item>
<item name="colorPrimary">@color/material_accent_500</item>
<item name="colorPrimaryDark">@color/material_accent_700</item>
</style>
<style name="Theme.Material.Dialog.Alert" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="colorAccent">@color/material_accent_500</item>
<item name="colorPrimary">@color/material_accent_500</item>
<item name="colorPrimaryDark">@color/material_accent_700</item>
</style>
<style name="PopupTheme" parent="Theme.Material.Dialog">
<item name="android:windowNoTitle">true</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
<item name="android:windowSoftInputMode">stateAlwaysHidden</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
</style>
<style name="Theme.Material.Dialog" parent="Theme.Material.DialogBase"/>
<style name="Theme.Material.Dialog.Alert" parent="Theme.Material.Dialog.AlertBase"/>
</resources>
......@@ -74,6 +74,10 @@
<string name="stat">网络流量</string>
<string name="stat_summary">发送:\t%1$s\t|\t接收:\t%2$s\n上传:\t%3$s\t|\t下载:\t%4$s</string>
<string name="stat_profiles">\n发送:\t%1$s\t|\t接收:\t%2$s</string>
<plurals name="bytes">
<item quantity="other">字节</item>
</plurals>
<!-- profile -->
<string name="add_profile_dialog">为影梭添加此配置文件?</string>
......
......@@ -13,7 +13,12 @@
<string name="nat_summary">Use NAT mode instead of VPN mode</string>
<string name="nat_summary_no_root">NAT mode is disabled without root</string>
<string name="stat">Network Traffic</string>
<string name="stat_summary">Send:\t%1$s\t|\tReceive:\t%2$s\nUpload:\t%3$s\t|\tDownload:\t%4$s</string>
<string name="stat_summary">Sent: \t%1$s\t|\tReceived: \t%2$s\nUpload: \t%3$s\t|\tDownload: \t%4$s</string>
<string name="stat_profiles">\nSent: \t%1$s\t|\tReceived: \t%2$s</string>
<plurals name="bytes">
<item quantity="one">Byte</item>
<item quantity="other">Bytes</item>
</plurals>
<!-- proxy category -->
<string name="proxy_cat">Server Settings</string>
......
......@@ -14,16 +14,20 @@
<item name="colorPrimary">@color/material_accent_500</item>
<item name="colorPrimaryDark">@color/material_accent_700</item>
</style>
<style name="Theme.Material.Dialog" parent="Theme.AppCompat.Light.Dialog">
<style name="Theme.Material.DialogBase" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/material_accent_500</item>
<item name="colorPrimary">@color/material_accent_500</item>
<item name="colorPrimaryDark">@color/material_accent_700</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<style name="Theme.Material.Dialog.Alert" parent="Theme.AppCompat.Light.Dialog.Alert">
<style name="Theme.Material.Dialog.AlertBase" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="colorAccent">@color/material_accent_500</item>
<item name="colorPrimary">@color/material_accent_500</item>
<item name="colorPrimaryDark">@color/material_accent_700</item>
</style>
<style name="Theme.Material.Dialog" parent="Theme.Material.DialogBase">
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<style name="Theme.Material.Dialog.Alert" parent="Theme.Material.Dialog.AlertBase">
<item name="android:windowBackground">@android:color/transparent</item>
</style>
......
......@@ -61,7 +61,7 @@ class ParserActivity extends Activity {
.setTitle(R.string.add_profile_dialog)
.setCancelable(false)
.setPositiveButton(android.R.string.yes, ((dialog: DialogInterface, id: Int) => {
ShadowsocksApplication.profileManager.createOrUpdateProfile(profile.get)
ShadowsocksApplication.profileManager.createProfile(profile.get)
dialog.dismiss()
}): DialogInterface.OnClickListener)
.setNegativeButton(android.R.string.no, ((dialog: DialogInterface, id: Int) => {
......
......@@ -10,11 +10,13 @@ import android.support.v7.widget.Toolbar.OnMenuItemClickListener
import android.support.v7.widget.helper.ItemTouchHelper
import android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback
import android.support.v7.widget.{DefaultItemAnimator, LinearLayoutManager, RecyclerView, Toolbar}
import android.text.style.TextAppearanceSpan
import android.text.{Spanned, SpannableStringBuilder}
import android.view.View.{OnAttachStateChangeListener, OnClickListener}
import android.view.{LayoutInflater, MenuItem, View, ViewGroup}
import android.widget.{LinearLayout, ImageView, CheckedTextView, Toast}
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.utils.{Parser, Utils}
import com.github.shadowsocks.utils.{TrafficMonitor, Parser, Utils}
import com.google.zxing.integration.android.IntentIntegrator
import net.glxn.qrgen.android.QRCode
......@@ -54,10 +56,23 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
})
}
def updateText() {
val builder = new SpannableStringBuilder
builder.append(item.name)
if (item.tx != 0 || item.rx != 0) {
val start = builder.length
builder.append(getString(R.string.stat_profiles,
TrafficMonitor.formatTraffic(item.tx), TrafficMonitor.formatTraffic(item.rx)))
builder.setSpan(new TextAppearanceSpan(ProfileManagerActivity.this, android.R.style.TextAppearance_Small),
start + 1, builder.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
text.setText(builder)
}
def bind(item: Profile) {
text.setText(item.name)
text.setChecked(item.id == ShadowsocksApplication.profileId)
this.item = item
updateText()
text.setChecked(item.id == ShadowsocksApplication.profileId)
}
def onClick(v: View) = {
......@@ -153,7 +168,7 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
val scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
if (scanResult != null) Parser.parse(scanResult.getContents) match {
case Some(profile) => ShadowsocksApplication.profileManager.createOrUpdateProfile(profile)
case Some(profile) => ShadowsocksApplication.profileManager.createProfile(profile)
case _ => // ignore
}
}
......
......@@ -138,7 +138,6 @@ object Shadowsocks {
case Key.isUdpDns => updateSwitchPreference(pref, profile.udpdns)
case Key.isAuth => updateSwitchPreference(pref, profile.auth)
case Key.isIpv6 => updateSwitchPreference(pref, profile.ipv6)
case _ =>
}
}
}
......@@ -476,17 +475,15 @@ class Shadowsocks
} else {
TrafficMonitor.reset()
}
val pm = getSystemService(Context.POWER_SERVICE).asInstanceOf[PowerManager]
if (pm.isScreenOn) {
val trafficStat = getString(R.string.stat_summary).formatLocal(Locale.ENGLISH,
TrafficMonitor.getTxRate, TrafficMonitor.getRxRate,
TrafficMonitor.getTxTotal, TrafficMonitor.getRxTotal)
handler.post(() => {
preferences.findPreference(Key.stat).setSummary(trafficStat)
})
}
val trafficStat = getString(R.string.stat_summary).formatLocal(Locale.ENGLISH,
TrafficMonitor.getTxRate, TrafficMonitor.getRxRate,
TrafficMonitor.getTxTotal, TrafficMonitor.getRxTotal)
handler.post(() => {
preferences.findPreference(Key.stat).setSummary(trafficStat)
})
}
}
task.run()
timer = new Timer(true)
timer.schedule(task, TIMER_INTERVAL * 1000, TIMER_INTERVAL * 1000)
}
......
......@@ -65,35 +65,36 @@ class DBHelper(val context: Context)
if (oldVersion < 7) {
profileDao.executeRawNoArgs("DROP TABLE IF EXISTS 'profile';")
onCreate(database, connectionSource)
} else {
if (oldVersion < 8) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
}
if (oldVersion < 9) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
}
if (oldVersion < 10) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN auth SMALLINT;")
}
if (oldVersion < 11) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
}
if (oldVersion < 12) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
onCreate(database, connectionSource)
profileDao.executeRawNoArgs(
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," +
" udpdns, auth, ipv6, individual) " +
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, auth," +
" ipv6, individual FROM `tmp`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
}
if (oldVersion < 13) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN tx LONG;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN rx LONG;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN date DATE;")
}
return
}
if (oldVersion < 8) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
}
if (oldVersion < 9) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
}
if (oldVersion < 10) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN auth SMALLINT;")
}
if (oldVersion < 11) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
}
if (oldVersion < 12) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
onCreate(database, connectionSource)
profileDao.executeRawNoArgs(
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," +
" udpdns, auth, ipv6, individual) " +
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, auth," +
" ipv6, individual FROM `tmp`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
return
}
if (oldVersion < 13) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN tx LONG;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN rx LONG;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN date DATE;")
}
}
}
......
......@@ -49,15 +49,27 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
var profileAddedListener: Profile => Any = _
def setProfileAddedListener(listener: Profile => Any) = this.profileAddedListener = listener
def createOrUpdateProfile(profile: Profile): Boolean = {
def createProfile(p: Profile = null): Profile = {
try {
val profile = if (p == null) new Profile else p
profile.id = 0
ShadowsocksApplication.currentProfile match {
case Some(oldProfile) =>
// Copy Feature Settings from old profile
profile.route = oldProfile.route
profile.ipv6 = oldProfile.ipv6
profile.proxyApps = oldProfile.proxyApps
profile.individual = oldProfile.individual
profile.udpdns = oldProfile.udpdns
case _ =>
}
dbHelper.profileDao.createOrUpdate(profile)
if (profileAddedListener != null) profileAddedListener(profile)
true
profile
} catch {
case ex: Exception =>
Log.e(Shadowsocks.TAG, "addProfile", ex)
false
p
}
}
......@@ -114,11 +126,7 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
def load(id: Int): Profile = {
val profile = getProfile(id) getOrElse {
val p = new Profile()
createOrUpdateProfile(p)
p
}
val profile = getProfile(id) getOrElse createProfile()
val edit = settings.edit()
edit.putBoolean(Key.isProxyApps, profile.proxyApps)
......@@ -175,7 +183,7 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
remotePort = 443
password = "u1rRWTssNv0p"
}
createOrUpdateProfile(profile)
createProfile(profile)
profile
}
}
package com.github.shadowsocks.utils
import java.lang.{Math, System}
import java.text.DecimalFormat
import android.net.TrafficStats
import android.os.Process
import java.lang.{String, System, Math}
import java.util.Locale
import com.github.shadowsocks.{R, ShadowsocksApplication}
case class Traffic(tx: Long, rx: Long, timestamp: Long)
......@@ -19,31 +21,33 @@ object TrafficMonitor {
var txTotal: Long = 0
var rxTotal: Long = 0
def getTraffic(): Traffic = {
def getTraffic: Traffic = {
new Traffic(Math.max(TrafficStats.getTotalTxBytes - TrafficStats.getUidTxBytes(uid), 0),
Math.max(TrafficStats.getTotalRxBytes - TrafficStats.getUidRxBytes(uid), 0), System.currentTimeMillis())
}
def formatTraffic(n: Long): String = {
if (n <= 1024) {
"%d KB".formatLocal(Locale.ENGLISH, n)
} else if (n > 1024) {
"%d MB".formatLocal(Locale.ENGLISH, n / 1024)
} else if (n > 1024 * 1024) {
"%d GB".formatLocal(Locale.ENGLISH, n / 1024 / 1024)
} else if (n > 1024 * 1024 * 1024) {
"%d TB".formatLocal(Locale.ENGLISH, n / 1024 / 1024 / 1024)
} else {
">1024 TB"
private val units = Array("KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "BB", "NB", "DB", "CB")
private val numberFormat = new DecimalFormat("0.00")
def formatTraffic(size: Long): String = {
var n: Double = size
var i = -1
while (n >= 1024) {
n /= 1024
i = i + 1
}
if (i < 0) size + " " + ShadowsocksApplication.instance.getResources.getQuantityString(R.plurals.bytes, size.toInt)
else numberFormat.format(n) + ' ' + units(i)
}
def update() {
val now = getTraffic
txRate = (now.tx - last.tx) / (now.timestamp - last.timestamp)
rxRate = (now.rx - last.rx) / (now.timestamp - last.timestamp)
txTotal += (now.tx - last.tx) / 1024
rxTotal += (now.rx - last.rx) / 1024
val delta = now.timestamp - last.timestamp
if (delta != 0) {
txRate = (now.tx - last.tx) / delta
rxRate = (now.rx - last.rx) / delta
}
txTotal += now.tx - last.tx
rxTotal += now.rx - last.rx
last = now
}
......@@ -80,4 +84,3 @@ object TrafficMonitor {
}
}
class TrafficMonitor ()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment