Compare commits
No commits in common. "046da599b7a614501fa9d0c136769a211178ae98" and "6781410b87f6ea6f1074d0a7faf326e0dba2b727" have entirely different histories.
046da599b7
...
6781410b87
|
@ -1,111 +1,21 @@
|
||||||
package com.p_vacho.neat_calendar.activities
|
package com.p_vacho.neat_calendar.activities
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.ImageButton
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.p_vacho.neat_calendar.MyApplication
|
|
||||||
import com.p_vacho.neat_calendar.R
|
import com.p_vacho.neat_calendar.R
|
||||||
import com.p_vacho.neat_calendar.adapters.NotificationAdapter
|
|
||||||
import com.p_vacho.neat_calendar.api.RetrofitClient
|
|
||||||
import com.p_vacho.neat_calendar.api.models.InvitationResponse
|
|
||||||
import com.p_vacho.neat_calendar.api.models.NotificationResponse
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
class NotificationsActivity : AppCompatActivity() {
|
class NotificationsActivity : AppCompatActivity() {
|
||||||
private lateinit var rvNotifications: RecyclerView
|
|
||||||
private lateinit var btnBack: ImageButton
|
|
||||||
|
|
||||||
private lateinit var notifications: MutableList<NotificationResponse>
|
|
||||||
private lateinit var invitations: MutableMap<String, InvitationResponse> // invitation id -> invitation
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContentView(R.layout.activity_notifications)
|
setContentView(R.layout.activity_notification)
|
||||||
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||||
insets
|
insets
|
||||||
}
|
}
|
||||||
|
|
||||||
rvNotifications = findViewById(R.id.rvNotifications)
|
|
||||||
btnBack = findViewById(R.id.btnBack)
|
|
||||||
|
|
||||||
rvNotifications.layoutManager = LinearLayoutManager(this)
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
|
||||||
notifications = fetchNotifications().toMutableList()
|
|
||||||
invitations = fetchInvitations().toMutableMap()
|
|
||||||
rvNotifications.adapter = NotificationAdapter(notifications, ::handleNotificationAction, ::handleNotificationClick) {
|
|
||||||
notification -> invitations[notification.data]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
btnBack.setOnClickListener { finish() }
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private suspend fun fetchNotifications(): List<NotificationResponse> {
|
|
||||||
val userId = (application as MyApplication).tokenManager.userId
|
|
||||||
|
|
||||||
if (userId == null) {
|
|
||||||
finish()
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val notifications = withContext(Dispatchers.IO) {
|
|
||||||
RetrofitClient.notificationsService.getUserNotifications(userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
return notifications
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun fetchInvitations(): Map<String, InvitationResponse> {
|
|
||||||
val userId = (application as MyApplication).tokenManager.userId
|
|
||||||
if (userId == null) {
|
|
||||||
finish()
|
|
||||||
return emptyMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
val fetchedInvitations = withContext(Dispatchers.IO) {
|
|
||||||
RetrofitClient.invitationService.getIncomingInvitations(userId)
|
|
||||||
}
|
|
||||||
return fetchedInvitations.associateBy { it.id }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleNotificationAction(notification: NotificationResponse, action: NotificationAdapter.Action, position: Int) {
|
|
||||||
when (action) {
|
|
||||||
NotificationAdapter.Action.ACCEPT -> {
|
|
||||||
//TODO("Handle accept action")
|
|
||||||
}
|
|
||||||
NotificationAdapter.Action.DECLINE -> {
|
|
||||||
//TODO("Handle decline action")
|
|
||||||
}
|
|
||||||
NotificationAdapter.Action.VIEW_EVENT -> {
|
|
||||||
//TODO("Handle viewing the event")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleNotificationClick(notification: NotificationResponse, position: Int) {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
val updatedNotification =
|
|
||||||
RetrofitClient.notificationsService.markNotificationRead(notification.id)
|
|
||||||
|
|
||||||
notifications[position] = updatedNotification
|
|
||||||
val adapter = rvNotifications.adapter as NotificationAdapter
|
|
||||||
adapter.notifyItemChanged(position)
|
|
||||||
|
|
||||||
Toast.makeText(this@NotificationsActivity, "Marked as read", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,137 +0,0 @@
|
||||||
package com.p_vacho.neat_calendar.adapters
|
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.ImageButton
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.p_vacho.neat_calendar.R
|
|
||||||
import com.p_vacho.neat_calendar.api.models.InvitationResponse
|
|
||||||
import com.p_vacho.neat_calendar.api.models.NotificationResponse
|
|
||||||
import java.time.Duration
|
|
||||||
import java.time.OffsetDateTime
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
|
|
||||||
class NotificationAdapter(
|
|
||||||
private val notifications: MutableList<NotificationResponse>,
|
|
||||||
private val onActionClick: (NotificationResponse, Action, Int) -> Unit,
|
|
||||||
private val onNotificationClick: (NotificationResponse, Int) -> Unit,
|
|
||||||
private val getInvitationData: (NotificationResponse) -> InvitationResponse?,
|
|
||||||
) : RecyclerView.Adapter<NotificationAdapter.NotificationViewHolder>() {
|
|
||||||
|
|
||||||
enum class Action {
|
|
||||||
ACCEPT, DECLINE, VIEW_EVENT
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class NotificationViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
|
||||||
val message: TextView = view.findViewById(R.id.notificationMessage)
|
|
||||||
val notificationTime: TextView = view.findViewById(R.id.notificationTime)
|
|
||||||
val acceptButton: ImageButton = view.findViewById(R.id.acceptButton)
|
|
||||||
val declineButton: ImageButton = view.findViewById(R.id.declineButton)
|
|
||||||
val viewEventButton: ImageButton = view.findViewById(R.id.viewEventButton)
|
|
||||||
val invitationActions: View = view.findViewById(R.id.invitationActions)
|
|
||||||
val unreadIndicator: View = view.findViewById(R.id.notificationIndicator)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationViewHolder {
|
|
||||||
val view = LayoutInflater.from(parent.context)
|
|
||||||
.inflate(R.layout.item_notification, parent, false)
|
|
||||||
return NotificationViewHolder(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: NotificationViewHolder, position: Int) {
|
|
||||||
val notification = notifications[position]
|
|
||||||
|
|
||||||
// Format and set the creation time
|
|
||||||
val formattedTime = formatNotificationTime(notification.created_at)
|
|
||||||
holder.notificationTime.text = formattedTime
|
|
||||||
|
|
||||||
// Set visibility based on read/unread status
|
|
||||||
holder.unreadIndicator.visibility = if (notification.read) View.GONE else View.VISIBLE
|
|
||||||
|
|
||||||
// Handle invitation actions
|
|
||||||
if (notification.event_type == "invitation") {
|
|
||||||
val invitation = getInvitationData(notification)
|
|
||||||
|
|
||||||
// TODO: Consider fetching the user names here to show
|
|
||||||
// TODO: Localize
|
|
||||||
if (invitation != null) {
|
|
||||||
if (notification.message == "new-invitation") {
|
|
||||||
holder.message.setText("You have received an event invitation")
|
|
||||||
} else if (notification.message == "invitation-accepted") {
|
|
||||||
holder.message.setText("Your event invitation has been accepted")
|
|
||||||
} else if (notification.message == "invitation-declined") {
|
|
||||||
holder.message.setText("Your event invitation has been declined")
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.invitationActions.visibility = View.VISIBLE
|
|
||||||
|
|
||||||
holder.acceptButton.setOnClickListener {
|
|
||||||
onActionClick(notification, Action.ACCEPT, position)
|
|
||||||
}
|
|
||||||
holder.declineButton.setOnClickListener {
|
|
||||||
onActionClick(notification, Action.DECLINE, position)
|
|
||||||
}
|
|
||||||
holder.viewEventButton.setOnClickListener {
|
|
||||||
onActionClick(notification, Action.VIEW_EVENT, position)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (notification.message == "new-invitation") {
|
|
||||||
holder.message.setText("You have received an event invitation [invitation deleted]")
|
|
||||||
} else if (notification.message == "invitation-accepted") {
|
|
||||||
holder.message.setText("Your event invitation has been accepted [invitation deleted]")
|
|
||||||
} else if (notification.message == "invitation-declined") {
|
|
||||||
holder.message.setText("Your event invitation has been declined [invitation deleted]")
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.invitationActions.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
holder.message.text = notification.message
|
|
||||||
|
|
||||||
holder.invitationActions.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set click listener for the whole notification (only for unread ones though)
|
|
||||||
if (!notification.read) {
|
|
||||||
holder.itemView.isClickable = true
|
|
||||||
holder.itemView.setOnClickListener {
|
|
||||||
onNotificationClick(notification, position)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
holder.itemView.isClickable = false
|
|
||||||
holder.itemView.setOnClickListener(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure buttons consume their click events
|
|
||||||
holder.acceptButton.isClickable = true
|
|
||||||
holder.declineButton.isClickable = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = notifications.size
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format the time at which a notification was received.
|
|
||||||
*
|
|
||||||
* If the notification is recent (setn within the last 24h), show the time
|
|
||||||
* in a format of (x hours/minutes/seconds ago), otherwise, use yyyy-MM-dd HH:mm
|
|
||||||
*/
|
|
||||||
private fun formatNotificationTime(createdAt: OffsetDateTime): String {
|
|
||||||
val now = OffsetDateTime.now()
|
|
||||||
val duration = Duration.between(createdAt, now)
|
|
||||||
|
|
||||||
return when {
|
|
||||||
duration.seconds < 60 -> "${duration.seconds}s ago"
|
|
||||||
duration.toMinutes() < 60 -> "${duration.toMinutes()}m ago"
|
|
||||||
duration.toHours() < 24 -> "${duration.toHours()}h ago"
|
|
||||||
else -> {
|
|
||||||
// Format as a date/time for older notifications
|
|
||||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
|
|
||||||
createdAt.format(formatter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +1,6 @@
|
||||||
package com.p_vacho.neat_calendar.api.models
|
package com.p_vacho.neat_calendar.api.models
|
||||||
|
|
||||||
import java.time.OffsetDateTime
|
|
||||||
|
|
||||||
data class InvitationRequest(
|
data class InvitationRequest(
|
||||||
val event_id: String,
|
val event_id: String,
|
||||||
val invitee_id: String,
|
val invitee_id: String,
|
||||||
)
|
|
||||||
|
|
||||||
data class InvitationResponse(
|
|
||||||
val id: String,
|
|
||||||
val invitor_id: String,
|
|
||||||
val invitee_id: String,
|
|
||||||
val event_id: String,
|
|
||||||
val status: String, // "accepted" / "declined" / "pending"
|
|
||||||
val sent_at: OffsetDateTime,
|
|
||||||
val responded_at: OffsetDateTime?,
|
|
||||||
)
|
)
|
|
@ -5,7 +5,7 @@ import java.time.OffsetDateTime
|
||||||
data class NotificationResponse(
|
data class NotificationResponse(
|
||||||
val id: String,
|
val id: String,
|
||||||
val user_id: String,
|
val user_id: String,
|
||||||
val event_type: String, // "message" / "invitation"
|
val event_type: String, // "reminder" / "invitation"
|
||||||
val message: String,
|
val message: String,
|
||||||
val data: String,
|
val data: String,
|
||||||
val read: Boolean,
|
val read: Boolean,
|
||||||
|
|
|
@ -1,29 +1,10 @@
|
||||||
package com.p_vacho.neat_calendar.api.services
|
package com.p_vacho.neat_calendar.api.services
|
||||||
|
|
||||||
import com.p_vacho.neat_calendar.api.models.InvitationRequest
|
import com.p_vacho.neat_calendar.api.models.InvitationRequest
|
||||||
import com.p_vacho.neat_calendar.api.models.InvitationResponse
|
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
import retrofit2.http.GET
|
|
||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
import retrofit2.http.Path
|
|
||||||
|
|
||||||
interface InvitationsService {
|
interface InvitationsService {
|
||||||
@POST("invitations")
|
@POST("invitations")
|
||||||
suspend fun createInvitation(@Body invitationData: InvitationRequest): InvitationResponse
|
suspend fun createInvitation(@Body invitationData: InvitationRequest)
|
||||||
|
|
||||||
@GET("invitations/{invitation_id}")
|
|
||||||
suspend fun getInvitation(@Path("invitation_id") invitiationId: String): InvitationResponse
|
|
||||||
|
|
||||||
@GET("users/{user_id}/invitations")
|
|
||||||
suspend fun getInvitations(@Path("user_id") userId: String): List<InvitationResponse>
|
|
||||||
|
|
||||||
@GET("users/{user_id}/invitations/incoming")
|
|
||||||
suspend fun getIncomingInvitations(@Path("user_id") userId: String): List<InvitationResponse>
|
|
||||||
|
|
||||||
@POST("invitations/{invitation_id}/accept")
|
|
||||||
suspend fun acceptInvitation(@Path("invitation_id") invitationId: String): Unit
|
|
||||||
|
|
||||||
@POST("invitations/{invitation_id}/decline")
|
|
||||||
suspend fun declineInvitation(@Path("invitation_id") invitationId: String): Unit
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="?attr/colorControlHighlight">
|
|
||||||
<item>
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<solid android:color="?attr/colorSurface" />
|
|
||||||
<corners android:radius="8dp" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
</ripple>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="960"
|
|
||||||
android:viewportHeight="960">
|
|
||||||
<path
|
|
||||||
android:pathData="M382,720 L154,492l57,-57 171,171 367,-367 57,57 -424,424Z"
|
|
||||||
android:fillColor="#e8eaed"/>
|
|
||||||
</vector>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="960"
|
|
||||||
android:viewportHeight="960">
|
|
||||||
<path
|
|
||||||
android:pathData="M480,640q75,0 127.5,-52.5T660,460q0,-75 -52.5,-127.5T480,280q-75,0 -127.5,52.5T300,460q0,75 52.5,127.5T480,640ZM480,568q-45,0 -76.5,-31.5T372,460q0,-45 31.5,-76.5T480,352q45,0 76.5,31.5T588,460q0,45 -31.5,76.5T480,568ZM480,760q-146,0 -266,-81.5T40,460q54,-137 174,-218.5T480,160q146,0 266,81.5T920,460q-54,137 -174,218.5T480,760ZM480,460ZM480,680q113,0 207.5,-59.5T832,460q-50,-101 -144.5,-160.5T480,240q-113,0 -207.5,59.5T128,460q50,101 144.5,160.5T480,680Z"
|
|
||||||
android:fillColor="#e8eaed"/>
|
|
||||||
</vector>
|
|
10
app/src/main/res/layout/activity_notification.xml
Normal file
10
app/src/main/res/layout/activity_notification.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".activities.NotificationsActivity">
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,65 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/main"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".activities.NotificationsActivity">
|
|
||||||
|
|
||||||
<!-- Title Bar -->
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/titleBar"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:background="?android:attr/dividerHorizontal"
|
|
||||||
android:paddingStart="8dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
|
||||||
|
|
||||||
<!-- Back Button -->
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/btnBack"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:src="@drawable/ic_arrow_back"
|
|
||||||
android:contentDescription="@string/back"
|
|
||||||
app:tint="?android:attr/textColorPrimary" />
|
|
||||||
|
|
||||||
<!-- Title -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvTitle"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/notifications" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- RecyclerView for displaying notifications -->
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/rvNotifications"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/titleBar"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
tools:listitem="@layout/item_notification"
|
|
||||||
tools:itemCount="5"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:paddingBottom="24dp"
|
|
||||||
android:scrollbars="vertical"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
|
@ -1,91 +0,0 @@
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/main"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
android:clickable="true"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:elevation="16dp"
|
|
||||||
android:background="@drawable/bg_notification_item_ripple">
|
|
||||||
|
|
||||||
<!-- Read/Unread Indicator as vertical line -->
|
|
||||||
<View
|
|
||||||
android:id="@+id/notificationIndicator"
|
|
||||||
android:layout_width="4dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:background="@color/unreadIndicator"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
<!-- Message Text -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/notificationMessage"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
tools:text="Sample notification message"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="2"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/notificationIndicator"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
android:layout_marginLeft="12dp" />
|
|
||||||
|
|
||||||
<!-- Time Received -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/notificationTime"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
tools:text="2h ago"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textColor="@android:color/darker_gray"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/notificationMessage"
|
|
||||||
app:layout_constraintStart_toStartOf="@id/notificationMessage" />
|
|
||||||
|
|
||||||
<!-- Buttons for invitations -->
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/invitationActions"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/notificationTime"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/acceptButton"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:src="@drawable/ic_check"
|
|
||||||
android:contentDescription="@string/accept"
|
|
||||||
app:tint="@android:color/holo_green_dark" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/declineButton"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:src="@drawable/ic_close"
|
|
||||||
android:contentDescription="@string/decline"
|
|
||||||
app:tint="@android:color/holo_red_dark" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/viewEventButton"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:src="@drawable/ic_visibility"
|
|
||||||
android:contentDescription="@string/view_event"
|
|
||||||
app:tint="@android:color/holo_blue_dark" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
|
@ -7,5 +7,4 @@
|
||||||
<color name="splash_dark_background">#121212</color> <!-- Dark gray -->
|
<color name="splash_dark_background">#121212</color> <!-- Dark gray -->
|
||||||
|
|
||||||
<color name="event_indicator_color">#0035D0</color>
|
<color name="event_indicator_color">#0035D0</color>
|
||||||
<color name="unreadIndicator">#bb6633</color>
|
|
||||||
</resources>
|
</resources>
|
|
@ -75,8 +75,4 @@
|
||||||
<string name="choose_event_color">Choose Event Color</string>
|
<string name="choose_event_color">Choose Event Color</string>
|
||||||
<string name="select_time">Select Time</string>
|
<string name="select_time">Select Time</string>
|
||||||
<string name="manage_categories">Manage categories</string>
|
<string name="manage_categories">Manage categories</string>
|
||||||
<string name="accept">Accept</string>
|
|
||||||
<string name="decline">Decline</string>
|
|
||||||
<string name="notifications">Notifications</string>
|
|
||||||
<string name="view_event">View Event</string>
|
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue