From 71bd7e58b42743aa6d9ea692a7e314ccafe086e6 Mon Sep 17 00:00:00 2001 From: Jonatan Rhodin Date: Mon, 4 Sep 2023 12:52:39 +0200 Subject: [PATCH] Add http client module to use for prototyping --- android/lib/http-client/build.gradle.kts | 34 +++++++ .../http-client/src/main/AndroidManifest.xml | 4 + .../mullvadvpn/lib/http/MullvadHttpClient.kt | 94 +++++++++++++++++++ android/settings.gradle.kts | 3 +- 4 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 android/lib/http-client/build.gradle.kts create mode 100644 android/lib/http-client/src/main/AndroidManifest.xml create mode 100644 android/lib/http-client/src/main/java/net/mullvad/mullvadvpn/lib/http/MullvadHttpClient.kt diff --git a/android/lib/http-client/build.gradle.kts b/android/lib/http-client/build.gradle.kts new file mode 100644 index 000000000000..3f1f9e66b275 --- /dev/null +++ b/android/lib/http-client/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id(Dependencies.Plugin.androidLibraryId) + id(Dependencies.Plugin.kotlinAndroidId) +} + +android { + namespace = "net.mullvad.mullvadvpn.lib.http" + compileSdk = Versions.Android.compileSdkVersion + + defaultConfig { + minSdk = Versions.Android.minSdkVersion + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = Versions.jvmTarget + } + + lint { + lintConfig = file("${rootProject.projectDir}/config/lint.xml") + abortOnError = true + warningsAsErrors = true + } +} + +dependencies { + implementation(Dependencies.Kotlin.stdlib) + implementation(Dependencies.KotlinX.coroutinesAndroid) + implementation(Dependencies.androidVolley) +} diff --git a/android/lib/http-client/src/main/AndroidManifest.xml b/android/lib/http-client/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..8bdb7e14b389 --- /dev/null +++ b/android/lib/http-client/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/android/lib/http-client/src/main/java/net/mullvad/mullvadvpn/lib/http/MullvadHttpClient.kt b/android/lib/http-client/src/main/java/net/mullvad/mullvadvpn/lib/http/MullvadHttpClient.kt new file mode 100644 index 000000000000..4257ff2a6720 --- /dev/null +++ b/android/lib/http-client/src/main/java/net/mullvad/mullvadvpn/lib/http/MullvadHttpClient.kt @@ -0,0 +1,94 @@ +package net.mullvad.mullvadvpn.lib.http + +import android.content.Context +import android.util.Log +import com.android.volley.Request +import com.android.volley.toolbox.JsonObjectRequest +import com.android.volley.toolbox.RequestFuture +import com.android.volley.toolbox.Volley +import org.json.JSONObject + +/** + * This class should primarily be used to make calls to the api during the early stages of + * implementing a new endpoint. These calls should then be migrated to the daemon and this class + * should not be used outside of this narrow scope. + */ +class MullvadHttpClient(context: Context) { + private val queue = Volley.newRequestQueue(context) + + fun newPurchase(accountToken: String): String? { + val authToken = login(accountToken) + val response = + sendSimpleSynchronousRequest( + method = Request.Method.POST, + url = NEW_PURCHASE_URL, + token = authToken + ) + return response?.getString("obfuscated_external_account_id") + } + + fun acknowledgePurchase( + accountToken: String, + productId: String, + purchaseToken: String + ): Boolean { + val authToken = login(accountToken) + val response = + sendSimpleSynchronousRequest( + method = Request.Method.POST, + url = ACKNOWLEDGE_PURCHASE_URL, + token = authToken, + body = + JSONObject().apply { + put("product_id", productId) + put("token", purchaseToken) + } + ) + return response != null + } + + private fun login(accountToken: String): String { + val json = JSONObject().apply { put("account_number", accountToken) } + return sendSimpleSynchronousRequest(Request.Method.POST, AUTH_URL, json) + ?.getString("access_token") + ?: "" + } + + private fun sendSimpleSynchronousRequest( + method: Int, + url: String, + body: JSONObject? = null, + token: String? = null + ): JSONObject? { + val future = RequestFuture.newFuture() + val request = + object : JsonObjectRequest(method, url, body, future, future) { + override fun getHeaders(): MutableMap { + val headers = HashMap() + if (body != null) { + headers.put("Content-Type", "application/json") + } + if (token != null) { + headers.put("Authorization", "Bearer $token") + } + return headers + } + } + queue.add(request) + return try { + future.get() + } catch (e: Exception) { + Log.e("Error", "Could not login", e) + null + } + } + + companion object { + private const val API_BASE_URL = "https://api.mullvad.net" + private const val API_VERSION = "v1" + private const val AUTH_URL = "$API_BASE_URL/auth/$API_VERSION/token" + private const val NEW_PURCHASE_URL = "$API_BASE_URL/payments/google-play/new" + private const val ACKNOWLEDGE_PURCHASE_URL = + "$API_BASE_URL/payments/google-play/acknowledge" + } +} diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index 5df9c08f1e07..ce2e4890dfd6 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -8,7 +8,8 @@ include( ":lib:resource", ":lib:talpid", ":lib:theme", - ":lib:common-test" + ":lib:common-test", + ":lib:http-client" ) include(":test", ":test:common", ":test:e2e", ":test:mockapi")