Skip to content

Commit a22f352

Browse files
committed
Making the server URL configurable NdoleStudio#23
1 parent 9374001 commit a22f352

12 files changed

Lines changed: 140 additions & 35 deletions

File tree

android/app/src/main/java/com/httpsms/DeliveredReceiver.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ internal class DeliveredReceiver : BroadcastReceiver() {
2424
}
2525
Thread {
2626
Timber.i("delivered message with ID [${messageId}]")
27-
HttpSmsApiService(Settings.getApiKeyOrDefault(context)).sendDeliveredEvent(messageId!!, timestamp)
27+
HttpSmsApiService.create(context).sendDeliveredEvent(messageId!!, timestamp)
2828
}.start()
2929
}
3030

@@ -36,7 +36,7 @@ internal class DeliveredReceiver : BroadcastReceiver() {
3636

3737
Thread {
3838
Timber.i("message with ID [${messageId}] not delivered")
39-
HttpSmsApiService(Settings.getApiKeyOrDefault(context)).sendFailedEvent(messageId!!,timestamp, "NOT_DELIVERED")
39+
HttpSmsApiService.create(context).sendFailedEvent(messageId!!,timestamp, "NOT_DELIVERED")
4040
}.start()
4141
}
4242
}

android/app/src/main/java/com/httpsms/FirebaseMessagingService.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
6767

6868
if (Settings.isLoggedIn(this)) {
6969
Timber.d("updating phone with new fcm token")
70-
HttpSmsApiService(Settings.getApiKeyOrDefault(this)).updatePhone(Settings.getOwnerOrDefault(this), token)
70+
HttpSmsApiService.create(this).updatePhone(Settings.getOwnerOrDefault(this), token)
7171
}
7272

7373
}
@@ -129,13 +129,13 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
129129

130130
private fun handleFailed(context: Context, messageID: String) {
131131
Timber.d("sending failed event for message with ID [${messageID}]")
132-
HttpSmsApiService(Settings.getApiKeyOrDefault(context))
132+
HttpSmsApiService.create(context)
133133
.sendFailedEvent(messageID, ZonedDateTime.now(ZoneOffset.UTC), "MOBILE_APP_INACTIVE")
134134
}
135135

