Skip to content

Commit 9d5cc83

Browse files
committed
[webhooks] add webhooks list UI
1 parent 070aa88 commit 9d5cc83

File tree

10 files changed

+296
-15
lines changed

10 files changed

+296
-15
lines changed

app/src/main/java/me/capcom/smsgateway/modules/webhooks/db/WebHooksDao.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import me.capcom.smsgateway.modules.webhooks.domain.WebHookEvent
1212
@Dao
1313
interface WebHooksDao {
1414

15+
@Query("SELECT * FROM webHook")
16+
fun select(): List<WebHook>
17+
1518
@Query("SELECT * FROM webHook WHERE event = :event")
1619
fun selectByEvent(event: WebHookEvent): List<WebHook>
1720

app/src/main/java/me/capcom/smsgateway/modules/webhooks/domain/WebHookEvent.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ package me.capcom.smsgateway.modules.webhooks.domain
22

33
import com.google.gson.annotations.SerializedName
44

5-
enum class WebHookEvent {
5+
enum class WebHookEvent(val value: String) {
66
@SerializedName("sms:received")
7-
SmsReceived,
7+
SmsReceived("sms:received"),
88

99
@SerializedName("sms:sent")
10-
SmsSent,
10+
SmsSent("sms:sent"),
1111

1212
@SerializedName("sms:delivered")
13-
SmsDelivered,
13+
SmsDelivered("sms:delivered"),
1414

1515
@SerializedName("sms:failed")
16-
SmsFailed,
16+
SmsFailed("sms:failed"),
1717

1818
@SerializedName("system:ping")
19-
SystemPing,
19+
SystemPing("system:ping"),
2020
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package me.capcom.smsgateway.ui.adapters
2+
3+
import android.content.ClipData
4+
import android.content.ClipboardManager
5+
import android.content.Context
6+
import android.view.LayoutInflater
7+
import android.view.ViewGroup
8+
import android.widget.Toast
9+
import androidx.recyclerview.widget.DiffUtil
10+
import androidx.recyclerview.widget.ListAdapter
11+
import androidx.recyclerview.widget.RecyclerView
12+
import me.capcom.smsgateway.R
13+
import me.capcom.smsgateway.databinding.ItemWebhookBinding
14+
import me.capcom.smsgateway.modules.webhooks.domain.WebHookDTO
15+
16+
class WebhookAdapter : ListAdapter<WebHookDTO, WebhookAdapter.ViewHolder>(WebhookDiffCallback()) {
17+
class ViewHolder(private val binding: ItemWebhookBinding) :
18+
RecyclerView.ViewHolder(binding.root) {
19+
fun bind(webhook: WebHookDTO) {
20+
binding.apply {
21+
idText.text = binding.root.context.getString(R.string.webhook_id_format, webhook.id)
22+
urlText.text = webhook.url
23+
eventText.text = webhook.event.value
24+
sourceText.text = when (webhook.source) {
25+
me.capcom.smsgateway.domain.EntitySource.Local -> binding.root.context.getString(
26+
R.string.local
27+
)
28+
29+
me.capcom.smsgateway.domain.EntitySource.Gateway,
30+
me.capcom.smsgateway.domain.EntitySource.Cloud -> binding.root.context.getString(
31+
R.string.cloud
32+
)
33+
}
34+
sourceIcon.setImageResource(
35+
when (webhook.source) {
36+
me.capcom.smsgateway.domain.EntitySource.Local -> R.drawable.ic_local_server
37+
me.capcom.smsgateway.domain.EntitySource.Cloud,
38+
me.capcom.smsgateway.domain.EntitySource.Gateway -> R.drawable.ic_cloud_server
39+
}
40+
)
41+
}
42+
}
43+
}
44+
45+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
46+
val binding = ItemWebhookBinding.inflate(
47+
LayoutInflater.from(parent.context),
48+
parent,
49+
false
50+
)
51+
val holder = ViewHolder(binding)
52+
53+
binding.root.setOnClickListener {
54+
val position = holder.adapterPosition
55+
if (position != RecyclerView.NO_POSITION) {
56+
val webhook = getItem(position)
57+
val context = binding.root.context
58+
val clipboard =
59+
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
60+
val clip = ClipData.newPlainText("Webhook ID", webhook.id)
61+
clipboard.setPrimaryClip(clip)
62+
Toast.makeText(context, R.string.id_copied, Toast.LENGTH_SHORT).show()
63+
}
64+
}
65+
66+
return holder
67+
}
68+
69+
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
70+
holder.bind(getItem(position))
71+
}
72+
73+
class WebhookDiffCallback : DiffUtil.ItemCallback<WebHookDTO>() {
74+
override fun areItemsTheSame(oldItem: WebHookDTO, newItem: WebHookDTO): Boolean {
75+
return oldItem.id == newItem.id
76+
}
77+
78+
override fun areContentsTheSame(oldItem: WebHookDTO, newItem: WebHookDTO): Boolean {
79+
return oldItem == newItem
80+
}
81+
}
82+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package me.capcom.smsgateway.ui.settings
2+
3+
import android.os.Bundle
4+
import android.view.LayoutInflater
5+
import android.view.View
6+
import android.view.ViewGroup
7+
import androidx.core.view.isVisible
8+
import androidx.fragment.app.Fragment
9+
import androidx.recyclerview.widget.DividerItemDecoration
10+
import androidx.recyclerview.widget.LinearLayoutManager
11+
import me.capcom.smsgateway.databinding.FragmentWebhooksListBinding
12+
import me.capcom.smsgateway.modules.webhooks.WebHooksService
13+
import me.capcom.smsgateway.ui.adapters.WebhookAdapter
14+
import org.koin.android.ext.android.inject
15+
16+
class WebhooksListFragment : Fragment() {
17+
private var _binding: FragmentWebhooksListBinding? = null
18+
private val binding get() = _binding!!
19+
20+
private val webhookService: WebHooksService by inject()
21+
private val adapter: WebhookAdapter = WebhookAdapter()
22+
23+
override fun onCreateView(
24+
inflater: LayoutInflater,
25+
container: ViewGroup?,
26+
savedInstanceState: Bundle?
27+
): View {
28+
_binding = FragmentWebhooksListBinding.inflate(inflater, container, false)
29+
return binding.root
30+
}
31+
32+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
33+
super.onViewCreated(view, savedInstanceState)
34+
setupRecyclerView()
35+
loadWebhooks()
36+
}
37+
38+
private fun setupRecyclerView() {
39+
binding.webhookList.apply {
40+
layoutManager = LinearLayoutManager(context)
41+
adapter = this@WebhooksListFragment.adapter
42+
addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
43+
}
44+
}
45+
46+
private fun loadWebhooks() {
47+
val webhooks = webhookService.select(null)
48+
adapter.submitList(webhooks)
49+
binding.emptyState.isVisible = webhooks.isEmpty()
50+
binding.webhookList.isVisible = webhooks.isNotEmpty()
51+
}
52+
53+
override fun onDestroyView() {
54+
super.onDestroyView()
55+
_binding = null
56+
}
57+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:autoMirrored="true"
5+
android:viewportWidth="24"
6+
android:viewportHeight="24">
7+
8+
<path
9+
android:fillColor="@android:color/black"
10+
android:pathData="M4,10.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM4,4.5c-0.83,0 -1.5,0.67 -1.5,1.5S3.17,7.5 4,7.5 5.5,6.83 5.5,6 4.83,4.5 4,4.5zM4,16.5c-0.83,0 -1.5,0.68 -1.5,1.5s0.68,1.5 1.5,1.5 1.5,-0.68 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM7,19h14v-2L7,17v2zM7,13h14v-2L7,11v2zM7,5v2h14L21,5L7,5z" />
11+
12+
</vector>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:autoMirrored="true"
5+
android:viewportWidth="24"
6+
android:viewportHeight="24">
7+
8+
<path
9+
android:fillColor="@android:color/white"
10+
android:pathData="M4,10.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM4,4.5c-0.83,0 -1.5,0.67 -1.5,1.5S3.17,7.5 4,7.5 5.5,6.83 5.5,6 4.83,4.5 4,4.5zM4,16.5c-0.83,0 -1.5,0.68 -1.5,1.5s0.68,1.5 1.5,1.5 1.5,-0.68 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM7,19h14v-2L7,17v2zM7,13h14v-2L7,11v2zM7,5v2h14L21,5L7,5z" />
11+
12+
</vector>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
4+
android:layout_width="match_parent"
5+
android:layout_height="match_parent"
6+
android:background="?android:attr/colorBackground">
7+
8+
<androidx.recyclerview.widget.RecyclerView
9+
android:id="@+id/webhookList"
10+
android:layout_width="match_parent"
11+
android:layout_height="match_parent"
12+
android:clipToPadding="false"
13+
android:padding="8dp"
14+
tools:listitem="@layout/item_webhook" />
15+
16+
<LinearLayout
17+
android:id="@+id/emptyState"
18+
android:layout_width="match_parent"
19+
android:layout_height="match_parent"
20+
android:gravity="center"
21+
android:orientation="vertical"
22+
android:visibility="gone"
23+
tools:visibility="visible">
24+
25+
<ImageView
26+
android:layout_width="48dp"
27+
android:layout_height="48dp"
28+
android:src="@drawable/ic_webhook" />
29+
30+
<TextView
31+
android:layout_width="wrap_content"
32+
android:layout_height="wrap_content"
33+
android:layout_marginTop="16dp"
34+
android:text="@string/no_webhooks_found"
35+
android:textAppearance="?attr/textAppearanceCaption" />
36+
</LinearLayout>
37+
</FrameLayout>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
4+
android:layout_width="match_parent"
5+
android:layout_height="wrap_content"
6+
android:orientation="vertical"
7+
android:padding="8dp">
8+
9+
<TextView
10+
android:id="@+id/urlText"
11+
android:layout_width="match_parent"
12+
android:layout_height="wrap_content"
13+
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
14+
tools:text="https://example.com" />
15+
16+
<TextView
17+
android:id="@+id/idText"
18+
android:layout_width="match_parent"
19+
android:layout_height="wrap_content"
20+
android:ellipsize="end"
21+
android:maxLines="1"
22+
android:textAppearance="@style/TextAppearance.AppCompat.Caption" />
23+
24+
<LinearLayout
25+
android:layout_width="match_parent"
26+
android:layout_height="wrap_content"
27+
android:orientation="horizontal">
28+
29+
<ImageView
30+
android:id="@+id/sourceIcon"
31+
android:layout_width="24dp"
32+
android:layout_height="24dp"
33+
android:layout_marginEnd="8dp"
34+
android:src="@drawable/ic_local_server" />
35+
36+
<TextView
37+
android:id="@+id/sourceText"
38+
android:layout_width="wrap_content"
39+
android:layout_height="wrap_content"
40+
android:layout_gravity="center_vertical"
41+
android:layout_marginHorizontal="8dp"
42+
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
43+
tools:text="source" />
44+
45+
<TextView
46+
android:id="@+id/eventText"
47+
android:layout_width="wrap_content"
48+
android:layout_height="wrap_content"
49+
android:layout_gravity="center_vertical"
50+
android:layout_marginHorizontal="8dp"
51+
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
52+
tools:text="event" />
53+
</LinearLayout>
54+
</LinearLayout>

app/src/main/res/values/strings.xml

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
<string name="app_version_build">App version (build)</string>
77
<string name="battery_optimization">Battery optimization</string>
88
<string name="battery_optimization_already_disabled">Battery optimization already disabled</string>
9-
<string name="battery_optimization_is_not_supported_on_this_device">Battery optimization is not supported on this device</string>
9+
<string name="battery_optimization_is_not_supported_on_this_device">Battery optimization is not
10+
supported on this device</string>
1011
<string name="btn_cancel">Cancel</string>
1112
<string name="btn_continue">Continue</string>
1213
<string name="by_code">By Code</string>
1314
<string name="can_affect_battery_life">can affect battery life</string>
14-
<string name="click_continue_to_create_an_account_no_personal_information_is_required_nby_continuing_you_agree_to_our_privacy_policy_at_https_docs_sms_gate_app_privacy_policy">Click Continue to create an account. No personal information is required.\nBy continuing, you agree to our Privacy Policy at https://docs.sms-gate.app/privacy/policy/</string>
15+
<string name="click_continue_to_create_an_account_no_personal_information_is_required_nby_continuing_you_agree_to_our_privacy_policy_at_https_docs_sms_gate_app_privacy_policy">Click
16+
Continue to create an account. No personal information is required.\nBy continuing, you
17+
agree to our Privacy Policy at https://docs.sms-gate.app/privacy/policy/</string>
1518
<string name="cloud_server">Cloud server</string>
1619
<string name="cloud_server_dotdotdot">Cloud server…</string>
1720
<string name="copied_to_clipboard">Copied</string>
@@ -33,7 +36,8 @@
3336
<string name="internet_connection_unavailable">Internet connection: unavailable</string>
3437
<string name="interval_seconds">Interval (seconds)</string>
3538
<string name="invalid_url">Invalid URL</string>
36-
<string name="is_not_a_valid_port_must_be_between_1024_and_65535">%1$s is not a valid port. Must be between 1024 and 65535</string>
39+
<string name="is_not_a_valid_port_must_be_between_1024_and_65535">%1$s is not a valid port. Must
40+
be between 1024 and 65535</string>
3741
<string name="label_password">Password:</string>
3842
<string name="label_username">Username:</string>
3943
<string name="limits">Limits</string>
@@ -54,7 +58,8 @@
5458
<string name="not_registered">not registered</string>
5559
<string name="not_set">Not set</string>
5660
<string name="notification_title">SMS Gateway</string>
57-
<string name="online_status_at_the_cost_of_battery_life">Online status at the cost of battery life</string>
61+
<string name="online_status_at_the_cost_of_battery_life">Online status at the cost of battery
62+
life</string>
5863
<string name="passphrase">Passphrase</string>
5964
<string name="password">Password</string>
6065
<string name="password_changed_successfully">Password changed successfully</string>
@@ -63,7 +68,8 @@
6368
<string name="period">Period</string>
6469
<string name="ping">Ping</string>
6570
<string name="ping_service_is_active">Ping service is active</string>
66-
<string name="please_enter_one_time_code_displayed_on_already_registered_device">Please enter one-time code displayed on already registered device</string>
71+
<string name="please_enter_one_time_code_displayed_on_already_registered_device">Please enter
72+
one-time code displayed on already registered device</string>
6773
<string name="port">Port</string>
6874
<string name="port_credentials_etc">Port, credentials, etc.</string>
6975
<string name="private_token">Private Token</string>
@@ -96,14 +102,27 @@
96102
<string name="tab_text_home">Home</string>
97103
<string name="tab_text_messages">MESSAGES</string>
98104
<string name="tab_text_settings">SETTINGS</string>
99-
<string name="the_webhook_request_will_wait_for_an_internet_connection">The webhook request will wait for an internet connection</string>
100-
<string name="to_add_a_device_to_an_existing_account_please_fill_in_the_credentials_below">To add a device to an existing account, please fill in the credentials below.</string>
101-
<string name="to_apply_the_changes_restart_the_app_using_the_button_below">To apply the changes, restart the app using the button below.</string>
105+
<string name="the_webhook_request_will_wait_for_an_internet_connection">The webhook request will
106+
wait for an internet connection</string>
107+
<string name="to_add_a_device_to_an_existing_account_please_fill_in_the_credentials_below">To
108+
add a device to an existing account, please fill in the credentials below.</string>
109+
<string name="to_apply_the_changes_restart_the_app_using_the_button_below">To apply the changes,
110+
restart the app using the button below.</string>
102111
<string name="use_empty_to_disable">Use empty to disable</string>
103-
<string name="use_this_code_to_sign_in_on_another_device">Use this code to sign in on another device</string>
112+
<string name="use_this_code_to_sign_in_on_another_device">Use this code to sign in on another
113+
device</string>
104114
<string name="username">Username</string>
105115
<string name="username_must_be_at_least_3_characters">Username must be at least 3 characters</string>
106116
<string name="view">View</string>
107117
<string name="webhooks">Webhooks</string>
108118
<string name="webhooks_dotdotdot">Webhooks...</string>
119+
120+
<string name="webhook_list_title">Registered Webhooks</string>
121+
<string name="webhook_list_summary">View registered webhooks</string>
122+
123+
<string name="id_copied">Webhook ID copied to clipboard</string>
124+
<string name="no_webhooks_found">No webhooks configured</string>
125+
<string name="webhook_id_format">ID: %1$s</string>
126+
<string name="local">Local</string>
127+
<string name="cloud">Cloud</string>
109128
</resources>

app/src/main/res/xml/webhooks_preferences.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,9 @@
2222
app:title="@string/signing_key"
2323
app:useSimpleSummaryProvider="true" />
2424
</PreferenceCategory>
25+
<Preference
26+
android:icon="@drawable/ic_webhooks_list"
27+
app:fragment="me.capcom.smsgateway.ui.settings.WebhooksListFragment"
28+
app:summary="@string/webhook_list_summary"
29+
app:title="@string/webhook_list_title" />
2530
</androidx.preference.PreferenceScreen>

0 commit comments

Comments
 (0)