Commit a2848ce5 authored by Max Lv's avatar Max Lv

update UI

parent 6f55861d
......@@ -9,7 +9,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textSize="22sp"
android:textSize="24sp"
android:textColor="@color/title"
android:text="@string/app_name"
android:layout_alignParentLeft="true"
......@@ -20,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>
......@@ -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>
<?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)
......
......@@ -41,21 +41,16 @@ package com.github.shadowsocks
import android.app.{Activity, AlertDialog, ProgressDialog}
import android.content._
import android.content.res.AssetManager
import android.graphics.Typeface
import android.graphics.{Color, Bitmap, Typeface}
import android.os._
import android.preference.{Preference, PreferenceManager}
import android.preference._
import android.util.Log
import android.view.{ViewGroup, Gravity, ViewParent, KeyEvent}
import android.view.{View, ViewGroup, ViewParent, KeyEvent}
import android.widget._
import com.actionbarsherlock.view.Menu
import com.actionbarsherlock.view.MenuItem
import com.google.analytics.tracking.android.EasyTracker
import de.keyboardsurfer.android.widget.crouton.Crouton
import de.keyboardsurfer.android.widget.crouton.Style
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.io._
import java.util.Hashtable
import net.saik0.android.unifiedpreference.UnifiedPreferenceFragment
import net.saik0.android.unifiedpreference.UnifiedSherlockPreferenceActivity
......@@ -71,21 +66,107 @@ import net.simonvt.menudrawer.MenuDrawer
import com.github.shadowsocks.database._
import scala.collection.mutable.ListBuffer
import com.github.shadowsocks.database.Profile
import com.nostra13.universalimageloader.core.{ImageLoader, ImageLoaderConfiguration}
import com.nostra13.universalimageloader.core.download.BaseImageDownloader
import com.github.shadowsocks.database.Item
import com.github.shadowsocks.database.Category
import com.actionbarsherlock.view.MenuItem
import com.github.shadowsocks.preferences.{PasswordEditTextPreference, SummaryEditTextPreference}
import com.github.shadowsocks.database.Item
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.Category
import scala.Option
class ProfileIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int)
extends BaseImageDownloader(context, connectTimeout, readTimeout) {
def this(context: Context) {
this(context, 0, 0)
}
override def getStreamFromOtherSource(imageUri: String, extra: AnyRef): InputStream = {
val text = imageUri.substring(Scheme.PROFILE.length)
val size = Utils.dpToPx(context, 16).toInt
val idx = text.getBytes.last % 6
val color = Seq(Color.MAGENTA, Color.GREEN, Color.YELLOW, Color.BLUE, Color.DKGRAY, Color.CYAN)(
idx)
val bitmap = Utils.getBitmap(text, size, size, color)
val os = new ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os)
new ByteArrayInputStream(os.toByteArray)
}
}
object Typefaces {
def get(c: Context, assetPath: String): Typeface = {
cache synchronized {
if (!cache.containsKey(assetPath)) {
try {
val t: Typeface = Typeface.createFromAsset(c.getAssets, assetPath)
cache.put(assetPath, t)
} catch {
case e: Exception => {
Log.e(TAG, "Could not get typeface '" + assetPath + "' because " + e.getMessage)
return null
}
}
}
return cache.get(assetPath)
}
}
private final val TAG: String = "Typefaces"
private final val cache: Hashtable[String, Typeface] = new Hashtable[String, Typeface]
}
object Shadowsocks {
val PREFS_NAME = "Shadowsocks"
val PROXY_PREFS = Array(Key.proxy, Key.remotePort, Key.localPort, Key.sitekey, Key.encMethod)
val FEATRUE_PREFS = Array(Key.isGFWList, Key.isGlobalProxy, Key.proxyedApps,
Key.isTrafficStat, Key.isAutoConnect)
val PROXY_PREFS = Array(Key.profileName, Key.proxy, Key.remotePort, Key.localPort, Key.sitekey, Key.encMethod)
val FEATRUE_PREFS = Array(Key.isGFWList, Key.isGlobalProxy, Key.proxyedApps, Key.isTrafficStat,
Key.isAutoConnect)
val TAG = "Shadowsocks"
val REQUEST_CONNECT = 1
private var vpnEnabled = -1
var currentProfile: Profile = null
def updateListPreference(pref: Preference, value: String) {
pref.setSummary(value)
pref.asInstanceOf[ListPreference].setValue(value)
}
def updatePasswordEditTextPreference(pref: Preference, value: String) {
pref.setSummary(value)
pref.asInstanceOf[PasswordEditTextPreference].setText(value)
}
def updateSummaryEditTextPreference(pref: Preference, value: String) {
pref.setSummary(value)
pref.asInstanceOf[SummaryEditTextPreference].setText(value)
}
def updateCheckBoxPreference(pref: Preference, value: Boolean) {
pref.asInstanceOf[CheckBoxPreference].setChecked(value)
}
def updatePreference(pref: Preference, name: String, profile: Profile) {
name match {
case Key.profileName => updateSummaryEditTextPreference(pref, profile.name)
case Key.proxy => updateSummaryEditTextPreference(pref, profile.host)
case Key.remotePort => updateSummaryEditTextPreference(pref, profile.remotePort.toString)
case Key.localPort => updateSummaryEditTextPreference(pref, profile.localPort.toString)
case Key.sitekey => updatePasswordEditTextPreference(pref, profile.password)
case Key.encMethod => updateListPreference(pref, profile.method)
case Key.isGFWList => updateCheckBoxPreference(pref, profile.chnroute)
case Key.isGlobalProxy => updateCheckBoxPreference(pref, profile.global)
case Key.isTrafficStat => updateCheckBoxPreference(pref, profile.traffic)
case _ =>
}
}
def isServiceStarted(context: Context): Boolean = {
ShadowsocksService.isServiceStarted(context) || ShadowVpnService.isServiceStarted(context)
}
......@@ -105,6 +186,13 @@ object Shadowsocks {
}
}
private def updatePreferenceScreen() {
for (name <- Shadowsocks.PROXY_PREFS) {
val pref = findPreference(name)
Shadowsocks.updatePreference(pref, name, Shadowsocks.currentProfile)
}
}
override def onCreate(bundle: Bundle) {
super.onCreate(bundle)
val filter = new IntentFilter()
......@@ -112,6 +200,7 @@ object Shadowsocks {
receiver = new BroadcastReceiver {
def onReceive(p1: Context, p2: Intent) {
setPreferenceEnabled()
updatePreferenceScreen()
}
}
getActivity.getApplicationContext.registerReceiver(receiver, filter)
......@@ -132,7 +221,7 @@ object Shadowsocks {
}
}
class FeatureFragment extends UnifiedPreferenceFragment with OnSharedPreferenceChangeListener {
class FeatureFragment extends UnifiedPreferenceFragment {
var receiver: BroadcastReceiver = null
......@@ -144,8 +233,8 @@ object Shadowsocks {
if (pref != null) {
val status = getActivity.getSharedPreferences(Key.status, Context.MODE_PRIVATE)
val isRoot = status.getBoolean(Key.isRoot, false)
if (Seq(Key.isAutoConnect, Key.isGlobalProxy, Key.isTrafficStat,
Key.proxyedApps).contains(name)) {
if (Seq(Key.isAutoConnect, Key.isGlobalProxy, Key.isTrafficStat, Key.proxyedApps)
.contains(name)) {
pref.setEnabled(enabled && isRoot)
} else {
pref.setEnabled(enabled)
......@@ -154,6 +243,13 @@ object Shadowsocks {
}
}
private def updatePreferenceScreen() {
for (name <- Shadowsocks.FEATRUE_PREFS) {
val pref = findPreference(name)
Shadowsocks.updatePreference(pref, name, Shadowsocks.currentProfile)
}
}
override def onCreate(bundle: Bundle) {
super.onCreate(bundle)
val filter = new IntentFilter()
......@@ -161,6 +257,7 @@ object Shadowsocks {
receiver = new BroadcastReceiver {
def onReceive(p1: Context, p2: Intent) {
setPreferenceEnabled()
updatePreferenceScreen()
}
}
getActivity.getApplicationContext.registerReceiver(receiver, filter)
......@@ -179,40 +276,15 @@ object Shadowsocks {
override def onPause() {
super.onPause()
}
def onSharedPreferenceChanged(prefs: SharedPreferences, key: String) {
if (key == Key.isGlobalProxy) setPreferenceEnabled()
}
}
}
object Typefaces {
def get(c: Context, assetPath: String): Typeface = {
cache synchronized {
if (!cache.containsKey(assetPath)) {
try {
val t: Typeface = Typeface.createFromAsset(c.getAssets, assetPath)
cache.put(assetPath, t)
} catch {
case e: Exception => {
Log.e(TAG, "Could not get typeface '" + assetPath + "' because " + e.getMessage)
return null
}
}
}
return cache.get(assetPath)
}
}
private final val TAG: String = "Typefaces"
private final val cache: Hashtable[String, Typeface] = new Hashtable[String, Typeface]
}
class Shadowsocks
extends UnifiedSherlockPreferenceActivity
with CompoundButton.OnCheckedChangeListener
with OnSharedPreferenceChangeListener {
with OnSharedPreferenceChangeListener
with MenuAdapter.MenuListener{
private val MSG_CRASH_RECOVER: Int = 1
private val MSG_INITIAL_FINISH: Int = 2
......@@ -227,9 +299,9 @@ class Shadowsocks
lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE)
lazy val receiver = new StateBroadcastReceiver
lazy val drawer = MenuDrawer.attach(this)
lazy val listView = new ListView(this)
lazy val menuAdapter = new MenuAdapter(this, getMenuList)
lazy val profileManager = getApplication.asInstanceOf[ShadowsocksApplication].profileManager
lazy val listView = new ListView(this)
lazy val profileManager = new ProfileManager(settings, getApplication.asInstanceOf[ShadowsocksApplication].dbHelper)
private val handler: Handler = new Handler {
override def handleMessage(msg: Message) {
......@@ -291,10 +363,10 @@ class Shadowsocks
}
}
def onSharedPreferenceChanged(prefs: SharedPreferences, key: String) {
if (key == Key.isGlobalProxy) {
val enabled = state != State.CONNECTED && state != State.CONNECTING
setPreferenceEnabled(enabled)
override def onSharedPreferenceChanged(prefs: SharedPreferences, key: String) {
if (key == Key.profileName) {
profileManager.save()
menuAdapter.updateList(getMenuList, Shadowsocks.currentProfile.id)
}
}
......@@ -407,6 +479,10 @@ class Shadowsocks
setHeaderRes(R.xml.shadowsocks_headers)
super.onCreate(savedInstanceState)
Shadowsocks.currentProfile = {
profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse new Profile()
}
listView.setAdapter(menuAdapter)
drawer.setMenuView(listView)
......@@ -422,7 +498,6 @@ class Shadowsocks
getSupportActionBar.setDisplayShowTitleEnabled(false)
getSupportActionBar.setDisplayShowCustomEnabled(true)
// getSupportActionBar.setDisplayShowHomeEnabled(false)
val title: TextView = switchLayout.findViewById(R.id.title).asInstanceOf[TextView]
val tf: Typeface = Typefaces.get(this, "fonts/Iceland.ttf")
if (tf != null) title.setTypeface(tf)
......@@ -445,16 +520,49 @@ class Shadowsocks
handler.sendEmptyMessage(MSG_INITIAL_FINISH)
}
}
}
override def onActiveViewChanged(v: View, pos: Int) {
drawer.setActiveView(v, pos)
}
def updateProfile(id: Int) {
drawer.closeMenu(true)
handler.postDelayed(new Runnable {
def run() {
Shadowsocks.currentProfile = profileManager.load(id)
updatePreferenceScreen()
}
}, 600)
if (!isSinglePane) sendBroadcast(new Intent(Action.UPDATE_FRAGMENT))
}
def delProfile(id: Int): Boolean = {
drawer.closeMenu(true)
handler.postDelayed(new Runnable {
def run() {
profileManager.delProfile(id)
val profileId = {
val profiles = profileManager.getAllProfiles.getOrElse(List[Profile]())
if (profiles.isEmpty) -1 else profiles(0).id
}
Shadowsocks.currentProfile = profileManager.load(profileId)
updatePreferenceScreen()
menuAdapter.updateList(getMenuList, Shadowsocks.currentProfile.id)
}
}, 600)
if (!isSinglePane) sendBroadcast(new Intent(Action.UPDATE_FRAGMENT))
true
}
def getProfileList: List[Item] = {
val list = profileManager.getAllProfiles getOrElse List[Profile]()
list.map(p => new Item(p.id, p.name, -1, updateProfile))
list.map(p => new Item(p.id, p.name, -1, updateProfile, delProfile))
}
def getMenuList: List[Any] = {
......@@ -465,28 +573,31 @@ class Shadowsocks
buf ++= getProfileList
buf += new Item(-1, getString(R.string.add_profile), android.R.drawable.ic_menu_add, _ => {
updateProfile(-1)
profileManager.save()
menuAdapter.updateList(getMenuList, Shadowsocks.currentProfile.id)
})
buf += new Category("Settings")
buf += new Item(-1, getString(R.string.recovery), android.R.drawable.ic_menu_revert,
_ => {
EasyTracker.getTracker.sendEvent(Shadowsocks.TAG, "reset", getVersionName, 0L)
recovery()
})
buf += new Item(-100, getString(R.string.recovery), android.R.drawable.ic_menu_revert, _ => {
EasyTracker.getTracker.sendEvent(Shadowsocks.TAG, "reset", getVersionName, 0L)
recovery()
})
buf += new Item(-2, getString(R.string.flush_dnscache), android.R.drawable.ic_menu_delete,
_ => {
buf +=
new Item(-200, getString(R.string.flush_dnscache), android.R.drawable.ic_menu_delete, _ => {
EasyTracker.getTracker.sendEvent(Shadowsocks.TAG, "flush_dnscache", getVersionName, 0L)
flushDnsCache()
})
buf += new Item(-3, getString(R.string.about), android.R.drawable.ic_menu_info_details,
_ => {
EasyTracker.getTracker.sendEvent(Shadowsocks.TAG, "about", getVersionName, 0L)
showAbout()
})
buf += new Item(-300, getString(R.string.about), android.R.drawable.ic_menu_info_details, _ => {
EasyTracker.getTracker.sendEvent(Shadowsocks.TAG, "about", getVersionName, 0L)
showAbout()
})
buf.toList
}
override def onKeyDown(keyCode: Int, event: KeyEvent): Boolean = {
......@@ -516,6 +627,7 @@ class Shadowsocks
protected override def onPause() {
super.onPause()
prepared = false
settings.unregisterOnSharedPreferenceChangeListener(this)
}
protected override def onResume() {
......@@ -549,6 +661,8 @@ class Shadowsocks
}
switchButton.setOnCheckedChangeListener(this)
settings.registerOnSharedPreferenceChangeListener(this)
}
private def setPreferenceEnabled(enabled: Boolean) {
......@@ -562,8 +676,8 @@ class Shadowsocks
for (name <- Shadowsocks.FEATRUE_PREFS) {
val pref = findPreference(name)
if (pref != null) {
if (Seq(Key.isAutoConnect, Key.isGlobalProxy, Key.isTrafficStat,
Key.proxyedApps).contains(name)) {
if (Seq(Key.isAutoConnect, Key.isGlobalProxy, Key.isTrafficStat, Key.proxyedApps)
.contains(name)) {
pref.setEnabled(enabled && isRoot)
} else {
pref.setEnabled(enabled)
......@@ -572,6 +686,18 @@ class Shadowsocks
}
}
private def updatePreferenceScreen() {
val profile = Shadowsocks.currentProfile
for (name <- Shadowsocks.PROXY_PREFS) {
val pref = findPreference(name)
Shadowsocks.updatePreference(pref, name, profile)
}
for (name <- Shadowsocks.FEATRUE_PREFS) {
val pref = findPreference(name)
Shadowsocks.updatePreference(pref, name, profile)
}
}
override def onStart() {
super.onStart()
EasyTracker.getInstance.activityStart(this)
......@@ -790,7 +916,7 @@ class Shadowsocks
setPreferenceEnabled(enabled = true)
}
}
sendBroadcast(new Intent(Action.UPDATE_FRAGMENT))
if (!isSinglePane) sendBroadcast(new Intent(Action.UPDATE_FRAGMENT))
}
}
......
......@@ -44,7 +44,6 @@ import com.github.shadowsocks.database.{ProfileManager, DBHelper}
class ShadowsocksApplication extends Application {
lazy val dbHelper = new DBHelper(this)
lazy val profileManager = new ProfileManager(dbHelper)
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) {
......
......@@ -46,7 +46,7 @@ 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, 3) {
class DBHelper(val context: Context) extends OrmLiteSqliteOpenHelper(context, "profile.db", null, 7) {
lazy val profileDao:Dao[Profile,Int] = getDao(classOf[Profile])
......
......@@ -39,39 +39,39 @@
package com.github.shadowsocks.database
import android.content.{DialogInterface, 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 com.github.shadowsocks.R
import android.view.View.OnClickListener
import com.github.shadowsocks.{Shadowsocks, R}
import android.view.View.{OnLongClickListener, OnClickListener}
case class Item(id: Int, title: String, iconRes: Int, action: Int => Unit)
case class Item(id: Int, title: String, iconRes: Int,
click: Int => Unit, longClick: Int => Boolean = _ => false)
case class Category (title: String)
case class Category(title: String)
object MenuAdapter {
trait MenuListener {
def onActiveViewChanged(v: View)
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 {
class MenuAdapter(context: Context, var items: List[Any]) extends BaseAdapter {
def setListener(listener: MenuAdapter.MenuListener) {
mListener = listener
this.listener = listener
}
def setActivePosition(activePosition: Int) {
mActivePosition = activePosition
this.activePosition = activePosition
}
@Override def getCount: Int = {
......@@ -121,32 +121,46 @@ class MenuAdapter (context: Context, var items: List[Any]) extends BaseAdapter {
}
val tv: TextView = v.asInstanceOf[TextView]
tv.setText(value.title)
tv.setCompoundDrawablesWithIntrinsicBounds(value.iconRes, 0, 0, 0)
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.action(item.id)
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)
case _ =>
}
v.setTag(R.id.mdActiveViewPosition, position)
if (position == mActivePosition) {
if (mListener != null) mListener.onActiveViewChanged(v)
if (position == activePosition) {
if (listener != null) listener.onActiveViewChanged(v, position)
}
v
}
def updateList(list: List[Any]) {
def updateList(list: List[Any], activeId: Int) {
items = list
activePosition = items.indexWhere{
case item: Item => item.id == activeId
case _ => false
}
notifyDataSetChanged()
}
private var mListener: MenuAdapter.MenuListener = null
private var mActivePosition: Int = 0
private var listener: MenuAdapter.MenuListener = null
private var activePosition: Int = -1
}
......@@ -42,14 +42,14 @@ package com.github.shadowsocks.database
import com.j256.ormlite.field.{DataType, DatabaseField}
class Profile {
@DatabaseField(id = true)
@DatabaseField(generatedId = true)
var id: Int = 0
@DatabaseField
var name: String = null
var name: String = "Untitled"
@DatabaseField
var host: String = null
var host: String = ""
@DatabaseField
var localPort: Int = 0
......@@ -58,13 +58,13 @@ class Profile {
var remotePort: Int = 0
@DatabaseField
var password: String = null
var password: String = ""
@DatabaseField
var method: String = null
var method: String = ""
@DatabaseField
var date: String = null
var date: String = ""
@DatabaseField
var upload: Int = 0
......@@ -78,9 +78,15 @@ class Profile {
@DatabaseField
var global: Boolean = false
@DatabaseField
var bypass: Boolean = false
@DatabaseField
var traffic: Boolean = false
@DatabaseField(dataType = DataType.LONG_STRING)
var individual: String = null
var individual: String = ""
@DatabaseField(dataType = DataType.LONG_STRING)
var description: String = null
var description: String = ""
}
......@@ -40,18 +40,20 @@
package com.github.shadowsocks.database
import android.util.Log
import com.github.shadowsocks.Shadowsocks
import com.github.shadowsocks._
import android.content.{SharedPreferences, Context}
class ProfileManager(dbHelper: DBHelper) {
def addProfile(profile: Profile): Boolean = {
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)
return false
false
}
true
}
def getProfile(id: Int): Option[Profile] = {
......@@ -70,12 +72,12 @@ class ProfileManager(dbHelper: DBHelper) {
def delProfile(id: Int): Boolean = {
try {
dbHelper.profileDao.deleteById(id)
true
} catch {
case ex: Exception =>
Log.e(Shadowsocks.TAG, "delProfile", ex)
return false
false
}
true
}
def getAllProfiles: Option[List[Profile]] = {
......@@ -88,4 +90,70 @@ class ProfileManager(dbHelper: DBHelper) {
None
}
}
def load(id: Int): Profile = {
Log.d(Shadowsocks.TAG, id + " ")
save()
val profile = getProfile(id) getOrElse {
val p = new Profile()
createOrUpdateProfile(p)
p
}
Log.d(Shadowsocks.TAG, "load " + profile.id + " " + profile.name)
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