├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── frogoboxdev.jks ├── libs │ └── achartengine-1.2.0.jar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── countries.json │ ├── nopie_openvpn.arm64-v8a │ ├── nopie_openvpn.armeabi │ ├── nopie_openvpn.armeabi-v7a │ ├── nopie_openvpn.mips │ ├── nopie_openvpn.x86 │ ├── nopie_openvpn.x86_64 │ ├── pie_openvpn.arm64-v8a │ ├── pie_openvpn.armeabi │ ├── pie_openvpn.armeabi-v7a │ ├── pie_openvpn.mips │ ├── pie_openvpn.x86 │ ├── pie_openvpn.x86_64 │ └── world_map.geo.json │ ├── java │ ├── com │ │ └── frogobox │ │ │ └── viprox │ │ │ ├── BaseApplication.kt │ │ │ ├── base │ │ │ ├── BaseActivity.kt │ │ │ └── BaseFragment.kt │ │ │ ├── source │ │ │ ├── local │ │ │ │ └── DBHelper.java │ │ │ └── model │ │ │ │ ├── Country.kt │ │ │ │ ├── GetSpeedTestHostsHandler.java │ │ │ │ └── Server.kt │ │ │ ├── ui │ │ │ ├── AboutUsActivity.kt │ │ │ ├── CountryActivity.kt │ │ │ ├── MainActivity.kt │ │ │ ├── SplashActivity.kt │ │ │ ├── VPNInfoActivity.kt │ │ │ └── VPNListActivity.kt │ │ │ └── util │ │ │ ├── ConnectionQuality.java │ │ │ ├── Constant.kt │ │ │ ├── CountriesNames.java │ │ │ ├── NetworkState.java │ │ │ ├── NotificationUtils.java │ │ │ ├── NotificationVO.java │ │ │ ├── NumberPickerPreference.java │ │ │ ├── PropertiesService.java │ │ │ ├── Stopwatch.java │ │ │ ├── TotalTraffic.java │ │ │ ├── dns │ │ │ ├── HttpDownloadTest.java │ │ │ ├── HttpUploadTest.java │ │ │ └── PingTest.java │ │ │ ├── encoders │ │ │ ├── Base64.java │ │ │ ├── Base64Encoder.java │ │ │ └── Encoder.java │ │ │ └── io │ │ │ └── pem │ │ │ ├── PemGenerationException.java │ │ │ ├── PemHeader.java │ │ │ ├── PemObject.java │ │ │ ├── PemObjectGenerator.java │ │ │ ├── PemReader.java │ │ │ └── PemWriter.java │ └── de │ │ └── blinkt │ │ └── openvpn │ │ ├── VpnProfile.java │ │ └── core │ │ ├── CIDRIP.java │ │ ├── ConfigParser.java │ │ ├── Connection.java │ │ ├── DeviceStateReceiver.java │ │ ├── LogFileHandler.java │ │ ├── LogItem.java │ │ ├── NativeUtils.java │ │ ├── NetworkSpace.java │ │ ├── OpenVPNManagement.java │ │ ├── OpenVPNService.java │ │ ├── OpenVPNThread.java │ │ ├── OpenVpnManagementThread.java │ │ ├── PRNGFixes.java │ │ ├── ProfileManager.java │ │ ├── ProxyDetection.java │ │ ├── VPNLaunchHelper.java │ │ ├── VpnStatus.java │ │ └── X509Utils.java │ ├── jniLibs │ ├── arm64-v8a │ │ ├── libjbcrypto.so │ │ ├── libopenvpn.so │ │ └── libopvpnutil.so │ ├── armeabi-v7a │ │ ├── libjbcrypto.so │ │ ├── libopenvpn.so │ │ └── libopvpnutil.so │ ├── armeabi │ │ ├── libjbcrypto.so │ │ ├── libopenvpn.so │ │ └── libopvpnutil.so │ ├── mips │ │ ├── libjbcrypto.so │ │ ├── libopenvpn.so │ │ └── libopvpnutil.so │ ├── x86 │ │ ├── libjbcrypto.so │ │ ├── libopenvpn.so │ │ └── libopvpnutil.so │ └── x86_64 │ │ ├── libjbcrypto.so │ │ ├── libopenvpn.so │ │ └── libopvpnutil.so │ └── res │ ├── drawable │ ├── bg_button.xml │ ├── bg_button_connected.xml │ ├── bg_button_no_connected.xml │ ├── bg_card_dark.xml │ ├── bg_card_toolbar.xml │ ├── bg_card_white.xml │ ├── ic_btn_connect_vpn.xml │ ├── ic_connect_good.xml │ ├── ic_dummy_flag.xml │ ├── ic_frogobox.png │ ├── ic_launcher.xml │ ├── ic_toolbar_about_us.xml │ ├── ic_toolbar_back_home.xml │ ├── ic_toolbar_back_home_white.xml │ ├── ic_toolbar_location.xml │ └── info_servers_bg.xml │ ├── font │ └── montserrat_regular.ttf │ ├── layout │ ├── activity_about_us.xml │ ├── activity_country.xml │ ├── activity_main.xml │ ├── activity_splash.xml │ ├── activity_vpninfo.xml │ ├── activity_vpnlist.xml │ ├── ads_banner.xml │ ├── toolbar_main.xml │ ├── view_item_country.xml │ └── view_item_server.xml │ ├── menu │ └── menu_toolbar_main.xml │ ├── values-v21 │ └── styles.xml │ ├── values │ ├── colors.xml │ ├── dimens.xml │ ├── reference.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── preferences.xml ├── build.gradle ├── docs └── image │ ├── ss_1.png │ ├── ss_2.png │ └── ss_3.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── travis.yml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: https://saweria.co/amirisback 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | /.gradle 3 | .gradle 4 | .idea 5 | /local.properties 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | .DS_Store 10 | build 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | 15 | built application files 16 | .apk 17 | .ap_ 18 | 19 | files for the dex VM 20 | *.dex 21 | 22 | Java class files 23 | *.class 24 | 25 | generated files 26 | bin/ 27 | gen/ 28 | .externalNativeBuild/ 29 | 30 | Local configuration file (sdk path, etc) 31 | local.properties 32 | app/version.properties 33 | 34 | # SonarQube 35 | .sonar/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![ScreenShoot Apps](https://raw.githubusercontent.com/amirisback/amirisback/master/docs/image/deprecated.png?raw=true) 2 | 3 | ## Viprox VPN 4 | Easy VPN For Reskin 5 | 6 | ## Screen Shoot 7 | 8 | | UI 1 | UI 2 | UI 3 | 9 | |:----:|:----:|:----:| 10 | | | | | 11 | 12 | ## Version Release 13 | This Is Latest Release 14 | 15 | $version_release = 1.0.0 16 | 17 | ## Colaborator 18 | Very open to anyone, I'll write your name under this, please contribute by sending an email to me 19 | 20 | - Mail To faisalamircs@gmail.com 21 | - Subject : Github _ [Github-Username-Account] _ [Language] _ [Repository-Name] 22 | - Example : Github_amirisback_kotlin_admob-helper-implementation 23 | 24 | Name Of Contribute 25 | - Muhammad Faisal Amir 26 | - Waiting List 27 | - Waiting List 28 | 29 | Waiting for your contribute 30 | 31 | ## Attention !!! 32 | - Please enjoy and don't forget fork and give a star 33 | - Don't Forget Follow My Github Account 34 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-kapt' 4 | 5 | android { 6 | 7 | // Declaration admob id for release 8 | def releaseAdmobPublisherId = "" 9 | def releaseAdmobBanner = "" 10 | def releaseAdmobInterstitial = "" 11 | def releaseAdmobInterstitialVideo = "" 12 | def releaseAdmobRewarded = "" 13 | def releaseAdmobRewardedInterstitial = "" 14 | def releaseAdmobNativeAdvanced = "" 15 | def releaseAdmobNativeAdvancedVideo = "" 16 | 17 | compileSdk projectCompileSdk 18 | 19 | defaultConfig { 20 | applicationId projectApplicationId 21 | minSdk projectMinSdk 22 | targetSdk projectTargetSdk 23 | versionCode projectVersionCode 24 | versionName projectVersionName 25 | 26 | multiDexEnabled true 27 | vectorDrawables.useSupportLibrary = true 28 | 29 | // Declaration apps name debug mode 30 | def debugAttribute = " Development" 31 | def nameAppDebug = nameApp + debugAttribute 32 | 33 | // Naming APK // AAB 34 | setProperty("archivesBaseName", nameAPK + versionName + "#" + versionCode + "-" 35 | + new Date().format('ddMMyy')) 36 | 37 | resConfigs "en", "id" 38 | 39 | // Inject app name for debug 40 | resValue "string", "app_name", nameAppDebug 41 | 42 | // Declaration admob id for debug 43 | def debugAdmobPublisherId = "ca-app-pub-3940256099942544~3347511713" 44 | def debugAdmobBanner = "ca-app-pub-3940256099942544/6300978111" 45 | def debugAdmobInterstitial = "ca-app-pub-3940256099942544/1033173712" 46 | def debugAdmobInterstitialVideo = "ca-app-pub-3940256099942544/8691691433" 47 | def debugAdmobRewarded = "ca-app-pub-3940256099942544/5224354917" 48 | def debugAdmobRewardedInterstitial = "ca-app-pub-3940256099942544/5354046379" 49 | def debugAdmobNativeAdvanced = "ca-app-pub-3940256099942544/2247696110" 50 | def debugAdmobNativeAdvancedVideo = "ca-app-pub-3940256099942544/1044960115" 51 | 52 | // Inject admob id for debug 53 | resValue "string", "admob_publisher_id", debugAdmobPublisherId 54 | resValue "string", "admob_banner", debugAdmobBanner 55 | resValue "string", "admob_interstitial", debugAdmobInterstitial 56 | resValue "string", "admob_interstitial_video", debugAdmobInterstitialVideo 57 | resValue "string", "admob_rewarded", debugAdmobRewarded 58 | resValue "string", "admob_rewarded_interstitial", debugAdmobRewardedInterstitial 59 | resValue "string", "admob_native_advanced", debugAdmobNativeAdvanced 60 | resValue "string", "admob_native_advanced_video", debugAdmobNativeAdvancedVideo 61 | 62 | } 63 | 64 | signingConfigs { 65 | release { 66 | // You need to specify either an absolute path or include the 67 | // keystore file in the same directory as the build.gradle file. 68 | // [PROJECT FOLDER NAME/app/[COPY YOUT KEY STORE] .jks in here 69 | storeFile file("frogoboxdev.jks") 70 | storePassword "cronoclez" 71 | keyAlias "frogobox" 72 | keyPassword "xeonranger" 73 | } 74 | } 75 | 76 | buildTypes { 77 | 78 | release { 79 | minifyEnabled true 80 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 81 | 82 | // Generated Signed APK / AAB 83 | signingConfig signingConfigs.release 84 | 85 | // Inject app name for release 86 | resValue "string", "app_name", nameApp 87 | 88 | // Inject admob id for release 89 | resValue "string", "admob_publisher_id", releaseAdmobPublisherId 90 | resValue "string", "admob_banner", releaseAdmobBanner 91 | resValue "string", "admob_interstitial", releaseAdmobInterstitial 92 | resValue "string", "admob_interstitial_video", releaseAdmobInterstitialVideo 93 | resValue "string", "admob_rewarded", releaseAdmobRewarded 94 | resValue "string", "admob_rewarded_interstitial", releaseAdmobRewardedInterstitial 95 | resValue "string", "admob_native_advanced", releaseAdmobNativeAdvanced 96 | resValue "string", "admob_native_advanced_video", releaseAdmobNativeAdvancedVideo 97 | 98 | } 99 | 100 | } 101 | 102 | buildFeatures { 103 | viewBinding true 104 | compose true 105 | } 106 | 107 | compileOptions { 108 | sourceCompatibility JavaVersion.VERSION_11 109 | targetCompatibility JavaVersion.VERSION_11 110 | } 111 | 112 | composeOptions { 113 | kotlinCompilerExtensionVersion compose_version 114 | kotlinCompilerVersion kotlin_version 115 | } 116 | 117 | kotlinOptions { 118 | jvmTarget = JavaVersion.VERSION_11.toString() 119 | useIR = true 120 | } 121 | 122 | packagingOptions { 123 | resources { 124 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 125 | } 126 | } 127 | 128 | } 129 | 130 | dependencies { 131 | implementation fileTree(dir: 'libs', include: ['*.jar']) 132 | implementation files('libs/achartengine-1.2.0.jar') 133 | 134 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 135 | 136 | implementation 'androidx.core:core-ktx:1.6.0' 137 | implementation 'androidx.appcompat:appcompat:1.3.1' 138 | implementation 'androidx.constraintlayout:constraintlayout:2.1.1' 139 | implementation "androidx.legacy:legacy-support-v4:1.0.0" 140 | 141 | implementation "androidx.compose.ui:ui:$compose_version" 142 | implementation "androidx.compose.material:material:$compose_version" 143 | implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" 144 | 145 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' 146 | implementation 'androidx.activity:activity-compose:1.3.1' 147 | 148 | implementation 'com.google.code.gson:gson:2.8.8' 149 | implementation 'com.google.android.material:material:1.4.0' 150 | 151 | implementation "com.google.android.gms:play-services-ads:20.3.0" 152 | 153 | implementation 'com.github.frogobox:frogo-android-sdk:2.0.4' 154 | implementation 'com.github.frogobox:frogo-android-ui-kit:1.0.4' 155 | 156 | implementation 'com.github.amirisback:frogo-admob:4.1.2' 157 | implementation 'com.github.amirisback:frogo-recycler-view:3.8.7' 158 | implementation 'com.github.amirisback:frogo-log:2.0.4' 159 | 160 | implementation "com.github.bumptech.glide:glide:4.12.0" 161 | 162 | implementation "com.daimajia.numberprogressbar:library:1.2@aar" 163 | implementation "com.amitshekhar.android:android-networking:0.2.0" 164 | 165 | kapt "com.github.bumptech.glide:compiler:4.11.0" 166 | 167 | testImplementation 'junit:junit:4.13.2' 168 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 169 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 170 | 171 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" 172 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" 173 | } 174 | -------------------------------------------------------------------------------- /app/frogoboxdev.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/frogoboxdev.jks -------------------------------------------------------------------------------- /app/libs/achartengine-1.2.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/libs/achartengine-1.2.0.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Vasilkoff/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | -dontwarn com.caverock.androidsvg.** 20 | -dontwarn okio.** -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 46 | 47 | 51 | 52 | 53 | 54 | 55 | 56 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /app/src/main/assets/nopie_openvpn.arm64-v8a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/nopie_openvpn.arm64-v8a -------------------------------------------------------------------------------- /app/src/main/assets/nopie_openvpn.armeabi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/nopie_openvpn.armeabi -------------------------------------------------------------------------------- /app/src/main/assets/nopie_openvpn.armeabi-v7a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/nopie_openvpn.armeabi-v7a -------------------------------------------------------------------------------- /app/src/main/assets/nopie_openvpn.mips: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/nopie_openvpn.mips -------------------------------------------------------------------------------- /app/src/main/assets/nopie_openvpn.x86: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/nopie_openvpn.x86 -------------------------------------------------------------------------------- /app/src/main/assets/nopie_openvpn.x86_64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/nopie_openvpn.x86_64 -------------------------------------------------------------------------------- /app/src/main/assets/pie_openvpn.arm64-v8a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/pie_openvpn.arm64-v8a -------------------------------------------------------------------------------- /app/src/main/assets/pie_openvpn.armeabi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/pie_openvpn.armeabi -------------------------------------------------------------------------------- /app/src/main/assets/pie_openvpn.armeabi-v7a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/pie_openvpn.armeabi-v7a -------------------------------------------------------------------------------- /app/src/main/assets/pie_openvpn.mips: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/pie_openvpn.mips -------------------------------------------------------------------------------- /app/src/main/assets/pie_openvpn.x86: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/pie_openvpn.x86 -------------------------------------------------------------------------------- /app/src/main/assets/pie_openvpn.x86_64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/assets/pie_openvpn.x86_64 -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/BaseApplication.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | 6 | class BaseApplication : Application() { 7 | 8 | companion object { 9 | lateinit var instance: BaseApplication 10 | } 11 | 12 | override fun onCreate() { 13 | super.onCreate() 14 | instance = this 15 | } 16 | 17 | override fun attachBaseContext(base: Context) { 18 | super.attachBaseContext(base) 19 | } 20 | 21 | fun getResourceString(resId: Int): String { 22 | return instance.getString(resId) 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.base 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.view.Menu 6 | import android.view.MenuItem 7 | import android.view.WindowManager 8 | import androidx.core.content.ContextCompat 9 | import androidx.viewbinding.ViewBinding 10 | import com.androidnetworking.AndroidNetworking 11 | import com.androidnetworking.common.Priority 12 | import com.androidnetworking.error.ANError 13 | import com.androidnetworking.interfaces.JSONArrayRequestListener 14 | import com.frogobox.admob.core.FrogoAdmob 15 | import com.frogobox.sdk.core.FrogoActivity 16 | import com.frogobox.viprox.R 17 | import com.frogobox.viprox.util.Constant.Variable.EXTRA_AUTO_CONNECTION 18 | import com.frogobox.viprox.util.Constant.Variable.EXTRA_FAST_CONNECTION 19 | import com.frogobox.viprox.source.local.DBHelper 20 | import com.frogobox.viprox.source.model.Server 21 | import com.frogobox.viprox.util.CountriesNames 22 | import com.frogobox.viprox.util.PropertiesService 23 | import com.frogobox.viprox.util.TotalTraffic 24 | import com.frogobox.viprox.ui.VPNInfoActivity 25 | import com.google.android.gms.ads.AdView 26 | import com.google.gson.Gson 27 | import org.json.JSONArray 28 | import org.json.JSONException 29 | import org.json.JSONObject 30 | import java.util.* 31 | 32 | /** 33 | * Created by Faisal Amir 34 | * FrogoBox Inc License 35 | * ========================================= 36 | * ImplementationAdmob 37 | * Copyright (C) 27/11/2019. 38 | * All rights reserved 39 | * ----------------------------------------- 40 | * Name : Muhammad Faisal Amir 41 | * E-mail : faisalamircs@gmail.com 42 | * Github : github.com/amirisback 43 | * LinkedIn : linkedin.com/in/faisalamircs 44 | * ----------------------------------------- 45 | * FrogoBox Software Industries 46 | * com.frogobox.evpn.base 47 | * 48 | */ 49 | abstract class BaseActivity : FrogoActivity() { 50 | 51 | var connectedServer: Server? = null 52 | protected var hideCurrentConnection = false 53 | protected var dbHelper = DBHelper(this) 54 | protected var localeCountries: MutableMap = CountriesNames.getCountries() 55 | 56 | override fun onCreate(savedInstanceState: Bundle?) { 57 | super.onCreate(savedInstanceState) 58 | setupAdmob() 59 | } 60 | 61 | private fun setupAdmob(){ 62 | setPublisher() 63 | setBanner() 64 | setInterstitial() 65 | } 66 | 67 | private fun setupAdsPublisher(mPublisherId: String) { 68 | FrogoAdmob.setupPublisherID(mPublisherId) 69 | FrogoAdmob.Publisher.setupPublisher(this) 70 | } 71 | 72 | private fun setupAdsBanner(mAdUnitId: String) { 73 | FrogoAdmob.setupBannerAdUnitID(mAdUnitId) 74 | } 75 | 76 | private fun setupAdsInterstitial(mAdUnitId: String) { 77 | FrogoAdmob.setupInterstialAdUnitID(mAdUnitId) 78 | FrogoAdmob.Interstitial.setupInterstitial(this) 79 | } 80 | 81 | private fun setPublisher() { 82 | setupAdsPublisher(getString(R.string.admob_publisher_id)) 83 | } 84 | 85 | private fun setBanner() { 86 | setupAdsBanner(getString(R.string.admob_banner)) 87 | } 88 | 89 | private fun setInterstitial() { 90 | setupAdsInterstitial(getString(R.string.admob_interstitial)) 91 | } 92 | 93 | fun setupShowAdsBanner(mAdView: AdView) { 94 | FrogoAdmob.Banner.setupBanner(mAdView) 95 | FrogoAdmob.Banner.showBanner(mAdView) 96 | } 97 | 98 | fun setupShowAdsInterstitial() { 99 | FrogoAdmob.Interstitial.showInterstitial(this) 100 | } 101 | 102 | override fun onPause() { 103 | super.onPause() 104 | TotalTraffic.saveTotal() 105 | } 106 | 107 | protected fun setupNoLimitStatBar() { 108 | val windows = window // in Activity's onCreate() for instance 109 | windows.setFlags( 110 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, 111 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 112 | ) 113 | } 114 | 115 | override fun onCreateOptionsMenu(menu: Menu?): Boolean { 116 | return true 117 | } 118 | 119 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 120 | return when (item.itemId) { 121 | android.R.id.home -> { 122 | finish() 123 | true 124 | } 125 | else -> super.onOptionsItemSelected(item) 126 | } 127 | } 128 | 129 | protected fun getRandomServer(): Server? { 130 | return if (PropertiesService.getCountryPriority()) { 131 | dbHelper.getGoodRandomServer(PropertiesService.getSelectedCountry()) 132 | } else { 133 | dbHelper.getGoodRandomServer(null) 134 | } 135 | } 136 | 137 | protected open fun newConnecting(server: Server?, fastConnection: Boolean, autoConnection: Boolean) { 138 | if (server != null) { 139 | val intent = Intent(this, VPNInfoActivity::class.java) 140 | intent.putExtra(Server::class.java.canonicalName, Gson().toJson(server)) 141 | intent.putExtra(EXTRA_FAST_CONNECTION, fastConnection) 142 | intent.putExtra(EXTRA_AUTO_CONNECTION, autoConnection) 143 | startActivity(intent) 144 | } 145 | } 146 | 147 | protected open fun ipInfoResult(){ 148 | 149 | } 150 | 151 | protected fun hasConnectedServer(): Boolean { 152 | return connectedServer != null 153 | } 154 | 155 | protected open fun getIpInfo(server: Server) { 156 | val serverList: MutableList = ArrayList() 157 | serverList.add(server) 158 | getIpInfoFromServerList(serverList) 159 | } 160 | 161 | fun getColorRes(res: Int): Int { 162 | return ContextCompat.getColor(this, res) 163 | } 164 | 165 | protected open fun getIpInfoFromServerList(serverList: List) { 166 | val jsonArray = JSONArray() 167 | for (server in serverList) { 168 | val jsonObject = JSONObject() 169 | try { 170 | jsonObject.put("query", server.ip) 171 | jsonObject.put("lang", Locale.getDefault().language) 172 | jsonArray.put(jsonObject) 173 | } catch (e: JSONException) { 174 | e.printStackTrace() 175 | } 176 | } 177 | AndroidNetworking.post(getString(R.string.url_check_ip_batch)) 178 | .addJSONArrayBody(jsonArray) 179 | .setTag("getIpInfo") 180 | .setPriority(Priority.MEDIUM) 181 | .build() 182 | .getAsJSONArray(object : JSONArrayRequestListener { 183 | override fun onResponse(response: JSONArray) { 184 | dbHelper.setIpInfo(response, serverList) 185 | // TODO WHEN VPN CONNECTED 186 | } 187 | 188 | override fun onError(error: ANError) {} 189 | }) 190 | } 191 | 192 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.base 2 | 3 | import android.os.Bundle 4 | import androidx.viewbinding.ViewBinding 5 | import com.frogobox.sdk.core.FrogoFragment 6 | 7 | /** 8 | * Created by Faisal Amir 9 | * FrogoBox Inc License 10 | * ========================================= 11 | * ImplementationAdmob 12 | * Copyright (C) 25/11/2019. 13 | * All rights reserved 14 | * ----------------------------------------- 15 | * Name : Muhammad Faisal Amir 16 | * E-mail : faisalamircs@gmail.com 17 | * Github : github.com/amirisback 18 | * LinkedIn : linkedin.com/in/faisalamircs 19 | * ----------------------------------------- 20 | * FrogoBox Software Industries 21 | * com.frogobox.evpn.activity 22 | * 23 | */ 24 | abstract class BaseFragment : FrogoFragment() { 25 | 26 | lateinit var mBaseActivity: BaseActivity<*> 27 | 28 | override fun onCreate(savedInstanceState: Bundle?) { 29 | super.onCreate(savedInstanceState) 30 | mBaseActivity = (activity as BaseActivity<*>) 31 | } 32 | 33 | protected fun setupShowAdsInterstitial() { 34 | mBaseActivity.setupShowAdsInterstitial() 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/source/model/Country.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.source.model 2 | 3 | data class Country( 4 | val countryName: String, 5 | val capitalName: String, 6 | val capitalLatitude: Double, 7 | val capitalLongitude: Double, 8 | val countryCode: String) -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/source/model/GetSpeedTestHostsHandler.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.source.model; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.net.URL; 7 | import java.util.Arrays; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | 11 | 12 | public class GetSpeedTestHostsHandler extends Thread { 13 | 14 | HashMap mapKey = new HashMap<>(); 15 | HashMap> mapValue = new HashMap<>(); 16 | double selfLat = 0.0; 17 | double selfLon = 0.0; 18 | boolean finished = false; 19 | 20 | 21 | public HashMap getMapKey() { 22 | return mapKey; 23 | } 24 | 25 | public HashMap> getMapValue() { 26 | return mapValue; 27 | } 28 | 29 | public double getSelfLat() { 30 | return selfLat; 31 | } 32 | 33 | public double getSelfLon() { 34 | return selfLon; 35 | } 36 | 37 | public boolean isFinished() { 38 | return finished; 39 | } 40 | 41 | @Override 42 | public void run() { 43 | //Get latitude, longitude 44 | try { 45 | URL url = new URL("http://www.speedtest.net/speedtest-config.php"); 46 | InputStream is = url.openStream(); 47 | 48 | int ptr = 0; 49 | StringBuffer buffer = new StringBuffer(); 50 | while ((ptr = is.read()) != -1) { 51 | buffer.append((char) ptr); 52 | if (!buffer.toString().contains("isp=")) { 53 | continue; 54 | } 55 | selfLat = Double.parseDouble(buffer.toString().split("lat=\"")[1].split(" ")[0].replace("\"", "")); 56 | selfLon = Double.parseDouble(buffer.toString().split("lon=\"")[1].split(" ")[0].replace("\"", "")); 57 | break; 58 | } 59 | } catch (Exception ex) { 60 | ex.printStackTrace(); 61 | return; 62 | } 63 | 64 | String uploadAddress = ""; 65 | String name = ""; 66 | String country = ""; 67 | String cc = ""; 68 | String sponsor = ""; 69 | String lat = ""; 70 | String lon = ""; 71 | String host = ""; 72 | 73 | 74 | //Best server 75 | int count = 0; 76 | try { 77 | URL url = new URL("http://www.speedtest.net/speedtest-servers-static.php"); 78 | InputStream is = url.openStream(); 79 | 80 | BufferedReader br = new BufferedReader(new InputStreamReader(is)); 81 | String line; 82 | while ((line = br.readLine()) != null) { 83 | if (line.contains(" ls = Arrays.asList(lat, lon, name, country, cc, sponsor, host); 94 | mapKey.put(count, uploadAddress); 95 | mapValue.put(count, ls); 96 | 97 | count++; 98 | } 99 | } 100 | 101 | is.close(); 102 | br.close(); 103 | } catch (Exception ex) { 104 | ex.printStackTrace(); 105 | } 106 | 107 | finished = true; 108 | } 109 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/source/model/Server.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.source.model 2 | 3 | /** 4 | * Created by Kusenko on 29.09.2016. 5 | */ 6 | data class Server( 7 | var hostName: String? = null, 8 | var ip: String? = null, 9 | var score: String? = null, 10 | var ping: String? = null, 11 | var speed: String? = null, 12 | var countryLong: String? = null, 13 | var countryShort: String? = null, 14 | var numVpnSessions: String? = null, 15 | var uptime: String? = null, 16 | var totalUsers: String? = null, 17 | var totalTraffic: String? = null, 18 | var logType: String? = null, 19 | var operator: String? = null, 20 | var message: String? = null, 21 | var configData: String? = null, 22 | var quality: Int = 0, 23 | var city: String? = null, 24 | var type: Int = 0, 25 | var regionName: String? = null, 26 | var lat: Double = 0.0, 27 | var lon: Double = 0.0 28 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/ui/AboutUsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.ui 2 | 3 | import android.os.Bundle 4 | import com.frogobox.viprox.base.BaseActivity 5 | import com.frogobox.viprox.databinding.ActivityAboutUsBinding 6 | 7 | class AboutUsActivity : BaseActivity() { 8 | 9 | override fun setupViewBinding(): ActivityAboutUsBinding { 10 | return ActivityAboutUsBinding.inflate(layoutInflater) 11 | } 12 | 13 | override fun setupViewModel() { 14 | } 15 | 16 | override fun setupUI(savedInstanceState: Bundle?) { 17 | setupDetailActivity("") 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/ui/CountryActivity.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.ui 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.ViewGroup 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.bumptech.glide.Glide 10 | import com.frogobox.recycler.core.FrogoLayoutManager 11 | import com.frogobox.recycler.core.IFrogoBuilderRvBinding 12 | import com.frogobox.viprox.base.BaseActivity 13 | import com.frogobox.viprox.databinding.ActivityCountryBinding 14 | import com.frogobox.viprox.databinding.ViewItemCountryBinding 15 | import com.frogobox.viprox.util.Constant 16 | import com.frogobox.viprox.source.model.Server 17 | import com.frogobox.viprox.util.CountriesNames 18 | 19 | class CountryActivity : BaseActivity() { 20 | 21 | override fun setupViewBinding(): ActivityCountryBinding { 22 | return ActivityCountryBinding.inflate(layoutInflater) 23 | } 24 | 25 | override fun setupViewModel() { 26 | 27 | } 28 | 29 | override fun setupUI(savedInstanceState: Bundle?) { 30 | setupDetailActivity("") 31 | setupShowAdsInterstitial() 32 | setupShowAdsBanner(binding.ads.admobAdview) 33 | setupRecyclerView() 34 | } 35 | 36 | private fun setupRecyclerView() { 37 | 38 | binding.recyclerViewCountry.builderBinding(object : 39 | IFrogoBuilderRvBinding { 40 | 41 | override fun onItemClicked(data: Server) { 42 | val intent = Intent(this@CountryActivity, VPNListActivity::class.java) 43 | intent.putExtra(Constant.Variable.EXTRA_COUNTRY, data.countryShort) 44 | startActivity(intent) 45 | } 46 | 47 | override fun onItemLongClicked(data: Server) {} 48 | 49 | override fun setViewBinding(parent: ViewGroup): ViewItemCountryBinding { 50 | return ViewItemCountryBinding.inflate( 51 | LayoutInflater.from(parent.context), 52 | parent, 53 | false 54 | ) 55 | } 56 | 57 | override fun setupData(): List { 58 | return dbHelper.uniqueCountries 59 | } 60 | 61 | override fun setupInitComponent(binding: ViewItemCountryBinding, data: Server) { 62 | binding.apply { 63 | val localeCountries: MutableMap = CountriesNames.getCountries() 64 | val localeCountryName = 65 | if (localeCountries[data.countryShort] != null) localeCountries[data.countryShort] else data.countryLong 66 | 67 | tvCountryName.text = localeCountryName 68 | Glide.with(binding.root.context).load(Constant().getFlagImageUrl(data)) 69 | .into(ivServerFlag) 70 | } 71 | } 72 | 73 | override fun setupLayoutManager(context: Context): RecyclerView.LayoutManager { 74 | return FrogoLayoutManager.staggeredGridLayout(2) 75 | } 76 | }) 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/ui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.ui 2 | 3 | import android.os.Bundle 4 | import android.view.Menu 5 | import android.view.MenuItem 6 | import android.view.View 7 | import com.frogobox.viprox.R 8 | import com.frogobox.viprox.base.BaseActivity 9 | import com.frogobox.viprox.databinding.ActivityMainBinding 10 | import com.frogobox.viprox.util.PropertiesService 11 | 12 | class MainActivity : BaseActivity() { 13 | 14 | override fun setupViewBinding(): ActivityMainBinding { 15 | return ActivityMainBinding.inflate(layoutInflater) 16 | } 17 | 18 | override fun setupViewModel() {} 19 | 20 | override fun setupUI(savedInstanceState: Bundle?) { 21 | binding.apply { 22 | setSupportActionBar(toolbar.toolbarMain) 23 | setupShowAdsInterstitial() 24 | setupShowAdsBanner(ads.admobAdview) 25 | setupViewFunction() 26 | } 27 | } 28 | 29 | override fun onCreateOptionsMenu(menu: Menu?): Boolean { 30 | menuInflater.inflate(R.menu.menu_toolbar_main, menu) 31 | return true 32 | } 33 | 34 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 35 | return when (item.itemId) { 36 | R.id.toolbar_menu_about -> { 37 | baseStartActivity() 38 | true 39 | } 40 | 41 | R.id.toolbar_location -> { 42 | baseStartActivity() 43 | true 44 | } 45 | 46 | else -> super.onOptionsItemSelected(item) 47 | } 48 | } 49 | 50 | private fun setupViewFunction() { 51 | binding.btnQuickConnect.setOnClickListener { v: View? -> 52 | if (getRandomServer() != null) { 53 | newConnecting(getRandomServer(), fastConnection = true, autoConnection = true) 54 | } else { 55 | showToast( 56 | String.format( 57 | resources.getString(R.string.error_random_country), 58 | PropertiesService.getSelectedCountry() 59 | ) 60 | ) 61 | } 62 | } 63 | 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/ui/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.ui 2 | 3 | import android.content.DialogInterface 4 | import android.os.Bundle 5 | import android.os.Handler 6 | import android.os.Message 7 | import android.view.View 8 | import androidx.appcompat.app.AlertDialog 9 | import com.androidnetworking.AndroidNetworking 10 | import com.androidnetworking.common.Priority 11 | import com.androidnetworking.error.ANError 12 | import com.androidnetworking.interfaces.DownloadListener 13 | import com.frogobox.viprox.BuildConfig 14 | import com.frogobox.viprox.R 15 | import com.frogobox.viprox.base.BaseActivity 16 | import com.frogobox.viprox.databinding.ActivitySplashBinding 17 | import com.frogobox.viprox.util.Constant.Variable.BASE_FILE_NAME 18 | import com.frogobox.viprox.util.Constant.Variable.BASE_URL 19 | import com.frogobox.viprox.util.Constant.Variable.DOWNLOAD_PROGRESS 20 | import com.frogobox.viprox.util.Constant.Variable.EXTRA_FIRST_PREMIUM_LOAD 21 | import com.frogobox.viprox.util.Constant.Variable.LOADING_SUCCESS 22 | import com.frogobox.viprox.util.Constant.Variable.LOAD_ERROR 23 | import com.frogobox.viprox.util.Constant.Variable.PARSE_PROGRESS 24 | import com.frogobox.viprox.util.Constant.Variable.PRECENTAGE_MAX 25 | import com.frogobox.viprox.util.Constant.Variable.SWITCH_TO_RESULT 26 | import com.frogobox.viprox.util.NetworkState 27 | import com.frogobox.viprox.util.PropertiesService 28 | import okhttp3.OkHttpClient 29 | import java.io.BufferedReader 30 | import java.io.FileReader 31 | import java.io.IOException 32 | import java.util.concurrent.TimeUnit 33 | 34 | class SplashActivity : BaseActivity() { 35 | 36 | private var updateHandler: Handler? = null 37 | private val premiumStage = true 38 | private var percentDownload = 0 39 | 40 | override fun setupViewBinding(): ActivitySplashBinding { 41 | return ActivitySplashBinding.inflate(layoutInflater) 42 | } 43 | 44 | override fun setupViewModel() {} 45 | 46 | override fun setupUI(savedInstanceState: Bundle?) { 47 | runBackgroundProcess() 48 | binding.tvVersionApp.text = "v" + BuildConfig.VERSION_NAME 49 | } 50 | 51 | override fun onResume() { 52 | super.onResume() 53 | downloadCSVFile(BASE_URL, BASE_FILE_NAME) 54 | } 55 | 56 | private fun runBackgroundProcess() { 57 | binding.apply { 58 | if (NetworkState.isOnline()) { 59 | if (loadStatus) { 60 | baseStartActivity() 61 | finish() 62 | } else { 63 | loadStatus = true 64 | } 65 | } else { 66 | val builder = AlertDialog.Builder(this@SplashActivity) 67 | builder.setTitle(getString(R.string.network_error)) 68 | .setMessage(getString(R.string.network_error_message)) 69 | .setNegativeButton( 70 | getString(R.string.ok) 71 | ) { dialog: DialogInterface, id: Int -> 72 | dialog.cancel() 73 | onBackPressed() 74 | } 75 | builder.create().show() 76 | } 77 | if (intent.getBooleanExtra(EXTRA_FIRST_PREMIUM_LOAD, false)) { 78 | loaderPremiumText.visibility = View.VISIBLE 79 | } 80 | numberProgressBar.max = PRECENTAGE_MAX 81 | updateHandler = Handler(Handler.Callback { msg: Message -> 82 | when (msg.arg1) { 83 | LOAD_ERROR -> { 84 | commentsText.setText(msg.arg2) 85 | numberProgressBar.progress = PRECENTAGE_MAX 86 | } 87 | DOWNLOAD_PROGRESS -> { 88 | commentsText.text = getString(R.string.downloading_csv_text) 89 | numberProgressBar.progress = msg.arg2 90 | } 91 | PARSE_PROGRESS -> { 92 | commentsText.setText(R.string.parsing_csv_text) 93 | numberProgressBar.progress = msg.arg2 94 | } 95 | LOADING_SUCCESS -> { 96 | commentsText.setText(R.string.successfully_loaded) 97 | numberProgressBar.progress = PRECENTAGE_MAX 98 | val end = Message() 99 | end.arg1 = SWITCH_TO_RESULT 100 | updateHandler!!.sendMessageDelayed(end, 500) 101 | } 102 | SWITCH_TO_RESULT -> { 103 | if (PropertiesService.getConnectOnStart()) { 104 | val randomServer = getRandomServer() 105 | if (randomServer != null) { 106 | newConnecting(randomServer, true, true) 107 | } else { 108 | baseStartActivity() 109 | } 110 | } else { 111 | baseStartActivity() 112 | } 113 | } 114 | } 115 | true 116 | }) 117 | numberProgressBar.progress = 0 118 | } 119 | } 120 | 121 | private fun downloadCSVFile(url: String, fileName: String) { 122 | val okHttpClient = OkHttpClient().newBuilder() 123 | .connectTimeout(60, TimeUnit.SECONDS) 124 | .readTimeout(60, TimeUnit.SECONDS) 125 | .writeTimeout(60, TimeUnit.SECONDS) 126 | .build() 127 | AndroidNetworking.download(url, cacheDir.path, fileName) 128 | .setTag("downloadCSV") 129 | .setPriority(Priority.MEDIUM) 130 | .setOkHttpClient(okHttpClient) 131 | .build() 132 | .setDownloadProgressListener { bytesDownloaded, totalBytes -> 133 | var totalBytes = totalBytes 134 | if (totalBytes <= 0) { // when we dont know the file size, assume it is 1200000 bytes :) 135 | totalBytes = 1200000 136 | } 137 | percentDownload = (PRECENTAGE_MAX * bytesDownloaded / totalBytes).toInt() 138 | val msg = Message() 139 | msg.arg1 = DOWNLOAD_PROGRESS 140 | msg.arg2 = percentDownload 141 | updateHandler!!.sendMessage(msg) 142 | } 143 | .startDownload(object : DownloadListener { 144 | override fun onDownloadComplete() { 145 | parseCSVFile(BASE_FILE_NAME) 146 | } 147 | 148 | override fun onError(error: ANError) { 149 | val msg = Message() 150 | msg.arg1 = LOAD_ERROR 151 | msg.arg2 = R.string.network_error 152 | updateHandler!!.sendMessage(msg) 153 | } 154 | }) 155 | } 156 | 157 | private fun parseCSVFile(fileName: String) { 158 | var reader: BufferedReader? = null 159 | try { 160 | reader = BufferedReader(FileReader(cacheDir.path + "/" + fileName)) 161 | } catch (e: IOException) { 162 | e.printStackTrace() 163 | val msg = Message() 164 | msg.arg1 = LOAD_ERROR 165 | msg.arg2 = R.string.csv_file_error 166 | updateHandler!!.sendMessage(msg) 167 | } 168 | if (reader != null) { 169 | try { 170 | val startLine = 2 171 | val type = 0 172 | dbHelper.clearTable() 173 | var counter = 0 174 | var line: String? = null 175 | while (reader.readLine().also { line = it } != null) { 176 | if (counter >= startLine) { 177 | dbHelper.putLine(line, type) 178 | } 179 | counter++ 180 | } 181 | val end = Message() 182 | end.arg1 = LOADING_SUCCESS 183 | updateHandler!!.sendMessageDelayed(end, 200) 184 | } catch (e: Exception) { 185 | e.printStackTrace() 186 | val msg = Message() 187 | msg.arg1 = LOAD_ERROR 188 | msg.arg2 = R.string.csv_file_error_parsing 189 | updateHandler!!.sendMessage(msg) 190 | } 191 | } 192 | } 193 | 194 | companion object { 195 | private var loadStatus = false 196 | } 197 | 198 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/ui/VPNListActivity.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.ui 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.ViewGroup 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.bumptech.glide.Glide 9 | import com.frogobox.recycler.core.FrogoLayoutManager 10 | import com.frogobox.recycler.core.IFrogoBuilderRvBinding 11 | import com.frogobox.viprox.R 12 | import com.frogobox.viprox.base.BaseActivity 13 | import com.frogobox.viprox.databinding.ActivityVpnlistBinding 14 | import com.frogobox.viprox.databinding.ViewItemServerBinding 15 | import com.frogobox.viprox.util.Constant 16 | import com.frogobox.viprox.util.Constant.Variable.EXTRA_COUNTRY 17 | import com.frogobox.viprox.source.model.Server 18 | import com.frogobox.viprox.util.ConnectionQuality 19 | import de.blinkt.openvpn.core.VpnStatus 20 | 21 | class VPNListActivity : BaseActivity() { 22 | 23 | override fun setupViewBinding(): ActivityVpnlistBinding { 24 | return ActivityVpnlistBinding.inflate(layoutInflater) 25 | } 26 | 27 | override fun setupViewModel() {} 28 | 29 | override fun setupUI(savedInstanceState: Bundle?) { 30 | setupShowAdsBanner(findViewById(R.id.admob_adview)) 31 | setupShowAdsInterstitial() 32 | setupDetailActivity("") 33 | 34 | val country = intent.getStringExtra(EXTRA_COUNTRY) 35 | 36 | if (!VpnStatus.isVPNActive()) { 37 | connectedServer = null 38 | } 39 | 40 | if (country != null) { 41 | getIpInfoFromServerList(dbHelper.getServersByCountryCode(country)) 42 | setupRecyclerView(country) 43 | } 44 | 45 | } 46 | 47 | private fun setupRecyclerView(country: String) { 48 | 49 | binding.recyclerView.builderBinding(object : 50 | IFrogoBuilderRvBinding { 51 | override fun onItemClicked(data: Server) { 52 | baseStartActivity(Server::class.java.canonicalName!!, data) 53 | } 54 | 55 | override fun onItemLongClicked(data: Server) {} 56 | 57 | override fun setViewBinding(parent: ViewGroup): ViewItemServerBinding { 58 | return ViewItemServerBinding.inflate( 59 | LayoutInflater.from(parent.context), 60 | parent, 61 | false 62 | ) 63 | } 64 | 65 | override fun setupData(): List { 66 | return dbHelper.getServersByCountryCode(country) 67 | } 68 | 69 | override fun setupInitComponent(binding: ViewItemServerBinding, data: Server) { 70 | binding.apply { 71 | Glide.with(binding.root.context).load(Constant().getFlagImageUrl(data)) 72 | .into(imageFlag) 73 | imageConnect.setImageResource( 74 | binding.root.context.resources.getIdentifier( 75 | ConnectionQuality.getConnectIcon( 76 | data.quality 77 | ), "drawable", binding.root.context.packageName 78 | ) 79 | ) 80 | textHostName.text = data.hostName 81 | textIP.text = data.ip 82 | textCity.text = data.city 83 | } 84 | } 85 | 86 | override fun setupLayoutManager(context: Context): RecyclerView.LayoutManager { 87 | return FrogoLayoutManager.linearLayoutVertical(context) 88 | } 89 | }) 90 | 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/ConnectionQuality.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util; 2 | 3 | 4 | 5 | public class ConnectionQuality { 6 | 7 | private static final String CONNECT_BAD = "ic_connect_bad"; 8 | private static final String CONNECT_GOOD = "ic_connect_good"; 9 | private static final String CONNECT_EXCELLENT = "ic_connect_excellent"; 10 | private static final String CONNECT_INACTIVE = "ic_connect_inactive"; 11 | 12 | private static final String POINT_BAD = "ic_point_red"; 13 | private static final String POINT_GOOD = "ic_point_yellow"; 14 | private static final String POINT_EXCELLENT = "ic_point_green"; 15 | private static final String POINT_INACTIVE = "ic_point_grey"; 16 | 17 | private static final String SIMPLE_POINT_BAD = "ic_simple_point_red"; 18 | private static final String SIMPLE_POINT_GOOD = "ic_simple_point_yellow"; 19 | private static final String SIMPLE_POINT_EXCELLENT = "ic_simple_point_green"; 20 | private static final String SIMPLE_POINT_INACTIVE = "ic_simple_point_grey"; 21 | 22 | public static String getConnectIcon(int quality) { 23 | switch (quality) { 24 | case 0: 25 | return CONNECT_INACTIVE; 26 | case 1: 27 | return CONNECT_BAD; 28 | case 2: 29 | return CONNECT_GOOD; 30 | case 3: 31 | return CONNECT_EXCELLENT; 32 | default: 33 | return CONNECT_INACTIVE; 34 | } 35 | } 36 | 37 | public static String getPointIcon(int quality) { 38 | switch (quality) { 39 | case 0: 40 | return POINT_INACTIVE; 41 | case 1: 42 | return POINT_BAD; 43 | case 2: 44 | return POINT_GOOD; 45 | case 3: 46 | return POINT_EXCELLENT; 47 | default: 48 | return POINT_INACTIVE; 49 | } 50 | } 51 | 52 | public static String getSimplePointIcon(int quality) { 53 | switch (quality) { 54 | case 0: 55 | return SIMPLE_POINT_INACTIVE; 56 | case 1: 57 | return SIMPLE_POINT_BAD; 58 | case 2: 59 | return SIMPLE_POINT_GOOD; 60 | case 3: 61 | return SIMPLE_POINT_EXCELLENT; 62 | default: 63 | return SIMPLE_POINT_INACTIVE; 64 | } 65 | } 66 | 67 | public static int getConnectionQuality(String speedStr, String sessionsStr, String pingStr) { 68 | 69 | int speed = Integer.parseInt(speedStr); 70 | int sessions = Integer.parseInt(sessionsStr); 71 | 72 | int ping = 0; 73 | if (!(pingStr.equals("-") || pingStr.equals(""))) { 74 | ping = Integer.parseInt(pingStr); 75 | } 76 | 77 | if (speed > 10000000 && ping < 30 && (sessions != 0 && sessions < 100)) { 78 | return 3; 79 | } else if (speed < 1000000 || ping > 100 || (sessions == 0 || sessions > 150)) { 80 | return 1; 81 | } else { 82 | return 2; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/Constant.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util 2 | 3 | import com.frogobox.viprox.util.Constant.Variable.BASE_FLAG_IMAGE_SIZE 4 | import com.frogobox.viprox.util.Constant.Variable.BASE_FLAG_IMAGE_URL 5 | import com.frogobox.viprox.source.model.Server 6 | 7 | /** 8 | * Created by Faisal Amir 9 | * FrogoBox Inc License 10 | * ========================================= 11 | * Frogobox-VPN-Hero 12 | * Copyright (C) 25/11/2019. 13 | * All rights reserved 14 | * ----------------------------------------- 15 | * Name : Muhammad Faisal Amir 16 | * E-mail : faisalamircs@gmail.com 17 | * Github : github.com/amirisback 18 | * LinkedIn : linkedin.com/in/faisalamircs 19 | * ----------------------------------------- 20 | * FrogoBox Software Industries 21 | * com.frogobox.evpn.helper 22 | * 23 | */ 24 | class Constant { 25 | 26 | object Variable { 27 | 28 | const val LOAD_ERROR = 0 29 | const val DOWNLOAD_PROGRESS = 1 30 | const val PARSE_PROGRESS = 2 31 | const val LOADING_SUCCESS = 3 32 | const val SWITCH_TO_RESULT = 4 33 | 34 | const val PRECENTAGE_MAX = 100 35 | 36 | const val START_VPN_PROFILE = 70 37 | 38 | const val BASE_URL = "http://www.vpngate.net/api/iphone/" 39 | const val BASE_FILE_NAME = "vpngate.csv" 40 | const val BROADCAST_ACTION = "de.blinkt.openvpn.VPN_STATUS" 41 | 42 | const val EXTRA_COUNTRY = "EXTRA_COUNTRY" 43 | const val EXTRA_FAST_CONNECTION = "EXTRA_FAST_CONNECTION" 44 | const val EXTRA_AUTO_CONNECTION = "EXTRA_AUTO_CONNECTION" 45 | const val EXTRA_FIRST_PREMIUM_LOAD = "firstPremiumLoad" 46 | const val EXTRA_STATUS = "status" 47 | 48 | const val BASE_FLAG_IMAGE_URL = "https://www.countryflags.io/" 49 | const val BASE_FLAG_IMAGE_SIZE = "/flat/64.png" 50 | } 51 | 52 | 53 | fun getFlagImageUrl(data: Server): String { 54 | var code: String = data.countryShort!!.toLowerCase() 55 | if (code == "do") code = "dom" 56 | return "$BASE_FLAG_IMAGE_URL$code$BASE_FLAG_IMAGE_SIZE" 57 | } 58 | 59 | 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/CountriesNames.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util; 2 | 3 | import java.util.HashMap; 4 | import java.util.Locale; 5 | import java.util.Map; 6 | 7 | 8 | 9 | public class CountriesNames { 10 | 11 | public static Map getCountries() { 12 | Map countries = new HashMap(); 13 | 14 | String[] isoCountries = Locale.getISOCountries(); 15 | for (String country : isoCountries) { 16 | Locale locale = new Locale("", country); 17 | String iso = locale.getISO3Country(); 18 | String code = locale.getCountry(); 19 | String name = locale.getDisplayCountry(); 20 | 21 | if (!"".equals(iso) && !"".equals(code) 22 | && !"".equals(name)) { 23 | countries.put(code, name); 24 | } 25 | } 26 | 27 | return countries; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/NetworkState.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | 7 | import com.frogobox.viprox.BaseApplication; 8 | 9 | 10 | public class NetworkState { 11 | 12 | public static boolean isOnline() { 13 | ConnectivityManager cm = 14 | (ConnectivityManager) BaseApplication.instance.getSystemService(Context.CONNECTIVITY_SERVICE); 15 | NetworkInfo netInfo = cm.getActiveNetworkInfo(); 16 | return netInfo != null && netInfo.isConnectedOrConnecting(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/NotificationUtils.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationManager; 5 | import android.app.PendingIntent; 6 | import android.content.ContentResolver; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.graphics.Bitmap; 10 | import android.graphics.BitmapFactory; 11 | import android.media.Ringtone; 12 | import android.media.RingtoneManager; 13 | import android.net.Uri; 14 | 15 | import androidx.core.app.NotificationCompat; 16 | import android.text.Html; 17 | 18 | import com.frogobox.viprox.R; 19 | import com.frogobox.viprox.ui.MainActivity; 20 | 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | import java.net.HttpURLConnection; 24 | import java.net.URL; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | public class NotificationUtils { 29 | private static final int NOTIFICATION_ID = 200; 30 | private static final String PUSH_NOTIFICATION = "pushNotification"; 31 | private static final String CHANNEL_ID = "myChannel"; 32 | private static final String URL = "url"; 33 | private static final String ACTIVITY = "activity"; 34 | Map activityMap = new HashMap<>(); 35 | private Context mContext; 36 | 37 | public NotificationUtils(Context mContext) { 38 | this.mContext = mContext; 39 | //Populate activity map 40 | activityMap.put("MainActivity", MainActivity.class); 41 | // activityMap.put("SecondActivity", SecondActivity.class); 42 | } 43 | 44 | /** 45 | * Displays notification based on parameters 46 | * 47 | * @param notificationVO 48 | * @param resultIntent 49 | */ 50 | public void displayNotification(com.frogobox.viprox.util.NotificationVO notificationVO, Intent resultIntent) { 51 | { 52 | String message = notificationVO.getMessage(); 53 | String title = notificationVO.getTitle(); 54 | String iconUrl = notificationVO.getIconUrl(); 55 | String action = notificationVO.getAction(); 56 | String destination = notificationVO.getActionDestination(); 57 | Bitmap iconBitMap = null; 58 | if (iconUrl != null) { 59 | iconBitMap = getBitmapFromURL(iconUrl); 60 | } 61 | final int icon = R.drawable.ic_launcher; 62 | 63 | PendingIntent resultPendingIntent; 64 | 65 | if (URL.equals(action)) { 66 | Intent notificationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(destination)); 67 | 68 | resultPendingIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0); 69 | } else if (ACTIVITY.equals(action) && activityMap.containsKey(destination)) { 70 | resultIntent = new Intent(mContext, activityMap.get(destination)); 71 | 72 | resultPendingIntent = 73 | PendingIntent.getActivity( 74 | mContext, 75 | 0, 76 | resultIntent, 77 | PendingIntent.FLAG_CANCEL_CURRENT 78 | ); 79 | } else { 80 | resultIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); 81 | resultPendingIntent = 82 | PendingIntent.getActivity( 83 | mContext, 84 | 0, 85 | resultIntent, 86 | PendingIntent.FLAG_CANCEL_CURRENT 87 | ); 88 | } 89 | 90 | 91 | final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder( 92 | mContext, CHANNEL_ID); 93 | 94 | Notification notification; 95 | 96 | if (iconBitMap == null) { 97 | //When Inbox Style is applied, user can expand the notification 98 | NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); 99 | 100 | inboxStyle.addLine(message); 101 | notification = mBuilder.setSmallIcon(icon).setTicker(title).setWhen(0) 102 | .setAutoCancel(true) 103 | .setContentTitle(title) 104 | .setContentIntent(resultPendingIntent) 105 | .setStyle(inboxStyle) 106 | .setSmallIcon(R.drawable.ic_launcher) 107 | .setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), icon)) 108 | .setContentText(message) 109 | .build(); 110 | 111 | } else { 112 | //If Bitmap is created from URL, show big icon 113 | NotificationCompat.BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle(); 114 | bigPictureStyle.setBigContentTitle(title); 115 | bigPictureStyle.setSummaryText(Html.fromHtml(message).toString()); 116 | bigPictureStyle.bigPicture(iconBitMap); 117 | notification = mBuilder.setSmallIcon(icon).setTicker(title).setWhen(0) 118 | .setAutoCancel(true) 119 | .setContentTitle(title) 120 | .setContentIntent(resultPendingIntent) 121 | .setStyle(bigPictureStyle) 122 | .setSmallIcon(R.drawable.ic_launcher) 123 | .setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), icon)) 124 | .setContentText(message) 125 | .build(); 126 | } 127 | 128 | NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 129 | notificationManager.notify(NOTIFICATION_ID, notification); 130 | } 131 | } 132 | 133 | /** 134 | * Downloads push notification image before displaying it in 135 | * the notification tray 136 | * 137 | * @param strURL : URL of the notification Image 138 | * @return : BitMap representation of notification Image 139 | */ 140 | private Bitmap getBitmapFromURL(String strURL) { 141 | try { 142 | URL url = new URL(strURL); 143 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 144 | connection.setDoInput(true); 145 | connection.connect(); 146 | InputStream input = connection.getInputStream(); 147 | return BitmapFactory.decodeStream(input); 148 | } catch (IOException e) { 149 | e.printStackTrace(); 150 | return null; 151 | } 152 | } 153 | 154 | /** 155 | * Playing notification sound 156 | */ 157 | public void playNotificationSound() { 158 | try { 159 | Uri alarmSound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE 160 | + "://" + mContext.getPackageName() + "/raw/notification"); 161 | Ringtone r = RingtoneManager.getRingtone(mContext, alarmSound); 162 | r.play(); 163 | } catch (Exception e) { 164 | e.printStackTrace(); 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/NotificationVO.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util; 2 | 3 | /** 4 | * Created by Abhi on 13 Nov 2017 013. 5 | */ 6 | 7 | public class NotificationVO { 8 | private String title; 9 | private String message; 10 | private String iconUrl; 11 | private String action; 12 | private String actionDestination; 13 | 14 | public String getTitle() { 15 | return title; 16 | } 17 | 18 | public void setTitle(String title) { 19 | this.title = title; 20 | } 21 | 22 | public String getMessage() { 23 | return message; 24 | } 25 | 26 | public void setMessage(String message) { 27 | this.message = message; 28 | } 29 | 30 | public String getIconUrl() { 31 | return iconUrl; 32 | } 33 | 34 | public void setIconUrl(String iconUrl) { 35 | this.iconUrl = iconUrl; 36 | } 37 | 38 | public String getAction() { 39 | return action; 40 | } 41 | 42 | public void setAction(String action) { 43 | this.action = action; 44 | } 45 | 46 | public String getActionDestination() { 47 | return actionDestination; 48 | } 49 | 50 | public void setActionDestination(String actionDestination) { 51 | this.actionDestination = actionDestination; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/NumberPickerPreference.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.preference.DialogPreference; 6 | import android.util.AttributeSet; 7 | import android.view.Gravity; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.FrameLayout; 11 | import android.widget.NumberPicker; 12 | 13 | 14 | 15 | public class NumberPickerPreference extends DialogPreference { 16 | 17 | 18 | public static final int MAX_VALUE = 120; 19 | public static final int MIN_VALUE = 10; 20 | 21 | public static final boolean WRAP_SELECTOR_WHEEL = true; 22 | 23 | private NumberPicker picker; 24 | private int value; 25 | 26 | public NumberPickerPreference(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | } 29 | 30 | public NumberPickerPreference(Context context, AttributeSet attrs, int defStyleAttr) { 31 | super(context, attrs, defStyleAttr); 32 | } 33 | 34 | @Override 35 | protected View onCreateDialogView() { 36 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( 37 | ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 38 | layoutParams.gravity = Gravity.CENTER; 39 | 40 | picker = new NumberPicker(getContext()); 41 | picker.setLayoutParams(layoutParams); 42 | 43 | FrameLayout dialogView = new FrameLayout(getContext()); 44 | dialogView.addView(picker); 45 | 46 | return dialogView; 47 | } 48 | 49 | @Override 50 | protected void onBindDialogView(View view) { 51 | super.onBindDialogView(view); 52 | picker.setMinValue(MIN_VALUE); 53 | picker.setMaxValue(MAX_VALUE); 54 | picker.setWrapSelectorWheel(WRAP_SELECTOR_WHEEL); 55 | picker.setValue(getValue()); 56 | } 57 | 58 | @Override 59 | protected void onDialogClosed(boolean positiveResult) { 60 | if (positiveResult) { 61 | picker.clearFocus(); 62 | int newValue = picker.getValue(); 63 | if (callChangeListener(newValue)) { 64 | setValue(newValue); 65 | } 66 | } 67 | } 68 | 69 | @Override 70 | protected Object onGetDefaultValue(TypedArray a, int index) { 71 | return a.getInt(index, MIN_VALUE); 72 | } 73 | 74 | @Override 75 | protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { 76 | setValue(restorePersistedValue ? getPersistedInt(MIN_VALUE) : (Integer) defaultValue); 77 | } 78 | 79 | public void setValue(int value) { 80 | this.value = value; 81 | persistInt(this.value); 82 | } 83 | 84 | public int getValue() { 85 | return this.value; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/PropertiesService.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util; 2 | 3 | import android.content.SharedPreferences; 4 | import android.preference.PreferenceManager; 5 | 6 | import com.frogobox.viprox.BaseApplication; 7 | 8 | 9 | 10 | public class PropertiesService { 11 | 12 | private static SharedPreferences prefs; 13 | private static final String DOWNLOADED_DATA_KEY = "downloaded_data"; 14 | private static final String UPLOADED_DATA_KEY = "uploaded_data"; 15 | private static final String AUTOMATIC_SWITCHING = "automaticSwitching"; 16 | private static final String COUNTRY_PRIORITY = "countryPriority"; 17 | private static final String CONNECT_ON_START = "connectOnStart"; 18 | private static final String AUTOMATIC_SWITCHING_SECONDS = "automaticSwitchingSeconds"; 19 | private static final String SELECTED_COUNTRY = "selectedCountry"; 20 | private static final String SHOW_RATING = "show_rating"; 21 | private static final String SHOW_NOTE = "show_note"; 22 | 23 | private synchronized static SharedPreferences getPrefs(){ 24 | if (prefs == null) { 25 | prefs = PreferenceManager.getDefaultSharedPreferences(BaseApplication.instance); 26 | } 27 | return prefs; 28 | } 29 | 30 | public static long getDownloaded(){ 31 | return getPrefs().getLong(DOWNLOADED_DATA_KEY, 0); 32 | } 33 | 34 | public static void setDownloaded(long count){ 35 | getPrefs().edit().putLong(DOWNLOADED_DATA_KEY, count).apply(); 36 | } 37 | 38 | public static long getUploaded(){ 39 | return getPrefs().getLong(UPLOADED_DATA_KEY, 0); 40 | } 41 | 42 | public static void setUploaded(long count){ 43 | getPrefs().edit().putLong(UPLOADED_DATA_KEY, count).apply(); 44 | } 45 | 46 | public static boolean getConnectOnStart(){ 47 | return getPrefs().getBoolean(CONNECT_ON_START, false); 48 | } 49 | 50 | public static boolean getAutomaticSwitching(){ 51 | return getPrefs().getBoolean(AUTOMATIC_SWITCHING, true); 52 | } 53 | 54 | public static int getAutomaticSwitchingSeconds(){ 55 | return getPrefs().getInt(AUTOMATIC_SWITCHING_SECONDS, 40); 56 | } 57 | 58 | public static boolean getCountryPriority(){ 59 | return getPrefs().getBoolean(COUNTRY_PRIORITY, false); 60 | } 61 | 62 | public static String getSelectedCountry(){ 63 | return getPrefs().getString(SELECTED_COUNTRY, null); 64 | } 65 | 66 | public static boolean getShowRating(){ 67 | return getPrefs().getBoolean(SHOW_RATING, true); 68 | } 69 | 70 | public static void setShowRating(boolean showRating){ 71 | getPrefs().edit().putBoolean(SHOW_RATING, showRating).apply(); 72 | } 73 | 74 | public static boolean getShowNote(){ 75 | return getPrefs().getBoolean(SHOW_NOTE, true); 76 | } 77 | 78 | public static void setShowNote(boolean showNote){ 79 | getPrefs().edit().putBoolean(SHOW_NOTE, showNote).apply(); 80 | } 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/Stopwatch.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Calendar; 5 | import java.util.TimeZone; 6 | 7 | public class Stopwatch { 8 | 9 | private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); 10 | private Calendar calendar = Calendar.getInstance(); 11 | private long startTime = System.currentTimeMillis(); 12 | 13 | public Stopwatch() { 14 | sdf.setTimeZone(TimeZone.getTimeZone("UTC")); 15 | } 16 | 17 | public long getDiff() { 18 | return System.currentTimeMillis() - startTime; 19 | } 20 | 21 | public String getElapsedTime() { 22 | calendar.setTimeInMillis(getDiff()); 23 | return sdf.format(calendar.getTime()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/TotalTraffic.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import de.blinkt.openvpn.core.OpenVPNService; 10 | 11 | 12 | 13 | public class TotalTraffic { 14 | 15 | public static final String TRAFFIC_ACTION = "traffic_action"; 16 | 17 | public static final String DOWNLOAD_ALL = "download_all"; 18 | public static final String DOWNLOAD_SESSION = "download_session"; 19 | public static final String UPLOAD_ALL = "upload_all"; 20 | public static final String UPLOAD_SESSION = "upload_session"; 21 | 22 | public static long inTotal; 23 | public static long outTotal; 24 | 25 | 26 | public static void calcTraffic(Context context, long in, long out, long diffIn, long diffOut) { 27 | List totalTraffic = getTotalTraffic(diffIn, diffOut); 28 | 29 | Intent traffic = new Intent(); 30 | traffic.setAction(TRAFFIC_ACTION); 31 | traffic.putExtra(DOWNLOAD_ALL, totalTraffic.get(0)); 32 | traffic.putExtra(DOWNLOAD_SESSION, OpenVPNService.humanReadableByteCount(in, false)); 33 | traffic.putExtra(UPLOAD_ALL, totalTraffic.get(1)); 34 | traffic.putExtra(UPLOAD_SESSION, OpenVPNService.humanReadableByteCount(out, false)); 35 | 36 | context.sendBroadcast(traffic); 37 | } 38 | 39 | public static List getTotalTraffic() { 40 | return getTotalTraffic(0, 0); 41 | } 42 | 43 | public static List getTotalTraffic(long in, long out) { 44 | List totalTraffic = new ArrayList(); 45 | 46 | if (inTotal == 0) 47 | inTotal = PropertiesService.getDownloaded(); 48 | 49 | if (outTotal == 0) 50 | outTotal = PropertiesService.getUploaded(); 51 | 52 | inTotal = inTotal + in; 53 | outTotal = outTotal + out; 54 | 55 | totalTraffic.add(OpenVPNService.humanReadableByteCount(inTotal, false)); 56 | totalTraffic.add(OpenVPNService.humanReadableByteCount(outTotal, false)); 57 | 58 | return totalTraffic; 59 | } 60 | 61 | public static void saveTotal() { 62 | if (inTotal != 0) 63 | PropertiesService.setDownloaded(inTotal); 64 | 65 | if (outTotal != 0) 66 | PropertiesService.setUploaded(outTotal); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/dns/HttpDownloadTest.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util.dns; 2 | 3 | import java.io.InputStream; 4 | import java.math.BigDecimal; 5 | import java.math.RoundingMode; 6 | import java.net.HttpURLConnection; 7 | import java.net.URL; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class HttpDownloadTest extends Thread { 12 | 13 | public String fileURL = ""; 14 | long startTime = 0; 15 | long endTime = 0; 16 | double downloadElapsedTime = 0; 17 | int downloadedByte = 0; 18 | double finalDownloadRate = 0.0; 19 | boolean finished = false; 20 | double instantDownloadRate = 0; 21 | int timeout = 15; 22 | 23 | HttpURLConnection httpConn = null; 24 | 25 | public HttpDownloadTest(String fileURL) { 26 | this.fileURL = fileURL; 27 | } 28 | 29 | private double round(double value, int places) { 30 | if (places < 0) throw new IllegalArgumentException(); 31 | 32 | BigDecimal bd; 33 | try { 34 | bd = new BigDecimal(value); 35 | } catch (Exception ex) { 36 | return 0.0; 37 | } 38 | bd = bd.setScale(places, RoundingMode.HALF_UP); 39 | return bd.doubleValue(); 40 | } 41 | 42 | public double getInstantDownloadRate() { 43 | return instantDownloadRate; 44 | } 45 | 46 | public void setInstantDownloadRate(int downloadedByte, double elapsedTime) { 47 | 48 | if (downloadedByte >= 0) { 49 | this.instantDownloadRate = round((Double) (((downloadedByte * 8) / (1000 * 1000)) / elapsedTime), 2); 50 | } else { 51 | this.instantDownloadRate = 0.0; 52 | } 53 | } 54 | 55 | public double getFinalDownloadRate() { 56 | return round(finalDownloadRate, 2); 57 | } 58 | 59 | public boolean isFinished() { 60 | return finished; 61 | } 62 | 63 | @Override 64 | public void run() { 65 | URL url = null; 66 | downloadedByte = 0; 67 | int responseCode = 0; 68 | 69 | List fileUrls = new ArrayList<>(); 70 | fileUrls.add(fileURL + "random4000x4000.jpg"); 71 | fileUrls.add(fileURL + "random3000x3000.jpg"); 72 | 73 | startTime = System.currentTimeMillis(); 74 | 75 | outer: 76 | for (String link : fileUrls) { 77 | try { 78 | url = new URL(link); 79 | httpConn = (HttpURLConnection) url.openConnection(); 80 | responseCode = httpConn.getResponseCode(); 81 | } catch (Exception ex) { 82 | ex.printStackTrace(); 83 | } 84 | 85 | try { 86 | if (responseCode == HttpURLConnection.HTTP_OK) { 87 | byte[] buffer = new byte[10240]; 88 | 89 | InputStream inputStream = httpConn.getInputStream(); 90 | int len = 0; 91 | 92 | while ((len = inputStream.read(buffer)) != -1) { 93 | downloadedByte += len; 94 | endTime = System.currentTimeMillis(); 95 | downloadElapsedTime = (endTime - startTime) / 1000.0; 96 | setInstantDownloadRate(downloadedByte, downloadElapsedTime); 97 | if (downloadElapsedTime >= timeout) { 98 | break outer; 99 | } 100 | } 101 | 102 | inputStream.close(); 103 | httpConn.disconnect(); 104 | } else { 105 | System.out.println("Link not found..."); 106 | } 107 | } catch (Exception ex) { 108 | ex.printStackTrace(); 109 | } 110 | } 111 | 112 | endTime = System.currentTimeMillis(); 113 | downloadElapsedTime = (endTime - startTime) / 1000.0; 114 | finalDownloadRate = ((downloadedByte * 8) / (1000 * 1000.0)) / downloadElapsedTime; 115 | 116 | finished = true; 117 | } 118 | 119 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/dns/HttpUploadTest.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util.dns; 2 | 3 | import static com.frogobox.viprox.util.dns.HttpUploadTest.uploadedKByte; 4 | 5 | import java.io.DataOutputStream; 6 | import java.math.BigDecimal; 7 | import java.math.RoundingMode; 8 | import java.net.HttpURLConnection; 9 | import java.net.URL; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | 13 | public class HttpUploadTest extends Thread { 14 | 15 | public String fileURL = ""; 16 | static int uploadedKByte = 0; 17 | double uploadElapsedTime = 0; 18 | boolean finished = false; 19 | double elapsedTime = 0; 20 | double finalUploadRate = 0.0; 21 | long startTime; 22 | 23 | public HttpUploadTest(String fileURL) { 24 | this.fileURL = fileURL; 25 | } 26 | 27 | private double round(double value, int places) { 28 | if (places < 0) throw new IllegalArgumentException(); 29 | 30 | BigDecimal bd; 31 | try { 32 | bd = new BigDecimal(value); 33 | } catch (Exception ex) { 34 | return 0.0; 35 | } 36 | bd = bd.setScale(places, RoundingMode.HALF_UP); 37 | return bd.doubleValue(); 38 | } 39 | 40 | public boolean isFinished() { 41 | return finished; 42 | } 43 | 44 | public double getInstantUploadRate() { 45 | try { 46 | BigDecimal bd = new BigDecimal(uploadedKByte); 47 | } catch (Exception ex) { 48 | return 0.0; 49 | } 50 | 51 | if (uploadedKByte >= 0) { 52 | long now = System.currentTimeMillis(); 53 | elapsedTime = (now - startTime) / 1000.0; 54 | return round((Double) (((uploadedKByte / 1000.0) * 8) / elapsedTime), 2); 55 | } else { 56 | return 0.0; 57 | } 58 | } 59 | 60 | public double getFinalUploadRate() { 61 | return round(finalUploadRate, 2); 62 | } 63 | 64 | @Override 65 | public void run() { 66 | try { 67 | URL url = new URL(fileURL); 68 | uploadedKByte = 0; 69 | startTime = System.currentTimeMillis(); 70 | 71 | ExecutorService executor = Executors.newFixedThreadPool(4); 72 | for (int i = 0; i < 4; i++) { 73 | executor.execute(new HandlerUpload(url)); 74 | } 75 | executor.shutdown(); 76 | while (!executor.isTerminated()) { 77 | try { 78 | Thread.sleep(100); 79 | } catch (InterruptedException ex) { 80 | } 81 | } 82 | 83 | long now = System.currentTimeMillis(); 84 | uploadElapsedTime = (now - startTime) / 1000.0; 85 | finalUploadRate = (Double) (((uploadedKByte / 1000.0) * 8) / uploadElapsedTime); 86 | 87 | } catch (Exception ex) { 88 | ex.printStackTrace(); 89 | } 90 | 91 | finished = true; 92 | } 93 | } 94 | 95 | class HandlerUpload extends Thread { 96 | 97 | URL url; 98 | 99 | public HandlerUpload(URL url) { 100 | this.url = url; 101 | } 102 | 103 | public void run() { 104 | byte[] buffer = new byte[150 * 1024]; 105 | long startTime = System.currentTimeMillis(); 106 | int timeout = 10; 107 | 108 | while (true) { 109 | 110 | try { 111 | HttpURLConnection conn = null; 112 | conn = (HttpURLConnection) url.openConnection(); 113 | conn.setDoOutput(true); 114 | conn.setRequestMethod("POST"); 115 | conn.setRequestProperty("Connection", "Keep-Alive"); 116 | 117 | DataOutputStream dos = new DataOutputStream(conn.getOutputStream()); 118 | 119 | 120 | dos.write(buffer, 0, buffer.length); 121 | dos.flush(); 122 | 123 | conn.getResponseCode(); 124 | 125 | uploadedKByte += buffer.length / 1024.0; 126 | long endTime = System.currentTimeMillis(); 127 | double uploadElapsedTime = (endTime - startTime) / 1000.0; 128 | if (uploadElapsedTime >= timeout) { 129 | break; 130 | } 131 | 132 | dos.close(); 133 | conn.disconnect(); 134 | } catch (Exception ex) { 135 | ex.printStackTrace(); 136 | } 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/dns/PingTest.java: -------------------------------------------------------------------------------- 1 | package com.frogobox.viprox.util.dns; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStreamReader; 5 | import java.util.HashMap; 6 | 7 | public class PingTest extends Thread { 8 | 9 | HashMap result = new HashMap<>(); 10 | String server = ""; 11 | int count; 12 | double instantRtt = 0; 13 | double avgRtt = 0.0; 14 | boolean finished = false; 15 | boolean started = false; 16 | 17 | public PingTest(String serverIpAddress, int pingTryCount) { 18 | this.server = serverIpAddress; 19 | this.count = pingTryCount; 20 | } 21 | 22 | public double getAvgRtt() { 23 | return avgRtt; 24 | } 25 | 26 | public double getInstantRtt() { 27 | return instantRtt; 28 | } 29 | 30 | public boolean isFinished() { 31 | return finished; 32 | } 33 | 34 | @Override 35 | public void run() { 36 | try { 37 | ProcessBuilder ps = new ProcessBuilder("ping", "-c " + count, this.server); 38 | 39 | ps.redirectErrorStream(true); 40 | Process pr = ps.start(); 41 | 42 | BufferedReader in = new BufferedReader(new InputStreamReader(pr.getInputStream())); 43 | String line; 44 | String output = ""; 45 | while ((line = in.readLine()) != null) { 46 | if (line.contains("icmp_seq")) { 47 | instantRtt = Double.parseDouble(line.split(" ")[line.split(" ").length - 2].replace("time=", "")); 48 | } 49 | if (line.startsWith("rtt ")) { 50 | avgRtt = Double.parseDouble(line.split("/")[4]); 51 | break; 52 | } 53 | } 54 | pr.waitFor(); 55 | in.close(); 56 | 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | } 60 | 61 | finished = true; 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/encoders/Base64.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package com.frogobox.viprox.util.encoders; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | 9 | public class Base64 10 | { 11 | private static final Encoder encoder = new Base64Encoder(); 12 | 13 | 14 | public static byte[] encode( 15 | byte[] data) 16 | { 17 | int len = (data.length + 2) / 3 * 4; 18 | ByteArrayOutputStream bOut = new ByteArrayOutputStream(len); 19 | 20 | try 21 | { 22 | encoder.encode(data, 0, data.length, bOut); 23 | } 24 | catch (IOException e) 25 | { 26 | throw new RuntimeException("exception encoding base64 string: " + e); 27 | } 28 | 29 | return bOut.toByteArray(); 30 | } 31 | 32 | 33 | public static int encode( 34 | byte[] data, 35 | OutputStream out) 36 | throws IOException 37 | { 38 | return encoder.encode(data, 0, data.length, out); 39 | } 40 | 41 | 42 | public static int encode( 43 | byte[] data, 44 | int off, 45 | int length, 46 | OutputStream out) 47 | throws IOException 48 | { 49 | return encoder.encode(data, off, length, out); 50 | } 51 | 52 | 53 | public static byte[] decode( 54 | byte[] data) 55 | { 56 | int len = data.length / 4 * 3; 57 | ByteArrayOutputStream bOut = new ByteArrayOutputStream(len); 58 | 59 | try 60 | { 61 | encoder.decode(data, 0, data.length, bOut); 62 | } 63 | catch (IOException e) 64 | { 65 | throw new RuntimeException("exception decoding base64 string: " + e); 66 | } 67 | 68 | return bOut.toByteArray(); 69 | } 70 | 71 | 72 | public static byte[] decode( 73 | String data) 74 | { 75 | int len = data.length() / 4 * 3; 76 | ByteArrayOutputStream bOut = new ByteArrayOutputStream(len); 77 | 78 | try 79 | { 80 | encoder.decode(data, bOut); 81 | } 82 | catch (IOException e) 83 | { 84 | throw new RuntimeException("exception decoding base64 string: " + e); 85 | } 86 | 87 | return bOut.toByteArray(); 88 | } 89 | 90 | 91 | public static int decode( 92 | String data, 93 | OutputStream out) 94 | throws IOException 95 | { 96 | return encoder.decode(data, out); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/encoders/Base64Encoder.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package com.frogobox.viprox.util.encoders; 4 | 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | 8 | public class Base64Encoder 9 | implements Encoder 10 | { 11 | protected final byte[] encodingTable = 12 | { 13 | (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 14 | (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 15 | (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 16 | (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 17 | (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 18 | (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 19 | (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 20 | (byte)'v', 21 | (byte)'w', (byte)'x', (byte)'y', (byte)'z', 22 | (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', 23 | (byte)'7', (byte)'8', (byte)'9', 24 | (byte)'+', (byte)'/' 25 | }; 26 | 27 | protected byte padding = (byte)'='; 28 | 29 | 30 | protected final byte[] decodingTable = new byte[128]; 31 | 32 | protected void initialiseDecodingTable() 33 | { 34 | for (int i = 0; i < encodingTable.length; i++) 35 | { 36 | decodingTable[encodingTable[i]] = (byte)i; 37 | } 38 | } 39 | 40 | public Base64Encoder() 41 | { 42 | initialiseDecodingTable(); 43 | } 44 | 45 | 46 | public int encode( 47 | byte[] data, 48 | int off, 49 | int length, 50 | OutputStream out) 51 | throws IOException 52 | { 53 | int modulus = length % 3; 54 | int dataLength = (length - modulus); 55 | int a1, a2, a3; 56 | 57 | for (int i = off; i < off + dataLength; i += 3) 58 | { 59 | a1 = data[i] & 0xff; 60 | a2 = data[i + 1] & 0xff; 61 | a3 = data[i + 2] & 0xff; 62 | 63 | out.write(encodingTable[(a1 >>> 2) & 0x3f]); 64 | out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]); 65 | out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]); 66 | out.write(encodingTable[a3 & 0x3f]); 67 | } 68 | 69 | 70 | int b1, b2, b3; 71 | int d1, d2; 72 | 73 | switch (modulus) 74 | { 75 | case 0: 76 | break; 77 | case 1: 78 | d1 = data[off + dataLength] & 0xff; 79 | b1 = (d1 >>> 2) & 0x3f; 80 | b2 = (d1 << 4) & 0x3f; 81 | 82 | out.write(encodingTable[b1]); 83 | out.write(encodingTable[b2]); 84 | out.write(padding); 85 | out.write(padding); 86 | break; 87 | case 2: 88 | d1 = data[off + dataLength] & 0xff; 89 | d2 = data[off + dataLength + 1] & 0xff; 90 | 91 | b1 = (d1 >>> 2) & 0x3f; 92 | b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f; 93 | b3 = (d2 << 2) & 0x3f; 94 | 95 | out.write(encodingTable[b1]); 96 | out.write(encodingTable[b2]); 97 | out.write(encodingTable[b3]); 98 | out.write(padding); 99 | break; 100 | } 101 | 102 | return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4); 103 | } 104 | 105 | private boolean ignore( 106 | char c) 107 | { 108 | return (c == '\n' || c =='\r' || c == '\t' || c == ' '); 109 | } 110 | 111 | 112 | public int decode( 113 | byte[] data, 114 | int off, 115 | int length, 116 | OutputStream out) 117 | throws IOException 118 | { 119 | byte b1, b2, b3, b4; 120 | int outLen = 0; 121 | 122 | int end = off + length; 123 | 124 | while (end > off) 125 | { 126 | if (!ignore((char)data[end - 1])) 127 | { 128 | break; 129 | } 130 | 131 | end--; 132 | } 133 | 134 | int i = off; 135 | int finish = end - 4; 136 | 137 | i = nextI(data, i, finish); 138 | 139 | while (i < finish) 140 | { 141 | b1 = decodingTable[data[i++]]; 142 | 143 | i = nextI(data, i, finish); 144 | 145 | b2 = decodingTable[data[i++]]; 146 | 147 | i = nextI(data, i, finish); 148 | 149 | b3 = decodingTable[data[i++]]; 150 | 151 | i = nextI(data, i, finish); 152 | 153 | b4 = decodingTable[data[i++]]; 154 | 155 | out.write((b1 << 2) | (b2 >> 4)); 156 | out.write((b2 << 4) | (b3 >> 2)); 157 | out.write((b3 << 6) | b4); 158 | 159 | outLen += 3; 160 | 161 | i = nextI(data, i, finish); 162 | } 163 | 164 | outLen += decodeLastBlock(out, (char)data[end - 4], (char)data[end - 3], (char)data[end - 2], (char)data[end - 1]); 165 | 166 | return outLen; 167 | } 168 | 169 | private int nextI(byte[] data, int i, int finish) 170 | { 171 | while ((i < finish) && ignore((char)data[i])) 172 | { 173 | i++; 174 | } 175 | return i; 176 | } 177 | 178 | 179 | public int decode( 180 | String data, 181 | OutputStream out) 182 | throws IOException 183 | { 184 | byte b1, b2, b3, b4; 185 | int length = 0; 186 | 187 | int end = data.length(); 188 | 189 | while (end > 0) 190 | { 191 | if (!ignore(data.charAt(end - 1))) 192 | { 193 | break; 194 | } 195 | 196 | end--; 197 | } 198 | 199 | int i = 0; 200 | int finish = end - 4; 201 | 202 | i = nextI(data, i, finish); 203 | 204 | while (i < finish) 205 | { 206 | b1 = decodingTable[data.charAt(i++)]; 207 | 208 | i = nextI(data, i, finish); 209 | 210 | b2 = decodingTable[data.charAt(i++)]; 211 | 212 | i = nextI(data, i, finish); 213 | 214 | b3 = decodingTable[data.charAt(i++)]; 215 | 216 | i = nextI(data, i, finish); 217 | 218 | b4 = decodingTable[data.charAt(i++)]; 219 | 220 | out.write((b1 << 2) | (b2 >> 4)); 221 | out.write((b2 << 4) | (b3 >> 2)); 222 | out.write((b3 << 6) | b4); 223 | 224 | length += 3; 225 | 226 | i = nextI(data, i, finish); 227 | } 228 | 229 | length += decodeLastBlock(out, data.charAt(end - 4), data.charAt(end - 3), data.charAt(end - 2), data.charAt(end - 1)); 230 | 231 | return length; 232 | } 233 | 234 | private int decodeLastBlock(OutputStream out, char c1, char c2, char c3, char c4) 235 | throws IOException 236 | { 237 | byte b1, b2, b3, b4; 238 | 239 | if (c3 == padding) 240 | { 241 | b1 = decodingTable[c1]; 242 | b2 = decodingTable[c2]; 243 | 244 | out.write((b1 << 2) | (b2 >> 4)); 245 | 246 | return 1; 247 | } 248 | else if (c4 == padding) 249 | { 250 | b1 = decodingTable[c1]; 251 | b2 = decodingTable[c2]; 252 | b3 = decodingTable[c3]; 253 | 254 | out.write((b1 << 2) | (b2 >> 4)); 255 | out.write((b2 << 4) | (b3 >> 2)); 256 | 257 | return 2; 258 | } 259 | else 260 | { 261 | b1 = decodingTable[c1]; 262 | b2 = decodingTable[c2]; 263 | b3 = decodingTable[c3]; 264 | b4 = decodingTable[c4]; 265 | 266 | out.write((b1 << 2) | (b2 >> 4)); 267 | out.write((b2 << 4) | (b3 >> 2)); 268 | out.write((b3 << 6) | b4); 269 | 270 | return 3; 271 | } 272 | } 273 | 274 | private int nextI(String data, int i, int finish) 275 | { 276 | while ((i < finish) && ignore(data.charAt(i))) 277 | { 278 | i++; 279 | } 280 | return i; 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/encoders/Encoder.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package com.frogobox.viprox.util.encoders; 4 | 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | 8 | 9 | public interface Encoder 10 | { 11 | int encode(byte[] data, int off, int length, OutputStream out) throws IOException; 12 | 13 | int decode(byte[] data, int off, int length, OutputStream out) throws IOException; 14 | 15 | int decode(String data, OutputStream out) throws IOException; 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/io/pem/PemGenerationException.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package com.frogobox.viprox.util.io.pem; 4 | 5 | import java.io.IOException; 6 | 7 | @SuppressWarnings("serial") 8 | public class PemGenerationException 9 | extends IOException 10 | { 11 | private Throwable cause; 12 | 13 | public PemGenerationException(String message, Throwable cause) 14 | { 15 | super(message); 16 | this.cause = cause; 17 | } 18 | 19 | public PemGenerationException(String message) 20 | { 21 | super(message); 22 | } 23 | 24 | public Throwable getCause() 25 | { 26 | return cause; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/io/pem/PemHeader.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package com.frogobox.viprox.util.io.pem; 4 | 5 | public class PemHeader 6 | { 7 | private String name; 8 | private String value; 9 | 10 | public PemHeader(String name, String value) 11 | { 12 | this.name = name; 13 | this.value = value; 14 | } 15 | 16 | public String getName() 17 | { 18 | return name; 19 | } 20 | 21 | public String getValue() 22 | { 23 | return value; 24 | } 25 | 26 | public int hashCode() 27 | { 28 | return getHashCode(this.name) + 31 * getHashCode(this.value); 29 | } 30 | 31 | public boolean equals(Object o) 32 | { 33 | if (!(o instanceof PemHeader)) 34 | { 35 | return false; 36 | } 37 | 38 | PemHeader other = (PemHeader)o; 39 | 40 | return other == this || (isEqual(this.name, other.name) && isEqual(this.value, other.value)); 41 | } 42 | 43 | private int getHashCode(String s) 44 | { 45 | if (s == null) 46 | { 47 | return 1; 48 | } 49 | 50 | return s.hashCode(); 51 | } 52 | 53 | private boolean isEqual(String s1, String s2) 54 | { 55 | if (s1 == s2) 56 | { 57 | return true; 58 | } 59 | 60 | if (s1 == null || s2 == null) 61 | { 62 | return false; 63 | } 64 | 65 | return s1.equals(s2); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/io/pem/PemObject.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package com.frogobox.viprox.util.io.pem; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | @SuppressWarnings("all") 10 | public class PemObject 11 | implements PemObjectGenerator 12 | { 13 | private static final List EMPTY_LIST = Collections.unmodifiableList(new ArrayList()); 14 | 15 | private String type; 16 | private List headers; 17 | private byte[] content; 18 | 19 | 20 | public PemObject(String type, byte[] content) 21 | { 22 | this(type, EMPTY_LIST, content); 23 | } 24 | 25 | 26 | public PemObject(String type, List headers, byte[] content) 27 | { 28 | this.type = type; 29 | this.headers = Collections.unmodifiableList(headers); 30 | this.content = content; 31 | } 32 | 33 | public String getType() 34 | { 35 | return type; 36 | } 37 | 38 | public List getHeaders() 39 | { 40 | return headers; 41 | } 42 | 43 | public byte[] getContent() 44 | { 45 | return content; 46 | } 47 | 48 | public PemObject generate() 49 | throws PemGenerationException 50 | { 51 | return this; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/io/pem/PemObjectGenerator.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package com.frogobox.viprox.util.io.pem; 4 | 5 | public interface PemObjectGenerator 6 | { 7 | PemObject generate() 8 | throws PemGenerationException; 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/io/pem/PemReader.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package com.frogobox.viprox.util.io.pem; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.IOException; 7 | import java.io.Reader; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | import com.frogobox.viprox.util.encoders.Base64; 12 | 13 | public class PemReader 14 | extends BufferedReader 15 | { 16 | private static final String BEGIN = "-----BEGIN "; 17 | private static final String END = "-----END "; 18 | 19 | public PemReader(Reader reader) 20 | { 21 | super(reader); 22 | } 23 | 24 | public PemObject readPemObject() 25 | throws IOException 26 | { 27 | String line = readLine(); 28 | 29 | while (line != null && !line.startsWith(BEGIN)) 30 | { 31 | line = readLine(); 32 | } 33 | 34 | if (line != null) 35 | { 36 | line = line.substring(BEGIN.length()); 37 | int index = line.indexOf('-'); 38 | String type = line.substring(0, index); 39 | 40 | if (index > 0) 41 | { 42 | return loadObject(type); 43 | } 44 | } 45 | 46 | return null; 47 | } 48 | 49 | private PemObject loadObject(String type) 50 | throws IOException 51 | { 52 | String line; 53 | String endMarker = END + type; 54 | StringBuilder buf = new StringBuilder(); 55 | List headers = new ArrayList(); 56 | 57 | while ((line = readLine()) != null) 58 | { 59 | if (line.indexOf(":") >= 0) 60 | { 61 | int index = line.indexOf(':'); 62 | String hdr = line.substring(0, index); 63 | String value = line.substring(index + 1).trim(); 64 | 65 | headers.add(new PemHeader(hdr, value)); 66 | 67 | continue; 68 | } 69 | 70 | if (line.indexOf(endMarker) != -1) 71 | { 72 | break; 73 | } 74 | 75 | buf.append(line.trim()); 76 | } 77 | 78 | if (line == null) 79 | { 80 | throw new IOException(endMarker + " not found"); 81 | } 82 | 83 | return new PemObject(type, headers, Base64.decode(buf.toString())); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/viprox/util/io/pem/PemWriter.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package com.frogobox.viprox.util.io.pem; 4 | 5 | import java.io.BufferedWriter; 6 | import java.io.IOException; 7 | import java.io.Writer; 8 | import java.util.Iterator; 9 | 10 | import com.frogobox.viprox.util.encoders.Base64; 11 | 12 | 13 | @SuppressWarnings("all") 14 | public class PemWriter 15 | extends BufferedWriter 16 | { 17 | private static final int LINE_LENGTH = 64; 18 | 19 | private final int nlLength; 20 | private char[] buf = new char[LINE_LENGTH]; 21 | 22 | 23 | public PemWriter(Writer out) 24 | { 25 | super(out); 26 | 27 | String nl = System.getProperty("line.separator"); 28 | if (nl != null) 29 | { 30 | nlLength = nl.length(); 31 | } 32 | else 33 | { 34 | nlLength = 2; 35 | } 36 | } 37 | 38 | 39 | public int getOutputSize(PemObject obj) 40 | { 41 | 42 | int size = (2 * (obj.getType().length() + 10 + nlLength)) + 6 + 4; 43 | 44 | if (!obj.getHeaders().isEmpty()) 45 | { 46 | for (Iterator it = obj.getHeaders().iterator(); it.hasNext();) 47 | { 48 | PemHeader hdr = (PemHeader)it.next(); 49 | 50 | size += hdr.getName().length() + ": ".length() + hdr.getValue().length() + nlLength; 51 | } 52 | 53 | size += nlLength; 54 | } 55 | 56 | 57 | int dataLen = ((obj.getContent().length + 2) / 3) * 4; 58 | 59 | size += dataLen + (((dataLen + LINE_LENGTH - 1) / LINE_LENGTH) * nlLength); 60 | 61 | return size; 62 | } 63 | 64 | public void writeObject(PemObjectGenerator objGen) 65 | throws IOException 66 | { 67 | PemObject obj = objGen.generate(); 68 | 69 | writePreEncapsulationBoundary(obj.getType()); 70 | 71 | if (!obj.getHeaders().isEmpty()) 72 | { 73 | for (Iterator it = obj.getHeaders().iterator(); it.hasNext();) 74 | { 75 | PemHeader hdr = (PemHeader)it.next(); 76 | 77 | this.write(hdr.getName()); 78 | this.write(": "); 79 | this.write(hdr.getValue()); 80 | this.newLine(); 81 | } 82 | 83 | this.newLine(); 84 | } 85 | 86 | writeEncoded(obj.getContent()); 87 | writePostEncapsulationBoundary(obj.getType()); 88 | } 89 | 90 | private void writeEncoded(byte[] bytes) 91 | throws IOException 92 | { 93 | bytes = Base64.encode(bytes); 94 | 95 | for (int i = 0; i < bytes.length; i += buf.length) 96 | { 97 | int index = 0; 98 | 99 | while (index != buf.length) 100 | { 101 | if ((i + index) >= bytes.length) 102 | { 103 | break; 104 | } 105 | buf[index] = (char)bytes[i + index]; 106 | index++; 107 | } 108 | this.write(buf, 0, index); 109 | this.newLine(); 110 | } 111 | } 112 | 113 | private void writePreEncapsulationBoundary( 114 | String type) 115 | throws IOException 116 | { 117 | this.write("-----BEGIN " + type + "-----"); 118 | this.newLine(); 119 | } 120 | 121 | private void writePostEncapsulationBoundary( 122 | String type) 123 | throws IOException 124 | { 125 | this.write("-----END " + type + "-----"); 126 | this.newLine(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java: -------------------------------------------------------------------------------- 1 | package de.blinkt.openvpn.core; 2 | 3 | import java.util.Locale; 4 | 5 | class CIDRIP { 6 | 7 | String mIp; 8 | int len; 9 | 10 | public CIDRIP(String ip, String mask) { 11 | mIp = ip; 12 | long netmask = getInt(mask); 13 | 14 | netmask += 1l << 32; 15 | 16 | int lenZeros = 0; 17 | while ((netmask & 0x1) == 0) { 18 | lenZeros++; 19 | netmask = netmask >> 1; 20 | } 21 | 22 | if (netmask != (0x1ffffffffl >> lenZeros)) { 23 | 24 | len = 32; 25 | } else { 26 | len = 32 - lenZeros; 27 | } 28 | 29 | } 30 | 31 | public CIDRIP(String address, int prefix_length) { 32 | len = prefix_length; 33 | mIp = address; 34 | } 35 | 36 | static long getInt(String ipaddr) { 37 | String[] ipt = ipaddr.split("\\."); 38 | long ip = 0; 39 | 40 | ip += Long.parseLong(ipt[0]) << 24; 41 | ip += Integer.parseInt(ipt[1]) << 16; 42 | ip += Integer.parseInt(ipt[2]) << 8; 43 | ip += Integer.parseInt(ipt[3]); 44 | 45 | return ip; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return String.format(Locale.ENGLISH, "%s/%d", mIp, len); 51 | } 52 | 53 | public boolean normalise() { 54 | long ip = getInt(mIp); 55 | 56 | long newip = ip & (0xffffffffl << (32 - len)); 57 | if (newip != ip) { 58 | mIp = String.format("%d.%d.%d.%d", (newip & 0xff000000) >> 24, (newip & 0xff0000) >> 16, (newip & 0xff00) >> 8, newip & 0xff); 59 | return true; 60 | } else { 61 | return false; 62 | } 63 | 64 | } 65 | 66 | public long getInt() { 67 | return getInt(mIp); 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /app/src/main/java/de/blinkt/openvpn/core/Connection.java: -------------------------------------------------------------------------------- 1 | package de.blinkt.openvpn.core; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.io.Serializable; 6 | 7 | public class Connection implements Serializable, Cloneable { 8 | public static final int CONNECTION_DEFAULT_TIMEOUT = 120; 9 | private static final long serialVersionUID = 92031902903829089L; 10 | public String mServerName = "de.blinkt.openvpn"; 11 | public String mServerPort = "1194"; 12 | public boolean mUseUdp = true; 13 | public String mCustomConfiguration = ""; 14 | public boolean mUseCustomConfig = false; 15 | public boolean mEnabled = true; 16 | public int mConnectTimeout = 0; 17 | 18 | public String getConnectionBlock() { 19 | String cfg = ""; 20 | cfg += "remote "; 21 | cfg += mServerName; 22 | cfg += " "; 23 | cfg += mServerPort; 24 | if (mUseUdp) 25 | cfg += " udp\n"; 26 | else 27 | cfg += " tcp-client\n"; 28 | 29 | if (mConnectTimeout != 0) 30 | cfg += String.format(" connect-timeout %d\n", mConnectTimeout); 31 | 32 | 33 | if (!TextUtils.isEmpty(mCustomConfiguration) && mUseCustomConfig) { 34 | cfg += mCustomConfiguration; 35 | cfg += "\n"; 36 | } 37 | return cfg; 38 | } 39 | 40 | @Override 41 | public Connection clone() throws CloneNotSupportedException { 42 | return (Connection) super.clone(); 43 | } 44 | 45 | public boolean isOnlyRemote() { 46 | return TextUtils.isEmpty(mCustomConfiguration) || !mUseCustomConfig; 47 | } 48 | 49 | public int getTimeout() { 50 | if (mConnectTimeout <= 0) 51 | return CONNECTION_DEFAULT_TIMEOUT; 52 | else 53 | return mConnectTimeout; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java: -------------------------------------------------------------------------------- 1 | package de.blinkt.openvpn.core; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.os.Message; 6 | 7 | import com.frogobox.viprox.R; 8 | 9 | import java.io.BufferedInputStream; 10 | import java.io.File; 11 | import java.io.FileInputStream; 12 | import java.io.FileNotFoundException; 13 | import java.io.FileOutputStream; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.OutputStream; 17 | import java.io.UnsupportedEncodingException; 18 | import java.nio.BufferOverflowException; 19 | import java.nio.ByteBuffer; 20 | import java.util.Locale; 21 | 22 | class LogFileHandler extends Handler { 23 | public static final int LOG_MESSAGE = 103; 24 | public static final int MAGIC_BYTE = 0x55; 25 | public static final String LOGFILE_NAME = "logcache.dat"; 26 | final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); 27 | static final int TRIM_LOG_FILE = 100; 28 | static final int FLUSH_TO_DISK = 101; 29 | static final int LOG_INIT = 102; 30 | protected OutputStream mLogFile; 31 | 32 | 33 | public LogFileHandler(Looper looper) { 34 | super(looper); 35 | } 36 | 37 | public static String bytesToHex(byte[] bytes, int len) { 38 | len = Math.min(bytes.length, len); 39 | char[] hexChars = new char[len * 2]; 40 | for (int j = 0; j < len; j++) { 41 | int v = bytes[j] & 0xFF; 42 | hexChars[j * 2] = hexArray[v >>> 4]; 43 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 44 | } 45 | return new String(hexChars); 46 | } 47 | 48 | @Override 49 | public void handleMessage(Message msg) { 50 | try { 51 | if (msg.what == LOG_INIT) { 52 | if (mLogFile != null) 53 | throw new RuntimeException("mLogFile not null"); 54 | readLogCache((File) msg.obj); 55 | openLogFile((File) msg.obj); 56 | } else if (msg.what == LOG_MESSAGE && msg.obj instanceof LogItem) { 57 | 58 | if (mLogFile == null) 59 | return; 60 | writeLogItemToDisk((LogItem) msg.obj); 61 | } else if (msg.what == TRIM_LOG_FILE) { 62 | trimLogFile(); 63 | for (LogItem li : VpnStatus.getlogbuffer()) 64 | writeLogItemToDisk(li); 65 | } else if (msg.what == FLUSH_TO_DISK) { 66 | flushToDisk(); 67 | } 68 | 69 | } catch (IOException | BufferOverflowException e) { 70 | e.printStackTrace(); 71 | VpnStatus.logError("Error during log cache: " + msg.what); 72 | VpnStatus.logException(e); 73 | } 74 | 75 | } 76 | 77 | private void flushToDisk() throws IOException { 78 | mLogFile.flush(); 79 | } 80 | 81 | private void trimLogFile() { 82 | try { 83 | mLogFile.flush(); 84 | ((FileOutputStream) mLogFile).getChannel().truncate(0); 85 | } catch (IOException e) { 86 | e.printStackTrace(); 87 | } 88 | } 89 | 90 | private void writeLogItemToDisk(LogItem li) throws IOException { 91 | 92 | 93 | byte[] liBytes = li.getMarschaledBytes(); 94 | 95 | writeEscapedBytes(liBytes); 96 | } 97 | 98 | public void writeEscapedBytes(byte[] bytes) throws IOException { 99 | int magic = 0; 100 | for (byte b : bytes) 101 | if (b == MAGIC_BYTE || b == MAGIC_BYTE + 1) 102 | magic++; 103 | 104 | byte eBytes[] = new byte[bytes.length + magic]; 105 | 106 | int i = 0; 107 | for (byte b : bytes) { 108 | if (b == MAGIC_BYTE || b == MAGIC_BYTE + 1) { 109 | eBytes[i++] = MAGIC_BYTE + 1; 110 | eBytes[i++] = (byte) (b - MAGIC_BYTE); 111 | } else { 112 | eBytes[i++] = b; 113 | } 114 | } 115 | 116 | byte[] lenBytes = ByteBuffer.allocate(4).putInt(bytes.length).array(); 117 | synchronized (mLogFile) { 118 | mLogFile.write(MAGIC_BYTE); 119 | mLogFile.write(lenBytes); 120 | mLogFile.write(eBytes); 121 | } 122 | } 123 | 124 | private void openLogFile(File cacheDir) throws FileNotFoundException { 125 | File logfile = new File(cacheDir, LOGFILE_NAME); 126 | mLogFile = new FileOutputStream(logfile); 127 | } 128 | 129 | private void readLogCache(File cacheDir) { 130 | try { 131 | File logfile = new File(cacheDir, LOGFILE_NAME); 132 | 133 | 134 | if (!logfile.exists() || !logfile.canRead()) 135 | return; 136 | 137 | readCacheContents(new FileInputStream(logfile)); 138 | 139 | } catch (java.io.IOException | java.lang.RuntimeException e) { 140 | VpnStatus.logError("Reading cached logfile failed"); 141 | VpnStatus.logException(e); 142 | e.printStackTrace(); 143 | 144 | } 145 | } 146 | 147 | protected void readCacheContents(InputStream in) throws IOException { 148 | 149 | 150 | BufferedInputStream logFile = new BufferedInputStream(in); 151 | 152 | byte[] buf = new byte[16384]; 153 | int read = logFile.read(buf, 0, 5); 154 | int itemsRead = 0; 155 | 156 | 157 | readloop: 158 | while (read >= 5) { 159 | int skipped = 0; 160 | while (buf[skipped] != MAGIC_BYTE) { 161 | skipped++; 162 | if (!(logFile.read(buf, skipped + 4, 1) == 1) || skipped + 10 > buf.length) { 163 | VpnStatus.logDebug(String.format(Locale.US, "Skipped %d bytes and no a magic byte found", skipped)); 164 | break readloop; 165 | } 166 | } 167 | if (skipped > 0) 168 | VpnStatus.logDebug(String.format(Locale.US, "Skipped %d bytes before finding a magic byte", skipped)); 169 | 170 | int len = ByteBuffer.wrap(buf, skipped + 1, 4).asIntBuffer().get(); 171 | 172 | 173 | int pos = 0; 174 | byte buf2[] = new byte[buf.length]; 175 | 176 | while (pos < len) { 177 | byte b = (byte) logFile.read(); 178 | if (b == MAGIC_BYTE) { 179 | VpnStatus.logDebug(String.format(Locale.US, "Unexpected magic byte found at pos %d, abort current log item", pos)); 180 | read = logFile.read(buf, 1, 4) + 1; 181 | continue readloop; 182 | } else if (b == MAGIC_BYTE + 1) { 183 | b = (byte) logFile.read(); 184 | if (b == 0) 185 | b = MAGIC_BYTE; 186 | else if (b == 1) 187 | b = MAGIC_BYTE + 1; 188 | else { 189 | VpnStatus.logDebug(String.format(Locale.US, "Escaped byte not 0 or 1: %d", b)); 190 | read = logFile.read(buf, 1, 4) + 1; 191 | continue readloop; 192 | } 193 | } 194 | buf2[pos++] = b; 195 | } 196 | 197 | restoreLogItem(buf2, len); 198 | 199 | 200 | read = logFile.read(buf, 0, 5); 201 | itemsRead++; 202 | if (itemsRead > 2 * VpnStatus.MAXLOGENTRIES) { 203 | VpnStatus.logError("Too many logentries read from cache, aborting."); 204 | read = 0; 205 | } 206 | 207 | } 208 | VpnStatus.logDebug(R.string.reread_log, itemsRead); 209 | } 210 | 211 | protected void restoreLogItem(byte[] buf, int len) throws UnsupportedEncodingException { 212 | 213 | LogItem li = new LogItem(buf, len); 214 | if (li.verify()) { 215 | VpnStatus.newLogItem(li, true); 216 | } else { 217 | VpnStatus.logError(String.format(Locale.getDefault(), 218 | "Could not read log item from file: %d: %s", 219 | len, bytesToHex(buf, Math.max(len, 80)))); 220 | } 221 | } 222 | 223 | } -------------------------------------------------------------------------------- /app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java: -------------------------------------------------------------------------------- 1 | package de.blinkt.openvpn.core; 2 | 3 | import java.security.InvalidKeyException; 4 | 5 | public class NativeUtils { 6 | static { 7 | System.loadLibrary("opvpnutil"); 8 | } 9 | 10 | public static native byte[] rsasign(byte[] input, int pkey) throws InvalidKeyException; 11 | 12 | public static native String[] getIfconfig() throws IllegalArgumentException; 13 | 14 | static native void jniclose(int fdint); 15 | 16 | public static native String getNativeAPI(); 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java: -------------------------------------------------------------------------------- 1 | package de.blinkt.openvpn.core; 2 | 3 | public interface OpenVPNManagement { 4 | int mBytecountInterval = 2; 5 | 6 | void reconnect(); 7 | 8 | void pause(pauseReason reason); 9 | 10 | void resume(); 11 | 12 | boolean stopVPN(boolean replaceConnection); 13 | 14 | void networkChange(boolean sameNetwork); 15 | 16 | void setPauseCallback(PausedStateCallback callback); 17 | 18 | enum pauseReason { 19 | noNetwork, 20 | userPause, 21 | screenOff, 22 | } 23 | 24 | interface PausedStateCallback { 25 | boolean shouldBeRunning(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java: -------------------------------------------------------------------------------- 1 | package de.blinkt.openvpn.core; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.util.Log; 5 | 6 | import com.frogobox.viprox.R; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.BufferedWriter; 10 | import java.io.FileWriter; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.InputStreamReader; 14 | import java.text.SimpleDateFormat; 15 | import java.util.Collections; 16 | import java.util.Date; 17 | import java.util.LinkedList; 18 | import java.util.Locale; 19 | import java.util.regex.Matcher; 20 | import java.util.regex.Pattern; 21 | 22 | import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus; 23 | 24 | public class OpenVPNThread implements Runnable { 25 | public static final int M_FATAL = (1 << 4); 26 | public static final int M_NONFATAL = (1 << 5); 27 | public static final int M_WARN = (1 << 6); 28 | public static final int M_DEBUG = (1 << 7); 29 | private static final String DUMP_PATH_STRING = "Dump path: "; 30 | @SuppressLint("SdCardPath") 31 | private static final String BROKEN_PIE_SUPPORT = "/data/data/de.blinkt.openvpn/cache/pievpn"; 32 | private final static String BROKEN_PIE_SUPPORT2 = "syntax error"; 33 | private static final String TAG = "OpenVPN"; 34 | private String[] mArgv; 35 | private Process mProcess; 36 | private String mNativeDir; 37 | private OpenVPNService mService; 38 | private String mDumpPath; 39 | private boolean mBrokenPie = false; 40 | private boolean mNoProcessExitStatus = false; 41 | 42 | public OpenVPNThread(OpenVPNService service, String[] argv, String nativelibdir) { 43 | mArgv = argv; 44 | mNativeDir = nativelibdir; 45 | mService = service; 46 | } 47 | 48 | public void stopProcess() { 49 | mProcess.destroy(); 50 | } 51 | 52 | void setReplaceConnection() { 53 | mNoProcessExitStatus = true; 54 | } 55 | 56 | @Override 57 | public void run() { 58 | try { 59 | Log.i(TAG, "Starting openvpn"); 60 | startOpenVPNThreadArgs(mArgv); 61 | Log.i(TAG, "OpenVPN process exited"); 62 | } catch (Exception e) { 63 | VpnStatus.logException("Starting OpenVPN Thread", e); 64 | Log.e(TAG, "OpenVPNThread Got " + e.toString()); 65 | } finally { 66 | int exitvalue = 0; 67 | try { 68 | if (mProcess != null) 69 | exitvalue = mProcess.waitFor(); 70 | } catch (IllegalThreadStateException ite) { 71 | VpnStatus.logError("Illegal Thread state: " + ite.getLocalizedMessage()); 72 | } catch (InterruptedException ie) { 73 | VpnStatus.logError("InterruptedException: " + ie.getLocalizedMessage()); 74 | } 75 | if (exitvalue != 0) { 76 | VpnStatus.logError("Process exited with exit value " + exitvalue); 77 | if (mBrokenPie) { 78 | 79 | String[] noPieArgv = VPNLaunchHelper.replacePieWithNoPie(mArgv); 80 | 81 | 82 | if (!noPieArgv.equals(mArgv)) { 83 | mArgv = noPieArgv; 84 | VpnStatus.logInfo("PIE Version could not be executed. Trying no PIE version"); 85 | run(); 86 | return; 87 | } 88 | 89 | } 90 | 91 | } 92 | 93 | if (!mNoProcessExitStatus) 94 | VpnStatus.updateStateString("NOPROCESS", "No process running.", R.string.state_noprocess, ConnectionStatus.LEVEL_NOTCONNECTED); 95 | 96 | if (mDumpPath != null) { 97 | try { 98 | BufferedWriter logout = new BufferedWriter(new FileWriter(mDumpPath + ".log")); 99 | SimpleDateFormat timeformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.GERMAN); 100 | for (LogItem li : VpnStatus.getlogbuffer()) { 101 | String time = timeformat.format(new Date(li.getLogtime())); 102 | logout.write(time + " " + li.getString(mService) + "\n"); 103 | } 104 | logout.close(); 105 | VpnStatus.logError(R.string.minidump_generated); 106 | } catch (IOException e) { 107 | VpnStatus.logError("Writing minidump log: " + e.getLocalizedMessage()); 108 | } 109 | } 110 | 111 | mService.processDied(); 112 | Log.i(TAG, "Exiting"); 113 | } 114 | } 115 | 116 | private void startOpenVPNThreadArgs(String[] argv) { 117 | LinkedList argvlist = new LinkedList(); 118 | Collections.addAll(argvlist, argv); 119 | ProcessBuilder pb = new ProcessBuilder(argvlist); 120 | String lbpath = genLibraryPath(argv, pb); 121 | pb.environment().put("LD_LIBRARY_PATH", lbpath); 122 | 123 | pb.redirectErrorStream(true); 124 | try { 125 | mProcess = pb.start(); 126 | 127 | mProcess.getOutputStream().close(); 128 | InputStream in = mProcess.getInputStream(); 129 | BufferedReader br = new BufferedReader(new InputStreamReader(in)); 130 | 131 | while (true) { 132 | String logline = br.readLine(); 133 | if (logline == null) 134 | return; 135 | 136 | if (logline.startsWith(DUMP_PATH_STRING)) 137 | mDumpPath = logline.substring(DUMP_PATH_STRING.length()); 138 | 139 | if (logline.startsWith(BROKEN_PIE_SUPPORT) || logline.contains(BROKEN_PIE_SUPPORT2)) 140 | mBrokenPie = true; 141 | 142 | 143 | Pattern p = Pattern.compile("(\\d+).(\\d+) ([0-9a-f])+ (.*)"); 144 | Matcher m = p.matcher(logline); 145 | if (m.matches()) { 146 | int flags = Integer.parseInt(m.group(3), 16); 147 | String msg = m.group(4); 148 | int logLevel = flags & 0x0F; 149 | 150 | VpnStatus.LogLevel logStatus = VpnStatus.LogLevel.INFO; 151 | 152 | if ((flags & M_FATAL) != 0) 153 | logStatus = VpnStatus.LogLevel.ERROR; 154 | else if ((flags & M_NONFATAL) != 0) 155 | logStatus = VpnStatus.LogLevel.WARNING; 156 | else if ((flags & M_WARN) != 0) 157 | logStatus = VpnStatus.LogLevel.WARNING; 158 | else if ((flags & M_DEBUG) != 0) 159 | logStatus = VpnStatus.LogLevel.VERBOSE; 160 | 161 | if (msg.startsWith("MANAGEMENT: CMD")) 162 | logLevel = Math.max(4, logLevel); 163 | 164 | 165 | VpnStatus.logMessageOpenVPN(logStatus, logLevel, msg); 166 | } else { 167 | VpnStatus.logInfo("P:" + logline); 168 | } 169 | } 170 | 171 | 172 | } catch (IOException e) { 173 | VpnStatus.logException("Error reading from output of OpenVPN process", e); 174 | stopProcess(); 175 | } 176 | 177 | 178 | } 179 | 180 | private String genLibraryPath(String[] argv, ProcessBuilder pb) { 181 | 182 | String applibpath = argv[0].replaceFirst("/cache/.*$", "/lib"); 183 | 184 | String lbpath = pb.environment().get("LD_LIBRARY_PATH"); 185 | if (lbpath == null) 186 | lbpath = applibpath; 187 | else 188 | lbpath = applibpath + ":" + lbpath; 189 | 190 | if (!applibpath.equals(mNativeDir)) { 191 | lbpath = mNativeDir + ":" + lbpath; 192 | } 193 | return lbpath; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java: -------------------------------------------------------------------------------- 1 | package de.blinkt.openvpn.core; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | import android.content.SharedPreferences.Editor; 7 | import android.preference.PreferenceManager; 8 | 9 | import java.io.IOException; 10 | import java.io.ObjectInputStream; 11 | import java.io.ObjectOutputStream; 12 | import java.util.Collection; 13 | import java.util.HashMap; 14 | import java.util.HashSet; 15 | import java.util.Set; 16 | 17 | import de.blinkt.openvpn.VpnProfile; 18 | 19 | public class ProfileManager { 20 | private static final String PREFS_NAME = "VPNList"; 21 | 22 | private static final String LAST_CONNECTED_PROFILE = "lastConnectedProfile"; 23 | private static ProfileManager instance; 24 | 25 | private static VpnProfile mLastConnectedVpn = null; 26 | private static VpnProfile tmpprofile = null; 27 | private HashMap profiles = new HashMap<>(); 28 | 29 | private ProfileManager() { 30 | } 31 | 32 | private static VpnProfile get(String key) { 33 | if (tmpprofile != null && tmpprofile.getUUIDString().equals(key)) 34 | return tmpprofile; 35 | 36 | if (instance == null) 37 | return null; 38 | return instance.profiles.get(key); 39 | 40 | } 41 | 42 | private static void checkInstance(Context context) { 43 | if (instance == null) { 44 | instance = new ProfileManager(); 45 | instance.loadVPNList(context); 46 | } 47 | } 48 | 49 | synchronized public static ProfileManager getInstance(Context context) { 50 | checkInstance(context); 51 | return instance; 52 | } 53 | 54 | public static void setConntectedVpnProfileDisconnected(Context c) { 55 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); 56 | Editor prefsedit = prefs.edit(); 57 | prefsedit.putString(LAST_CONNECTED_PROFILE, null); 58 | prefsedit.apply(); 59 | 60 | } 61 | 62 | 63 | public static void setConnectedVpnProfile(Context c, VpnProfile connectedProfile) { 64 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); 65 | Editor prefsedit = prefs.edit(); 66 | 67 | prefsedit.putString(LAST_CONNECTED_PROFILE, connectedProfile.getUUIDString()); 68 | prefsedit.apply(); 69 | mLastConnectedVpn = connectedProfile; 70 | 71 | } 72 | 73 | 74 | public static VpnProfile getLastConnectedProfile(Context c) { 75 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); 76 | 77 | String lastConnectedProfile = prefs.getString(LAST_CONNECTED_PROFILE, null); 78 | if (lastConnectedProfile != null) 79 | return get(c, lastConnectedProfile); 80 | else 81 | return null; 82 | } 83 | 84 | public static void setTemporaryProfile(VpnProfile tmp) { 85 | ProfileManager.tmpprofile = tmp; 86 | } 87 | 88 | public static boolean isTempProfile() { 89 | return mLastConnectedVpn == tmpprofile; 90 | } 91 | 92 | public static VpnProfile get(Context context, String profileUUID) { 93 | checkInstance(context); 94 | return get(profileUUID); 95 | } 96 | 97 | public static VpnProfile getLastConnectedVpn() { 98 | return mLastConnectedVpn; 99 | } 100 | 101 | public static VpnProfile getAlwaysOnVPN(Context context) { 102 | checkInstance(context); 103 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 104 | 105 | String uuid = prefs.getString("alwaysOnVpn", null); 106 | return get(uuid); 107 | 108 | } 109 | 110 | public Collection getProfiles() { 111 | return profiles.values(); 112 | } 113 | 114 | public VpnProfile getProfileByName(String name) { 115 | for (VpnProfile vpnp : profiles.values()) { 116 | if (vpnp.getName().equals(name)) { 117 | return vpnp; 118 | } 119 | } 120 | return null; 121 | } 122 | 123 | public void saveProfileList(Context context) { 124 | SharedPreferences sharedprefs = context.getSharedPreferences(PREFS_NAME, Activity.MODE_PRIVATE); 125 | Editor editor = sharedprefs.edit(); 126 | editor.putStringSet("vpnlist", profiles.keySet()); 127 | 128 | 129 | int counter = sharedprefs.getInt("counter", 0); 130 | editor.putInt("counter", counter + 1); 131 | editor.apply(); 132 | 133 | } 134 | 135 | public void addProfile(VpnProfile profile) { 136 | profiles.put(profile.getUUID().toString(), profile); 137 | 138 | } 139 | 140 | public void saveProfile(Context context, VpnProfile profile) { 141 | ObjectOutputStream vpnfile; 142 | try { 143 | vpnfile = new ObjectOutputStream(context.openFileOutput((profile.getUUID().toString() + ".vp"), Activity.MODE_PRIVATE)); 144 | 145 | vpnfile.writeObject(profile); 146 | vpnfile.flush(); 147 | vpnfile.close(); 148 | } catch (IOException e) { 149 | VpnStatus.logException("saving VPN profile", e); 150 | throw new RuntimeException(e); 151 | } 152 | } 153 | 154 | private void loadVPNList(Context context) { 155 | profiles = new HashMap<>(); 156 | SharedPreferences listpref = context.getSharedPreferences(PREFS_NAME, Activity.MODE_PRIVATE); 157 | Set vlist = listpref.getStringSet("vpnlist", null); 158 | if (vlist == null) { 159 | vlist = new HashSet<>(); 160 | } 161 | 162 | for (String vpnentry : vlist) { 163 | try { 164 | ObjectInputStream vpnfile = new ObjectInputStream(context.openFileInput(vpnentry + ".vp")); 165 | VpnProfile vp = ((VpnProfile) vpnfile.readObject()); 166 | 167 | 168 | if (vp == null || vp.mName == null || vp.getUUID() == null) 169 | continue; 170 | 171 | vp.upgradeProfile(); 172 | profiles.put(vp.getUUID().toString(), vp); 173 | 174 | } catch (IOException | ClassNotFoundException e) { 175 | VpnStatus.logException("Loading VPN List", e); 176 | } 177 | } 178 | } 179 | 180 | public void removeProfile(Context context, VpnProfile profile) { 181 | String vpnentry = profile.getUUID().toString(); 182 | profiles.remove(vpnentry); 183 | saveProfileList(context); 184 | context.deleteFile(vpnentry + ".vp"); 185 | if (mLastConnectedVpn == profile) 186 | mLastConnectedVpn = null; 187 | 188 | } 189 | } -------------------------------------------------------------------------------- /app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java: -------------------------------------------------------------------------------- 1 | package de.blinkt.openvpn.core; 2 | 3 | import com.frogobox.viprox.R; 4 | 5 | import java.net.InetSocketAddress; 6 | import java.net.MalformedURLException; 7 | import java.net.Proxy; 8 | import java.net.ProxySelector; 9 | import java.net.SocketAddress; 10 | import java.net.URISyntaxException; 11 | import java.net.URL; 12 | import java.util.List; 13 | 14 | import de.blinkt.openvpn.VpnProfile; 15 | 16 | public class ProxyDetection { 17 | static SocketAddress detectProxy(VpnProfile vp) { 18 | try { 19 | URL url = new URL(String.format("https://%s:%s", vp.mServerName, vp.mServerPort)); 20 | Proxy proxy = getFirstProxy(url); 21 | 22 | if (proxy == null) 23 | return null; 24 | SocketAddress addr = proxy.address(); 25 | if (addr instanceof InetSocketAddress) { 26 | return addr; 27 | } 28 | 29 | } catch (MalformedURLException e) { 30 | VpnStatus.logError(R.string.getproxy_error, e.getLocalizedMessage()); 31 | } catch (URISyntaxException e) { 32 | VpnStatus.logError(R.string.getproxy_error, e.getLocalizedMessage()); 33 | } 34 | return null; 35 | } 36 | 37 | static Proxy getFirstProxy(URL url) throws URISyntaxException { 38 | System.setProperty("java.net.useSystemProxies", "true"); 39 | List proxylist = ProxySelector.getDefault().select(url.toURI()); 40 | if (proxylist != null) { 41 | for (Proxy proxy : proxylist) { 42 | SocketAddress addr = proxy.address(); 43 | 44 | if (addr != null) { 45 | return proxy; 46 | } 47 | } 48 | 49 | } 50 | return null; 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java: -------------------------------------------------------------------------------- 1 | package de.blinkt.openvpn.core; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Build; 7 | 8 | import com.frogobox.viprox.R; 9 | 10 | import java.io.File; 11 | import java.io.FileOutputStream; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.util.Arrays; 15 | import java.util.Vector; 16 | 17 | import de.blinkt.openvpn.VpnProfile; 18 | 19 | public class VPNLaunchHelper { 20 | private static final String MININONPIEVPN = "nopie_openvpn"; 21 | private static final String MINIPIEVPN = "pie_openvpn"; 22 | private static final String OVPNCONFIGFILE = "android.conf"; 23 | 24 | private static String writeMiniVPN(Context context) { 25 | String[] abis; 26 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 27 | abis = getSupportedABIsLollipop(); 28 | else 29 | abis = new String[]{Build.CPU_ABI, Build.CPU_ABI2}; 30 | 31 | String nativeAPI = NativeUtils.getNativeAPI(); 32 | if (!nativeAPI.equals(abis[0])) { 33 | VpnStatus.logWarning(R.string.abi_mismatch, Arrays.toString(abis), nativeAPI); 34 | abis = new String[]{nativeAPI}; 35 | } 36 | 37 | for (String abi : abis) { 38 | 39 | File vpnExecutable = new File(context.getCacheDir(), getMiniVPNExecutableName() + "." + abi); 40 | if ((vpnExecutable.exists() && vpnExecutable.canExecute()) || writeMiniVPNBinary(context, abi, vpnExecutable)) { 41 | return vpnExecutable.getPath(); 42 | } 43 | } 44 | 45 | return null; 46 | } 47 | 48 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 49 | private static String[] getSupportedABIsLollipop() { 50 | return Build.SUPPORTED_ABIS; 51 | } 52 | 53 | private static String getMiniVPNExecutableName() { 54 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 55 | return MINIPIEVPN; 56 | else 57 | return MININONPIEVPN; 58 | } 59 | 60 | 61 | public static String[] replacePieWithNoPie(String[] mArgv) { 62 | mArgv[0] = mArgv[0].replace(MINIPIEVPN, MININONPIEVPN); 63 | return mArgv; 64 | } 65 | 66 | 67 | static String[] buildOpenvpnArgv(Context c) { 68 | Vector args = new Vector<>(); 69 | 70 | String binaryName = writeMiniVPN(c); 71 | 72 | 73 | if (binaryName == null) { 74 | VpnStatus.logError("Error writing minivpn binary"); 75 | return null; 76 | } 77 | 78 | args.add(binaryName); 79 | 80 | args.add("--config"); 81 | args.add(getConfigFilePath(c)); 82 | 83 | return args.toArray(new String[args.size()]); 84 | } 85 | 86 | private static boolean writeMiniVPNBinary(Context context, String abi, File mvpnout) { 87 | try { 88 | InputStream mvpn; 89 | 90 | try { 91 | mvpn = context.getAssets().open(getMiniVPNExecutableName() + "." + abi); 92 | } catch (IOException errabi) { 93 | VpnStatus.logInfo("Failed getting assets for archicture " + abi); 94 | return false; 95 | } 96 | 97 | 98 | FileOutputStream fout = new FileOutputStream(mvpnout); 99 | 100 | byte buf[] = new byte[4096]; 101 | 102 | int lenread = mvpn.read(buf); 103 | while (lenread > 0) { 104 | fout.write(buf, 0, lenread); 105 | lenread = mvpn.read(buf); 106 | } 107 | fout.close(); 108 | 109 | if (!mvpnout.setExecutable(true)) { 110 | VpnStatus.logError("Failed to make OpenVPN executable"); 111 | return false; 112 | } 113 | 114 | 115 | return true; 116 | } catch (IOException e) { 117 | VpnStatus.logException(e); 118 | return false; 119 | } 120 | 121 | } 122 | 123 | 124 | public static void startOpenVpn(VpnProfile startprofile, Context context) { 125 | Intent startVPN = startprofile.prepareStartService(context); 126 | if (startVPN != null) 127 | context.startService(startVPN); 128 | 129 | } 130 | 131 | public static String getConfigFilePath(Context context) { 132 | return context.getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE; 133 | } 134 | 135 | } -------------------------------------------------------------------------------- /app/src/main/java/de/blinkt/openvpn/core/X509Utils.java: -------------------------------------------------------------------------------- 1 | package de.blinkt.openvpn.core; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.text.TextUtils; 6 | 7 | import com.frogobox.viprox.R; 8 | import com.frogobox.viprox.util.io.pem.PemObject; 9 | import com.frogobox.viprox.util.io.pem.PemReader; 10 | 11 | import java.io.ByteArrayInputStream; 12 | import java.io.File; 13 | import java.io.FileInputStream; 14 | import java.io.FileNotFoundException; 15 | import java.io.FileReader; 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.io.Reader; 19 | import java.io.StringReader; 20 | import java.lang.reflect.InvocationTargetException; 21 | import java.lang.reflect.Method; 22 | import java.security.cert.Certificate; 23 | import java.security.cert.CertificateException; 24 | import java.security.cert.CertificateExpiredException; 25 | import java.security.cert.CertificateFactory; 26 | import java.security.cert.CertificateNotYetValidException; 27 | import java.security.cert.X509Certificate; 28 | import java.util.Date; 29 | import java.util.Hashtable; 30 | import java.util.Vector; 31 | 32 | import javax.security.auth.x500.X500Principal; 33 | 34 | import de.blinkt.openvpn.VpnProfile; 35 | 36 | public class X509Utils { 37 | public static Certificate[] getCertificatesFromFile(String certfilename) throws FileNotFoundException, CertificateException { 38 | CertificateFactory certFact = CertificateFactory.getInstance("X.509"); 39 | 40 | Vector certificates = new Vector<>(); 41 | if (VpnProfile.isEmbedded(certfilename)) { 42 | int subIndex = certfilename.indexOf("-----BEGIN CERTIFICATE-----"); 43 | do { 44 | subIndex = Math.max(0, subIndex); 45 | InputStream inStream = new ByteArrayInputStream(certfilename.substring(subIndex).getBytes()); 46 | certificates.add(certFact.generateCertificate(inStream)); 47 | 48 | subIndex = certfilename.indexOf("-----BEGIN CERTIFICATE-----", subIndex + 1); 49 | } while (subIndex > 0); 50 | return certificates.toArray(new Certificate[certificates.size()]); 51 | } else { 52 | InputStream inStream = new FileInputStream(certfilename); 53 | return new Certificate[]{certFact.generateCertificate(inStream)}; 54 | } 55 | 56 | 57 | } 58 | 59 | public static PemObject readPemObjectFromFile(String keyfilename) throws IOException { 60 | 61 | Reader inStream; 62 | 63 | if (VpnProfile.isEmbedded(keyfilename)) 64 | inStream = new StringReader(VpnProfile.getEmbeddedContent(keyfilename)); 65 | else 66 | inStream = new FileReader(new File(keyfilename)); 67 | 68 | PemReader pr = new PemReader(inStream); 69 | PemObject r = pr.readPemObject(); 70 | pr.close(); 71 | return r; 72 | } 73 | 74 | 75 | public static String getCertificateFriendlyName(Context c, String filename) { 76 | if (!TextUtils.isEmpty(filename)) { 77 | try { 78 | X509Certificate cert = (X509Certificate) getCertificatesFromFile(filename)[0]; 79 | String friendlycn = getCertificateFriendlyName(cert); 80 | friendlycn = getCertificateValidityString(cert, c.getResources()) + friendlycn; 81 | return friendlycn; 82 | 83 | } catch (Exception e) { 84 | VpnStatus.logError("Could not read certificate" + e.getLocalizedMessage()); 85 | } 86 | } 87 | return c.getString(R.string.cannotparsecert); 88 | } 89 | 90 | public static String getCertificateValidityString(X509Certificate cert, Resources res) { 91 | try { 92 | cert.checkValidity(); 93 | } catch (CertificateExpiredException ce) { 94 | return "EXPIRED: "; 95 | } catch (CertificateNotYetValidException cny) { 96 | return "NOT YET VALID: "; 97 | } 98 | 99 | Date certNotAfter = cert.getNotAfter(); 100 | Date now = new Date(); 101 | long timeLeft = certNotAfter.getTime() - now.getTime(); 102 | 103 | 104 | if (timeLeft > 90l * 24 * 3600 * 1000) { 105 | long months = getMonthsDifference(now, certNotAfter); 106 | return res.getString(R.string.months_left, months); 107 | } else if (timeLeft > 72 * 3600 * 1000) { 108 | long days = timeLeft / (24 * 3600 * 1000); 109 | return res.getString(R.string.days_left, days); 110 | } else { 111 | long hours = timeLeft / (3600 * 1000); 112 | 113 | return res.getString(R.string.hours_left, hours); 114 | } 115 | } 116 | 117 | public static int getMonthsDifference(Date date1, Date date2) { 118 | int m1 = date1.getYear() * 12 + date1.getMonth(); 119 | int m2 = date2.getYear() * 12 + date2.getMonth(); 120 | return m2 - m1 + 1; 121 | } 122 | 123 | public static String getCertificateFriendlyName(X509Certificate cert) { 124 | X500Principal principal = cert.getSubjectX500Principal(); 125 | byte[] encodedSubject = principal.getEncoded(); 126 | String friendlyName = null; 127 | 128 | 129 | Exception exp = null; 130 | try { 131 | Class X509NameClass = Class.forName("com.android.org.bouncycastle.asn1.x509.X509Name"); 132 | Method getInstance = X509NameClass.getMethod("getInstance", Object.class); 133 | 134 | Hashtable defaultSymbols = (Hashtable) X509NameClass.getField("DefaultSymbols").get(X509NameClass); 135 | 136 | if (!defaultSymbols.containsKey("1.2.840.113549.1.9.1")) 137 | defaultSymbols.put("1.2.840.113549.1.9.1", "eMail"); 138 | 139 | Object subjectName = getInstance.invoke(X509NameClass, encodedSubject); 140 | 141 | Method toString = X509NameClass.getMethod("toString", boolean.class, Hashtable.class); 142 | 143 | friendlyName = (String) toString.invoke(subjectName, true, defaultSymbols); 144 | 145 | } catch (ClassNotFoundException e) { 146 | exp = e; 147 | } catch (NoSuchMethodException e) { 148 | exp = e; 149 | } catch (InvocationTargetException e) { 150 | exp = e; 151 | } catch (IllegalAccessException e) { 152 | exp = e; 153 | } catch (NoSuchFieldException e) { 154 | exp = e; 155 | } 156 | if (exp != null) 157 | VpnStatus.logException("Getting X509 Name from certificate", exp); 158 | 159 | 160 | if (friendlyName == null) 161 | friendlyName = principal.getName(); 162 | 163 | 164 | String[] parts = friendlyName.split(","); 165 | for (int i = 0; i < parts.length; i++) { 166 | String part = parts[i]; 167 | if (part.startsWith("1.2.840.113549.1.9.1=#16")) { 168 | parts[i] = "email=" + ia5decode(part.replace("1.2.840.113549.1.9.1=#16", "")); 169 | } 170 | } 171 | friendlyName = TextUtils.join(",", parts); 172 | return friendlyName; 173 | } 174 | 175 | public static boolean isPrintableChar(char c) { 176 | Character.UnicodeBlock block = Character.UnicodeBlock.of(c); 177 | return (!Character.isISOControl(c)) && 178 | block != null && 179 | block != Character.UnicodeBlock.SPECIALS; 180 | } 181 | 182 | private static String ia5decode(String ia5string) { 183 | String d = ""; 184 | for (int i = 1; i < ia5string.length(); i = i + 2) { 185 | String hexstr = ia5string.substring(i - 1, i + 1); 186 | char c = (char) Integer.parseInt(hexstr, 16); 187 | if (isPrintableChar(c)) { 188 | d += c; 189 | } else if (i == 1 && (c == 0x12 || c == 0x1b)) { 190 | ; 191 | } else { 192 | d += "\\x" + hexstr; 193 | } 194 | } 195 | return d; 196 | } 197 | } -------------------------------------------------------------------------------- /app/src/main/jniLibs/arm64-v8a/libjbcrypto.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/arm64-v8a/libjbcrypto.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/arm64-v8a/libopenvpn.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/arm64-v8a/libopenvpn.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/arm64-v8a/libopvpnutil.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/arm64-v8a/libopvpnutil.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi-v7a/libjbcrypto.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/armeabi-v7a/libjbcrypto.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi-v7a/libopenvpn.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/armeabi-v7a/libopenvpn.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi-v7a/libopvpnutil.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/armeabi-v7a/libopvpnutil.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi/libjbcrypto.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/armeabi/libjbcrypto.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi/libopenvpn.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/armeabi/libopenvpn.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi/libopvpnutil.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/armeabi/libopvpnutil.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/mips/libjbcrypto.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/mips/libjbcrypto.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/mips/libopenvpn.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/mips/libopenvpn.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/mips/libopvpnutil.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/mips/libopvpnutil.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/x86/libjbcrypto.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/x86/libjbcrypto.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/x86/libopenvpn.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/x86/libopenvpn.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/x86/libopvpnutil.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/x86/libopvpnutil.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/x86_64/libjbcrypto.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/x86_64/libjbcrypto.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/x86_64/libopenvpn.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/x86_64/libopenvpn.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/x86_64/libopvpnutil.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/jniLibs/x86_64/libopvpnutil.so -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_button_connected.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_button_no_connected.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_card_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_card_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_card_white.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_btn_connect_vpn.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_connect_good.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_dummy_flag.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_frogobox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/res/drawable/ic_frogobox.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbar_about_us.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbar_back_home.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbar_back_home_white.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toolbar_location.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/info_servers_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/font/montserrat_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/app/src/main/res/font/montserrat_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about_us.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 19 | 20 | 29 | 30 | 31 | 41 | 42 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_country.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 19 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 13 | 14 | 24 | 25 | 35 | 36 | 50 | 51 | 61 | 62 | 65 | 66 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 29 | 30 | 39 | 40 | 45 | 46 | 56 | 57 | 64 | 65 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_vpnlist.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ads_banner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/toolbar_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_item_country.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 35 | 36 | 41 | 42 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_item_server.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 22 | 23 | 32 | 33 | 41 | 42 | 48 | 49 | 55 | 56 | 57 | 58 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_toolbar_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 22 | 27 | 28 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #00695c 4 | #003d33 5 | #439889 6 | 7 | 8 | #00000000 9 | #2499e0 10 | #F22938 11 | #FF4081 12 | #FFFFFF 13 | #000000 14 | #24D149 15 | #808080 16 | #27282D 17 | #3DB39E 18 | #272A2D 19 | #FF9800 20 | #C62828 21 | 22 | #E9E7E7 23 | @color/colorBaseBlack 24 | 25 | @color/colorBackgroundWhite 26 | 27 | @color/colorBackgroundWhite 28 | @color/colorPrimary 29 | @color/tab_background 30 | 31 | @color/colorBaseRed 32 | @color/colorBaseWhite 33 | @color/colorBaseBlack 34 | @color/colorBaseGreen 35 | @color/colorBaseDarkGrey 36 | @color/colorBaseDarkBlackBlue 37 | 38 | @color/colorBaseWhite 39 | @color/colorBaseRed 40 | @color/colorBaseBlack 41 | @color/colorBaseGreen 42 | @color/colorBaseDarkGrey 43 | @color/colorBaseDarkBlackBlue 44 | @color/colorBaseSeaBlue 45 | @color/colorBaseWhite 46 | 47 | @color/colorBaseGreenTosca 48 | 49 | #109D59 50 | #10CAA1 51 | @android:color/white 52 | #DE000000 53 | #8A000000 54 | #e3e3e3 55 | #cccccc 56 | #EEEEEE 57 | @color/grey_ee 58 | #f0a734 59 | #e93d25 60 | #17d478 61 | #f7fbfb 62 | #747d87 63 | #3af3ff 64 | #242e3a 65 | #00000000 66 | 67 | #d1395c 68 | #14a895 69 | #2278d4 70 | #a854d4 71 | 72 | 73 | #f98da5 74 | #8cf9eb 75 | #93c6fd 76 | #e4b5fc 77 | 78 | 79 | @color/dot_light_screen1 80 | @color/dot_light_screen2 81 | @color/dot_light_screen3 82 | @color/dot_light_screen4 83 | 84 | 85 | 86 | @color/dot_dark_screen1 87 | @color/dot_dark_screen2 88 | @color/dot_dark_screen3 89 | @color/dot_dark_screen4 90 | 91 | 92 | #ffffff 93 | #356690 94 | 95 | #1c3f60 96 | #7f7f7f 97 | #71bad2 98 | #ffffff 99 | #ffffff 100 | 101 | #cbecfd 102 | #356690 103 | 104 | #cbecfd 105 | #356690 106 | 107 | #ffffff 108 | 109 | #1c3f61 110 | 111 | #A7A7A7 112 | 113 | #cbecfd 114 | #f1dd77 115 | 116 | #cbecfd 117 | #356690 118 | 119 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 30dp 4 | 20dp 5 | 120dp 6 | 30dp 7 | 16dp 8 | 40dp 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16dp 16 | 16dp 17 | 8dp 18 | 176dp 19 | 16dp 20 | 60dp 21 | 22 | 12dp 23 | 64dp 24 | 32dp 25 | 24dp 26 | 16dp 27 | 8dp 28 | 4dp 29 | 30 | 180dp 31 | 32 | 33 | 1dp 34 | 0dp 35 | 36 | 50dp 37 | 220dp 38 | 125dp 39 | 60dp 40 | 50dp 41 | 5dip 42 | 43 | 44 | 24sp 45 | 24sp 46 | 20sp 47 | 18sp 48 | 16sp 49 | 12sp 50 | 10sp 51 | 52 | 95dp 53 | 200dp 54 | 150dp 55 | 2dp 56 | 20dp 57 | 100dp 58 | 72dp 59 | 96dp 60 | 360dp 61 | 120dp 62 | 300dp 63 | 200dp 64 | 132dp 65 | 66 | 144dp 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /app/src/main/res/values/reference.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @android:drawable/ic_menu_close_clear_cancel 4 | @android:drawable/ic_media_play 5 | @android:drawable/ic_media_pause 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 25 | 26 | 33 | 34 | 37 | 38 | 49 | 50 | 59 | 60 | 67 | 68 | 77 | 78 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 15 | 16 | 18 | 23 | 24 | 27 | 32 | 36 | 37 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | ext { 6 | // project settings 7 | nameApp = "Viprox VPN" 8 | nameAPK = nameApp.toLowerCase().replace(" ", "-") 9 | 10 | def appDomain = "com" 11 | def appDevConsole = "frogobox" 12 | def appName = "viprox" 13 | 14 | def versionMajor = 1 15 | def versionMinor = 0 16 | def versionPatch = 1 17 | 18 | projectCompileSdk = 31 19 | projectMinSdk = 21 20 | projectTargetSdk = projectCompileSdk 21 | 22 | projectApplicationId = "$appDomain.$appDevConsole.$appName" 23 | projectVersionCode = (versionMajor * 100) + (versionMinor * 10) + (versionPatch * 1) 24 | projectVersionName = "$versionMajor.$versionMinor.$versionPatch" 25 | 26 | // dependencies version 27 | kotlin_version = '1.5.21' 28 | koin_version = "3.1.1" 29 | room_version = "2.3.0" 30 | 31 | compose_version = "1.0.1" 32 | activity_ktx_version = "1.3.1" 33 | fragment_ktx_version = '1.3.6' 34 | } 35 | 36 | repositories { 37 | google() 38 | jcenter() 39 | mavenCentral() 40 | } 41 | 42 | dependencies { 43 | classpath 'com.android.tools.build:gradle:7.0.2' 44 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 45 | 46 | // NOTE: Do not place your application dependencies here; they belong 47 | // in the individual module build.gradle files 48 | } 49 | } 50 | 51 | allprojects { 52 | repositories { 53 | google() 54 | jcenter() 55 | mavenCentral() 56 | maven { url "https://jitpack.io" } 57 | } 58 | } 59 | 60 | task clean(type: Delete) { 61 | delete rootProject.buildDir 62 | } 63 | -------------------------------------------------------------------------------- /docs/image/ss_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/docs/image/ss_1.png -------------------------------------------------------------------------------- /docs/image/ss_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/docs/image/ss_2.png -------------------------------------------------------------------------------- /docs/image/ss_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/docs/image/ss_3.png -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | android.enableJetifier=true 20 | android.useAndroidX=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirisback/vpn/cf6770030bc5d3e289a75e739f2e41a1a1d9e654/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Oct 27 21:02:38 ICT 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | before_install: 4 | - chmod +x gradlew 5 | 6 | script: ./gradlew 7 | 8 | android: 9 | components: 10 | # Uncomment the lines below if you want to 11 | # use the latest revision of Android SDK Tools 12 | - platform-tools 13 | - tools 14 | 15 | # The BuildTools version used by your project 16 | - build-tools-28.0.3 17 | 18 | # The SDK version used to compile your project 19 | - android-28 20 | --------------------------------------------------------------------------------