Move app to mobile subproject

This commit is contained in:
Mygod
2017-01-12 23:31:52 +08:00
parent 2c1c4b9b4f
commit 0030c29b62
177 changed files with 77 additions and 173 deletions
@@ -0,0 +1,144 @@
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* 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/>. */
/* */
/*******************************************************************************/
package com.github.shadowsocks.database
import android.content.Context
import android.content.pm.ApplicationInfo
import android.database.sqlite.SQLiteDatabase
import com.github.shadowsocks.ShadowsocksApplication.app
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper
import com.j256.ormlite.dao.Dao
import com.j256.ormlite.support.ConnectionSource
import com.j256.ormlite.table.TableUtils
import scala.collection.JavaConverters._
import scala.collection.mutable
object DBHelper {
final val PROFILE = "profile.db"
private var apps: mutable.Buffer[ApplicationInfo] = _
def isAllDigits(x: String): Boolean = !x.isEmpty && (x forall Character.isDigit)
def updateProxiedApps(context: Context, old: String): String = {
synchronized(if (apps == null) apps = context.getPackageManager.getInstalledApplications(0).asScala)
val uidSet = old.split('|').filter(isAllDigits).map(_.toInt).toSet
apps.filter(ai => uidSet.contains(ai.uid)).map(_.packageName).mkString("\n")
}
}
class DBHelper(val context: Context)
extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 21) {
import DBHelper._
lazy val profileDao: Dao[Profile, Int] = getDao(classOf[Profile])
def onCreate(database: SQLiteDatabase, connectionSource: ConnectionSource) {
TableUtils.createTable(connectionSource, classOf[Profile])
}
def recreate(database: SQLiteDatabase, connectionSource: ConnectionSource) {
TableUtils.dropTable(connectionSource, classOf[Profile], true)
onCreate(database, connectionSource)
}
def onUpgrade(database: SQLiteDatabase, connectionSource: ConnectionSource, oldVersion: Int,
newVersion: Int) {
if (oldVersion != newVersion) {
if (oldVersion < 7) {
recreate(database, connectionSource)
return
}
try {
if (oldVersion < 8) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
}
if (oldVersion < 9) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR DEFAULT 'all';")
} else if (oldVersion < 19) {
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'all' WHERE route IS NULL;")
}
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("BEGIN TRANSACTION;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
TableUtils.createTable(connectionSource, classOf[Profile])
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`;")
profileDao.executeRawNoArgs("COMMIT;")
} else 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 VARCHAR;")
}
if (oldVersion < 15) {
if (oldVersion >= 12) profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN userOrder LONG;")
var i = 0
for (profile <- profileDao.queryForAll.asScala) {
if (oldVersion < 14) profile.individual = updateProxiedApps(context, profile.individual)
profile.userOrder = i
profileDao.update(profile)
i += 1
}
}
if (oldVersion < 16) {
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'bypass-lan-china' WHERE route = 'bypass-china'")
}
if (oldVersion < 17) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcp SMALLINT;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpcli VARCHAR DEFAULT " +
"'--crypt none --mode normal --mtu 1200 --nocomp --dscp 46 --parityshard 0';")
} else if (oldVersion < 20) {
profileDao.executeRawNoArgs("UPDATE `profile` SET kcpcli = '--crypt none --mode normal --mtu 1200 --nocomp " +
"--dscp 46 --parityshard 0' WHERE kcpcli IS NULL;")
}
if (oldVersion < 18) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpPort INTEGER DEFAULT 8399;")
} else if (oldVersion < 19) {
profileDao.executeRawNoArgs("UPDATE `profile` SET kcpPort = 8399 WHERE kcpPort = 0;")
}
if (oldVersion < 21) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN remoteDns VARCHAR DEFAULT '8.8.8.8';")
}
} catch {
case ex: Exception =>
app.track(ex)
recreate(database, connectionSource)
return
}
}
}
}
@@ -0,0 +1,150 @@
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* 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/>. */
/* */
/*******************************************************************************/
package com.github.shadowsocks.database
import java.net.URLEncoder
import java.util.Locale
import android.content.SharedPreferences
import android.os.Binder
import android.text.TextUtils
import android.util.Base64
import com.github.shadowsocks.utils.Key
import com.j256.ormlite.field.{DataType, DatabaseField}
class Profile {
@DatabaseField(generatedId = true)
var id: Int = _
@DatabaseField
var name: String = ""
@DatabaseField
var host: String = "198.199.101.152"
// hopefully hashCode = mHandle doesn't change, currently this is true from KitKat to Nougat
@DatabaseField
var localPort: Int = 1080 + Binder.getCallingUserHandle.hashCode
@DatabaseField
var remotePort: Int = 8388
@DatabaseField
var password: String = "u1rRWTssNv0p"
@DatabaseField
var method: String = "aes-256-cfb"
@DatabaseField
var route: String = "all"
@DatabaseField
var remoteDns: String = "8.8.8.8"
@DatabaseField
var proxyApps: Boolean = _
@DatabaseField
var bypass: Boolean = _
@DatabaseField
var udpdns: Boolean = _
@DatabaseField
var auth: Boolean = _
@DatabaseField
var ipv6: Boolean = _
@DatabaseField(dataType = DataType.LONG_STRING)
var individual: String = ""
@DatabaseField
var tx: Long = _
@DatabaseField
var rx: Long = _
@DatabaseField
val date: java.util.Date = new java.util.Date()
@DatabaseField
var userOrder: Long = _
@DatabaseField
var kcp: Boolean = _
@DatabaseField
var kcpPort: Int = 8399
@DatabaseField
var kcpcli: String = "--crypt none --mode normal --mtu 1200 --nocomp --dscp 46 --parityshard 0"
def formattedAddress: String = (if (host.contains(":")) "[%s]:%d" else "%s:%d").format(host, remotePort)
def nameIsEmpty: Boolean = name == null || name.isEmpty
def getName: String = if (nameIsEmpty) formattedAddress else name
override def toString: String = "ss://" + Base64.encodeToString("%s%s:%s@%s:%d".formatLocal(Locale.ENGLISH,
method, if (auth) "-auth" else "", password, host, remotePort).getBytes, Base64.NO_PADDING | Base64.NO_WRAP) +
(if (TextUtils.isEmpty(name)) "" else '#' + URLEncoder.encode(name, "utf-8"))
def isMethodUnsafe: Boolean = "table".equalsIgnoreCase(method) || "rc4".equalsIgnoreCase(method)
def serialize(editor: SharedPreferences.Editor): SharedPreferences.Editor = editor
.putString(Key.name, name)
.putString(Key.host, host)
.putInt(Key.localPort, localPort)
.putInt(Key.remotePort, remotePort)
.putString(Key.password, password)
.putString(Key.route, route)
.putString(Key.remoteDns, remoteDns)
.putString(Key.method, method)
.putBoolean(Key.proxyApps, proxyApps)
.putBoolean(Key.bypass, bypass)
.putBoolean(Key.udpdns, udpdns)
.putBoolean(Key.auth, auth)
.putBoolean(Key.ipv6, ipv6)
.putString(Key.individual, individual)
.putBoolean(Key.kcp, kcp)
.putInt(Key.kcpPort, kcpPort)
.putString(Key.kcpcli, kcpcli)
.remove(Key.dirty)
def deserialize(pref: SharedPreferences) {
// It's assumed that default values are never used, so 0/false/null is always used even if that isn't the case
name = pref.getString(Key.name, null)
host = pref.getString(Key.host, null)
localPort = pref.getInt(Key.localPort, 0)
remotePort = pref.getInt(Key.remotePort, 0)
password = pref.getString(Key.password, null)
method = pref.getString(Key.method, null)
route = pref.getString(Key.route, null)
remoteDns = pref.getString(Key.remoteDns, null)
proxyApps = pref.getBoolean(Key.proxyApps, false)
bypass = pref.getBoolean(Key.bypass, false)
udpdns = pref.getBoolean(Key.udpdns, false)
auth = pref.getBoolean(Key.auth, false)
ipv6 = pref.getBoolean(Key.ipv6, false)
individual = pref.getString(Key.individual, null)
kcp = pref.getBoolean(Key.kcp, false)
kcpPort = pref.getInt(Key.kcpPort, 0)
kcpcli = pref.getString(Key.kcpcli, null)
}
}
@@ -0,0 +1,124 @@
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* 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/>. */
/* */
/*******************************************************************************/
package com.github.shadowsocks.database
import android.util.Log
import com.github.shadowsocks.ProfilesFragment
import com.github.shadowsocks.ShadowsocksApplication.app
object ProfileManager {
private final val TAG = "ProfileManager"
}
class ProfileManager(dbHelper: DBHelper) {
import ProfileManager._
def createProfile(p: Profile = null): Profile = {
val profile = if (p == null) new Profile else p
profile.id = 0
try {
app.currentProfile match {
case Some(oldProfile) =>
// Copy Feature Settings from old profile
profile.route = oldProfile.route
profile.ipv6 = oldProfile.ipv6
profile.proxyApps = oldProfile.proxyApps
profile.bypass = oldProfile.bypass
profile.individual = oldProfile.individual
profile.udpdns = oldProfile.udpdns
case _ =>
}
val last = dbHelper.profileDao.queryRaw(dbHelper.profileDao.queryBuilder.selectRaw("MAX(userOrder)")
.prepareStatementString).getFirstResult
if (last != null && last.length == 1 && last(0) != null) profile.userOrder = last(0).toInt + 1
dbHelper.profileDao.createOrUpdate(profile)
if (ProfilesFragment.instance != null) ProfilesFragment.instance.profilesAdapter.add(profile)
} catch {
case ex: Exception =>
Log.e(TAG, "addProfile", ex)
app.track(ex)
}
profile
}
def updateProfile(profile: Profile): Boolean = {
try {
dbHelper.profileDao.update(profile)
true
} catch {
case ex: Exception =>
Log.e(TAG, "updateProfile", ex)
app.track(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(TAG, "getProfile", ex)
app.track(ex)
None
}
}
def delProfile(id: Int): Boolean = {
try {
dbHelper.profileDao.deleteById(id)
if (ProfilesFragment.instance != null) ProfilesFragment.instance.profilesAdapter.removeId(id)
true
} catch {
case ex: Exception =>
Log.e(TAG, "delProfile", ex)
app.track(ex)
false
}
}
def getFirstProfile: Option[Profile] = {
try {
val result = dbHelper.profileDao.query(dbHelper.profileDao.queryBuilder.limit(1L).prepare)
if (result.size == 1) Option(result.get(0)) else None
} catch {
case ex: Exception =>
Log.e(TAG, "getAllProfiles", ex)
app.track(ex)
None
}
}
def getAllProfiles: Option[List[Profile]] = {
try {
import scala.collection.JavaConversions._
Option(dbHelper.profileDao.query(dbHelper.profileDao.queryBuilder.orderBy("userOrder", true).prepare).toList)
} catch {
case ex: Exception =>
Log.e(TAG, "getAllProfiles", ex)
app.track(ex)
None
}
}
}