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:
yuhan6665
2021-03-20 01:00:12 -04:00
parent 375a209beb
commit 572955dd1e
38 changed files with 274 additions and 1103 deletions
+2 -1
View File
@@ -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
View File
@@ -1 +0,0 @@
/build
-25
View File
@@ -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
View File
@@ -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
View File
@@ -1 +1 @@
include ':app', ':dpreference'
include ':app'