diff --git a/build-aar.sh b/build-aar.sh new file mode 100644 index 0000000..5b9796b --- /dev/null +++ b/build-aar.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +set -e + +export ANDROID_HOME="/root/java/android-sdk" +export ANDROID_NDK_HOME="${ANDROID_HOME}/ndk-bundle" + +export GOROOT="/usr/local/go" +export GOBIN="$GOROOT/bin" +export GOPATH="/root/go" +export GO111MODULE=off +export GOPROXY=direct + +export PATH=${PATH}:${GOBIN}:${GOPATH}/bin:${ANDROID_HOME}/tools:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/platform-tools + +# change dir +cd ${GOPATH}/src/AndroidLibV2rayLite + +download_data=0 +update_go_dep=0 + +for param in "$@"; do + case $param in + data*) + download_data=1 + ;; + dep*) + update_go_dep=1 + ;; + esac +done + +echo "Update go dep......" +if [[ ${update_go_dep} == "1" ]] ; then + # download dep + go get -u github.com/golang/protobuf/protoc-gen-go/... + go get -u golang.org/x/mobile/cmd/... + go get -u github.com/jteeuwen/go-bindata/... + go get -u -insecure v2ray.com/core/... + + #go get AndroidLibV2rayLite +fi + + +if [[ ${download_data} == "1" ]] ; then + echo "Download geo data....." + /bin/bash gen_assets.sh download +fi + +cd shippedBinarys && make shippedBinary && cd .. + +echo "compile aar" +gomobile init && gomobile bind -v -tags json . diff --git a/build.gradle b/build.gradle index 399c637..112b903 100644 --- a/build.gradle +++ b/build.gradle @@ -14,8 +14,8 @@ buildscript { junitVersion = '4.13' androidTestVersion = '1.2.0' androidEspressoVersion = '3.2.0' - versionCode = 5000868 - versionName = '5.1.10-nightly' + versionCode = 5000878 + versionName = '5.1.11-nightly' resConfigs = ['ar', 'es', 'fa', 'fr', 'ja', 'ko', 'ru', 'tr', 'zh-rCN', 'zh-rTW'] } diff --git a/core/build.gradle b/core/build.gradle index d11d65e..f39093b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -26,7 +26,29 @@ android { "room.schemaLocation": "$projectDir/schemas".toString(), ] } - + flavorDimensions "encryption" + productFlavors { + ssvpn { + dimension "encryption" + resValue 'string', 'app_name', "SS VPN" + resValue 'string', 'about_title', "SS VPN %s" + resValue 'string', 'admob_appid', "ca-app-pub-2194043486084479~5832794268" + resValue 'string', 'banner_adUnitId', "ca-app-pub-2194043486084479/4292721507" + resValue 'string', 'native_adUnitId', "ca-app-pub-2194043486084479/8267385919" + } + v2free { + dimension "encryption" + resValue 'string', 'app_name', "V2free" + resValue 'string', 'about_title', "V2free %s" + resValue 'string', 'admob_appid', "ca-app-pub-2194043486084479~8520668797" + resValue 'string', 'banner_adUnitId', "ca-app-pub-2194043486084479/7710984154" + resValue 'string', 'native_adUnitId', "ca-app-pub-2194043486084479/5507178325" + } + } + sourceSets { + ssvpn.setRoot('src/ssvpn') + v2free.setRoot('src/v2free') + } compileOptions { coreLibraryDesugaringEnabled true sourceCompatibility javaVersion @@ -55,7 +77,7 @@ def roomVersion = '2.2.3' def workVersion = '2.3.1' dependencies { api project(':plugin') - implementation project(':dpreference') + //implementation project(':dpreference') implementation project(':libv2ray') //implementation project(':ssvpn-v2ray') implementation project(':obfs') diff --git a/core/src/androidTest/java/com/github/shadowsocks/database/ProfileTest.kt b/core/src/androidTest/java/com/github/shadowsocks/database/ProfileTest.kt index 61aefcc..9543acd 100644 --- a/core/src/androidTest/java/com/github/shadowsocks/database/ProfileTest.kt +++ b/core/src/androidTest/java/com/github/shadowsocks/database/ProfileTest.kt @@ -31,6 +31,6 @@ class ProfileTest { .toList() Assert.assertEquals(1, results.size) Assert.assertEquals("ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888#example-server".toUri(), - results.single().toUri()) + results.single()) } } diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml index e3b625c..62d8466 100644 --- a/core/src/main/AndroidManifest.xml +++ b/core/src/main/AndroidManifest.xml @@ -178,5 +178,10 @@ android:directBootAware="true" tools:ignore="Instantiatable" tools:replace="android:directBootAware"/> + diff --git a/core/src/main/java/SpeedUpVPN/VpnEncrypt.kt b/core/src/main/java/SpeedUpVPN/VpnEncrypt.kt index edad504..0de4c16 100644 --- a/core/src/main/java/SpeedUpVPN/VpnEncrypt.kt +++ b/core/src/main/java/SpeedUpVPN/VpnEncrypt.kt @@ -24,18 +24,19 @@ object VpnEncrypt{ @JvmStatic fun aesEncrypt(v:String, secretKey:String=theKey) = AES256.encrypt(v, secretKey) @JvmStatic fun aesDecrypt(v:String, secretKey:String=theKey) = AES256.decrypt(v, secretKey) @JvmStatic fun readFileAsTextUsingInputStream(fileName: String) = File(fileName).inputStream().readBytes().toString(Charsets.UTF_8) - @JvmStatic fun getUniqueID(): Int { + @JvmStatic fun getUniqueID(appname:String): Int { var szDevIDShort = "168169" try { szDevIDShort+=Build.BOARD.length % 10 + Build.BRAND.length % 10 + Build.DEVICE.length % 10 + Build.MANUFACTURER.length % 10 + Build.MODEL.length % 10 + Build.PRODUCT.length % 10 - } catch (exception: Exception) {} + } catch (exception: Exception) {exception.printStackTrace()} var serial: String? = null try { - serial = Build::class.java.getField("SERIAL")[null].toString() + serial = Build::class.java.getField("SERIAL")[null].toString()+appname // Go ahead and return the serial for api => 9 - return UUID(szDevIDShort.hashCode() as Long, serial.hashCode() as Long).hashCode() + return UUID(szDevIDShort.hashCode().toLong(), serial.hashCode().toLong()).hashCode() } catch (exception: Exception) { // String needs to be initialized - serial = "https://git.io/jww" // some value + exception.printStackTrace() + serial = "https://git.io/jww"+appname // some value } return UUID(szDevIDShort.hashCode().toLong(), serial.hashCode().toLong()).hashCode() diff --git a/core/src/main/java/com/github/shadowsocks/Core.kt b/core/src/main/java/com/github/shadowsocks/Core.kt index a30cb08..39f4bb9 100644 --- a/core/src/main/java/com/github/shadowsocks/Core.kt +++ b/core/src/main/java/com/github/shadowsocks/Core.kt @@ -42,7 +42,10 @@ import androidx.work.WorkManager import com.crashlytics.android.Crashlytics import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.aidl.ShadowsocksConnection -import com.github.shadowsocks.bg.* +import com.github.shadowsocks.bg.BaseService +import com.github.shadowsocks.bg.ProxyTestService +import com.github.shadowsocks.bg.V2RayTestService +import com.github.shadowsocks.core.BuildConfig import com.github.shadowsocks.core.R import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.ProfileManager @@ -59,13 +62,13 @@ import kotlinx.coroutines.DEBUG_PROPERTY_NAME import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import me.dozen.dpreference.DPreference import java.io.File import java.io.IOException -import kotlin.reflect.KClass -import me.dozen.dpreference.DPreference import java.net.InetSocketAddress import java.net.Socket import java.net.UnknownHostException +import kotlin.reflect.KClass object Core { const val TAG = "Core" @@ -84,6 +87,9 @@ object Core { DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER } + const val appName = BuildConfig.FLAVOR + val applicationId = if (appName === "v2free") "free.v2ray.proxy.VPN" else "free.shadowsocks.proxy.VPN" + val activeProfileIds get() = ProfileManager.getProfile(DataStore.profileId).let { if (it == null) emptyList() else listOfNotNull(it.id, it.udpFallback) } @@ -128,7 +134,7 @@ object Core { GlobalScope.launch { var builtinSubUrls = app.resources.getStringArray(R.array.builtinSubUrls) for (i in 0 until builtinSubUrls.size) { - var builtinSub=SSRSubManager.createBuiltInSub(builtinSubUrls.get(i)) + var builtinSub=SSRSubManager.createSSSub(builtinSubUrls.get(i),VpnEncrypt.vpnGroupName) if (builtinSub != null) break } if (DataStore.is_get_free_servers)importFreeSubs() @@ -142,7 +148,7 @@ object Core { GlobalScope.launch { var freesuburl = app.resources.getStringArray(R.array.freesuburl) for (i in freesuburl.indices) { - var freeSub=SSRSubManager.createSSSub(freesuburl[i]) + var freeSub=SSRSubManager.createSSSub(freesuburl[i],VpnEncrypt.freesubGroupName) if (freeSub != null) break } } diff --git a/core/src/main/java/com/github/shadowsocks/database/AppConfig.kt b/core/src/main/java/com/github/shadowsocks/database/AppConfig.kt index 6bd51af..c6b917d 100644 --- a/core/src/main/java/com/github/shadowsocks/database/AppConfig.kt +++ b/core/src/main/java/com/github/shadowsocks/database/AppConfig.kt @@ -5,7 +5,6 @@ package com.github.shadowsocks.database * App Config Const */ object AppConfig { - const val ANG_PACKAGE = "free.v2ray.proxy.VPN" const val ANG_CONFIG = "ang_config" const val PREF_CURR_CONFIG = "pref_v2ray_config" const val PREF_CURR_CONFIG_GUID = "pref_v2ray_config_guid" diff --git a/core/src/main/java/com/github/shadowsocks/database/Profile.kt b/core/src/main/java/com/github/shadowsocks/database/Profile.kt index a1776a6..46cd77e 100644 --- a/core/src/main/java/com/github/shadowsocks/database/Profile.kt +++ b/core/src/main/java/com/github/shadowsocks/database/Profile.kt @@ -30,6 +30,7 @@ import android.util.Log import android.util.LongSparseArray import androidx.core.net.toUri import androidx.room.* +import com.github.shadowsocks.Core import com.github.shadowsocks.plugin.PluginConfiguration import com.github.shadowsocks.plugin.PluginOptions import com.github.shadowsocks.preference.DataStore @@ -250,7 +251,7 @@ data class Profile( Log.e(TAG, "Invalid Vmess URI: ${it.value}") null } - }.filterNotNull().toMutableSet() + }.filterNotNull() fun findAllSSRUrls(data: CharSequence?, feature: Profile? = null) = pattern_ssr.findAll(data ?: "").map { @@ -283,10 +284,19 @@ data class Profile( Log.e(TAG, "Invalid SSR URI: ${it.value}") null } - }.filterNotNull().toMutableSet() + }.filterNotNull() - fun findAllSSUrls(data: CharSequence?, feature: Profile? = null) = findAllUrls(data,feature).toMutableSet() - fun findAllUrls(data: CharSequence?, feature: Profile? = null) = pattern.findAll(data ?: "").map { + fun findAllUrls(response: CharSequence?, feature: Profile? = null):MutableSet { + var profilesSet:MutableSet = LinkedHashSet() + val ssrProfiles = findAllSSRUrls(response, Core.currentProfile?.first) + val ssPofiles = findAllSSUrls(response, Core.currentProfile?.first) + val v2Profiles= findAllVmessUrls(response, Core.currentProfile?.first) + profilesSet.addAll(ssPofiles) + profilesSet.addAll(ssrProfiles) + profilesSet.addAll(v2Profiles) + return profilesSet + } + fun findAllSSUrls(data: CharSequence?, feature: Profile? = null) = pattern.findAll(data ?: "").map { val uri = it.value.toUri() try { if (uri.userInfo == null) { diff --git a/core/src/main/java/com/github/shadowsocks/database/SSRSubManager.kt b/core/src/main/java/com/github/shadowsocks/database/SSRSubManager.kt index f29af2b..5ba6aed 100644 --- a/core/src/main/java/com/github/shadowsocks/database/SSRSubManager.kt +++ b/core/src/main/java/com/github/shadowsocks/database/SSRSubManager.kt @@ -99,7 +99,7 @@ object SSRSubManager { if (limit != -1 && limit < count) { try { - val uqid=VpnEncrypt.getUniqueID() + val uqid=VpnEncrypt.getUniqueID(Core.appName) Log.e("uqid",uqid.toString()) val startPosition=uqid % count for (k in 0 until limit) { @@ -123,7 +123,7 @@ object SSRSubManager { } } - fun updateBuiltinProfiles(ssrSub: SSRSub, profiles : List,limit:Int) { + fun update(ssrSub: SSRSub, profiles : List,limit:Int=-1) { when { profiles.isEmpty() -> { deletProfiles(ssrSub) @@ -148,7 +148,7 @@ object SSRSubManager { if (limit != -1 && limit < count) { try { - val uqid=VpnEncrypt.getUniqueID() + val uqid=VpnEncrypt.getUniqueID(Core.appName) Log.e("uqid",uqid.toString()) val startPosition=uqid % count for (k in 0 until limit) { @@ -167,28 +167,11 @@ object SSRSubManager { limitProfiles.addAll(profiles) if (limitProfiles.isNotEmpty()) { - deletProfiles(ssrSub) + //deletProfiles(ssrSub) ProfileManager.createProfilesFromSub(limitProfiles, ssrSub.url_group) } } - fun update(ssrSub: SSRSub, profiles : List) { - when { - profiles.isEmpty() -> { - deletProfiles(ssrSub) - ssrSub.status = SSRSub.EMPTY - updateSSRSub(ssrSub) - return - } - else -> { - ssrSub.status = SSRSub.NORMAL - updateSSRSub(ssrSub) - } - } - - ProfileManager.createProfilesFromSub(profiles, ssrSub.url_group) - } - suspend fun updateAll() { val ssrsubs = getAllSSRSub() ssrsubs.forEach { @@ -207,10 +190,7 @@ object SSRSubManager { if (url.isEmpty()) return null try { val response = getResponse(url,mode) - val ssrProfiles = Profile.findAllSSRUrls(response, Core.currentProfile?.first) - val sspPofiles = Profile.findAllSSUrls(response, Core.currentProfile?.first) - ssrProfiles.addAll(sspPofiles) - val profiles=ssrProfiles.toList() + val profiles=Profile.findAllUrls(response, Core.currentProfile?.first).toList() if (profiles.isNullOrEmpty() || profiles[0].url_group.isEmpty()) return null val new = SSRSub(url = url, url_group = profiles[0].url_group) getAllSSRSub().forEach { @@ -229,69 +209,37 @@ object SSRSubManager { return null } } - suspend fun createBuiltInSub(url: String): SSRSub? { - Log.println(Log.ERROR,"------","start createBuiltInSub...") + suspend fun createSSSub(url: String, group: String?=null): SSRSub? { if (url.isEmpty()) return null try { - val response = getResponse(url,"aes") + val response = if (VpnEncrypt.vpnGroupName==group) + getResponse(url,"aes") + else + getResponse(url) + var limit = -1 if (response.indexOf("MAX=") == 0) { limit = response.split("\n")[0].split("MAX=")[1] .replace("\\D+".toRegex(), "").toInt() } - - var profilesSet:MutableSet = LinkedHashSet() - val ssrProfiles = Profile.findAllSSRUrls(response, Core.currentProfile?.first) - val ssPofiles = Profile.findAllSSUrls(response, Core.currentProfile?.first) - val v2Profiles= Profile.findAllVmessUrls(response, Core.currentProfile?.first) - profilesSet.addAll(ssPofiles) - profilesSet.addAll(ssrProfiles) - profilesSet.addAll(v2Profiles) - var profiles:List? = profilesSet.toList() - if (profiles.isNullOrEmpty()) return null - val new = SSRSub(url = url, url_group = VpnEncrypt.vpnGroupName) - profiles.forEach { it.url_group = VpnEncrypt.vpnGroupName } - getAllSSRSub().forEach { - if (it.url_group == new.url_group) { - updateBuiltinProfiles(it, profiles,limit)//android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. - Log.println(Log.ERROR,"------","ssrsub existed, update.") - return it - } - } - createSSRSub(new) - updateBuiltinProfiles(new, profiles,limit) - Log.println(Log.ERROR,"------","success create ssrsub.") - return new - } catch (e: Exception) { - e.printStackTrace() - Log.e("------","failed create ssrsub",e) - return null - } - } - suspend fun createSSSub(url: String): SSRSub? { - if (url.isEmpty()) return null - try { - val response = getResponse(url) val profiles = Profile.findAllUrls(response, Core.currentProfile?.first).toList() if (profiles.isNullOrEmpty()) return null - var theGroupName = profiles[0].url_group - if (theGroupName.isNullOrEmpty() || theGroupName=="" || theGroupName != VpnEncrypt.vpnGroupName){ - theGroupName=VpnEncrypt.freesubGroupName - profiles.forEach { it.url_group = VpnEncrypt.freesubGroupName } - } - val new = SSRSub(url = url, url_group = theGroupName) + val subGroup = group ?: profiles[0].url_group + val new = SSRSub(url = url, url_group = subGroup) + profiles.forEach { it.url_group = subGroup } getAllSSRSub().forEach { if (it.url_group == new.url_group) { - update(it, profiles)//android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. + update(it, profiles,limit)//android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. Log.println(Log.ERROR,"------","ssrsub existed, update.") return it } } createSSRSub(new) - update(new, profiles) + update(new, profiles,limit) Log.println(Log.ERROR,"------","success create ssrsub.") return new } catch (e: Exception) { + e.printStackTrace() Log.e("------","failed create ssrsub",e) return null } diff --git a/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt b/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt index 84671be..a1e766d 100644 --- a/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt +++ b/core/src/main/java/com/github/shadowsocks/preference/DataStore.kt @@ -80,8 +80,9 @@ object DataStore : OnPreferenceDataStoreChangeListener { val isAutoUpdateServers: Boolean get() = publicStore.getBoolean(Key.isAutoUpdateServers, true) val is_get_free_servers: Boolean get() { return try { - val userCountry=Locale.getDefault().country - publicStore.getBoolean(Key.is_get_free_servers, "CN" != userCountry) + //val userCountry=Locale.getDefault().country + //publicStore.getBoolean(Key.is_get_free_servers, "CN" != userCountry) + publicStore.getBoolean(Key.is_get_free_servers, true) }catch (t:Throwable){ publicStore.getBoolean(Key.is_get_free_servers, true) } diff --git a/core/src/main/java/me/dozen/dpreference/DPreference.java b/core/src/main/java/me/dozen/dpreference/DPreference.java new file mode 100644 index 0000000..b5916b7 --- /dev/null +++ b/core/src/main/java/me/dozen/dpreference/DPreference.java @@ -0,0 +1,72 @@ +package me.dozen.dpreference; + + +import android.content.Context; + +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 void setPrefStringSet(final String key, final Set value) { + PrefAccessor.setStringSet(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 Set getPrefStringSet(final String key, final Set defaultValue) { + return PrefAccessor.getStringSet(mContext, mName, key, defaultValue); + } + + public void removePreference(final String key) { + PrefAccessor.remove(mContext, mName, key); + } + +} diff --git a/core/src/main/java/me/dozen/dpreference/IOUtils.java b/core/src/main/java/me/dozen/dpreference/IOUtils.java new file mode 100644 index 0000000..f8b8b96 --- /dev/null +++ b/core/src/main/java/me/dozen/dpreference/IOUtils.java @@ -0,0 +1,61 @@ +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(); + } + } + + +} diff --git a/core/src/main/java/me/dozen/dpreference/IPrefImpl.java b/core/src/main/java/me/dozen/dpreference/IPrefImpl.java new file mode 100644 index 0000000..1e05769 --- /dev/null +++ b/core/src/main/java/me/dozen/dpreference/IPrefImpl.java @@ -0,0 +1,39 @@ +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 getPrefStringSet(String key, Set defaultValue); + + void setPrefStringSet(String key, Set value); + + void removePreference(final String key); + + boolean hasKey(String key); + +} diff --git a/core/src/main/java/me/dozen/dpreference/PrefAccessor.java b/core/src/main/java/me/dozen/dpreference/PrefAccessor.java new file mode 100644 index 0000000..5986928 --- /dev/null +++ b/core/src/main/java/me/dozen/dpreference/PrefAccessor.java @@ -0,0 +1,116 @@ +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 getStringSet(Context context, String name, String key, Set defaultValue) { + Uri URI = PreferenceProvider.buildUri(name, key, PreferenceProvider.PREF_STRING_SET); + Set 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 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); + } +} + diff --git a/core/src/main/java/me/dozen/dpreference/PreferenceImpl.java b/core/src/main/java/me/dozen/dpreference/PreferenceImpl.java new file mode 100644 index 0000000..bf36fa9 --- /dev/null +++ b/core/src/main/java/me/dozen/dpreference/PreferenceImpl.java @@ -0,0 +1,133 @@ +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 getPrefStringSet(String key, Set defaultValue) { + final SharedPreferences settings = + mContext.getSharedPreferences(mPrefName, Context.MODE_PRIVATE); + return settings.getStringSet(key, defaultValue); + } + + @Override + public void setPrefStringSet(String key, Set 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(); + } + +} diff --git a/core/src/main/java/me/dozen/dpreference/PreferenceProvider.java b/core/src/main/java/me/dozen/dpreference/PreferenceProvider.java new file mode 100644 index 0000000..c07de4c --- /dev/null +++ b/core/src/main/java/me/dozen/dpreference/PreferenceProvider.java @@ -0,0 +1,272 @@ +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 androidx.annotation.Nullable; +import android.text.TextUtils; +import com.github.shadowsocks.Core; +import com.github.shadowsocks.core.R; + +import java.util.HashMap; +import java.util.Map; +import com.github.shadowsocks.core.BuildConfig; + + +/** + * Created by wangyida on 15/12/18. + */ +public class PreferenceProvider extends ContentProvider { + + private static final String TAG = PreferenceProvider.class.getSimpleName(); + private static final String applicationId= BuildConfig.FLAVOR=="v2free"?"free.v2ray.proxy.VPN":"free.shadowsocks.proxy.VPN"; + private static final String AUTHORITY = applicationId+".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 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 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; + } + } + +} diff --git a/core/src/main/java/me/dozen/dpreference/StringSetConverter.java b/core/src/main/java/me/dozen/dpreference/StringSetConverter.java new file mode 100644 index 0000000..ae29ad5 --- /dev/null +++ b/core/src/main/java/me/dozen/dpreference/StringSetConverter.java @@ -0,0 +1,21 @@ +package me.dozen.dpreference; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.lang.reflect.Type; +import java.util.Set; + +public class StringSetConverter { + private static final Gson gson = new Gson(); + + public static String encode(Set src) { + return gson.toJson(src); + } + + public static Set decode(String json) { + Type setType = new TypeToken>() { + }.getType(); + return gson.fromJson(json, setType); + } +} diff --git a/core/src/main/res/values-fa/strings.xml b/core/src/main/res/values-fa/strings.xml index 7a83faa..91e9341 100644 --- a/core/src/main/res/values-fa/strings.xml +++ b/core/src/main/res/values-fa/strings.xml @@ -22,7 +22,6 @@ --> - "شدوساکس VPN" "دی‌ان‌اس سرور" "در حال آزمایش…" "ارتباط با اینترنت فراهم نیست" diff --git a/core/src/main/res/values-ru/strings.xml b/core/src/main/res/values-ru/strings.xml index 134322c..06ed066 100644 --- a/core/src/main/res/values-ru/strings.xml +++ b/core/src/main/res/values-ru/strings.xml @@ -78,7 +78,6 @@ "ЧаВо" "https://github.com/bannedbook/ssvpn/blob/master/.github/faq.ru.md" "О приложении" - "Shadowsocks %s" "Изменить" "Поделиться" "Добавить профиль" diff --git a/core/src/main/res/values-zh-rTW/strings.xml b/core/src/main/res/values-zh-rTW/strings.xml index 05ad1eb..bc44013 100644 --- a/core/src/main/res/values-zh-rTW/strings.xml +++ b/core/src/main/res/values-zh-rTW/strings.xml @@ -86,7 +86,6 @@ "設定" "常見問題" "關於" - "Shadowsocks %s" "編輯" "分享" "新增設定檔" diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 0f41616..c54e4ab 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -1,7 +1,5 @@ - - SS VPN Toggle Send email @@ -94,7 +92,6 @@ FAQ https://github.com/bannedbook/ssvpn/blob/master/.github/faq.md About - SS VPN %s Edit Share Add Profile diff --git a/gitupdate.bat b/gitupdate.bat index 4eebbdf..8c064c2 100644 --- a/gitupdate.bat +++ b/gitupdate.bat @@ -3,6 +3,6 @@ git pull origin master git add -A git commit -m "update" git push origin master -git tag -a v5.1.10 -m "release v5.1.10" +git tag -a v5.1.11 -m "release v5.1.11" git push origin --tags pause \ No newline at end of file diff --git a/mobile/build.gradle b/mobile/build.gradle index 566b1ce..001e7d3 100644 --- a/mobile/build.gradle +++ b/mobile/build.gradle @@ -1,4 +1,4 @@ -import com.android.build.OutputFile +//import com.android.build.OutputFile import java.util.regex.Matcher import java.util.regex.Pattern @@ -22,7 +22,6 @@ android { compileSdkVersion rootProject.compileSdkVersion archivesBaseName = "ssvpn" defaultConfig { - applicationId "free.shadowsocks.proxy.VPN" minSdkVersion rootProject.minSdkVersion targetSdkVersion rootProject.sdkVersion versionCode rootProject.versionCode @@ -30,6 +29,24 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resConfigs rootProject.resConfigs } + flavorDimensions "encryption" + productFlavors { + ssvpn { + dimension "encryption" + applicationId "free.shadowsocks.proxy.VPN" + resValue 'string', 'applicationId', applicationId + } + v2free { + dimension "encryption" + applicationId "free.v2ray.proxy.VPN" + resValue 'string', 'applicationId', applicationId + } + } + sourceSets { + ssvpn.setRoot('src/ssvpn') + v2free.setRoot('src/v2free') + } + buildTypes { debug { pseudoLocalesEnabled true diff --git a/mobile/google-services.json b/mobile/google-services.json deleted file mode 100644 index 2413809..0000000 --- a/mobile/google-services.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "project_info": { - "project_number": "781321250967", - "firebase_url": "https://ssvpn-51f25.firebaseio.com", - "project_id": "ssvpn-51f25", - "storage_bucket": "ssvpn-51f25.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:781321250967:android:540bd341af9d2a128621d2", - "android_client_info": { - "package_name": "free.shadowsocks.proxy.VPN" - } - }, - "oauth_client": [ - { - "client_id": "781321250967-prcg9fr1o6eevgs6bchofjhv15365qq9.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyCyaC1cc9mY-G8Wiz5Qbvy707cSY5zPMAg" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "781321250967-prcg9fr1o6eevgs6bchofjhv15365qq9.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - }, - "admob_app_id": "ca-app-pub-2194043486084479~5832794268" - } - ], - "configuration_version": "1" -} diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index d550327..4a3320f 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -97,9 +97,8 @@ - + android:value="@string/admob_appid"/> diff --git a/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt b/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt index 2597cda..129e378 100644 --- a/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt +++ b/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt @@ -174,9 +174,9 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref setContentView(R.layout.layout_main) MobileAds.initialize(this) {} - mAdView = findViewById(R.id.adView) - val adRequest = AdRequest.Builder().build() - mAdView.loadAd(adRequest) +// mAdView = findViewById(R.id.adView) +// val adRequest = AdRequest.Builder().build() +// mAdView.loadAd(adRequest) snackbar = findViewById(R.id.snackbar) snackbar.setOnApplyWindowInsetsListener(ListHolderListener) diff --git a/mobile/src/main/java/com/github/shadowsocks/ProfilesFragment.kt b/mobile/src/main/java/com/github/shadowsocks/ProfilesFragment.kt index ebaa664..0d70d8a 100644 --- a/mobile/src/main/java/com/github/shadowsocks/ProfilesFragment.kt +++ b/mobile/src/main/java/com/github/shadowsocks/ProfilesFragment.kt @@ -275,7 +275,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { if (adHost != null /*|| !item.isBuiltin()*/) return if (nativeAdView == null) { nativeAdView = layoutInflater.inflate(R.layout.ad_unified, adContainer, false) as UnifiedNativeAdView - AdLoader.Builder(context, "ca-app-pub-2194043486084479/8267385919").apply { + AdLoader.Builder(context, getString(R.string.native_adUnitId)).apply { forUnifiedNativeAd { unifiedNativeAd -> // You must call destroy on old ads when you are done with them, // otherwise you will have a memory leak. @@ -321,7 +321,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { } text1.text = item.formattedName text2.text = ArrayList().apply { - if (item.isBuiltin())this += "SSVPN Builtin" + if (item.isBuiltin())this += "Builtin VPN Server" else if (item.isBuiltin2()) this += "3rd-party free server" else if (item.url_group.isNotEmpty()) this += item.url_group else if (!item.name.isNullOrEmpty()) this += item.formattedAddress @@ -581,7 +581,6 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { return when (item.itemId) { R.id.update_servers -> { Core.updateBuiltinServers() - if(DataStore.is_get_free_servers)Core.importFreeSubs() true } R.id.action_scan_qr_code -> { diff --git a/mobile/src/main/res/layout/layout_main.xml b/mobile/src/main/res/layout/layout_main.xml index 75f41dc..03d90ff 100644 --- a/mobile/src/main/res/layout/layout_main.xml +++ b/mobile/src/main/res/layout/layout_main.xml @@ -7,16 +7,16 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> - + + + + - - - + + + + + + + + + + + + + + + + diff --git a/mobile/src/main/res/xml/pref_profile.xml b/mobile/src/main/res/xml/pref_profile.xml index 011b91d..1a949c7 100644 --- a/mobile/src/main/res/xml/pref_profile.xml +++ b/mobile/src/main/res/xml/pref_profile.xml @@ -137,7 +137,7 @@ app:icon="@drawable/ic_maps_360" app:title="@string/udp_fallback" app:summary="@string/plugin_disabled"> - diff --git a/mobile/src/main/res/xml/shortcuts.xml b/mobile/src/main/res/xml/shortcuts.xml index bdb6390..896223c 100644 --- a/mobile/src/main/res/xml/shortcuts.xml +++ b/mobile/src/main/res/xml/shortcuts.xml @@ -5,7 +5,7 @@ android:shortcutShortLabel="@string/quick_toggle" android:shortcutLongLabel="@string/quick_toggle"> diff --git a/mobile/src/main/res/raw/about.html b/mobile/src/ssvpn/res/raw/about.html similarity index 100% rename from mobile/src/main/res/raw/about.html rename to mobile/src/ssvpn/res/raw/about.html diff --git a/mobile/src/v2free/res/raw/about.html b/mobile/src/v2free/res/raw/about.html new file mode 100644 index 0000000..e1ebd17 --- /dev/null +++ b/mobile/src/v2free/res/raw/about.html @@ -0,0 +1,27 @@ + + + + + + +

