diff --git a/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.kt b/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.kt index eaa8153..fa16202 100644 --- a/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.kt +++ b/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayApi.kt @@ -86,6 +86,14 @@ class GatewayApi( }.body() } + suspend fun changePassword(token: String, request: PasswordChangeRequest) { + client.patch("$baseUrl/user/password") { + auth(token) + contentType(ContentType.Application.Json) + setBody(request) + } + } + private fun HttpRequestBuilder.auth(token: String) { header(HttpHeaders.Authorization, "Bearer $token") } @@ -118,6 +126,11 @@ class GatewayApi( val states: Map ) + data class PasswordChangeRequest( + val currentPassword: String, + val newPassword: String + ) + data class Message( val id: String, val message: String, diff --git a/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.kt b/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.kt index b105f2a..d6f4830 100644 --- a/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.kt +++ b/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewayService.kt @@ -63,6 +63,27 @@ class GatewayService( this._api = null } + suspend fun changePassword(current: String, new: String) { + val info = settings.registrationInfo + ?: throw IllegalStateException("The device is not registered on the server") + + this.api.changePassword( + info.token, + GatewayApi.PasswordChangeRequest(current, new) + ) + + settings.registrationInfo = info.copy(password = new) + + events.emit( + DeviceRegisteredEvent( + api.hostname, + info.login, + new, + ) + ) + } + + /////////////////////////////////////////////////////////////////////////// internal suspend fun getWebHooks(): List { val settings = settings.registrationInfo return if (settings != null) { diff --git a/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewaySettings.kt b/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewaySettings.kt index 4c617ab..e1e1b12 100644 --- a/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewaySettings.kt +++ b/app/src/main/java/me/capcom/smsgateway/modules/gateway/GatewaySettings.kt @@ -17,6 +17,11 @@ class GatewaySettings( get() = storage.get(REGISTRATION_INFO) set(value) = storage.set(REGISTRATION_INFO, value) + val username: String? + get() = registrationInfo?.login + val password: String? + get() = registrationInfo?.password + val privateUrl: String? get() = storage.get(CLOUD_URL) val privateToken: String? diff --git a/app/src/main/java/me/capcom/smsgateway/ui/SettingsFragment.kt b/app/src/main/java/me/capcom/smsgateway/ui/SettingsFragment.kt index d57c758..a1338ea 100644 --- a/app/src/main/java/me/capcom/smsgateway/ui/SettingsFragment.kt +++ b/app/src/main/java/me/capcom/smsgateway/ui/SettingsFragment.kt @@ -10,30 +10,17 @@ import android.provider.Settings import android.text.InputType import android.widget.Toast import androidx.annotation.RequiresApi -import androidx.core.content.edit import androidx.preference.EditTextPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import me.capcom.smsgateway.BuildConfig import me.capcom.smsgateway.R -import me.capcom.smsgateway.modules.gateway.GatewaySettings class SettingsFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.root_preferences, rootKey) - findPreference("gateway.cloud_url")?.setSummaryProvider { - val hostname = preferenceManager.sharedPreferences?.getString(it.key, null) - if (hostname.isNullOrEmpty()) { - preferenceManager.sharedPreferences?.edit(true) { - putString(it.key, GatewaySettings.PUBLIC_URL) - } - return@setSummaryProvider GatewaySettings.PUBLIC_URL - } - return@setSummaryProvider hostname - } - findPreference("transient.app_version")?.summary = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})" @@ -48,9 +35,7 @@ class SettingsFragment : PreferenceFragmentCompat() { } override fun onDisplayPreferenceDialog(preference: Preference) { - if (preference.key == "encryption.passphrase" - || preference.key == "gateway.private_token" - ) { + if (preference.key == "encryption.passphrase") { (preference as EditTextPreference).setOnBindEditTextListener { it.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD it.setSelectAllOnFocus(true) @@ -58,13 +43,6 @@ class SettingsFragment : PreferenceFragmentCompat() { } } - if (preference.key == "gateway.cloud_url") { - (preference as EditTextPreference).setOnBindEditTextListener { - it.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI - it.setSelectAllOnFocus(true) - } - } - if (preference.key == "ping.interval_seconds" || preference.key == "logs.lifetime_days" || preference.key == "webhooks.retry_count" diff --git a/app/src/main/java/me/capcom/smsgateway/ui/settings/BasePreferenceFragment.kt b/app/src/main/java/me/capcom/smsgateway/ui/settings/BasePreferenceFragment.kt new file mode 100644 index 0000000..b663261 --- /dev/null +++ b/app/src/main/java/me/capcom/smsgateway/ui/settings/BasePreferenceFragment.kt @@ -0,0 +1,26 @@ +package me.capcom.smsgateway.ui.settings + +import android.os.Bundle +import android.util.TypedValue +import android.view.View +import android.widget.Toast +import androidx.preference.PreferenceFragmentCompat + +abstract class BasePreferenceFragment : PreferenceFragmentCompat() { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val backgroundValue = TypedValue() + requireContext().theme.resolveAttribute( + android.R.attr.colorBackground, + backgroundValue, + true + ) + + view.setBackgroundColor(backgroundValue.data) + } + + protected fun showToast(message: String) { + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() + } +} \ No newline at end of file diff --git a/app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt b/app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt new file mode 100644 index 0000000..72c175f --- /dev/null +++ b/app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt @@ -0,0 +1,115 @@ +package me.capcom.smsgateway.ui.settings + +import android.annotation.SuppressLint +import android.os.Bundle +import android.text.InputType +import android.view.View +import androidx.core.content.edit +import androidx.core.view.isVisible +import androidx.lifecycle.lifecycleScope +import androidx.preference.EditTextPreference +import androidx.preference.Preference +import kotlinx.coroutines.launch +import me.capcom.smsgateway.R +import me.capcom.smsgateway.modules.gateway.GatewayService +import me.capcom.smsgateway.modules.gateway.GatewaySettings +import org.koin.android.ext.android.inject +import java.net.URL + +class CloudServerSettingsFragment : BasePreferenceFragment() { + + private val settings: GatewaySettings by inject() + private val service: GatewayService by inject() + + @SuppressLint("NotifyDataSetChanged") + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.cloud_server_preferences, rootKey) + + findPreference("gateway.cloud_url")?.setSummaryProvider { + val hostname = preferenceManager.sharedPreferences?.getString(it.key, null) + if (hostname.isNullOrEmpty()) { + preferenceManager.sharedPreferences?.edit(true) { + putString(it.key, GatewaySettings.PUBLIC_URL) + } + return@setSummaryProvider GatewaySettings.PUBLIC_URL + } + return@setSummaryProvider hostname + } + + + findPreference("gateway.cloud_url")?.setOnPreferenceChangeListener { _, newValue -> + val value = newValue as? String + if (value.isNullOrEmpty()) { + return@setOnPreferenceChangeListener true + } + + try { + URL(value) + } catch (e: Exception) { + showToast(getString(R.string.invalid_url)) + return@setOnPreferenceChangeListener false + } + + true + } + + findPreference("gateway.username")?.setSummaryProvider { + settings.username ?: getString(R.string.not_set) + } + findPreference("gateway.password")?.apply { + setSummaryProvider { + settings.password ?: getString(R.string.not_set) + } + + setOnPreferenceChangeListener { _, newValue -> + val value = newValue as? String + if (value == null || value.length < 14) { + showToast(getString(R.string.password_must_be_at_least_14_characters)) + return@setOnPreferenceChangeListener false + } + + this@CloudServerSettingsFragment.lifecycleScope.launch { + try { + requireActivity().findViewById(R.id.progressBar).isVisible = true + service.changePassword(settings.password ?: "", value) + listView.adapter?.notifyDataSetChanged() + showToast(getString(R.string.password_changed_successfully)) + } catch (e: Exception) { + showToast(getString(R.string.failed_to_change_password, e.message)) + } finally { + requireActivity().findViewById(R.id.progressBar).isVisible = false + } + } + + true + } + } + } + + override fun onDisplayPreferenceDialog(preference: Preference) { + if (preference.key == "gateway.cloud_url") { + (preference as EditTextPreference).setOnBindEditTextListener { + it.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI + it.setSelectAllOnFocus(true) + it.selectAll() + } + } + + if (preference.key == "gateway.private_token") { + (preference as EditTextPreference).setOnBindEditTextListener { + it.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD + it.setSelectAllOnFocus(true) + it.selectAll() + } + } + + if (preference.key == "gateway.password") { + (preference as EditTextPreference).setOnBindEditTextListener { + it.inputType = InputType.TYPE_CLASS_TEXT + it.text = null + } + } + + super.onDisplayPreferenceDialog(preference) + } +} \ No newline at end of file diff --git a/app/src/main/java/me/capcom/smsgateway/ui/settings/LocalServerSettingsFragment.kt b/app/src/main/java/me/capcom/smsgateway/ui/settings/LocalServerSettingsFragment.kt index 965dde7..a1e6e5c 100644 --- a/app/src/main/java/me/capcom/smsgateway/ui/settings/LocalServerSettingsFragment.kt +++ b/app/src/main/java/me/capcom/smsgateway/ui/settings/LocalServerSettingsFragment.kt @@ -2,28 +2,11 @@ package me.capcom.smsgateway.ui.settings import android.os.Bundle import android.text.InputType -import android.util.TypedValue -import android.view.View -import android.widget.Toast import androidx.preference.EditTextPreference import androidx.preference.Preference -import androidx.preference.PreferenceFragmentCompat import me.capcom.smsgateway.R -class LocalServerSettingsFragment : PreferenceFragmentCompat() { - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - val backgroundValue = TypedValue() - requireContext().theme.resolveAttribute( - android.R.attr.colorBackground, - backgroundValue, - true - ) - - view.setBackgroundColor(backgroundValue.data) - } +class LocalServerSettingsFragment : BasePreferenceFragment() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.local_server_preferences, rootKey) @@ -86,8 +69,4 @@ class LocalServerSettingsFragment : PreferenceFragmentCompat() { super.onDisplayPreferenceDialog(preference) } - - private fun showToast(message: String) { - Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() - } } \ No newline at end of file diff --git a/app/src/main/java/me/capcom/smsgateway/ui/settings/MessagesSettingsFragment.kt b/app/src/main/java/me/capcom/smsgateway/ui/settings/MessagesSettingsFragment.kt index 4c348a2..1928dc5 100644 --- a/app/src/main/java/me/capcom/smsgateway/ui/settings/MessagesSettingsFragment.kt +++ b/app/src/main/java/me/capcom/smsgateway/ui/settings/MessagesSettingsFragment.kt @@ -3,27 +3,11 @@ package me.capcom.smsgateway.ui.settings import android.content.SharedPreferences import android.os.Bundle import android.text.InputType -import android.util.TypedValue -import android.view.View import androidx.preference.EditTextPreference import androidx.preference.Preference -import androidx.preference.PreferenceFragmentCompat import me.capcom.smsgateway.R -class MessagesSettingsFragment : PreferenceFragmentCompat() { - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - val backgroundValue = TypedValue() - requireContext().theme.resolveAttribute( - android.R.attr.colorBackground, - backgroundValue, - true - ) - - view.setBackgroundColor(backgroundValue.data) - } +class MessagesSettingsFragment : BasePreferenceFragment() { override fun onResume() { super.onResume() diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ae750e9..dd37129 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -12,10 +12,33 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> - + android:layout_weight="1"> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f8c6127..995a594 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -7,4 +7,5 @@ #FF018786 #FF000000 #FFFFFFFF + #33FFFFFF \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 216bdd9..eb34342 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,76 +1,84 @@ + %1$s + API URL + API URL, private token, credentials, etc. SMS Gateway - SMS Gateway - SETTINGS - MESSAGES - Local server + App version (build) + Battery optimizations already disabled + Battery optimizations not supported on this device + can affect battery life Cloud server - api.sms-gate.app - Start on boot - Offline - Online - <a href>%1$s:%2$d</a> - <a href>%1$s:%2$d</a> + Cloud server… Copied - SMS Gateway - Local SMS Gateway notifications - SMS gateway is running on port %1$d - Not available - Not available - Messages - Home + Delays, Limits, etc. + Delays, seconds + Delete after, days + Disable battery optimizations + Disabled + + Enabled Encryption - Use empty to disable - Passphrase - Sending messages… - API URL - Private Token Ignored for public server - %1$s + Information + Interval (seconds) + %1$s is not a valid port. Must be between 1024 and 65535 + Password: + Username: Limits - Period + List of last 50 log entries + Local address: + Local Server… + Local SMS Gateway notifications + Logs + Maximum Messages count - Delays, seconds + Messages Minimum - Maximum - Set maximum value to activate - Information - App version (build) - Sending webhook… - Ping service is active - Delete after, days - Interval (seconds) - Online status at the cost of battery - life + More settings… + SMS Gateway + Online status at the cost of battery life + Passphrase + Password + Password must be at least 8 characters + Period Ping - Logs - View - List of last 50 log entries - Webhooks - The webhook request will wait for an internet connection + Ping service is active + Port + Port, credentials, etc. + Private Token + Public address: Require Internet connection - More settings… - Delays, Limits, etc. Retry count - Local address: - ... - Public address: - Username - Password - Username: - Password: + Sending messages… + Sending webhook… + Server Server address: - To apply the changes, restart the app using the button below. + Set maximum value to activate + api.sms-gate.app + <a href>%1$s:%2$d</a> + Not available + Local server + Offline + Online + <a href>%1$s:%2$d</a> + Not available + Start on boot + SMS Gateway + SMS gateway is running on port %1$d System - can affect battery life - Disable battery optimizations - Disabled - Enabled - Battery optimizations not supported on this device - Battery optimizations already disabled - Server - Port - Password must be at least 8 characters + Home + MESSAGES + SETTINGS + The webhook request will wait for an internet connection + To apply the changes, restart the app using the button below. + Use empty to disable + Username Username must be at least 3 characters - %1$s is not a valid port. Must be between 1024 and 65535 + View + Webhooks + Invalid URL + Not set + Password must be at least 14 characters + Password changed successfully + Failed to change password: %1$s \ No newline at end of file diff --git a/app/src/main/res/xml/cloud_server_preferences.xml b/app/src/main/res/xml/cloud_server_preferences.xml new file mode 100644 index 0000000..a4a0a27 --- /dev/null +++ b/app/src/main/res/xml/cloud_server_preferences.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index af0a43e..dbf41cd 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -2,21 +2,12 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> - - - - - + app:summary="@string/port_credentials_etc" + app:title="@string/local_server_dotdotdot" /> +