Move everything else to MMKV
- Exclude old armv5 so lib from MMKV - Move subscription to MMKV - Clean up settings key - Map settings to MMKV - Say goodbye to DPreference Fix some bugs: - null pointer check - server list refresh - show test result - custom config display - fix port number in DNS object shown as Double - shadowsocks, socks streamsettings - quic settings with wrong security - main list footer
This commit is contained in:
@@ -25,12 +25,14 @@ android {
|
||||
minifyEnabled false
|
||||
zipAlignEnabled false
|
||||
shrinkResources false
|
||||
ndk.abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug {
|
||||
minifyEnabled false
|
||||
zipAlignEnabled false
|
||||
shrinkResources false
|
||||
ndk.abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +69,6 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
testImplementation 'junit:junit:4.13'
|
||||
implementation project(':dpreference')
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.support.multidex.MultiDexApplication
|
||||
import android.support.v7.preference.PreferenceManager
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.util.AngConfigManager
|
||||
import me.dozen.dpreference.DPreference
|
||||
|
||||
class AngApplication : MultiDexApplication() {
|
||||
companion object {
|
||||
@@ -15,8 +14,6 @@ class AngApplication : MultiDexApplication() {
|
||||
var firstRun = false
|
||||
private set
|
||||
|
||||
val defaultDPreference by lazy { DPreference(this, packageName + "_preferences") }
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
|
||||
@@ -6,36 +6,39 @@ package com.v2ray.ang
|
||||
*/
|
||||
object AppConfig {
|
||||
const val ANG_PACKAGE = "com.v2ray.ang"
|
||||
const val ANG_CONFIG = "ang_config"
|
||||
const val PREF_CURR_CONFIG = "pref_v2ray_config"
|
||||
const val PREF_CURR_CONFIG_GUID = "pref_v2ray_config_guid"
|
||||
const val PREF_CURR_CONFIG_NAME = "pref_v2ray_config_name"
|
||||
const val PREF_CURR_CONFIG_DOMAIN = "pref_v2ray_config_domain"
|
||||
const val PREF_CURR_CONFIG_OUTBOUND_TAGS = "pref_v2ray_config_outbound_tags"
|
||||
const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium"
|
||||
|
||||
// legacy
|
||||
const val ANG_CONFIG = "ang_config"
|
||||
const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium"
|
||||
const val PREF_ROUTING_CUSTOM = "pref_routing_custom"
|
||||
|
||||
// Preferences mapped to MMKV
|
||||
const val PREF_MODE = "pref_mode"
|
||||
// const val PREF_BYPASS_MAINLAND = "pref_bypass_mainland"
|
||||
// const val PREF_START_ON_BOOT = "pref_start_on_boot"
|
||||
const val PREF_PER_APP_PROXY = "pref_per_app_proxy"
|
||||
// const val PREF_MUX_ENAimport libv2ray.Libv2rayBLED = "pref_mux_enabled"
|
||||
const val PREF_SPEED_ENABLED = "pref_speed_enabled"
|
||||
const val PREF_SNIFFING_ENABLED = "pref_sniffing_enabled"
|
||||
const val PREF_PROXY_SHARING = "pref_proxy_sharing_enabled"
|
||||
const val PREF_LOCAL_DNS_ENABLED = "pref_local_dns_enabled"
|
||||
const val PREF_REMOTE_DNS = "pref_remote_dns"
|
||||
const val PREF_DOMESTIC_DNS = "pref_domestic_dns"
|
||||
// const val PREF_SOCKS_PORT = "pref_socks_port"
|
||||
// const val PREF_HTTP_PORT = "pref_http_port"
|
||||
const val PREF_FORWARD_IPV6 = "pref_forward_ipv6"
|
||||
const val PREF_ROUTING_DOMAIN_STRATEGY = "pref_routing_domain_strategy"
|
||||
const val PREF_ROUTING_MODE = "pref_routing_mode"
|
||||
const val PREF_ROUTING_CUSTOM = "pref_routing_custom"
|
||||
const val PREF_V2RAY_ROUTING_AGENT = "pref_v2ray_routing_agent"
|
||||
const val PREF_V2RAY_ROUTING_DIRECT = "pref_v2ray_routing_direct"
|
||||
const val PREF_V2RAY_ROUTING_BLOCKED = "pref_v2ray_routing_blocked"
|
||||
const val PREF_PER_APP_PROXY = "pref_per_app_proxy"
|
||||
const val PREF_PER_APP_PROXY_SET = "pref_per_app_proxy_set"
|
||||
const val PREF_BYPASS_APPS = "pref_bypass_apps"
|
||||
// const val PREF_BYPASS_MAINLAND = "pref_bypass_mainland"
|
||||
// const val PREF_START_ON_BOOT = "pref_start_on_boot"
|
||||
// const val PREF_MUX_ENAimport libv2ray.Libv2rayBLED = "pref_mux_enabled"
|
||||
// const val PREF_SOCKS_PORT = "pref_socks_port"
|
||||
// const val PREF_HTTP_PORT = "pref_http_port"
|
||||
// const val PREF_DONATE = "pref_donate"
|
||||
// const val PREF_LICENSES = "pref_licenses"
|
||||
// const val PREF_FEEDBACK = "pref_feedback"
|
||||
// const val PREF_TG_GROUP = "pref_tg_group"
|
||||
// const val PREF_AUTO_RESTART = "pref_auto_restart"
|
||||
const val PREF_FORWARD_IPV6 = "pref_forward_ipv6"
|
||||
|
||||
const val HTTP_PROTOCOL: String = "http://"
|
||||
const val HTTPS_PROTOCOL: String = "https://"
|
||||
@@ -50,9 +53,6 @@ object AppConfig {
|
||||
const val TASKER_EXTRA_BUNDLE_GUID = "tasker_extra_bundle_guid"
|
||||
const val TASKER_DEFAULT_GUID = "Default"
|
||||
|
||||
const val PREF_V2RAY_ROUTING_AGENT = "pref_v2ray_routing_agent"
|
||||
const val PREF_V2RAY_ROUTING_DIRECT = "pref_v2ray_routing_direct"
|
||||
const val PREF_V2RAY_ROUTING_BLOCKED = "pref_v2ray_routing_blocked"
|
||||
const val TAG_AGENT = "proxy"
|
||||
const val TAG_DIRECT = "direct"
|
||||
const val TAG_BLOCKED = "block"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.v2ray.ang.dto
|
||||
|
||||
data class ServerAffiliationInfo(var testDelayMillis: Long) {
|
||||
data class ServerAffiliationInfo(var testDelayMillis: Long = 0L) {
|
||||
fun getTestDelayString(): String {
|
||||
if (testDelayMillis == 0L) {
|
||||
return ""
|
||||
|
||||
@@ -28,14 +28,7 @@ data class ServerConfig(
|
||||
streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean()))
|
||||
EConfigType.CUSTOM ->
|
||||
return ServerConfig(configType = configType)
|
||||
EConfigType.SHADOWSOCKS, EConfigType.SOCKS ->
|
||||
return ServerConfig(
|
||||
configType = configType,
|
||||
outboundBean = V2rayConfig.OutboundBean(
|
||||
protocol = configType.name.toLowerCase(),
|
||||
settings = V2rayConfig.OutboundBean.OutSettingsBean(
|
||||
servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean()))))
|
||||
EConfigType.TROJAN ->
|
||||
EConfigType.SHADOWSOCKS, EConfigType.SOCKS, EConfigType.TROJAN ->
|
||||
return ServerConfig(
|
||||
configType = configType,
|
||||
outboundBean = V2rayConfig.OutboundBean(
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.v2ray.ang.dto
|
||||
|
||||
data class SubscriptionItem(
|
||||
var remarks: String = "",
|
||||
var url: String = "",
|
||||
var enabled: Boolean = true,
|
||||
val addedTime: Long = System.currentTimeMillis()) {
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
package com.v2ray.ang.dto
|
||||
|
||||
import android.text.TextUtils
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.google.gson.JsonSerializer
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.lang.reflect.Type
|
||||
|
||||
data class V2rayConfig(
|
||||
val stats: Any? = null,
|
||||
@@ -284,7 +290,7 @@ data class V2rayConfig(
|
||||
"quic" -> {
|
||||
val quicSetting = streamSettings?.quicSettings ?: return null
|
||||
listOf(quicSetting.header.type,
|
||||
quicSetting.key,
|
||||
quicSetting.security,
|
||||
quicSetting.key)
|
||||
}
|
||||
else -> null
|
||||
@@ -336,4 +342,15 @@ data class V2rayConfig(
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun toPrettyPrinting(): String {
|
||||
return GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter( // custom serialiser is needed here since JSON by default parse number as Double, core will fail to start
|
||||
object: TypeToken<Double>() {}.type,
|
||||
JsonSerializer { src: Double?, _: Type?, _: JsonSerializationContext? -> JsonPrimitive(src?.toInt()) }
|
||||
)
|
||||
.create()
|
||||
.toJson(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.content.Context
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import com.v2ray.ang.AngApplication
|
||||
import me.dozen.dpreference.DPreference
|
||||
import me.drakeet.support.toast.ToastCompat
|
||||
import org.json.JSONObject
|
||||
import java.net.URLConnection
|
||||
@@ -16,13 +15,6 @@ import java.net.URLConnection
|
||||
val Context.v2RayApplication: AngApplication
|
||||
get() = applicationContext as AngApplication
|
||||
|
||||
// Usage note: DPreference use Android ContentProvider to redirect multi process access to main process.
|
||||
// Currently, RunSoLibV2RayDaemon process will run proxy core, keep minimum configuration and long running
|
||||
// in the background, support toggle on/off. That means it should NOT use DPreference after the initial
|
||||
// creation and setup of the service
|
||||
val Context.defaultDPreference: DPreference
|
||||
get() = v2RayApplication.defaultDPreference
|
||||
|
||||
inline fun Context.toast(message: Int): Toast = ToastCompat
|
||||
.makeText(this, message, Toast.LENGTH_SHORT)
|
||||
.apply {
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.v2ray.ang.extension
|
||||
|
||||
import android.preference.Preference
|
||||
|
||||
fun Preference.onClick(listener: () -> Unit) {
|
||||
setOnPreferenceClickListener {
|
||||
listener()
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,8 @@ import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.TAG_DIRECT
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.dto.ServerConfig
|
||||
import com.v2ray.ang.extension.defaultDPreference
|
||||
import com.v2ray.ang.extension.toSpeedString
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.extension.v2RayApplication
|
||||
import com.v2ray.ang.ui.MainActivity
|
||||
import com.v2ray.ang.util.MessageUtil
|
||||
import com.v2ray.ang.util.MmkvManager
|
||||
@@ -45,6 +43,7 @@ object V2RayServiceManager {
|
||||
val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback())
|
||||
private val mMsgReceive = ReceiveMessageHandler()
|
||||
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
|
||||
|
||||
var serviceControl: SoftReference<ServiceControl>? = null
|
||||
set(value) {
|
||||
@@ -64,12 +63,12 @@ object V2RayServiceManager {
|
||||
private var mNotificationManager: NotificationManager? = null
|
||||
|
||||
fun startV2Ray(context: Context) {
|
||||
if (context.v2RayApplication.defaultDPreference.getPrefBoolean(AppConfig.PREF_PROXY_SHARING, false)) {
|
||||
if (settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) == true) {
|
||||
context.toast(R.string.toast_warning_pref_proxysharing_short)
|
||||
}else{
|
||||
context.toast(R.string.toast_services_start)
|
||||
}
|
||||
val intent = if (context.v2RayApplication.defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") == "VPN") {
|
||||
val intent = if (settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" == "VPN") {
|
||||
Intent(context.applicationContext, V2RayVpnService::class.java)
|
||||
} else {
|
||||
Intent(context.applicationContext, V2RayProxyOnlyService::class.java)
|
||||
@@ -147,9 +146,9 @@ object V2RayServiceManager {
|
||||
v2rayPoint.configureFileContent = result.content
|
||||
v2rayPoint.domainName = config.getV2rayPointDomainAndPort()
|
||||
currentConfig = config
|
||||
v2rayPoint.enableLocalDNS = service.defaultDPreference.getPrefBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false)
|
||||
v2rayPoint.forwardIpv6 = service.defaultDPreference.getPrefBoolean(AppConfig.PREF_FORWARD_IPV6, false)
|
||||
v2rayPoint.proxyOnly = service.defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") != "VPN"
|
||||
v2rayPoint.enableLocalDNS = settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) ?: false
|
||||
v2rayPoint.forwardIpv6 = settingsStorage?.decodeBool(AppConfig.PREF_FORWARD_IPV6) ?: false
|
||||
v2rayPoint.proxyOnly = settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" == "VPN"
|
||||
|
||||
try {
|
||||
v2rayPoint.runLoop()
|
||||
@@ -314,10 +313,9 @@ object V2RayServiceManager {
|
||||
}
|
||||
|
||||
fun startSpeedNotification() {
|
||||
val service = serviceControl?.get()?.getService() ?: return
|
||||
if (mSubscription == null &&
|
||||
v2rayPoint.isRunning &&
|
||||
service.defaultDPreference.getPrefBoolean(AppConfig.PREF_SPEED_ENABLED, false)) {
|
||||
settingsStorage?.decodeBool(AppConfig.PREF_SPEED_ENABLED) == true) {
|
||||
var lastZeroSpeed = false
|
||||
val outboundTags = currentConfig?.getAllOutboundTags()
|
||||
outboundTags?.remove(TAG_DIRECT)
|
||||
|
||||
@@ -10,10 +10,10 @@ import android.os.ParcelFileDescriptor
|
||||
import android.os.StrictMode
|
||||
import android.support.annotation.RequiresApi
|
||||
import android.util.Log
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.extension.defaultDPreference
|
||||
import com.v2ray.ang.ui.PerAppProxyActivity
|
||||
import com.v2ray.ang.util.MmkvManager
|
||||
import com.v2ray.ang.util.Utils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
@@ -22,6 +22,8 @@ import java.io.File
|
||||
import java.lang.ref.SoftReference
|
||||
|
||||
class V2RayVpnService : VpnService(), ServiceControl {
|
||||
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
|
||||
|
||||
private lateinit var mInterface: ParcelFileDescriptor
|
||||
|
||||
/**
|
||||
@@ -91,8 +93,8 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
||||
// If the old interface has exactly the same parameters, use it!
|
||||
// Configure a builder while parsing the parameters.
|
||||
val builder = Builder()
|
||||
val enableLocalDns = defaultDPreference.getPrefBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false)
|
||||
val routingMode = defaultDPreference.getPrefString(AppConfig.PREF_ROUTING_MODE, "0")
|
||||
val enableLocalDns = settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) ?: false
|
||||
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "0"
|
||||
|
||||
parameters.split(" ")
|
||||
.map { it.split(",") }
|
||||
@@ -120,7 +122,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
||||
}
|
||||
|
||||
if(!enableLocalDns) {
|
||||
Utils.getRemoteDnsServers(defaultDPreference)
|
||||
Utils.getRemoteDnsServers()
|
||||
.forEach {
|
||||
builder.addDnsServer(it)
|
||||
}
|
||||
@@ -129,9 +131,9 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
||||
builder.setSession(V2RayServiceManager.currentConfig?.remarks.orEmpty())
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
|
||||
defaultDPreference.getPrefBoolean(AppConfig.PREF_PER_APP_PROXY, false)) {
|
||||
val apps = defaultDPreference.getPrefStringSet(PerAppProxyActivity.PREF_PER_APP_PROXY_SET, null)
|
||||
val bypassApps = defaultDPreference.getPrefBoolean(PerAppProxyActivity.PREF_BYPASS_APPS, false)
|
||||
settingsStorage?.decodeBool(AppConfig.PREF_PER_APP_PROXY) == true) {
|
||||
val apps = settingsStorage?.decodeStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
|
||||
val bypassApps = settingsStorage?.decodeBool(AppConfig.PREF_BYPASS_APPS) ?: false
|
||||
apps?.forEach {
|
||||
try {
|
||||
if (bypassApps)
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.BuildConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.dto.EConfigType
|
||||
import com.v2ray.ang.extension.defaultDPreference
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
|
||||
import com.v2ray.ang.service.V2RayServiceManager
|
||||
@@ -51,6 +50,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
|
||||
private val adapter by lazy { MainRecyclerAdapter(this) }
|
||||
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
|
||||
private var mItemTouchHelper: ItemTouchHelper? = null
|
||||
val mainViewModel: MainViewModel by lazy { ViewModelProviders.of(this).get(MainViewModel::class.java) }
|
||||
|
||||
@@ -63,7 +63,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
fab.setOnClickListener {
|
||||
if (mainViewModel.isRunning.value == true) {
|
||||
Utils.stopVService(this)
|
||||
} else if (defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") == "VPN") {
|
||||
} else if (settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" == "VPN") {
|
||||
val intent = VpnService.prepare(this)
|
||||
if (intent == null) {
|
||||
startV2Ray()
|
||||
@@ -297,7 +297,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
}
|
||||
if (count > 0) {
|
||||
toast(R.string.toast_success)
|
||||
adapter.notifyDataSetChanged()
|
||||
mainViewModel.reloadServerList()
|
||||
} else {
|
||||
toast(R.string.toast_failure)
|
||||
}
|
||||
@@ -381,18 +381,16 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
: Boolean {
|
||||
try {
|
||||
toast(R.string.title_sub_update)
|
||||
val subItem = AngConfigManager.configs.subItem
|
||||
for (k in 0 until subItem.count()) {
|
||||
if (TextUtils.isEmpty(subItem[k].id)
|
||||
|| TextUtils.isEmpty(subItem[k].remarks)
|
||||
|| TextUtils.isEmpty(subItem[k].url)
|
||||
MmkvManager.decodeSubscriptions().forEach {
|
||||
if (TextUtils.isEmpty(it.first)
|
||||
|| TextUtils.isEmpty(it.second.remarks)
|
||||
|| TextUtils.isEmpty(it.second.url)
|
||||
) {
|
||||
continue
|
||||
return@forEach
|
||||
}
|
||||
val id = subItem[k].id
|
||||
val url = subItem[k].url
|
||||
val url = it.second.url
|
||||
if (!Utils.isValidUrl(url)) {
|
||||
continue
|
||||
return@forEach
|
||||
}
|
||||
Log.d("Main", url)
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
@@ -403,7 +401,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
""
|
||||
}
|
||||
launch(Dispatchers.Main) {
|
||||
importBatchConfig(Utils.decode(configText), id)
|
||||
importBatchConfig(Utils.decode(configText), it.first)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
||||
}
|
||||
var isRunning = false
|
||||
|
||||
override fun getItemCount() = mActivity.mainViewModel.serverList.size
|
||||
override fun getItemCount() = mActivity.mainViewModel.serverList.size + 1
|
||||
|
||||
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
|
||||
if (holder is MainViewHolder) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.support.v7.preference.PreferenceManager
|
||||
import android.support.v7.widget.DividerItemDecoration
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
@@ -15,7 +16,6 @@ import android.view.View
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.extension.defaultDPreference
|
||||
import com.v2ray.ang.util.AppManagerUtil
|
||||
import kotlinx.android.synthetic.main.activity_bypass_list.*
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
@@ -35,13 +35,10 @@ import kotlinx.coroutines.launch
|
||||
import java.net.URL
|
||||
|
||||
class PerAppProxyActivity : BaseActivity() {
|
||||
companion object {
|
||||
const val PREF_PER_APP_PROXY_SET = "pref_per_app_proxy_set"
|
||||
const val PREF_BYPASS_APPS = "pref_bypass_apps"
|
||||
}
|
||||
|
||||
private var adapter: PerAppProxyAdapter? = null
|
||||
private var appsAll: List<AppInfo>? = null
|
||||
private val defaultSharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -52,7 +49,7 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
|
||||
recycler_view.addItemDecoration(dividerItemDecoration)
|
||||
|
||||
val blacklist = defaultDPreference.getPrefStringSet(PREF_PER_APP_PROXY_SET, null)
|
||||
val blacklist = defaultSharedPreferences.getStringSet(AppConfig.PREF_PER_APP_PROXY_SET, null)
|
||||
|
||||
AppManagerUtil.rxLoadNetworkAppList(this)
|
||||
.subscribeOn(Schedulers.io())
|
||||
@@ -142,14 +139,14 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
})
|
||||
|
||||
switch_per_app_proxy.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
defaultDPreference.setPrefBoolean(AppConfig.PREF_PER_APP_PROXY, isChecked)
|
||||
defaultSharedPreferences.edit().putBoolean(AppConfig.PREF_PER_APP_PROXY, isChecked).apply()
|
||||
}
|
||||
switch_per_app_proxy.isChecked = defaultDPreference.getPrefBoolean(AppConfig.PREF_PER_APP_PROXY, false)
|
||||
switch_per_app_proxy.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_PER_APP_PROXY, false)
|
||||
|
||||
switch_bypass_apps.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
defaultDPreference.setPrefBoolean(PREF_BYPASS_APPS, isChecked)
|
||||
defaultSharedPreferences.edit().putBoolean(AppConfig.PREF_BYPASS_APPS, isChecked).apply()
|
||||
}
|
||||
switch_bypass_apps.isChecked = defaultDPreference.getPrefBoolean(PREF_BYPASS_APPS, false)
|
||||
switch_bypass_apps.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_BYPASS_APPS, false)
|
||||
|
||||
et_search.setOnEditorActionListener { v, actionId, event ->
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||
@@ -183,7 +180,7 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
adapter?.let {
|
||||
defaultDPreference.setPrefStringSet(PREF_PER_APP_PROXY_SET, it.blacklist)
|
||||
defaultSharedPreferences.edit().putStringSet(AppConfig.PREF_PER_APP_PROXY_SET, it.blacklist).apply()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import android.app.Activity.RESULT_OK
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v7.preference.PreferenceManager
|
||||
import android.view.*
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.extension.defaultDPreference
|
||||
import com.v2ray.ang.util.Utils
|
||||
import kotlinx.android.synthetic.main.fragment_routing_settings.*
|
||||
import android.view.MenuInflater
|
||||
@@ -26,6 +26,8 @@ class RoutingSettingsFragment : Fragment() {
|
||||
private const val REQUEST_SCAN_APPEND = 12
|
||||
}
|
||||
|
||||
val defaultSharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(context) }
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
// Inflate the layout for this fragment
|
||||
@@ -43,7 +45,7 @@ class RoutingSettingsFragment : Fragment() {
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
val content = activity?.defaultDPreference?.getPrefString(arguments!!.getString(routing_arg), "")
|
||||
val content = defaultSharedPreferences.getString(arguments!!.getString(routing_arg), "")
|
||||
et_routing_content.text = Utils.getEditable(content!!)
|
||||
|
||||
setHasOptionsMenu(true)
|
||||
@@ -57,7 +59,7 @@ class RoutingSettingsFragment : Fragment() {
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.save_routing -> {
|
||||
val content = et_routing_content.text.toString()
|
||||
activity?.defaultDPreference?.setPrefString(arguments!!.getString(routing_arg), content)
|
||||
defaultSharedPreferences.edit().putString(arguments!!.getString(routing_arg), content).apply()
|
||||
activity?.toast(R.string.toast_success)
|
||||
true
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.v2ray.ang.dto.ServerConfig
|
||||
import com.v2ray.ang.dto.V2rayConfig
|
||||
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_NETWORK
|
||||
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_PORT
|
||||
import com.v2ray.ang.dto.V2rayConfig.Companion.TLS
|
||||
import com.v2ray.ang.dto.V2rayConfig.Companion.XTLS
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.util.MmkvManager
|
||||
@@ -35,6 +34,7 @@ import kotlinx.android.synthetic.main.activity_server_vmess.sp_stream_security
|
||||
class ServerActivity : BaseActivity() {
|
||||
|
||||
private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val editGuid by lazy { intent.getStringExtra("guid").orEmpty() }
|
||||
private val isRunning by lazy {
|
||||
intent.getBooleanExtra("isRunning", false)
|
||||
@@ -96,7 +96,7 @@ class ServerActivity : BaseActivity() {
|
||||
|
||||
et_remarks.text = Utils.getEditable(config.remarks)
|
||||
et_address.text = Utils.getEditable(outbound.getServerAddress().orEmpty())
|
||||
et_port.text = Utils.getEditable(outbound.getServerPort()?.or(DEFAULT_PORT).toString())
|
||||
et_port.text = Utils.getEditable(outbound.getServerPort()?.toString() ?: DEFAULT_PORT.toString())
|
||||
et_id.text = Utils.getEditable(outbound.getPassword().orEmpty())
|
||||
et_alterId?.text = Utils.getEditable(outbound.settings?.vnext?.get(0)?.users?.get(0)?.alterId.toString())
|
||||
if (config.configType == EConfigType.SOCKS) {
|
||||
@@ -229,18 +229,22 @@ class ServerActivity : BaseActivity() {
|
||||
if (config.configType == EConfigType.SHADOWSOCKS) {
|
||||
server.password = et_id.text.toString().trim()
|
||||
server.method = shadowsocksSecuritys[sp_security.selectedItemPosition]
|
||||
} else if (config.configType == EConfigType.SOCKS && !TextUtils.isEmpty(et_security.text) && !TextUtils.isEmpty(et_id.text)) {
|
||||
val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
||||
socksUsersBean.user = et_security.text.toString().trim()
|
||||
socksUsersBean.pass = et_id.text.toString().trim()
|
||||
server.users = listOf(socksUsersBean)
|
||||
} else if (config.configType == EConfigType.SOCKS) {
|
||||
if (TextUtils.isEmpty(et_security.text) && TextUtils.isEmpty(et_id.text)) {
|
||||
server.users = null
|
||||
} else {
|
||||
val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
||||
socksUsersBean.user = et_security.text.toString().trim()
|
||||
socksUsersBean.pass = et_id.text.toString().trim()
|
||||
server.users = listOf(socksUsersBean)
|
||||
}
|
||||
} else if (config.configType == EConfigType.TROJAN) {
|
||||
server.password = et_id.text.toString().trim()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean) {
|
||||
val requestHost = et_request_host.text.toString().trim()
|
||||
val requestHost = et_request_host?.text.toString().trim()
|
||||
val path = et_path?.text.toString().trim()
|
||||
var sni = streamSetting.populateTransportSettings(
|
||||
if (sp_network != null) networks[sp_network.selectedItemPosition] else DEFAULT_NETWORK,
|
||||
@@ -254,9 +258,18 @@ class ServerActivity : BaseActivity() {
|
||||
if (sni.isEmpty()) {
|
||||
sni = requestHost
|
||||
}
|
||||
val allowInsecure = if (sp_allow_insecure != null) {
|
||||
if (allowinsecures[sp_allow_insecure.selectedItemPosition].isBlank()) {
|
||||
false//settingsStorage?.decodeBool(PREF_ALLOW_INSECURE) ?: false
|
||||
} else {
|
||||
allowinsecures[sp_allow_insecure.selectedItemPosition].toBoolean()
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
streamSetting.populateTlsSettings(
|
||||
if (sp_stream_security != null) streamSecuritys[sp_stream_security.selectedItemPosition] else TLS,
|
||||
if (sp_allow_insecure != null) allowinsecures[sp_allow_insecure.selectedItemPosition].toBoolean() else false,
|
||||
if (sp_stream_security != null) streamSecuritys[sp_stream_security.selectedItemPosition] else "",
|
||||
allowInsecure,
|
||||
sni
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.util.MmkvManager
|
||||
import com.v2ray.ang.util.Utils
|
||||
import kotlinx.android.synthetic.main.activity_server_custom_config.*
|
||||
import java.lang.Exception
|
||||
|
||||
class ServerCustomConfigActivity : BaseActivity() {
|
||||
|
||||
@@ -46,7 +45,7 @@ class ServerCustomConfigActivity : BaseActivity() {
|
||||
*/
|
||||
private fun bindingServer(config: ServerConfig): Boolean {
|
||||
et_remarks.text = Utils.getEditable(config.remarks)
|
||||
tv_content.text = Utils.getEditable(Gson().toJson(config.fullConfig))
|
||||
tv_content.text = Utils.getEditable(config.fullConfig?.toPrettyPrinting().orEmpty())
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,12 @@ import android.support.v7.app.AlertDialog
|
||||
import android.text.TextUtils
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import com.google.gson.Gson
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.dto.AngConfig
|
||||
import com.v2ray.ang.dto.SubscriptionItem
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.util.AngConfigManager
|
||||
import com.v2ray.ang.util.MmkvManager
|
||||
import com.v2ray.ang.util.Utils
|
||||
import kotlinx.android.synthetic.main.activity_sub_edit.*
|
||||
|
||||
@@ -17,20 +19,17 @@ class SubEditActivity : BaseActivity() {
|
||||
var del_config: MenuItem? = null
|
||||
var save_config: MenuItem? = null
|
||||
|
||||
private lateinit var configs: AngConfig
|
||||
private var edit_index: Int = -1 //当前编辑的
|
||||
private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val editSubId by lazy { intent.getStringExtra("subId").orEmpty() }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_sub_edit)
|
||||
|
||||
configs = AngConfigManager.configs
|
||||
edit_index = intent.getIntExtra("position", -1)
|
||||
|
||||
title = getString(R.string.title_sub_setting)
|
||||
|
||||
if (edit_index >= 0) {
|
||||
bindingServer(configs.subItem[edit_index])
|
||||
val json = subStorage?.decodeString(editSubId)
|
||||
if (!json.isNullOrBlank()) {
|
||||
bindingServer(Gson().fromJson(json, SubscriptionItem::class.java))
|
||||
} else {
|
||||
clearServer()
|
||||
}
|
||||
@@ -40,7 +39,7 @@ class SubEditActivity : BaseActivity() {
|
||||
/**
|
||||
* bingding seleced server config
|
||||
*/
|
||||
fun bindingServer(subItem: AngConfig.SubItemBean): Boolean {
|
||||
private fun bindingServer(subItem: SubscriptionItem): Boolean {
|
||||
et_remarks.text = Utils.getEditable(subItem.remarks)
|
||||
et_url.text = Utils.getEditable(subItem.url)
|
||||
|
||||
@@ -50,7 +49,7 @@ class SubEditActivity : BaseActivity() {
|
||||
/**
|
||||
* clear or init server config
|
||||
*/
|
||||
fun clearServer(): Boolean {
|
||||
private fun clearServer(): Boolean {
|
||||
et_remarks.text = null
|
||||
et_url.text = null
|
||||
|
||||
@@ -60,12 +59,15 @@ class SubEditActivity : BaseActivity() {
|
||||
/**
|
||||
* save server config
|
||||
*/
|
||||
fun saveServer(): Boolean {
|
||||
val subItem: AngConfig.SubItemBean
|
||||
if (edit_index >= 0) {
|
||||
subItem = configs.subItem[edit_index]
|
||||
private fun saveServer(): Boolean {
|
||||
val subItem: SubscriptionItem
|
||||
val json = subStorage?.decodeString(editSubId)
|
||||
var subId = editSubId
|
||||
if (!json.isNullOrBlank()) {
|
||||
subItem = Gson().fromJson(json, SubscriptionItem::class.java)
|
||||
} else {
|
||||
subItem = AngConfig.SubItemBean()
|
||||
subId = Utils.getUuid()
|
||||
subItem = SubscriptionItem()
|
||||
}
|
||||
|
||||
subItem.remarks = et_remarks.text.toString()
|
||||
@@ -80,32 +82,23 @@ class SubEditActivity : BaseActivity() {
|
||||
return false
|
||||
}
|
||||
|
||||
if (AngConfigManager.addSubItem(subItem, edit_index) == 0) {
|
||||
toast(R.string.toast_success)
|
||||
finish()
|
||||
return true
|
||||
} else {
|
||||
toast(R.string.toast_failure)
|
||||
return false
|
||||
}
|
||||
subStorage?.encode(subId, Gson().toJson(subItem))
|
||||
toast(R.string.toast_success)
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* save server config
|
||||
*/
|
||||
fun deleteServer(): Boolean {
|
||||
if (edit_index >= 0) {
|
||||
private fun deleteServer(): Boolean {
|
||||
if (editSubId.isNotEmpty()) {
|
||||
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
if (AngConfigManager.removeSubItem(edit_index) == 0) {
|
||||
toast(R.string.toast_success)
|
||||
finish()
|
||||
} else {
|
||||
toast(R.string.toast_failure)
|
||||
}
|
||||
MmkvManager.removeSubscription(editSubId)
|
||||
finish()
|
||||
}
|
||||
.show()
|
||||
} else {
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -115,8 +108,7 @@ class SubEditActivity : BaseActivity() {
|
||||
del_config = menu?.findItem(R.id.del_config)
|
||||
save_config = menu?.findItem(R.id.save_config)
|
||||
|
||||
if (edit_index >= 0) {
|
||||
} else {
|
||||
if (editSubId.isEmpty()) {
|
||||
del_config?.isVisible = false
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
package com.v2ray.ang.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import com.v2ray.ang.R
|
||||
import kotlinx.android.synthetic.main.activity_sub_setting.*
|
||||
import android.os.Bundle
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import com.v2ray.ang.dto.SubscriptionItem
|
||||
import com.v2ray.ang.util.MmkvManager
|
||||
|
||||
class SubSettingActivity : BaseActivity() {
|
||||
|
||||
var subscriptions:List<Pair<String, SubscriptionItem>> = listOf()
|
||||
private val adapter by lazy { SubSettingRecyclerAdapter(this) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -27,7 +30,8 @@ class SubSettingActivity : BaseActivity() {
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
adapter.updateConfigList()
|
||||
subscriptions = MmkvManager.decodeSubscriptions()
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
@@ -40,14 +44,9 @@ class SubSettingActivity : BaseActivity() {
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.add_config -> {
|
||||
startActivity(Intent(this, SubEditActivity::class.java)
|
||||
.putExtra("position", -1)
|
||||
)
|
||||
adapter.updateConfigList()
|
||||
startActivity(Intent(this, SubEditActivity::class.java))
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,36 +7,27 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.dto.AngConfig
|
||||
import com.v2ray.ang.util.AngConfigManager
|
||||
import kotlinx.android.synthetic.main.item_recycler_sub_setting.view.*
|
||||
|
||||
class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter<SubSettingRecyclerAdapter.BaseViewHolder>() {
|
||||
|
||||
private var mActivity: SubSettingActivity = activity
|
||||
private lateinit var configs: AngConfig
|
||||
|
||||
init {
|
||||
updateConfigList()
|
||||
}
|
||||
|
||||
override fun getItemCount() = configs.subItem.count()
|
||||
override fun getItemCount() = mActivity.subscriptions.size
|
||||
|
||||
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
|
||||
if (holder is MainViewHolder) {
|
||||
val remarks = configs.subItem[position].remarks
|
||||
val url = configs.subItem[position].url
|
||||
|
||||
holder.name.text = remarks
|
||||
holder.url.text = url
|
||||
val subId = mActivity.subscriptions[position].first
|
||||
val subItem = mActivity.subscriptions[position].second
|
||||
holder.name.text = subItem.remarks
|
||||
holder.url.text = subItem.url
|
||||
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
|
||||
|
||||
holder.layout_edit.setOnClickListener {
|
||||
mActivity.startActivity(Intent(mActivity, SubEditActivity::class.java)
|
||||
.putExtra("position", position)
|
||||
.putExtra("subId", subId)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,15 +36,6 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
|
||||
.inflate(R.layout.item_recycler_sub_setting, parent, false))
|
||||
}
|
||||
|
||||
fun updateConfigList() {
|
||||
configs = AngConfigManager.configs
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
// fun updateSelectedItem() {
|
||||
// notifyItemChanged(configs.index)
|
||||
// }
|
||||
|
||||
open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
||||
|
||||
class MainViewHolder(itemView: View) : BaseViewHolder(itemView) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.v2ray.ang.util
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.support.v7.preference.PreferenceManager
|
||||
import android.text.TextUtils
|
||||
import com.google.gson.Gson
|
||||
import com.tencent.mmkv.MMKV
|
||||
@@ -20,8 +21,8 @@ import java.util.*
|
||||
object AngConfigManager {
|
||||
private lateinit var app: AngApplication
|
||||
private lateinit var angConfig: AngConfig
|
||||
val configs: AngConfig get() = angConfig
|
||||
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
|
||||
|
||||
fun inject(app: AngApplication) {
|
||||
this.app = app
|
||||
@@ -33,7 +34,8 @@ object AngConfigManager {
|
||||
*/
|
||||
private fun loadConfig() {
|
||||
try {
|
||||
val context = app.defaultDPreference.getPrefString(ANG_CONFIG, "")
|
||||
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(app)
|
||||
val context = defaultSharedPreferences.getString(ANG_CONFIG, "")
|
||||
if (!TextUtils.isEmpty(context)) {
|
||||
angConfig = Gson().fromJson(context, AngConfig::class.java)
|
||||
} else {
|
||||
@@ -56,18 +58,6 @@ object AngConfigManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* store config to file
|
||||
*/
|
||||
fun storeConfigFile() {
|
||||
try {
|
||||
val conf = Gson().toJson(angConfig)
|
||||
app.defaultDPreference.setPrefString(ANG_CONFIG, conf)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* import config form qrcode or...
|
||||
*/
|
||||
@@ -79,12 +69,12 @@ object AngConfigManager {
|
||||
|
||||
//maybe sub
|
||||
if (str.startsWith(HTTP_PROTOCOL) || str.startsWith(HTTPS_PROTOCOL)) {
|
||||
addSubItem(str)
|
||||
MmkvManager.importUrlAsSubscription(str)
|
||||
return 0
|
||||
}
|
||||
|
||||
var config: ServerConfig? = null
|
||||
val allowInsecure = false//app.defaultDPreference.getPrefBoolean(AppConfig.PREF_ALLOW_INSECURE, false)
|
||||
val allowInsecure = false//settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
|
||||
if (str.startsWith(EConfigType.VMESS.protocolScheme)) {
|
||||
config = ServerConfig.create(EConfigType.VMESS)
|
||||
val streamSetting = config.outboundBean?.streamSettings ?: return -1
|
||||
@@ -573,77 +563,4 @@ object AngConfigManager {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
private fun saveSubItem(subItem: ArrayList<AngConfig.SubItemBean>): Int {
|
||||
try {
|
||||
if (subItem.count() <= 0) {
|
||||
return -1
|
||||
}
|
||||
for (k in 0 until subItem.count()) {
|
||||
if (TextUtils.isEmpty(subItem[k].id)) {
|
||||
subItem[k].id = Utils.getUuid()
|
||||
}
|
||||
}
|
||||
angConfig.subItem = subItem
|
||||
|
||||
storeConfigFile()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fun addSubItem(subItem: AngConfig.SubItemBean, index: Int): Int {
|
||||
try {
|
||||
if (index >= 0) {
|
||||
//edit
|
||||
angConfig.subItem[index] = subItem
|
||||
} else {
|
||||
//add
|
||||
angConfig.subItem.add(subItem)
|
||||
}
|
||||
|
||||
saveSubItem(angConfig.subItem)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun addSubItem(url: String): Int {
|
||||
//already exists
|
||||
angConfig.subItem.forEach {
|
||||
if (it.url == url) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
val subItem = AngConfig.SubItemBean()
|
||||
subItem.remarks = "import sub"
|
||||
subItem.url = url
|
||||
return addSubItem(subItem, -1)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
fun removeSubItem(index: Int): Int {
|
||||
try {
|
||||
if (index < 0 || index > angConfig.subItem.count() - 1) {
|
||||
return -1
|
||||
}
|
||||
val subid = angConfig.subItem[index].id
|
||||
MmkvManager.removeServerViaSubid(subid)
|
||||
|
||||
angConfig.subItem.removeAt(index)
|
||||
|
||||
storeConfigFile()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.google.gson.Gson
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.dto.ServerAffiliationInfo
|
||||
import com.v2ray.ang.dto.ServerConfig
|
||||
import com.v2ray.ang.dto.SubscriptionItem
|
||||
|
||||
object MmkvManager {
|
||||
const val ID_MAIN = "MAIN"
|
||||
@@ -17,6 +18,16 @@ object MmkvManager {
|
||||
private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val serverStorage by lazy { MMKV.mmkvWithID(ID_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val serverAffStorage by lazy { MMKV.mmkvWithID(ID_SERVER_AFF, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val subStorage by lazy { MMKV.mmkvWithID(ID_SUB, MMKV.MULTI_PROCESS_MODE) }
|
||||
|
||||
fun decodeServerList(): MutableList<String> {
|
||||
val json = mainStorage?.decodeString(KEY_ANG_CONFIGS)
|
||||
return if (json.isNullOrBlank()) {
|
||||
mutableListOf()
|
||||
} else {
|
||||
Gson().fromJson(json, Array<String>::class.java).toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
fun decodeServerConfig(guid: String): ServerConfig? {
|
||||
if (guid.isBlank()) {
|
||||
@@ -36,11 +47,13 @@ object MmkvManager {
|
||||
guid
|
||||
}
|
||||
serverStorage?.encode(key, Gson().toJson(config))
|
||||
val serverList= Gson().fromJson(mainStorage?.decodeString(KEY_ANG_CONFIGS), Array<String>::class.java).toMutableList()
|
||||
serverList.add(guid)
|
||||
mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
|
||||
if (mainStorage?.decodeString(KEY_SELECTED_SERVER).isNullOrBlank()) {
|
||||
mainStorage?.encode(KEY_SELECTED_SERVER, guid)
|
||||
val serverList= decodeServerList()
|
||||
if (!serverList.contains(key)) {
|
||||
serverList.add(key)
|
||||
mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
|
||||
if (mainStorage?.decodeString(KEY_SELECTED_SERVER).isNullOrBlank()) {
|
||||
mainStorage?.encode(KEY_SELECTED_SERVER, key)
|
||||
}
|
||||
}
|
||||
return key
|
||||
}
|
||||
@@ -52,7 +65,7 @@ object MmkvManager {
|
||||
if (mainStorage?.decodeString(KEY_SELECTED_SERVER) == guid) {
|
||||
mainStorage?.remove(KEY_SELECTED_SERVER)
|
||||
}
|
||||
val serverList= Gson().fromJson(mainStorage?.decodeString(KEY_ANG_CONFIGS), Array<String>::class.java).toMutableList()
|
||||
val serverList= decodeServerList()
|
||||
serverList.remove(guid)
|
||||
mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
|
||||
serverStorage?.remove(guid)
|
||||
@@ -83,6 +96,15 @@ object MmkvManager {
|
||||
return Gson().fromJson(json, ServerAffiliationInfo::class.java)
|
||||
}
|
||||
|
||||
fun encodeServerTestDelayMillis(guid: String, testResult: Long) {
|
||||
if (guid.isBlank()) {
|
||||
return
|
||||
}
|
||||
val aff = decodeServerAffiliationInfo(guid) ?: ServerAffiliationInfo()
|
||||
aff.testDelayMillis = testResult
|
||||
serverAffStorage?.encode(guid, Gson().toJson(aff))
|
||||
}
|
||||
|
||||
fun clearAllTestDelayResults() {
|
||||
serverAffStorage?.allKeys()?.forEach { key ->
|
||||
decodeServerAffiliationInfo(key)?.let { aff ->
|
||||
@@ -91,4 +113,35 @@ object MmkvManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun importUrlAsSubscription(url: String): Int {
|
||||
val subscriptions = decodeSubscriptions()
|
||||
subscriptions.forEach {
|
||||
if (it.second.url == url) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
val subItem = SubscriptionItem()
|
||||
subItem.remarks = "import sub"
|
||||
subItem.url = url
|
||||
subStorage?.encode(Utils.getUuid(), Gson().toJson(subItem))
|
||||
return 1
|
||||
}
|
||||
|
||||
fun decodeSubscriptions(): List<Pair<String, SubscriptionItem>> {
|
||||
val subscriptions = mutableListOf<Pair<String, SubscriptionItem>>()
|
||||
subStorage?.allKeys()?.forEach { key ->
|
||||
val json = subStorage?.decodeString(key)
|
||||
if (!json.isNullOrBlank()) {
|
||||
subscriptions.add(Pair(key, Gson().fromJson(json, SubscriptionItem::class.java)))
|
||||
}
|
||||
}
|
||||
subscriptions.sortedBy { (_, value) -> value.addedTime }
|
||||
return subscriptions
|
||||
}
|
||||
|
||||
fun removeSubscription(subid: String) {
|
||||
subStorage?.remove(subid)
|
||||
removeServerViaSubid(subid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,16 +20,12 @@ import android.util.Log
|
||||
import android.util.Patterns
|
||||
import android.webkit.URLUtil
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.AngApplication
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.extension.defaultDPreference
|
||||
import com.v2ray.ang.extension.responseLength
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.extension.v2RayApplication
|
||||
import com.v2ray.ang.service.V2RayServiceManager
|
||||
import kotlinx.coroutines.isActive
|
||||
import me.dozen.dpreference.DPreference
|
||||
import java.io.IOException
|
||||
import java.net.*
|
||||
import kotlin.coroutines.coroutineContext
|
||||
@@ -37,6 +33,7 @@ import kotlin.coroutines.coroutineContext
|
||||
object Utils {
|
||||
|
||||
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val tcpTestingSockets = ArrayList<Socket?>()
|
||||
|
||||
/**
|
||||
@@ -126,8 +123,8 @@ object Utils {
|
||||
/**
|
||||
* get remote dns servers from preference
|
||||
*/
|
||||
fun getRemoteDnsServers(defaultDPreference: DPreference): ArrayList<String> {
|
||||
val remoteDns = defaultDPreference.getPrefString(AppConfig.PREF_REMOTE_DNS, AppConfig.DNS_AGENT)
|
||||
fun getRemoteDnsServers(): ArrayList<String> {
|
||||
val remoteDns = settingsStorage?.decodeString(AppConfig.PREF_REMOTE_DNS) ?: AppConfig.DNS_AGENT
|
||||
val ret = ArrayList<String>()
|
||||
if (!TextUtils.isEmpty(remoteDns)) {
|
||||
remoteDns
|
||||
@@ -147,8 +144,8 @@ object Utils {
|
||||
/**
|
||||
* get remote dns servers from preference
|
||||
*/
|
||||
fun getDomesticDnsServers(defaultDPreference: DPreference): ArrayList<String> {
|
||||
val domesticDns = defaultDPreference.getPrefString(AppConfig.PREF_DOMESTIC_DNS, AppConfig.DNS_DIRECT)
|
||||
fun getDomesticDnsServers(): ArrayList<String> {
|
||||
val domesticDns = settingsStorage?.decodeString(AppConfig.PREF_DOMESTIC_DNS) ?: AppConfig.DNS_DIRECT
|
||||
val ret = ArrayList<String>()
|
||||
if (!TextUtils.isEmpty(domesticDns)) {
|
||||
domesticDns
|
||||
|
||||
@@ -4,12 +4,12 @@ import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import com.google.gson.*
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.dto.V2rayConfig
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import com.v2ray.ang.dto.EConfigType
|
||||
import com.v2ray.ang.extension.defaultDPreference
|
||||
|
||||
object V2rayConfigUtil {
|
||||
// private val requestObj: JsonObject by lazy {
|
||||
@@ -19,6 +19,8 @@ object V2rayConfigUtil {
|
||||
// JSONObject("""{"version":"1.1","status":"200","reason":"OK","headers":{"Content-Type":["application/octet-stream","video/mpeg"],"Transfer-Encoding":["chunked"],"Connection":["keep-alive"],"Pragma":"no-cache"}}""")
|
||||
// }
|
||||
|
||||
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
|
||||
|
||||
data class Result(var status: Boolean, var content: String)
|
||||
|
||||
/**
|
||||
@@ -28,7 +30,7 @@ object V2rayConfigUtil {
|
||||
try {
|
||||
val config = MmkvManager.decodeServerConfig(guid) ?: return Result(false, "")
|
||||
if (config.configType == EConfigType.CUSTOM) {
|
||||
val customConfig = Gson().toJson(config.fullConfig)
|
||||
val customConfig = config.fullConfig?.toPrettyPrinting() ?: return Result(false, "")
|
||||
Log.d("V2rayConfigUtilGoLog", customConfig)
|
||||
return Result(true, customConfig)
|
||||
}
|
||||
@@ -56,7 +58,7 @@ object V2rayConfigUtil {
|
||||
//转成Json
|
||||
val v2rayConfig = Gson().fromJson(assets, V2rayConfig::class.java) ?: return result
|
||||
|
||||
//v2rayConfig.log.loglevel = context.defaultDPreference.getPrefString(AppConfig.PREF_LOGLEVEL, "warning")
|
||||
//v2rayConfig.log.loglevel = settingsStorage?.decodeString(AppConfig.PREF_LOGLEVEL) ?: "warning"
|
||||
|
||||
inbounds(v2rayConfig, context)
|
||||
|
||||
@@ -64,16 +66,13 @@ object V2rayConfigUtil {
|
||||
|
||||
routing(v2rayConfig, context)
|
||||
|
||||
if (context.defaultDPreference.getPrefBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false)) {
|
||||
if (settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) {
|
||||
customLocalDns(v2rayConfig, context)
|
||||
} else {
|
||||
customRemoteDns(v2rayConfig, context)
|
||||
}
|
||||
|
||||
val finalConfig = GsonBuilder().setPrettyPrinting().create().toJson(v2rayConfig)
|
||||
|
||||
result.status = true
|
||||
result.content = finalConfig
|
||||
result.content = v2rayConfig.toPrettyPrinting()
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -82,17 +81,17 @@ object V2rayConfigUtil {
|
||||
*/
|
||||
private fun inbounds(v2rayConfig: V2rayConfig, context: Context): Boolean {
|
||||
try {
|
||||
//val socksPort = Utils.parseInt(context.defaultDPreference.getPrefStringNonEmpty(AppConfig.PREF_SOCKS_PORT, AppConfig .PORT_SOCKS))
|
||||
//val httpPort = Utils.parseInt(context.defaultDPreference.getPrefStringNonEmpty(AppConfig.PREF_HTTP_PORT, AppConfig.PORT_HTTP))
|
||||
//val socksPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_SOCKS_PORT) ?: AppConfig.PORT_SOCKS)
|
||||
//val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT) ?: AppConfig.PORT_HTTP)
|
||||
|
||||
v2rayConfig.inbounds.forEach { curInbound ->
|
||||
if (!context.defaultDPreference.getPrefBoolean(AppConfig.PREF_PROXY_SHARING, false)) {
|
||||
if (!(settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) ?: false)) {
|
||||
//bind all inbounds to localhost if the user requests
|
||||
curInbound.listen = "127.0.0.1"
|
||||
}
|
||||
}
|
||||
v2rayConfig.inbounds[0].port = 10808
|
||||
v2rayConfig.inbounds[0].sniffing?.enabled = context.defaultDPreference.getPrefBoolean(AppConfig.PREF_SNIFFING_ENABLED, true)
|
||||
v2rayConfig.inbounds[0].sniffing?.enabled = settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED) ?: true
|
||||
|
||||
//v2rayConfig.inbounds[1].port = httpPort
|
||||
|
||||
@@ -114,12 +113,12 @@ object V2rayConfigUtil {
|
||||
*/
|
||||
private fun routing(v2rayConfig: V2rayConfig, context: Context): Boolean {
|
||||
try {
|
||||
routingUserRule(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_AGENT, ""), AppConfig.TAG_AGENT, v2rayConfig)
|
||||
routingUserRule(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_DIRECT, ""), AppConfig.TAG_DIRECT, v2rayConfig)
|
||||
routingUserRule(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED, ""), AppConfig.TAG_BLOCKED, v2rayConfig)
|
||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) ?: "", AppConfig.TAG_AGENT, v2rayConfig)
|
||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) ?: "", AppConfig.TAG_DIRECT, v2rayConfig)
|
||||
routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) ?: "", AppConfig.TAG_BLOCKED, v2rayConfig)
|
||||
|
||||
v2rayConfig.routing.domainStrategy = context.defaultDPreference.getPrefString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY, "IPIfNonMatch")
|
||||
val routingMode = context.defaultDPreference.getPrefString(AppConfig.PREF_ROUTING_MODE, "0")
|
||||
v2rayConfig.routing.domainStrategy = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY) ?: "IPIfNonMatch"
|
||||
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "0"
|
||||
|
||||
// Hardcode googleapis.cn
|
||||
val googleapisRoute = V2rayConfig.RoutingBean.RulesBean(
|
||||
@@ -234,32 +233,32 @@ object V2rayConfigUtil {
|
||||
try {
|
||||
val hosts = mutableMapOf<String, String>()
|
||||
val servers = ArrayList<Any>()
|
||||
val remoteDns = Utils.getRemoteDnsServers(context.defaultDPreference)
|
||||
val remoteDns = Utils.getRemoteDnsServers()
|
||||
|
||||
remoteDns.forEach {
|
||||
servers.add(it)
|
||||
}
|
||||
|
||||
val domesticDns = Utils.getDomesticDnsServers(context.defaultDPreference)
|
||||
val domesticDns = Utils.getDomesticDnsServers()
|
||||
val geositeCn = arrayListOf("geosite:cn")
|
||||
val geoipCn = arrayListOf("geoip:cn")
|
||||
|
||||
val agDomain = userRule2Domian(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_AGENT, ""))
|
||||
val agDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) ?: "")
|
||||
if (agDomain.size > 0) {
|
||||
servers.add(V2rayConfig.DnsBean.ServersBean(remoteDns.first(), 53, agDomain, null))
|
||||
}
|
||||
|
||||
val dirDomain = userRule2Domian(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_DIRECT, ""))
|
||||
val dirDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) ?: "")
|
||||
if (dirDomain.size > 0) {
|
||||
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, dirDomain, geoipCn))
|
||||
}
|
||||
|
||||
val routingMode = context.defaultDPreference.getPrefString(AppConfig.PREF_ROUTING_MODE, "0")
|
||||
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: "0"
|
||||
if (routingMode == "2" || routingMode == "3") {
|
||||
servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, geositeCn, geoipCn))
|
||||
}
|
||||
|
||||
val blkDomain = userRule2Domian(context.defaultDPreference.getPrefString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED, ""))
|
||||
val blkDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) ?: "")
|
||||
if (blkDomain.size > 0) {
|
||||
hosts.putAll(blkDomain.map { it to "127.0.0.1" })
|
||||
}
|
||||
@@ -279,7 +278,7 @@ object V2rayConfigUtil {
|
||||
port = 53,
|
||||
network = "tcp,udp")
|
||||
|
||||
//val localDnsPort = Utils.parseInt(context.defaultDPreference.getPrefStringNonEmpty(AppConfig.PREF_LOCAL_DNS_PORT, AppConfig.PORT_LOCAL_DNS))
|
||||
//val localDnsPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_LOCAL_DNS_PORT) ?: AppConfig.PORT_LOCAL_DNS)
|
||||
v2rayConfig.inbounds.add(
|
||||
V2rayConfig.InboundBean(
|
||||
tag = "dns-in",
|
||||
@@ -344,7 +343,7 @@ object V2rayConfigUtil {
|
||||
val servers = ArrayList<Any>()
|
||||
val hosts = mutableMapOf<String, String>()
|
||||
|
||||
Utils.getRemoteDnsServers(context.defaultDPreference).forEach {
|
||||
Utils.getRemoteDnsServers().forEach {
|
||||
servers.add(it)
|
||||
}
|
||||
// hardcode googleapi rule to fix play store problems
|
||||
|
||||
@@ -24,9 +24,8 @@ import java.util.*
|
||||
|
||||
class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
|
||||
private val serverAffStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_AFF, MMKV.MULTI_PROCESS_MODE) }
|
||||
|
||||
var serverList= Gson().fromJson(mainStorage?.decodeString(KEY_ANG_CONFIGS), Array<String>::class.java).toMutableList()
|
||||
var serverList= MmkvManager.decodeServerList()
|
||||
private set
|
||||
val isRunning by lazy { MutableLiveData<Boolean>() }
|
||||
val updateListAction by lazy { MutableLiveData<Int>() }
|
||||
@@ -49,7 +48,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
}
|
||||
|
||||
fun reloadServerList() {
|
||||
serverList = Gson().fromJson(mainStorage?.decodeString(KEY_ANG_CONFIGS), Array<String>::class.java).toMutableList()
|
||||
serverList = MmkvManager.decodeServerList()
|
||||
updateListAction.value = -1
|
||||
}
|
||||
|
||||
@@ -85,11 +84,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
tcpingTestScope.launch {
|
||||
val testResult = Utils.tcping(serverAddress, serverPort)
|
||||
launch(Dispatchers.Main) {
|
||||
MmkvManager.decodeServerAffiliationInfo(guid)?.let { aff ->
|
||||
aff.testDelayMillis = testResult
|
||||
serverAffStorage?.encode(guid, Gson().toJson(aff))
|
||||
updateListAction.value = serverList.indexOf(guid)
|
||||
}
|
||||
MmkvManager.encodeServerTestDelayMillis(guid, testResult)
|
||||
updateListAction.value = serverList.indexOf(guid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,14 @@ import android.arch.lifecycle.AndroidViewModel
|
||||
import android.content.SharedPreferences
|
||||
import android.support.v7.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.AppConfig
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import com.v2ray.ang.util.MmkvManager
|
||||
|
||||
class SettingsViewModel(application: Application) : AndroidViewModel(application), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
|
||||
|
||||
fun startListenPreferenceChange() {
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplication()).registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
@@ -23,18 +26,27 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
|
||||
Log.d(AppConfig.ANG_PACKAGE, "Observe settings changed: $key")
|
||||
when(key) {
|
||||
AppConfig.PREF_SNIFFING_ENABLED,
|
||||
AppConfig.PREF_PROXY_SHARING,
|
||||
AppConfig.PREF_LOCAL_DNS_ENABLED,
|
||||
AppConfig.PREF_MODE,
|
||||
AppConfig.PREF_REMOTE_DNS,
|
||||
AppConfig.PREF_DOMESTIC_DNS,
|
||||
AppConfig.PREF_ROUTING_DOMAIN_STRATEGY,
|
||||
AppConfig.PREF_ROUTING_MODE,
|
||||
AppConfig.PREF_V2RAY_ROUTING_AGENT,
|
||||
AppConfig.PREF_V2RAY_ROUTING_BLOCKED,
|
||||
AppConfig.PREF_V2RAY_ROUTING_DIRECT -> {
|
||||
GlobalScope.launch {
|
||||
}
|
||||
AppConfig.PREF_V2RAY_ROUTING_DIRECT, -> {
|
||||
settingsStorage?.encode(key, sharedPreferences.getString(key, ""))
|
||||
}
|
||||
AppConfig.PREF_SPEED_ENABLED,
|
||||
AppConfig.PREF_SNIFFING_ENABLED,
|
||||
AppConfig.PREF_PROXY_SHARING,
|
||||
AppConfig.PREF_LOCAL_DNS_ENABLED,
|
||||
AppConfig.PREF_FORWARD_IPV6,
|
||||
AppConfig.PREF_PER_APP_PROXY,
|
||||
AppConfig.PREF_BYPASS_APPS, -> {
|
||||
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
|
||||
}
|
||||
AppConfig.PREF_PER_APP_PROXY_SET -> {
|
||||
settingsStorage?.encode(key, sharedPreferences.getStringSet(key, setOf()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,25 +0,0 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion Integer.parseInt("$compileSdkVer")
|
||||
buildToolsVersion buildToolsVer
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 17
|
||||
targetSdkVersion Integer.parseInt("$targetSdkVer")
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
testImplementation 'junit:junit:4.13'
|
||||
implementation "com.android.support:support-annotations:$supportLibVersion"
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/wangyida/Library/Android/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
@@ -1,16 +0,0 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="me.dozen.dpreference">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:supportsRtl="true">
|
||||
|
||||
<provider
|
||||
android:name="me.dozen.dpreference.PreferenceProvider"
|
||||
android:enabled="true"
|
||||
android:authorities="com.v2ray.ang.dpreference"
|
||||
android:exported="false" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -1,86 +0,0 @@
|
||||
package me.dozen.dpreference;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by wangyida on 15-4-9.
|
||||
*/
|
||||
public class DPreference {
|
||||
|
||||
Context mContext;
|
||||
|
||||
/**
|
||||
* preference file name
|
||||
*/
|
||||
String mName;
|
||||
|
||||
private DPreference() {
|
||||
}
|
||||
|
||||
public DPreference(Context context, String name) {
|
||||
this.mContext = context;
|
||||
this.mName = name;
|
||||
}
|
||||
|
||||
public String getPrefString(final String key, final String defaultValue) {
|
||||
return PrefAccessor.getString(mContext, mName, key, defaultValue);
|
||||
}
|
||||
|
||||
public void setPrefString(final String key, final String value) {
|
||||
PrefAccessor.setString(mContext, mName, key, value);
|
||||
}
|
||||
|
||||
public boolean getPrefBoolean(final String key, final boolean defaultValue) {
|
||||
return PrefAccessor.getBoolean(mContext, mName, key, defaultValue);
|
||||
}
|
||||
|
||||
public void setPrefBoolean(final String key, final boolean value) {
|
||||
PrefAccessor.setBoolean(mContext, mName, key, value);
|
||||
}
|
||||
|
||||
public void setPrefInt(final String key, final int value) {
|
||||
PrefAccessor.setInt(mContext, mName, key, value);
|
||||
}
|
||||
|
||||
public int getPrefInt(final String key, final int defaultValue) {
|
||||
return PrefAccessor.getInt(mContext, mName, key, defaultValue);
|
||||
}
|
||||
|
||||
public void setPrefLong(final String key, final long value) {
|
||||
PrefAccessor.setLong(mContext, mName, key, value);
|
||||
}
|
||||
|
||||
public long getPrefLong(final String key, final long defaultValue) {
|
||||
return PrefAccessor.getLong(mContext, mName, key, defaultValue);
|
||||
}
|
||||
|
||||
public void setPrefStringSet(final String key, final Set<String> value) {
|
||||
PrefAccessor.setStringSet(mContext, mName, key, value);
|
||||
}
|
||||
|
||||
public Set<String> getPrefStringSet(final String key, final Set<String> defaultValue) {
|
||||
return PrefAccessor.getStringSet(mContext, mName, key, defaultValue);
|
||||
}
|
||||
|
||||
public void setPrefStringOrderedSet(final String key, final LinkedHashSet<String> value) {
|
||||
PrefAccessor.setString(mContext, mName, key, StringSetConverter.encode(value));
|
||||
}
|
||||
|
||||
public LinkedHashSet<String> getPrefStringOrderedSet(final String key, final LinkedHashSet<String> defaultValue) {
|
||||
String value = PrefAccessor.getString(mContext, mName, key, "");
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return StringSetConverter.decode(value);
|
||||
}
|
||||
|
||||
public void removePreference(final String key) {
|
||||
PrefAccessor.remove(mContext, mName, key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package me.dozen.dpreference;
|
||||
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
|
||||
final class IOUtils {
|
||||
|
||||
// NOTE: This class is focussed on InputStream, OutputStream, Reader and
|
||||
// Writer. Each method should take at least one of these as a parameter,
|
||||
// or return one of them.
|
||||
|
||||
private static final int EOF = -1;
|
||||
|
||||
/**
|
||||
* The default buffer size ({@value}) to use for {@link
|
||||
*/
|
||||
private static final int DEFAULT_BUFFER_SIZE = 1024;
|
||||
|
||||
public static void closeQuietly(InputStream is) {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void closeQuietly(OutputStream os) {
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void closeQuietly(Reader r) {
|
||||
if (r != null) {
|
||||
try {
|
||||
r.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void closeQuietly(Cursor cursor) {
|
||||
if (cursor != null && !cursor.isClosed()) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package me.dozen.dpreference;
|
||||
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by wangyida on 15/12/18.
|
||||
*/
|
||||
interface IPrefImpl {
|
||||
|
||||
String getPrefString(String key, String defaultValue);
|
||||
|
||||
void setPrefString(String key, String value);
|
||||
|
||||
boolean getPrefBoolean(String key, boolean defaultValue);
|
||||
|
||||
void setPrefBoolean(final String key, final boolean value);
|
||||
|
||||
void setPrefInt(final String key, final int value);
|
||||
|
||||
int getPrefInt(final String key, final int defaultValue);
|
||||
|
||||
void setPrefFloat(final String key, final float value);
|
||||
|
||||
float getPrefFloat(final String key, final float defaultValue);
|
||||
|
||||
void setPrefLong(final String key, final long value);
|
||||
|
||||
long getPrefLong(final String key, final long defaultValue);
|
||||
|
||||
Set<String> getPrefStringSet(String key, Set<String> defaultValue);
|
||||
|
||||
void setPrefStringSet(String key, Set<String> value);
|
||||
|
||||
void removePreference(final String key);
|
||||
|
||||
boolean hasKey(String key);
|
||||
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package me.dozen.dpreference;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by wangyida on 15/12/18.
|
||||
*/
|
||||
class PrefAccessor {
|
||||
|
||||
public static String getString(Context context, String name, String key, String defaultValue) {
|
||||
Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_STRING);
|
||||
String value = defaultValue;
|
||||
Cursor cursor = context.getContentResolver().query(URI, null, null, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
value = cursor.getString(cursor.getColumnIndex(PreferenceProvider.PREF_VALUE));
|
||||
}
|
||||
IOUtils.closeQuietly(cursor);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int getInt(Context context, String name, String key, int defaultValue) {
|
||||
Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_INT);
|
||||
int value = defaultValue;
|
||||
Cursor cursor = context.getContentResolver().query(URI, null, null, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
value = cursor.getInt(cursor.getColumnIndex(PreferenceProvider.PREF_VALUE));
|
||||
}
|
||||
IOUtils.closeQuietly(cursor);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static long getLong(Context context, String name, String key, long defaultValue) {
|
||||
Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_LONG);
|
||||
long value = defaultValue;
|
||||
Cursor cursor = context.getContentResolver().query(URI, null, null, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
value = cursor.getLong(cursor.getColumnIndex(PreferenceProvider.PREF_VALUE));
|
||||
}
|
||||
IOUtils.closeQuietly(cursor);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static boolean getBoolean(Context context, String name, String key, boolean defaultValue) {
|
||||
Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_BOOLEAN);
|
||||
int value = defaultValue ? 1 : 0;
|
||||
Cursor cursor = context.getContentResolver().query(URI, null, null, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
value = cursor.getInt(cursor.getColumnIndex(PreferenceProvider.PREF_VALUE));
|
||||
}
|
||||
IOUtils.closeQuietly(cursor);
|
||||
return value == 1;
|
||||
}
|
||||
|
||||
public static Set<String> getStringSet(Context context, String name, String key, Set<String> defaultValue) {
|
||||
Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_STRING_SET);
|
||||
Set<String> value = defaultValue;
|
||||
Cursor cursor = context.getContentResolver().query(URI, null, null, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
String cursorString = cursor.getString(cursor.getColumnIndex(PreferenceProvider.PREF_VALUE));
|
||||
value = StringSetConverter.decode(cursorString);
|
||||
}
|
||||
IOUtils.closeQuietly(cursor);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void remove(Context context, String name, String key) {
|
||||
Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_STRING);
|
||||
context.getContentResolver().delete(URI, null, null);
|
||||
}
|
||||
|
||||
public static void setString(Context context, String name, String key, String value) {
|
||||
Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_STRING);
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(PreferenceProvider.PREF_KEY, key);
|
||||
cv.put(PreferenceProvider.PREF_VALUE, value);
|
||||
context.getContentResolver().update(URI, cv, null, null);
|
||||
}
|
||||
|
||||
public static void setBoolean(Context context, String name, String key, boolean value) {
|
||||
Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_BOOLEAN);
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(PreferenceProvider.PREF_KEY, key);
|
||||
cv.put(PreferenceProvider.PREF_VALUE, value);
|
||||
context.getContentResolver().update(URI, cv, null, null);
|
||||
}
|
||||
|
||||
public static void setInt(Context context, String name, String key, int value) {
|
||||
Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_INT);
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(PreferenceProvider.PREF_KEY, key);
|
||||
cv.put(PreferenceProvider.PREF_VALUE, value);
|
||||
context.getContentResolver().update(URI, cv, null, null);
|
||||
}
|
||||
|
||||
public static void setLong(Context context, String name, String key, long value) {
|
||||
Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_LONG);
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(PreferenceProvider.PREF_KEY, key);
|
||||
cv.put(PreferenceProvider.PREF_VALUE, value);
|
||||
context.getContentResolver().update(URI, cv, null, null);
|
||||
}
|
||||
|
||||
public static void setStringSet(Context context, String name, String key, Set<String> value) {
|
||||
Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_STRING_SET);
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(PreferenceProvider.PREF_KEY, key);
|
||||
cv.put(PreferenceProvider.PREF_VALUE, StringSetConverter.encode(value));
|
||||
context.getContentResolver().update(URI, cv, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
package me.dozen.dpreference;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by wangyida on 15/12/18.
|
||||
*/
|
||||
class PreferenceImpl implements IPrefImpl {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private String mPrefName;
|
||||
|
||||
public PreferenceImpl(Context context, String prefName) {
|
||||
mContext = context;
|
||||
mPrefName = prefName;
|
||||
}
|
||||
|
||||
public String getPrefString(final String key,
|
||||
final String defaultValue) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
return settings.getString(key, defaultValue);
|
||||
}
|
||||
|
||||
public void setPrefString(final String key, final String value) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
settings.edit().putString(key, value).apply();
|
||||
}
|
||||
|
||||
public boolean getPrefBoolean(final String key,
|
||||
final boolean defaultValue) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
return settings.getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
public boolean hasKey(final String key) {
|
||||
return mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE)
|
||||
.contains(key);
|
||||
}
|
||||
|
||||
public void setPrefBoolean(final String key, final boolean value) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
settings.edit().putBoolean(key, value).apply();
|
||||
}
|
||||
|
||||
public void setPrefInt(final String key, final int value) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
settings.edit().putInt(key, value).apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPrefStringSet(String key, Set<String> defaultValue) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
return settings.getStringSet(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrefStringSet(String key, Set<String> value) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
settings.edit().putStringSet(key, value).apply();
|
||||
}
|
||||
|
||||
public void increasePrefInt(final String key) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
increasePrefInt(settings, key);
|
||||
}
|
||||
|
||||
public void increasePrefInt(final SharedPreferences sp, final String key) {
|
||||
final int v = sp.getInt(key, 0) + 1;
|
||||
sp.edit().putInt(key, v).apply();
|
||||
}
|
||||
|
||||
public void increasePrefInt(final SharedPreferences sp, final String key,
|
||||
final int increment) {
|
||||
final int v = sp.getInt(key, 0) + increment;
|
||||
sp.edit().putInt(key, v).apply();
|
||||
}
|
||||
|
||||
public int getPrefInt(final String key, final int defaultValue) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
return settings.getInt(key, defaultValue);
|
||||
}
|
||||
|
||||
public void setPrefFloat(final String key, final float value) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
settings.edit().putFloat(key, value).apply();
|
||||
}
|
||||
|
||||
public float getPrefFloat(final String key, final float defaultValue) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
return settings.getFloat(key, defaultValue);
|
||||
}
|
||||
|
||||
public void setPrefLong(final String key, final long value) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
settings.edit().putLong(key, value).apply();
|
||||
}
|
||||
|
||||
public long getPrefLong(final String key, final long defaultValue) {
|
||||
final SharedPreferences settings =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
return settings.getLong(key, defaultValue);
|
||||
}
|
||||
|
||||
|
||||
public void removePreference(final String key) {
|
||||
final SharedPreferences prefs =
|
||||
mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE);
|
||||
prefs.edit().remove(key).apply();
|
||||
}
|
||||
|
||||
public void clearPreference(final SharedPreferences p) {
|
||||
final SharedPreferences.Editor editor = p.edit();
|
||||
editor.clear();
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
package me.dozen.dpreference;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by wangyida on 15/12/18.
|
||||
*/
|
||||
public class PreferenceProvider extends ContentProvider {
|
||||
|
||||
private static final String TAG = PreferenceProvider.class.getSimpleName();
|
||||
|
||||
private static final String AUTHORITY = "com.v2ray.ang.dpreference";
|
||||
|
||||
public static final String CONTENT_PREF_BOOLEAN_URI = "content://" + AUTHORITY + "/boolean/";
|
||||
public static final String CONTENT_PREF_STRING_URI = "content://" + AUTHORITY + "/string/";
|
||||
public static final String CONTENT_PREF_INT_URI = "content://" + AUTHORITY + "/integer/";
|
||||
public static final String CONTENT_PREF_LONG_URI = "content://" + AUTHORITY + "/long/";
|
||||
public static final String CONTENT_PREF_STRING_SET_URI = "content://" + AUTHORITY + "/string_set/";
|
||||
|
||||
|
||||
public static final String PREF_KEY = "key";
|
||||
public static final String PREF_VALUE = "value";
|
||||
|
||||
public static final int PREF_BOOLEAN = 1;
|
||||
public static final int PREF_STRING = 2;
|
||||
public static final int PREF_INT = 3;
|
||||
public static final int PREF_LONG = 4;
|
||||
public static final int PREF_STRING_SET = 5;
|
||||
|
||||
private static final UriMatcher sUriMatcher;
|
||||
|
||||
static {
|
||||
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
sUriMatcher.addURI(AUTHORITY, "boolean/*/*", PREF_BOOLEAN);
|
||||
sUriMatcher.addURI(AUTHORITY, "string/*/*", PREF_STRING);
|
||||
sUriMatcher.addURI(AUTHORITY, "integer/*/*", PREF_INT);
|
||||
sUriMatcher.addURI(AUTHORITY, "long/*/*", PREF_LONG);
|
||||
sUriMatcher.addURI(AUTHORITY, "string_set/*/*", PREF_STRING_SET);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
MatrixCursor cursor = null;
|
||||
PrefModel model = getPrefModelByUri(uri);
|
||||
switch (sUriMatcher.match(uri)) {
|
||||
case PREF_BOOLEAN:
|
||||
if (getDPreference(model.getName()).hasKey(model.getKey())) {
|
||||
cursor = preferenceToCursor(getDPreference(model.getName()).getPrefBoolean(model.getKey(), false) ? 1 : 0);
|
||||
}
|
||||
break;
|
||||
case PREF_STRING:
|
||||
if (getDPreference(model.getName()).hasKey(model.getKey())) {
|
||||
cursor = preferenceToCursor(getDPreference(model.getName()).getPrefString(model.getKey(), ""));
|
||||
}
|
||||
break;
|
||||
case PREF_INT:
|
||||
if (getDPreference(model.getName()).hasKey(model.getKey())) {
|
||||
cursor = preferenceToCursor(getDPreference(model.getName()).getPrefInt(model.getKey(), -1));
|
||||
}
|
||||
break;
|
||||
case PREF_LONG:
|
||||
if (getDPreference(model.getName()).hasKey(model.getKey())) {
|
||||
cursor = preferenceToCursor(getDPreference(model.getName()).getPrefLong(model.getKey(), -1));
|
||||
}
|
||||
break;
|
||||
case PREF_STRING_SET:
|
||||
if (getDPreference(model.getName()).hasKey(model.getKey())) {
|
||||
cursor = preferenceToCursor(getDPreference(model.getName()).getPrefStringSet(model.getKey(), null));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
throw new IllegalStateException("insert unsupport!!!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
switch (sUriMatcher.match(uri)) {
|
||||
case PREF_BOOLEAN:
|
||||
case PREF_LONG:
|
||||
case PREF_STRING:
|
||||
case PREF_INT:
|
||||
case PREF_STRING_SET:
|
||||
PrefModel model = getPrefModelByUri(uri);
|
||||
if (model != null) {
|
||||
getDPreference(model.getName()).removePreference(model.getKey());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(" unsupported uri : " + uri);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
PrefModel model = getPrefModelByUri(uri);
|
||||
if (model == null) {
|
||||
throw new IllegalArgumentException("update prefModel is null");
|
||||
}
|
||||
switch (sUriMatcher.match(uri)) {
|
||||
case PREF_BOOLEAN:
|
||||
persistBoolean(model.getName(), values);
|
||||
break;
|
||||
case PREF_LONG:
|
||||
persistLong(model.getName(), values);
|
||||
break;
|
||||
case PREF_STRING:
|
||||
persistString(model.getName(), values);
|
||||
break;
|
||||
case PREF_INT:
|
||||
persistInt(model.getName(), values);
|
||||
break;
|
||||
case PREF_STRING_SET:
|
||||
persistStringSet(model.getName(), values);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("update unsupported uri : " + uri);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static String[] PREFERENCE_COLUMNS = {PREF_VALUE};
|
||||
|
||||
private <T> MatrixCursor preferenceToCursor(T value) {
|
||||
MatrixCursor matrixCursor = new MatrixCursor(PREFERENCE_COLUMNS, 1);
|
||||
MatrixCursor.RowBuilder builder = matrixCursor.newRow();
|
||||
builder.add(value);
|
||||
return matrixCursor;
|
||||
}
|
||||
|
||||
private void persistInt(String name, ContentValues values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException(" values is null!!!");
|
||||
}
|
||||
String kInteger = values.getAsString(PREF_KEY);
|
||||
int vInteger = values.getAsInteger(PREF_VALUE);
|
||||
getDPreference(name).setPrefInt(kInteger, vInteger);
|
||||
}
|
||||
|
||||
private void persistBoolean(String name, ContentValues values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException(" values is null!!!");
|
||||
}
|
||||
String kBoolean = values.getAsString(PREF_KEY);
|
||||
boolean vBoolean = values.getAsBoolean(PREF_VALUE);
|
||||
getDPreference(name).setPrefBoolean(kBoolean, vBoolean);
|
||||
}
|
||||
|
||||
private void persistLong(String name, ContentValues values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException(" values is null!!!");
|
||||
}
|
||||
String kLong = values.getAsString(PREF_KEY);
|
||||
long vLong = values.getAsLong(PREF_VALUE);
|
||||
getDPreference(name).setPrefLong(kLong, vLong);
|
||||
}
|
||||
|
||||
private void persistString(String name, ContentValues values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException(" values is null!!!");
|
||||
}
|
||||
String kString = values.getAsString(PREF_KEY);
|
||||
String vString = values.getAsString(PREF_VALUE);
|
||||
getDPreference(name).setPrefString(kString, vString);
|
||||
}
|
||||
|
||||
private void persistStringSet(String name, ContentValues values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException(" values is null!!!");
|
||||
}
|
||||
String kString = values.getAsString(PREF_KEY);
|
||||
String vString = values.getAsString(PREF_VALUE);
|
||||
getDPreference(name).setPrefStringSet(kString, StringSetConverter.decode(vString));
|
||||
}
|
||||
|
||||
private static Map<String, IPrefImpl> sPreferences = new HashMap<>();
|
||||
|
||||
private IPrefImpl getDPreference(String name) {
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
throw new IllegalArgumentException("getDPreference name is null!!!");
|
||||
}
|
||||
if (sPreferences.get(name) == null) {
|
||||
IPrefImpl pref = new PreferenceImpl(getContext(), name);
|
||||
sPreferences.put(name, pref);
|
||||
}
|
||||
return sPreferences.get(name);
|
||||
}
|
||||
|
||||
private PrefModel getPrefModelByUri(Uri uri) {
|
||||
if (uri == null || uri.getPathSegments().size() != 3) {
|
||||
throw new IllegalArgumentException("getPrefModelByUri uri is wrong : " + uri);
|
||||
}
|
||||
String name = uri.getPathSegments().get(1);
|
||||
String key = uri.getPathSegments().get(2);
|
||||
return new PrefModel(name, key);
|
||||
}
|
||||
|
||||
|
||||
public static Uri buildUri(String name, String key, int type) {
|
||||
return Uri.parse(getUriByType(type) + name + "/" + key);
|
||||
}
|
||||
|
||||
private static String getUriByType(int type) {
|
||||
switch (type) {
|
||||
case PreferenceProvider.PREF_BOOLEAN:
|
||||
return PreferenceProvider.CONTENT_PREF_BOOLEAN_URI;
|
||||
case PreferenceProvider.PREF_INT:
|
||||
return PreferenceProvider.CONTENT_PREF_INT_URI;
|
||||
case PreferenceProvider.PREF_LONG:
|
||||
return PreferenceProvider.CONTENT_PREF_LONG_URI;
|
||||
case PreferenceProvider.PREF_STRING:
|
||||
return PreferenceProvider.CONTENT_PREF_STRING_URI;
|
||||
case PreferenceProvider.PREF_STRING_SET:
|
||||
return PreferenceProvider.CONTENT_PREF_STRING_SET_URI;
|
||||
}
|
||||
throw new IllegalStateException("unsupport preftype : " + type);
|
||||
}
|
||||
|
||||
private static class PrefModel {
|
||||
String name;
|
||||
|
||||
String key;
|
||||
|
||||
public PrefModel(String name, String key) {
|
||||
this.name = name;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package me.dozen.dpreference;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class StringSetConverter {
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
public static String encode(Set<String> src) {
|
||||
return gson.toJson(src);
|
||||
}
|
||||
|
||||
public static LinkedHashSet<String> decode(String json) {
|
||||
Type setType = new TypeToken<LinkedHashSet<String>>() {
|
||||
}.getType();
|
||||
return gson.fromJson(json, setType);
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
include ':app', ':dpreference'
|
||||
include ':app'
|
||||
|
||||
Reference in New Issue
Block a user