Commit c4c74c01 authored by Max Lv's avatar Max Lv

Merge pull request #38 from shadowsocks/multi-profile

Multi profile
parents ae2319ca 1155c731
......@@ -2,7 +2,6 @@ libraryDependencies ++= Seq(
"com.google.android" % "support-v4" % "r12",
"com.google.android.analytics" % "analytics" % "2.0beta4",
"dnsjava" % "dnsjava" % "2.1.5",
"com.nostra13.universalimageloader" % "universal-image-loader" % "1.8.4",
"com.google.android.admob" % "admob" % "6.3.1"
)
......@@ -10,6 +9,13 @@ libraryDependencies ++= Seq(
"com.actionbarsherlock" % "actionbarsherlock" % "4.4.0" artifacts(Artifact("actionbarsherlock", "apklib", "apklib")),
"net.saik0.android.unifiedpreference" % "unifiedpreference" % "0.0.2" artifacts(Artifact("unifiedpreference", "apklib", "apklib")),
"org.jraf" % "android-switch-backport" % "1.0" artifacts(Artifact("android-switch-backport", "apklib", "apklib")),
"de.keyboardsurfer.android.widget" % "crouton" % "1.7"
"net.simonvt.menudrawer" % "menudrawer" % "3.0.4" artifacts(Artifact("menudrawer", "apklib", "apklib"))
)
libraryDependencies ++= Seq(
"de.keyboardsurfer.android.widget" % "crouton" % "1.7",
"com.nostra13.universalimageloader" % "universal-image-loader" % "1.8.4",
"com.j256.ormlite" % "ormlite-core" % "4.47",
"com.j256.ormlite" % "ormlite-android" % "4.47"
)
......@@ -5,8 +5,8 @@ import sbtandroid._
import sbtandroid.AndroidPlugin._
object App {
val version = "1.8.3"
val versionCode = 48
val version = "1.9.0"
val versionCode = 50
}
object General {
......@@ -28,6 +28,7 @@ object General {
"-keep interface com.actionbarsherlock.** { *; }",
"-keep class org.jraf.android.backport.** { *; }",
"-keep class com.github.shadowsocks.** { *; }",
"-keep class * extends com.j256.ormlite.** { *; }",
"-keepattributes *Annotation*")
val proguardSettings = Seq (
......@@ -36,8 +37,7 @@ object General {
)
val miscSettings = Seq (
cachePasswords := true,
publicServer := "127.0.0.1"
cachePasswords := true
)
lazy val fullAndroidSettings =
......
......@@ -6,4 +6,4 @@ resolvers += Resolver.url("madeye private releases", new URL("http://madeye-mave
addSbtPlugin("org.scala-sbt" % "sbt-android" % "0.7.1-SNAPSHOT")
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.4.0")
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.5.1")
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 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.
-->
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/md__list_pressed_holo_dark" />
<item android:drawable="@drawable/md__list_longpressed_holo" />
</transition>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:drawable="@color/md__transparent" />
<!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
<item android:state_focused="true" android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/md__list_selector_disabled_holo_dark" />
<item android:state_focused="true" android:state_enabled="false" android:drawable="@drawable/md__list_selector_disabled_holo_dark" />
<item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/md__list_selector_background_transition_holo_dark" />
<item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/md__list_selector_background_transition_holo_dark" />
<item android:state_focused="true" android:drawable="@drawable/md__list_focused_holo" />
</selector>
......@@ -9,8 +9,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_marginLeft="16dp"
android:textSize="28sp"
android:textSize="24sp"
android:textColor="@color/title"
android:text="@string/app_name"
android:layout_alignParentLeft="true"
......@@ -21,6 +20,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight = "10dp"
android:layout_centerVertical="true"/>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/MenuDrawer.Widget.Category" />
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/MenuDrawer.Widget.Title" />
\ No newline at end of file
......@@ -22,5 +22,7 @@
<color name="grey1">#646464</color>
<color name="grey2">#c8c8c8</color>
<color name="grey3">#323232</color>
<color name="md__transparent">#00000000</color>
<color name="md__defaultBackground">#FF555555</color>
</resources>
\ No newline at end of file
<resources>
<!-- ID used when defining the content layout in XML. -->
<item name="mdContent" type="id"/>
<!-- ID used when defining the menu layout in XML. -->
<item name="mdMenu" type="id"/>
<!-- The ID of the content container. -->
<item name="md__content" type="id"/>
<!-- The ID of the menu container. -->
<item name="md__menu" type="id"/>
<!-- The ID of the drawer. -->
<item name="md__drawer" type="id"/>
<!-- Used with View#setTag(int) to specify a position for the active view. -->
<item name="mdActiveViewPosition" type="id"/>
<item name="mdItem" type="id"/>
<item name="md__translationX" type="id"/>
<item name="md__translationY" type="id"/>
</resources>
\ No newline at end of file
......@@ -10,6 +10,8 @@
<!-- proxy category -->
<string name="proxy_cat">Server Settings</string>
<string name="profile">Profile</string>
<string name="profile_summary">Profile name</string>
<string name="proxy">Server</string>
<string name="proxy_summary">Hostname or IP Address of your Shadowsocks server</string>
<string name="remote_port">Remote Port</string>
......@@ -73,5 +75,6 @@
<string name="about_title">Shadowsocks %s</string>
<string name="flush_dnscache">Flush DNS Cache</string>
<string name="flushing">Flushing</string>
<string name="add_profile">Add Profile</string>
</resources>
......@@ -23,11 +23,47 @@
<!-- Variation on the Light theme that turns off the title -->
<style name="Theme.Shadowsocks" parent="@style/Theme.Shadow">
<item name="switchStyle">@style/Widget.Shadow.Switch</item>
<item name="menuDrawerStyle">@style/MenuDrawerStyle</item>
</style>
<style name="Theme.AppManager" parent="@style/Theme.Shadow">
<item name="switchStyle">@style/Widget.Holo.Light.CompoundButton.Switch</item>
</style>
<style name="MenuDrawer"/>
<style name="MenuDrawer.Widget"/>
<style name="MenuDrawer.Widget.Category">
<item name="android:textStyle">bold</item>
<item name="android:textColor">?android:attr/textColorSecondaryInverse</item>
<item name="android:textSize">14sp</item>
<item name="android:textAllCaps">true</item>
<item name="android:gravity">center_vertical</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:background">@drawable/md__category_background</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
</style>
<style name="MenuDrawer.Widget.Title">
<item name="android:background">@drawable/md__list_selector_holo_dark</item>
<item name="android:textAppearance">?android:attr/textAppearance</item>
<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="android:textSize">18sp</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:paddingRight">32dp</item>
<item name="android:paddingTop">8dp</item>
<item name="android:paddingBottom">8dp</item>
<item name="android:drawablePadding">16dp</item>
<item name="android:gravity">center_vertical</item>
</style>
<style name="MenuDrawerStyle" parent="Widget.MenuDrawer">
<item name="mdActiveIndicator">@drawable/menu_arrow</item>
<item name="mdMenuSize">250dp</item>
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue="Default"
android:key="profileName"
android:summary="@string/profile_summary"
android:title="@string/profile">
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue="198.199.101.152"
android:key="proxy"
......
......@@ -109,7 +109,6 @@ class AppManager extends SherlockActivity with OnCheckedChangeListener with OnCl
val MSG_LOAD_START = 1
val MSG_LOAD_FINISH = 2
val SCHEME = "app://"
val STUB = android.R.drawable.sym_def_app_icon
implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]) = new ObjectArrayTools(a)
......@@ -184,7 +183,7 @@ class AppManager extends SherlockActivity with OnCheckedChangeListener with OnCl
.cacheOnDisc()
.displayer(new FadeInBitmapDisplayer(300))
.build()
ImageLoader.getInstance().displayImage("app://" + app.uid, entry.icon, options)
ImageLoader.getInstance().displayImage(Scheme.APP + app.uid, entry.icon, options)
entry.text.setText(app.name)
val box: CheckBox = entry.box
......@@ -327,7 +326,7 @@ class AppManager extends SherlockActivity with OnCheckedChangeListener with OnCl
}
override def getStreamFromOtherSource(imageUri: String, extra: AnyRef): InputStream = {
val uid = imageUri.substring(SCHEME.length).toInt
val uid = imageUri.substring(Scheme.APP.length).toInt
val drawable = Utils.getAppIcon(getBaseContext, uid)
val bitmap = Utils.drawableToBitmap(drawable)
......
......@@ -40,9 +40,13 @@ package com.github.shadowsocks
import android.app.Application
import com.google.analytics.tracking.android.EasyTracker
import com.github.shadowsocks.database.{ProfileManager, DBHelper}
class ShadowsocksApplication extends Application {
lazy val dbHelper = new DBHelper(this)
override def onCreate() {
EasyTracker.getInstance.setContext(this)
}
}
......@@ -324,18 +324,8 @@ class ShadowsocksService extends Service {
val icon = getResources.getDrawable(R.drawable.ic_stat_shadowsocks)
if (rate >= 0) {
val bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth * 4,
icon.getIntrinsicHeight * 4, Bitmap.Config.ARGB_8888)
val r = rate.toString
val size = bitmap.getHeight / 4
val canvas = new Canvas(bitmap)
val paint = new Paint()
paint.setColor(Color.WHITE)
paint.setTextSize(size)
val bounds = new Rect()
paint.getTextBounds(r, 0, r.length, bounds)
canvas.drawText(r, (bitmap.getWidth - bounds.width()) / 2,
bitmap.getHeight - (bitmap.getHeight - bounds.height()) / 2, paint)
val bitmap =Utils.getBitmap(rate.toString, icon.getIntrinsicWidth * 4,
icon.getIntrinsicHeight * 4, Color.TRANSPARENT)
builder.setLargeIcon(bitmap)
if (rate < 1000) {
......
......@@ -49,10 +49,11 @@ import org.apache.http.conn.util.InetAddressUtils
import scala.collection.mutable.ArrayBuffer
import org.xbill.DNS._
import scala.Some
import android.graphics.{Canvas, Bitmap}
import android.graphics._
import android.app.ActivityManager
import android.os.Build
import android.provider.Settings
import scala.Some
object Config {
val SHADOWSOCKS = "{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}"
......@@ -112,10 +113,13 @@ object Config {
}
case class Config(isGlobalProxy: Boolean, isGFWList: Boolean, isBypassApps: Boolean, isTrafficStat: Boolean,
var proxy: String, sitekey: String, encMethod: String, remotePort: Int,
profileName: String, var proxy: String, sitekey: String, encMethod: String, remotePort: Int,
localPort: Int, proxiedAppString: String)
object Key {
val profileId = "profileId"
val profileName = "profileName"
val proxied = "Proxyed"
val isRoot = "isRoot"
......@@ -137,6 +141,11 @@ object Key {
val localPort = "port"
}
object Scheme {
val APP = "app://"
val PROFILE = "profile://"
}
object State {
val INIT = 0
val CONNECTING = 1
......@@ -162,6 +171,7 @@ object Extra {
edit.putBoolean(Key.isBypassApps, config.isBypassApps)
edit.putBoolean(Key.isTrafficStat, config.isTrafficStat)
edit.putString(Key.profileName, config.profileName)
edit.putString(Key.proxy, config.proxy)
edit.putString(Key.sitekey, config.sitekey)
edit.putString(Key.encMethod, config.encMethod)
......@@ -177,6 +187,7 @@ object Extra {
val isBypassApps = intent.getBooleanExtra(Key.isBypassApps, false)
val isTrafficStat = intent.getBooleanExtra(Key.isTrafficStat, false)
val profileName = intent.getStringExtra(Key.profileName)
val proxy = intent.getStringExtra(Key.proxy)
val sitekey = intent.getStringExtra(Key.sitekey)
val encMethod = intent.getStringExtra(Key.encMethod)
......@@ -184,7 +195,7 @@ object Extra {
val localPort = intent.getIntExtra(Key.localPort, 1984)
val proxiedString = intent.getStringExtra(Key.proxied)
new Config(isGlobalProxy, isGFWList, isBypassApps, isTrafficStat, proxy, sitekey, encMethod, remotePort,
new Config(isGlobalProxy, isGFWList, isBypassApps, isTrafficStat, profileName, proxy, sitekey, encMethod, remotePort,
localPort, proxiedString)
}
......@@ -194,6 +205,7 @@ object Extra {
val isBypassApps = settings.getBoolean(Key.isBypassApps, false)
val isTrafficStat = settings.getBoolean(Key.isTrafficStat, false)
val profileName = settings.getString(Key.profileName, "default")
val proxy = settings.getString(Key.proxy, "127.0.0.1") match {
case "198.199.101.152" => BuildConfig.SERVER
case s: String => s
......@@ -232,6 +244,7 @@ object Extra {
intent.putExtra(Key.isBypassApps, isBypassApps)
intent.putExtra(Key.isTrafficStat, isTrafficStat)
intent.putExtra(Key.profileName, profileName)
intent.putExtra(Key.proxy, proxy)
intent.putExtra(Key.sitekey, sitekey)
intent.putExtra(Key.encMethod, encMethod)
......@@ -265,6 +278,36 @@ object Utils {
var data_path: String = null
var rootTries = 0
/*
* round or floor depending on whether you are using offsets(floor) or
* widths(round)
*/
def dpToPx (context: Context, dp: Float): Float = {
val density = context.getResources.getDisplayMetrics.density
dp * density
}
def pxToDp (context: Context, px: Float): Float = {
val density = context.getResources.getDisplayMetrics.density
px / density
}
def getBitmap(text: String, width: Int, height: Int, background: Int): Bitmap = {
val bitmap = Bitmap.createBitmap(width,
height, Bitmap.Config.ARGB_8888)
val size = bitmap.getHeight / 4
val canvas = new Canvas(bitmap)
canvas.drawColor(background)
val paint = new Paint()
paint.setColor(Color.WHITE)
paint.setTextSize(size)
val bounds = new Rect()
paint.getTextBounds(text, 0, text.length, bounds)
canvas.drawText(text, (bitmap.getWidth - bounds.width()) / 2,
bitmap.getHeight - (bitmap.getHeight - bounds.height()) / 2, paint)
bitmap
}
// Blocked > 3 seconds
def toggleAirplaneMode(context: Context) {
if (Build.VERSION.SDK_INT >= 17) {
......
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2013 <max.c.lv@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (oo) //###############-
* -#################\\ / VV \ //#################-
* -###################\\/ \//###################-
* _#/|##########/\######( /\ )######/\##########|\#_
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
* ` ` ` ` / | | | | \ ' ' ' '
* ( | | | | )
* __\ | | | | /__
* (vvv(VVV)(VVV)vvv)
*
* HERE BE DRAGONS
*
*/
package com.github.shadowsocks.database
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import com.j256.ormlite.support.ConnectionSource
import com.j256.ormlite.table.TableUtils
import com.j256.ormlite.dao.Dao
class DBHelper(val context: Context) extends OrmLiteSqliteOpenHelper(context, "profile.db", null, 7) {
lazy val profileDao:Dao[Profile,Int] = getDao(classOf[Profile])
def onCreate(database: SQLiteDatabase, connectionSource: ConnectionSource) {
TableUtils.createTable(connectionSource, classOf[Profile])
}
def onUpgrade(database: SQLiteDatabase, connectionSource: ConnectionSource, oldVersion: Int, newVersion: Int) {
TableUtils.dropTable(connectionSource, classOf[Profile], true)
onCreate(database, connectionSource)
}
}
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2013 <max.c.lv@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (oo) //###############-
* -#################\\ / VV \ //#################-
* -###################\\/ \//###################-
* _#/|##########/\######( /\ )######/\##########|\#_
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
* ` ` ` ` / | | | | \ ' ' ' '
* ( | | | | )
* __\ | | | | /__
* (vvv(VVV)(VVV)vvv)
*
* HERE BE DRAGONS
*
*/
package com.github.shadowsocks.database
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.TextView
import com.github.shadowsocks.{Shadowsocks, R}
import android.view.View.{OnLongClickListener, OnClickListener}
case class Item(id: Int, title: String, iconRes: Int,
click: Int => Unit, longClick: Int => Boolean = _ => false)
case class Category(title: String)
object MenuAdapter {
trait MenuListener {
def onActiveViewChanged(v: View, pos: Int)
}
val UNDEFINED = -1
val CATEGORY = 0
val ITEM = 1
}
class MenuAdapter(context: Context, var items: List[Any]) extends BaseAdapter {
def setListener(listener: MenuAdapter.MenuListener) {
this.listener = listener
}
def setActiveId(activeId: Int) {
this.activeId = activeId
}
@Override def getCount: Int = {
items.size
}
@Override def getItem(position: Int): Object = {
items(position).asInstanceOf[Object]
}
@Override def getItemId(position: Int): Long = {
position
}
@Override override def getItemViewType(position: Int): Int = {
getItem(position) match {
case Category => MenuAdapter.CATEGORY
case Item => MenuAdapter.ITEM
case _ => MenuAdapter.UNDEFINED
}
}
@Override override def getViewTypeCount: Int = {
3
}
@Override override def isEnabled(position: Int): Boolean = {
getItemViewType(position) != MenuAdapter.UNDEFINED
}
@Override override def areAllItemsEnabled: Boolean = {
false
}
@Override def getView(position: Int, convertView: View, parent: ViewGroup): View = {
var v: View = convertView
val item = getItem(position)
item match {
case value: Category =>
if (v == null) {
v = LayoutInflater.from(context).inflate(R.layout.menu_row_category, parent, false)
}
v.asInstanceOf[TextView].setText(value.title)
case value: Item =>
if (v == null) {
v = LayoutInflater.from(context).inflate(R.layout.menu_row_item, parent, false)
}
val tv: TextView = v.asInstanceOf[TextView]
tv.setText(value.title)
if (value.iconRes != -1) {
tv.setCompoundDrawablesWithIntrinsicBounds(value.iconRes, 0, 0, 0)
}
tv.setOnClickListener(new OnClickListener {
def onClick(view: View) {
val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
item.click(item.id)
}
})
tv.setOnLongClickListener(new OnLongClickListener {
def onLongClick(view: View): Boolean = {
val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
item.longClick(item.id)
}
})
v.setTag(R.id.mdItem, value)
if (value.id == activeId) {
if (listener != null) listener.onActiveViewChanged(v, position)
}
case _ =>
}
v.setTag(R.id.mdActiveViewPosition, position)
v
}
def updateList(list: List[Any], activeId: Int) {
items = list
this.activeId = activeId
notifyDataSetChanged()
}
private var listener: MenuAdapter.MenuListener = null
private var activeId: Int = -1
}
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2013 <max.c.lv@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (oo) //###############-
* -#################\\ / VV \ //#################-
* -###################\\/ \//###################-
* _#/|##########/\######( /\ )######/\##########|\#_
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
* ` ` ` ` / | | | | \ ' ' ' '
* ( | | | | )
* __\ | | | | /__
* (vvv(VVV)(VVV)vvv)
*
* HERE BE DRAGONS
*
*/
package com.github.shadowsocks.database
import com.j256.ormlite.field.{DataType, DatabaseField}
class Profile {
@DatabaseField(generatedId = true)
var id: Int = 0
@DatabaseField
var name: String = "Untitled"
@DatabaseField
var host: String = ""
@DatabaseField
var localPort: Int = 0
@DatabaseField
var remotePort: Int = 0
@DatabaseField
var password: String = ""
@DatabaseField
var method: String = ""
@DatabaseField
var date: String = ""
@DatabaseField
var upload: Int = 0
@DatabaseField
var download: Int = 0
@DatabaseField
var chnroute: Boolean = false
@DatabaseField
var global: Boolean = false
@DatabaseField
var bypass: Boolean = false
@DatabaseField
var traffic: Boolean = false
@DatabaseField(dataType = DataType.LONG_STRING)
var individual: String = ""
@DatabaseField(dataType = DataType.LONG_STRING)
var description: String = ""
}
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2013 <max.c.lv@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (oo) //###############-
* -#################\\ / VV \ //#################-
* -###################\\/ \//###################-
* _#/|##########/\######( /\ )######/\##########|\#_
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
* ` ` ` ` / | | | | \ ' ' ' '
* ( | | | | )
* __\ | | | | /__
* (vvv(VVV)(VVV)vvv)
*
* HERE BE DRAGONS
*
*/
package com.github.shadowsocks.database
import android.util.Log
import com.github.shadowsocks._
import android.content.{SharedPreferences, Context}
class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
def createOrUpdateProfile(profile: Profile): Boolean = {
try {
dbHelper.profileDao.createOrUpdate(profile)
true
} catch {
case ex: Exception =>
Log.e(Shadowsocks.TAG, "addProfile", ex)
false
}
}
def getProfile(id: Int): Option[Profile] = {
try {
dbHelper.profileDao.queryForId(id) match {
case profile: Profile => Option(profile)
case _ => None
}
} catch {
case ex: Exception =>
Log.e(Shadowsocks.TAG, "getProfile", ex)
return None
}
}
def delProfile(id: Int): Boolean = {
try {
dbHelper.profileDao.deleteById(id)
true
} catch {
case ex: Exception =>
Log.e(Shadowsocks.TAG, "delProfile", ex)
false
}
}
def getAllProfiles: Option[List[Profile]] = {
try {
import scala.collection.JavaConversions._
Option(dbHelper.profileDao.queryForAll().toList)
} catch {
case ex: Exception =>
Log.e(Shadowsocks.TAG, "getAllProfiles", ex)
None
}
}
def reload(id: Int): Profile = {
save()
load(id)
}
def load(id: Int): Profile = {
val profile = getProfile(id) getOrElse {
val p = new Profile()
createOrUpdateProfile(p)
p
}
val edit = settings.edit()
edit.putBoolean(Key.isGlobalProxy, profile.global)
edit.putBoolean(Key.isGFWList, profile.chnroute)
edit.putBoolean(Key.isBypassApps, profile.bypass)
edit.putBoolean(Key.isTrafficStat, profile.traffic)
edit.putString(Key.profileName, profile.name)
edit.putString(Key.proxy, profile.host)
edit.putString(Key.sitekey, profile.password)
edit.putString(Key.encMethod, profile.method)
edit.putString(Key.remotePort, profile.remotePort.toString)
edit.putString(Key.localPort, profile.localPort.toString)
edit.putString(Key.proxied, profile.individual)
edit.putInt(Key.profileId, profile.id)
edit.commit()
profile
}
def save() {
val profile = new Profile()
profile.global = settings.getBoolean(Key.isGlobalProxy, false)
profile.chnroute = settings.getBoolean(Key.isGFWList, false)
profile.bypass = settings.getBoolean(Key.isBypassApps, false)
profile.traffic = settings.getBoolean(Key.isTrafficStat, false)
profile.name = settings.getString(Key.profileName, "default")
profile.host = settings.getString(Key.proxy, "127.0.0.1")
profile.password = settings.getString(Key.sitekey, "default")
profile.method = settings.getString(Key.encMethod, "table")
profile.remotePort = try {
Integer.valueOf(settings.getString(Key.remotePort, "1984"))
} catch {
case ex: NumberFormatException => {
1984
}
}
profile.localPort = try {
Integer.valueOf(settings.getString(Key.localPort, "1984"))
} catch {
case ex: NumberFormatException => {
1984
}
}
profile.individual = settings.getString(Key.proxied, "")
profile.id = settings.getInt(Key.profileId, -1)
Log.d(Shadowsocks.TAG, "save " + profile.id + " " + profile.name)
createOrUpdateProfile(profile)
}
}
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