V2free, A Android VPN base on V2ray, written in Kotlin.

+

Copyright (C) by bannedbook fanqiang

+

This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version.

+

This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details.

+

You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/.

+

Open Source Licenses

+
    +
  • Shadowsocks-android: GPLv3 
  • +
  • ShadowsocksRb: GPLv3 
  • +
  • Polipo: MIT 
  • +
  • V2rayNG: GPLv3 
  • +
+ + diff --git a/obfs/src/main/AndroidManifest.xml b/obfs/src/main/AndroidManifest.xml index e9736ed..d14ff3c 100644 --- a/obfs/src/main/AndroidManifest.xml +++ b/obfs/src/main/AndroidManifest.xml @@ -3,9 +3,9 @@ - + android:authorities="${applicationId}.ObfsBinaryProvider"> diff --git a/obfs/src/main/java/com/github/shadowsocks/plugin/obfs_local/BinaryProvider.kt b/obfs/src/main/java/free/shadowsocks/proxy/VPN/ObfsBinaryProvider.kt similarity index 86% rename from obfs/src/main/java/com/github/shadowsocks/plugin/obfs_local/BinaryProvider.kt rename to obfs/src/main/java/free/shadowsocks/proxy/VPN/ObfsBinaryProvider.kt index 02297ad..4287f81 100644 --- a/obfs/src/main/java/com/github/shadowsocks/plugin/obfs_local/BinaryProvider.kt +++ b/obfs/src/main/java/free/shadowsocks/proxy/VPN/ObfsBinaryProvider.kt @@ -1,4 +1,4 @@ -package com.github.shadowsocks.plugin.obfs_local +package free.shadowsocks.proxy.VPN import android.net.Uri import android.os.ParcelFileDescriptor @@ -7,7 +7,7 @@ import com.github.shadowsocks.plugin.PathProvider import java.io.File import java.io.FileNotFoundException -class SSVpnBinaryProvider : NativePluginProvider() { +class ObfsBinaryProvider : NativePluginProvider() { override fun populateFiles(provider: PathProvider) { provider.addPath("obfs-local", 755) } diff --git a/obfs/src/main/java/free/v2ray/proxy/VPN/ObfsBinaryProvider.kt b/obfs/src/main/java/free/v2ray/proxy/VPN/ObfsBinaryProvider.kt new file mode 100644 index 0000000..c36acaf --- /dev/null +++ b/obfs/src/main/java/free/v2ray/proxy/VPN/ObfsBinaryProvider.kt @@ -0,0 +1,20 @@ +package free.v2ray.proxy.VPN + +import android.net.Uri +import android.os.ParcelFileDescriptor +import com.github.shadowsocks.plugin.NativePluginProvider +import com.github.shadowsocks.plugin.PathProvider +import java.io.File +import java.io.FileNotFoundException + +class ObfsBinaryProvider : NativePluginProvider() { + override fun populateFiles(provider: PathProvider) { + provider.addPath("obfs-local", 755) + } + + override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libobfs-local.so" + override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) { + "/obfs-local" -> ParcelFileDescriptor.open(File(getExecutable()), ParcelFileDescriptor.MODE_READ_ONLY) + else -> throw FileNotFoundException() + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 7f2e888..64b1b61 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ include ':libv2ray' //include ':ssvpn-v2ray' include ':obfs' -include ':mobile', ':core', ':plugin', ':dpreference' +include ':mobile', ':core', ':plugin'//, ':dpreference'