136136
private fun getMessage(context: Context, messageID: String): Message? {
137137
Timber.d("fetching message with ID [${messageID}]")
138-
val message = HttpSmsApiService(Settings.getApiKeyOrDefault(context)).getOutstandingMessage(messageID)
138+
val message = HttpSmsApiService.create(context).getOutstandingMessage(messageID)
139139

140140
if (message != null) {
141141
Timber.d("fetched message with ID [${message.id}]")

android/app/src/main/java/com/httpsms/HttpSmsApiService.kt

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.httpsms
22

3+
import android.content.Context
34
import okhttp3.MediaType.Companion.toMediaType
45
import okhttp3.OkHttpClient
56
import okhttp3.Request
@@ -13,17 +14,24 @@ import java.util.logging.Level
1314
import java.util.logging.Logger.getLogger
1415

1516

16-
class HttpSmsApiService(private val apiKey: String) {
17+
class HttpSmsApiService(private val apiKey: String, private val baseURL: URI) {
1718
private val apiKeyHeader = "x-api-key"
18-
//private val baseURL = URI("https://a276-145-14-19-43.ngrok.io/")
19-
private val baseURL = URI("https://api.httpsms.com")
2019
private val jsonMediaType = "application/json; charset=utf-8".toMediaType()
2120
private val client = OkHttpClient.Builder().retryOnConnectionFailure(true).build()
2221

2322
init {
2423
getLogger(OkHttpClient::class.java.name).level = Level.FINE
2524
}
2625

26+
companion object {
27+
fun create(context: Context): HttpSmsApiService {
28+
return HttpSmsApiService(
29+
Settings.getApiKeyOrDefault(context),
30+
Settings.getServerUrlOrDefault(context)
31+
)
32+
}
33+
}
34+
2735
fun getOutstandingMessage(messageID: String): Message? {
2836
val request: Request = Request.Builder()
2937
.url(baseURL.resolve("/v1/messages/outstanding?message_id=${messageID}").toURL())
@@ -174,21 +182,25 @@ class HttpSmsApiService(private val apiKey: String) {
174182
}
175183

176184

177-
fun validateApiKey(): String? {
185+
fun validateApiKey(): Pair<String?, String?> {
178186
val request: Request = Request.Builder()
179187
.url(baseURL.resolve("/v1/users/me").toURL())
180188
.header(apiKeyHeader, apiKey)
181189
.get()
182190
.build()
183191

184-
val response = client.newCall(request).execute()
185-
if (!response.isSuccessful) {
186-
Timber.e("error response [${response.body?.string()}] with code [${response.code}] while verifying apiKey [$apiKey]")
187-
return "Cannot validate the API key. Check if it is correct and try again."
188-
}
192+
try {
193+
val response = client.newCall(request).execute()
194+
if (!response.isSuccessful) {
195+
Timber.e("error response [${response.body?.string()}] with code [${response.code}] while verifying apiKey [$apiKey]")
196+
return Pair("Cannot validate the API key. Check if it is correct and try again.", null);
197+
}
189198

190-
response.close()
191-
Timber.i("api key [$apiKey] is valid" )
192-
return null
199+
response.close()
200+
Timber.i("api key [$apiKey] and server url [$baseURL] are valid" )
201+
return Pair(null, null)
202+
} catch (ex: Exception) {
203+
return Pair(null, ex.message)
204+
}
193205
}
194206
}

android/app/src/main/java/com/httpsms/LoginActivity.kt

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import android.os.Bundle
99
import android.telephony.PhoneNumberUtils
1010
import android.telephony.TelephonyManager
1111
import android.view.View
12+
import android.webkit.URLUtil
1213
import androidx.appcompat.app.AppCompatActivity
1314
import androidx.core.app.ActivityCompat
1415
import androidx.lifecycle.MutableLiveData
@@ -17,6 +18,7 @@ import com.google.android.material.progressindicator.LinearProgressIndicator
1718
import com.google.android.material.textfield.TextInputEditText
1819
import com.google.android.material.textfield.TextInputLayout
1920
import timber.log.Timber
21+
import java.net.URI
2022

2123
class LoginActivity : AppCompatActivity() {
2224
override fun onCreate(savedInstanceState: Bundle?) {
@@ -25,6 +27,7 @@ class LoginActivity : AppCompatActivity() {
2527
setContentView(R.layout.activity_login)
2628
registerListeners()
2729
setPhoneNumber()
30+
setServerURL()
2831
}
2932

3033
private fun registerListeners() {
@@ -43,6 +46,12 @@ class LoginActivity : AppCompatActivity() {
4346
Timber.d("phone number [$phoneNumber] set successfully")
4447
}
4548

49+
private fun setServerURL() {
50+
val serverUrlInput = findViewById<TextInputEditText>(R.id.loginServerUrlInput)
51+
serverUrlInput.setText(getString(R.string.default_server_url))
52+
Timber.d("default server url [${serverUrlInput.text.toString()}] set successfully")
53+
}
54+
4655
@SuppressLint("HardwareIds")
4756
private fun getPhoneNumber(context: Context): String? {
4857
val telephonyManager = this.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
@@ -81,6 +90,12 @@ class LoginActivity : AppCompatActivity() {
8190
val apiKey = findViewById<TextInputEditText>(R.id.loginApiKeyTextInput)
8291
apiKey.isEnabled = false
8392

93+
val serverUrlLayout = findViewById<TextInputLayout>(R.id.loginServerUrlLayout)
94+
serverUrlLayout.error = null
95+
96+
val serverUrl = findViewById<TextInputEditText>(R.id.loginServerUrlInput)
97+
serverUrl.isEnabled = false
98+
8499
val phoneNumberLayout = findViewById<TextInputLayout>(R.id.loginPhoneNumberLayout)
85100
phoneNumberLayout.error = null
86101

@@ -89,6 +104,7 @@ class LoginActivity : AppCompatActivity() {
89104

90105
val resetView = fun () {
91106
apiKey.isEnabled = true
107+
serverUrl.isEnabled = true
92108
progressBar.visibility = View.INVISIBLE
93109
phoneNumber.isEnabled = true
94110
loginButton().isEnabled = true
@@ -104,17 +120,38 @@ class LoginActivity : AppCompatActivity() {
104120
return
105121
}
106122

107-
val liveData = MutableLiveData<String?>()
123+
if(!URLUtil.isValidUrl(serverUrl.text.toString().trim())) {
124+
Timber.e("url number [${serverUrl.text.toString()}] is not a valid URL")
125+
resetView()
126+
serverUrlLayout.error = "Server URL [${serverUrl.text.toString()}] is invalid"
127+
return
128+
}
129+
130+
if (!URLUtil.isHttpsUrl(serverUrl.text.toString().trim())) {
131+
Timber.e("url number [${serverUrl.text.toString()}] is not an https URL")
132+
resetView()
133+
serverUrlLayout.error = "Server URL [${serverUrl.text.toString()}] must be HTTPS"
134+
return
135+
}
136+
137+
val liveData = MutableLiveData<Pair<String?, String?>>()
108138
liveData.observe(this) { authResult ->
109139
run {
110140
progressBar.visibility = View.INVISIBLE
111-
if (authResult != null) {
141+
if (authResult.first != null) {
142+
resetView()
143+
apiKeyLayout.error = authResult.first
144+
return@run
145+
}
146+
147+
if (authResult.second != null) {
112148
resetView()
113-
apiKeyLayout.error = authResult
149+
serverUrlLayout.error = authResult.second
114150
return@run
115151
}
116152

117153
Settings.setApiKeyAsync(this, apiKey.text.toString())
154+
Settings.setServerUrlAsync(this, serverUrl.text.toString().trim())
118155

119156
val e164PhoneNumber = PhoneNumberUtils.formatNumberToE164(
120157
phoneNumber.text.toString(),
@@ -128,9 +165,9 @@ class LoginActivity : AppCompatActivity() {
128165
}
129166

130167
Thread {
131-
val error = HttpSmsApiService(apiKey.text.toString()).validateApiKey()
168+
val error = HttpSmsApiService(apiKey.text.toString(), URI(serverUrl.text.toString().trim())).validateApiKey()
132169
liveData.postValue(error)
133-
Timber.i("login successful")
170+
Timber.d("finished validating api URL")
134171
}.start()
135172
}
136173

android/app/src/main/java/com/httpsms/MainActivity.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,7 @@ class MainActivity : AppCompatActivity() {
140140
}
141141

142142
Thread {
143-
val updated = HttpSmsApiService(Settings.getApiKeyOrDefault(context))
144-
.updatePhone(Settings.getOwnerOrDefault(context), Settings.getFcmToken(context) ?: "")
143+
val updated = HttpSmsApiService.create(context).updatePhone(Settings.getOwnerOrDefault(context), Settings.getFcmToken(context) ?: "")
145144
if (updated) {
146145
Settings.setFcmTokenLastUpdateTimestampAsync(context, currentTimeStamp)
147146
Timber.i("fcm token uploaded successfully")
@@ -329,7 +328,7 @@ class MainActivity : AppCompatActivity() {
329328
Thread {
330329
var error: String? = null
331330
try {
332-
HttpSmsApiService(Settings.getApiKeyOrDefault(context)).storeHeartbeat(Settings.getOwnerOrDefault(context))
331+
HttpSmsApiService.create(context).storeHeartbeat(Settings.getOwnerOrDefault(context))
333332
Settings.setHeartbeatTimestampAsync(applicationContext, System.currentTimeMillis())
334333
} catch (exception: Exception) {
335334
Timber.e(exception)

android/app/src/main/java/com/httpsms/ReceivedReceiver.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class ReceivedReceiver: BroadcastReceiver()
4747

4848
Thread {
4949
Timber.i("forwarding received message from [${from}]")
50-
HttpSmsApiService(Settings.getApiKeyOrDefault(context)).receive(from, to, content, timestamp)
50+
HttpSmsApiService.create(context).receive(from, to, content, timestamp)
5151
}.start()
5252
}
5353
}

android/app/src/main/java/com/httpsms/SentReceiver.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ internal class SentReceiver : BroadcastReceiver() {
2929

3030
Thread {
3131
Timber.d("sent message with ID [${messageId}]")
32-
HttpSmsApiService(Settings.getApiKeyOrDefault(context)).sendSentEvent(messageId!!,timestamp)
32+
HttpSmsApiService.create(context).sendSentEvent(messageId!!,timestamp)
3333
}.start()
3434
}
3535

@@ -41,7 +41,7 @@ internal class SentReceiver : BroadcastReceiver() {
4141

4242
Thread {
4343
Timber.i("message with ID [${messageId}] not sent with reason [$reason]")
44-
HttpSmsApiService(Settings.getApiKeyOrDefault(context)).sendFailedEvent(messageId!!, timestamp, reason)
44+
HttpSmsApiService.create(context).sendFailedEvent(messageId!!, timestamp, reason)
4545
}.start()
4646
}
4747
}

android/app/src/main/java/com/httpsms/Settings.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ package com.httpsms
33
import android.content.Context
44
import androidx.preference.PreferenceManager
55
import timber.log.Timber
6+
import java.net.URI
67

78
object Settings {
89
private const val DEFAULT_PHONE_NUMBER = "66836863" // NOT_FOUND :)
910

1011
private const val SETTINGS_OWNER = "SETTINGS_OWNER"
1112
private const val SETTINGS_ACTIVE = "SETTINGS_ACTIVE_STATUS"
1213
private const val SETTINGS_API_KEY = "SETTINGS_API_KEY"
14+
private const val SETTINGS_SERVER_URL = "SETTINGS_SERVER_URL"
1315
private const val SETTINGS_FCM_TOKEN = "SETTINGS_FCM_TOKEN"
1416
private const val SETTINGS_FCM_TOKEN_UPDATE_TIMESTAMP = "SETTINGS_FCM_TOKEN_UPDATE_TIMESTAMP"
1517
private const val SETTINGS_HEARTBEAT_TIMESTAMP = "SETTINGS_HEARTBEAT_TIMESTAMP"
@@ -108,6 +110,31 @@ object Settings {
108110
return getApiKey(context) ?: ""
109111
}
110112

113+
fun getServerUrlOrDefault(context:Context): URI {
114+
val urlString = getServerUrl(context) ?: "https://api.httpsms.com"
115+
return URI(urlString)
116+
}
117+
118+
private fun getServerUrl(context: Context): String? {
119+
Timber.d(Settings::getServerUrl.name)
120+
121+
val serverUrl = PreferenceManager
122+
.getDefaultSharedPreferences(context)
123+
.getString(this.SETTINGS_SERVER_URL,null)
124+
125+
Timber.d("SETTINGS_SERVER_URL: [$serverUrl]")
126+
return serverUrl
127+
}
128+
129+
fun setServerUrlAsync(context: Context, serverURL: String?) {
130+
Timber.d(Settings::SETTINGS_SERVER_URL.name)
131+
132+
PreferenceManager.getDefaultSharedPreferences(context)
133+
.edit()
134+
.putString(this.SETTINGS_SERVER_URL, serverURL)
135+
.apply()
136+
}
137+
111138
fun setApiKeyAsync(context: Context, apiKey: String?) {
112139
Timber.d(Settings::setApiKeyAsync.name)
113140

android/app/src/main/java/com/httpsms/worker/HeartbeatWorker.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class HeartbeatWorker(appContext: Context, workerParams: WorkerParameters) : Wor
2020
return Result.failure()
2121
}
2222

23-
HttpSmsApiService(Settings.getApiKeyOrDefault(applicationContext))
23+
HttpSmsApiService.create(applicationContext)
2424
.storeHeartbeat(Settings.getOwnerOrDefault(applicationContext))
2525
Timber.d("finished sending heartbeat to server")
2626

@@ -33,4 +33,4 @@ class HeartbeatWorker(appContext: Context, workerParams: WorkerParameters) : Wor
3333

3434

3535

36-
}
36+
}

android/app/src/main/res/layout/activity_login.xml

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,26 @@
3535
app:layout_constraintHorizontal_bias="0.498"
3636
app:layout_constraintStart_toStartOf="parent" />
3737

38+
<com.google.android.material.textfield.TextInputLayout
39+
android:id="@+id/loginApiKeyTextInputLayout"
40+
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
41+
android:layout_width="match_parent"
42+
android:layout_height="wrap_content"
43+
app:errorEnabled="true"
44+
android:hint="@string/text_area_api_key"
45+
app:layout_constraintBottom_toBottomOf="parent"
46+
app:layout_constraintTop_toTopOf="parent"
47+
tools:layout_editor_absoluteX="16dp">
48+
49+
<com.google.android.material.textfield.TextInputEditText
50+
android:id="@+id/loginApiKeyTextInput"
51+
android:layout_width="match_parent"
52+
android:layout_height="wrap_content"
53+
android:inputType="textMultiLine"
54+
tools:ignore="TextContrastCheck" />
55+
56+
</com.google.android.material.textfield.TextInputLayout>
57+
3858
<com.google.android.material.textfield.TextInputLayout
3959
android:id="@+id/loginPhoneNumberLayout"
4060
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
@@ -58,19 +78,23 @@
5878

5979
</com.google.android.material.textfield.TextInputLayout>
6080

81+
6182
<com.google.android.material.textfield.TextInputLayout
62-
android:id="@+id/loginApiKeyTextInputLayout"
83+
android:id="@+id/loginServerUrlLayout"
6384
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
6485
android:layout_width="match_parent"
6586
android:layout_height="wrap_content"
87+
android:layout_marginTop="-24dp"
88+
android:hint="@string/server_url"
6689
app:errorEnabled="true"
67-
android:hint="@string/text_area_api_key"
90+
app:placeholderText="@string/login_server_url_hint"
6891
app:layout_constraintBottom_toBottomOf="parent"
69-
app:layout_constraintTop_toTopOf="parent"
92+
app:layout_constraintTop_toBottomOf="@+id/loginPhoneNumberLayout"
93+
app:layout_constraintVertical_bias="0.137"
7094
tools:layout_editor_absoluteX="16dp">
7195

7296
<com.google.android.material.textfield.TextInputEditText
73-
android:id="@+id/loginApiKeyTextInput"
97+
android:id="@+id/loginServerUrlInput"
7498
android:layout_width="match_parent"
7599
android:layout_height="wrap_content"
76100
android:inputType="textMultiLine"
@@ -86,7 +110,7 @@
86110
android:orientation="vertical"
87111
app:layout_constraintEnd_toEndOf="parent"
88112
app:layout_constraintStart_toStartOf="parent"
89-
app:layout_constraintTop_toBottomOf="@+id/loginPhoneNumberLayout">
113+
app:layout_constraintTop_toBottomOf="@+id/loginServerUrlLayout">
90114

91115
<com.google.android.material.button.MaterialButton
92116
android:id="@+id/loginButton"

0 commit comments

Comments
 (0)