mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-09 08:52:21 +00:00
add push notif firebase
This commit is contained in:
8
.idea/appInsightsSettings.xml
generated
8
.idea/appInsightsSettings.xml
generated
@ -8,10 +8,10 @@
|
||||
<InsightsFilterSettings>
|
||||
<option name="connection">
|
||||
<ConnectionSetting>
|
||||
<option name="appId" value="PLACEHOLDER" />
|
||||
<option name="mobileSdkAppId" value="" />
|
||||
<option name="projectId" value="" />
|
||||
<option name="projectNumber" value="" />
|
||||
<option name="appId" value="com.alya.ecommerce_serang" />
|
||||
<option name="mobileSdkAppId" value="1:284675201257:android:2755670e3dbb1b48683878" />
|
||||
<option name="projectId" value="ecommerce-serang" />
|
||||
<option name="projectNumber" value="284675201257" />
|
||||
</ConnectionSetting>
|
||||
</option>
|
||||
<option name="signal" value="SIGNAL_UNSPECIFIED" />
|
||||
|
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.9.0" />
|
||||
<option name="version" value="2.1.0" />
|
||||
</component>
|
||||
</project>
|
@ -6,6 +6,7 @@ plugins {
|
||||
id("androidx.navigation.safeargs")
|
||||
id("kotlin-parcelize")
|
||||
alias(libs.plugins.dagger.hilt) // Use alias from catalog
|
||||
id("com.google.gms.google-services")
|
||||
}
|
||||
|
||||
val localProperties = Properties().apply {
|
||||
@ -22,8 +23,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.alya.ecommerce_serang"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
@ -119,6 +120,11 @@ dependencies {
|
||||
|
||||
implementation("io.socket:socket.io-client:2.1.0") // or latest version
|
||||
|
||||
//fcm token
|
||||
implementation(platform("com.google.firebase:firebase-bom:33.13.0"))
|
||||
implementation("com.google.firebase:firebase-analytics")
|
||||
implementation("com.google.firebase:firebase-messaging-ktx")
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
29
app/google-services.json
Normal file
29
app/google-services.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "284675201257",
|
||||
"project_id": "ecommerce-serang",
|
||||
"storage_bucket": "ecommerce-serang.firebasestorage.app"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:284675201257:android:2755670e3dbb1b48683878",
|
||||
"android_client_info": {
|
||||
"package_name": "com.alya.ecommerce_serang"
|
||||
}
|
||||
},
|
||||
"oauth_client": [],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyB-nWHsVbdV4PPIH06JZSStIVXjv9Qc4iU"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
@ -61,6 +61,7 @@
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="dataSync" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.profile.mystore.profile.shipping_service.ShippingServiceActivity"
|
||||
android:exported="false"/>
|
||||
@ -148,6 +149,25 @@
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name=".ui.notif.fcm.FCMService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@drawable/outline_notifications_24" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_color"
|
||||
android:resource="@color/blue_500" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||
android:value="fcm_default_channel" />
|
||||
</application>
|
||||
|
||||
|
||||
|
||||
</manifest>
|
@ -1,18 +1,48 @@
|
||||
package com.alya.ecommerce_serang.app
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
@HiltAndroidApp
|
||||
class App : Application(){
|
||||
// override fun onCreate() {
|
||||
// super.onCreate()
|
||||
//
|
||||
// val sessionManager = SessionManager(this)
|
||||
// if (sessionManager.getUserId() != null) {
|
||||
// val serviceIntent = Intent(this, SimpleWebSocketService::class.java)
|
||||
// startService(serviceIntent)
|
||||
// }
|
||||
// }
|
||||
private val TAG = "AppSerang"
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
// Initialize Firebase
|
||||
FirebaseApp.initializeApp(this)
|
||||
|
||||
// Request FCM token at app startup
|
||||
retrieveFCMToken()
|
||||
}
|
||||
|
||||
private fun retrieveFCMToken() {
|
||||
FirebaseMessaging.getInstance().token
|
||||
.addOnCompleteListener { task ->
|
||||
if (!task.isSuccessful) {
|
||||
Log.e(TAG, "Failed to get FCM token", task.exception)
|
||||
return@addOnCompleteListener
|
||||
}
|
||||
|
||||
val token = task.result
|
||||
Log.d(TAG, "FCM token retrieved: $token")
|
||||
|
||||
// Save token locally
|
||||
val sharedPreferences = getSharedPreferences("FCM_PREFS", Context.MODE_PRIVATE)
|
||||
sharedPreferences.edit().putString("FCM_TOKEN", token).apply()
|
||||
|
||||
// Send to your server
|
||||
sendTokenToServer(token)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendTokenToServer(token: String) {
|
||||
// TODO: Implement your API call
|
||||
Log.d(TAG, "Would send token to server: $token")
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import com.alya.ecommerce_serang.data.api.dto.ShippingServiceRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.StoreAddressResponse
|
||||
import com.alya.ecommerce_serang.data.api.dto.UpdateCart
|
||||
import com.alya.ecommerce_serang.data.api.dto.UpdateChatRequest
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.CheckStoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse
|
||||
@ -81,6 +82,15 @@ interface ApiService {
|
||||
@Body registerRequest: RegisterRequest
|
||||
): Response<RegisterResponse>
|
||||
|
||||
@GET("checkstore")
|
||||
suspend fun checkStore (): Response<CheckStoreResponse>
|
||||
|
||||
@Multipart
|
||||
@POST("registerstore")
|
||||
suspend fun registerStore(
|
||||
|
||||
): Response<>
|
||||
|
||||
@POST("otp")
|
||||
suspend fun getOTP(
|
||||
@Body otpRequest: OtpRequest
|
||||
|
@ -131,102 +131,6 @@ class UserRepository(private val apiService: ApiService) {
|
||||
}
|
||||
}
|
||||
|
||||
// suspend fun sendChatMessage(
|
||||
// storeId: Int,
|
||||
// message: String,
|
||||
// productId: Int,
|
||||
// imageFile: File? = null
|
||||
// ): Result<SendChatResponse> {
|
||||
// return try {
|
||||
// // Create multipart request builder
|
||||
// val requestBodyBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
//
|
||||
// // Add text fields
|
||||
// requestBodyBuilder.addFormDataPart("store_id", storeId.toString())
|
||||
// requestBodyBuilder.addFormDataPart("message", message)
|
||||
// requestBodyBuilder.addFormDataPart("product_id", productId.toString())
|
||||
//
|
||||
// // Add image if it exists
|
||||
// if (imageFile != null && imageFile.exists()) {
|
||||
// val requestFile = imageFile.asRequestBody("image/*".toMediaTypeOrNull())
|
||||
// requestBodyBuilder.addFormDataPart("chatimg", imageFile.name, requestFile)
|
||||
// }
|
||||
//
|
||||
// // Build the final request body
|
||||
// val requestBody = requestBodyBuilder.build()
|
||||
//
|
||||
// // Make the API call using a custom endpoint that takes a plain MultipartBody
|
||||
// val response = apiService.sendChatLineWithBody(requestBody)
|
||||
//
|
||||
// if (response.isSuccessful) {
|
||||
// response.body()?.let {
|
||||
// Result.Success(it)
|
||||
// } ?: Result.Error(Exception("Send chat response is empty"))
|
||||
// } else {
|
||||
// val errorBody = response.errorBody()?.string() ?: "Unknown error"
|
||||
// Log.e("ChatRepository", "HTTP Error: ${response.code()}, Body: $errorBody")
|
||||
// Result.Error(Exception("API Error: ${response.code()} - $errorBody"))
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Log.e("ChatRepository", "Exception sending message", e)
|
||||
// e.printStackTrace()
|
||||
// Result.Error(e)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Updates the status of a message (sent, delivered, read)
|
||||
// *
|
||||
// * @param messageId The ID of the message to update
|
||||
// * @param status The new status to set
|
||||
// * @return Result containing the updated message details or error
|
||||
// */
|
||||
// suspend fun updateMessageStatus(
|
||||
// messageId: Int,
|
||||
// status: String
|
||||
// ): Result<UpdateChatResponse> {
|
||||
// return try {
|
||||
// val requestBody = UpdateChatRequest(
|
||||
// id = messageId,
|
||||
// status = status
|
||||
// )
|
||||
//
|
||||
// val response = apiService.updateChatStatus(requestBody)
|
||||
//
|
||||
// if (response.isSuccessful) {
|
||||
// response.body()?.let {
|
||||
// Result.Success(it)
|
||||
// } ?: Result.Error(Exception("Update status response is empty"))
|
||||
// } else {
|
||||
// Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error"))
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Result.Error(e)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Gets the chat history for a specific chat room
|
||||
// *
|
||||
// * @param chatRoomId The ID of the chat room
|
||||
// * @return Result containing the list of chat messages or error
|
||||
// */
|
||||
// suspend fun getChatHistory(chatRoomId: Int): Result<ChatHistoryResponse> {
|
||||
// return try {
|
||||
// val response = apiService.getChatDetail(chatRoomId)
|
||||
//
|
||||
// if (response.isSuccessful) {
|
||||
// response.body()?.let {
|
||||
// Result.Success(it)
|
||||
// } ?: Result.Error(Exception("Chat history response is empty"))
|
||||
// } else {
|
||||
// Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error"))
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Result.Error(e)
|
||||
// }
|
||||
// }
|
||||
|
||||
companion object{
|
||||
private const val TAG = "UserRepository"
|
||||
}
|
||||
|
@ -0,0 +1,81 @@
|
||||
package com.alya.ecommerce_serang.ui.notif.fcm
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.google.firebase.messaging.FirebaseMessagingService
|
||||
import com.google.firebase.messaging.RemoteMessage
|
||||
|
||||
class FCMService : FirebaseMessagingService() {
|
||||
private val TAG = "FCMService"
|
||||
|
||||
override fun onNewToken(token: String) {
|
||||
super.onNewToken(token)
|
||||
Log.d(TAG, "Refreshed FCM token: $token")
|
||||
|
||||
// Store the token locally
|
||||
storeTokenLocally(token)
|
||||
|
||||
// Send token to your server
|
||||
sendTokenToServer(token)
|
||||
}
|
||||
|
||||
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
||||
super.onMessageReceived(remoteMessage)
|
||||
Log.d(TAG, "From: ${remoteMessage.from}")
|
||||
|
||||
// Handle data payload
|
||||
if (remoteMessage.data.isNotEmpty()) {
|
||||
Log.d(TAG, "Message data payload: ${remoteMessage.data}")
|
||||
// Process data payload if needed
|
||||
}
|
||||
|
||||
// Handle notification payload
|
||||
remoteMessage.notification?.let {
|
||||
Log.d(TAG, "Message notification: ${it.title} / ${it.body}")
|
||||
showNotification(it.title, it.body)
|
||||
}
|
||||
}
|
||||
|
||||
private fun storeTokenLocally(token: String) {
|
||||
val sharedPreferences = getSharedPreferences("FCM_PREFS", Context.MODE_PRIVATE)
|
||||
sharedPreferences.edit().putString("FCM_TOKEN", token).apply()
|
||||
}
|
||||
|
||||
private fun sendTokenToServer(token: String) {
|
||||
// TODO: Implement API call to your server to send the token
|
||||
// This is a placeholder - you'll need to replace with actual API call to your server
|
||||
Log.d(TAG, "Token would be sent to server: $token")
|
||||
}
|
||||
|
||||
private fun showNotification(title: String?, body: String?) {
|
||||
val channelId = "fcm_default_channel"
|
||||
|
||||
// Create notification channel for Android O and above
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(
|
||||
channelId,
|
||||
"FCM Notifications",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
)
|
||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
// Build notification
|
||||
val notificationBuilder = NotificationCompat.Builder(this, channelId)
|
||||
.setSmallIcon(R.drawable.outline_notifications_24) // Make sure this resource exists
|
||||
.setContentTitle(title ?: "New Message")
|
||||
.setContentText(body ?: "You have a new notification")
|
||||
.setAutoCancel(true)
|
||||
|
||||
// Show notification
|
||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val notificationId = System.currentTimeMillis().toInt()
|
||||
notificationManager.notify(notificationId, notificationBuilder.build())
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.alya.ecommerce_serang.ui.notif.fcm
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
|
||||
object FCMTokenManager {
|
||||
private const val TAG = "FCMTokenManager"
|
||||
|
||||
fun getToken(callback: (String?) -> Unit) {
|
||||
FirebaseMessaging.getInstance().token
|
||||
.addOnCompleteListener { task ->
|
||||
if (!task.isSuccessful) {
|
||||
Log.e(TAG, "Failed to get FCM token", task.exception)
|
||||
callback(null)
|
||||
return@addOnCompleteListener
|
||||
}
|
||||
|
||||
val token = task.result
|
||||
Log.d(TAG, "FCM token retrieved: $token")
|
||||
callback(token)
|
||||
}
|
||||
}
|
||||
|
||||
fun getStoredToken(context: Context): String? {
|
||||
val sharedPreferences = context.getSharedPreferences("FCM_PREFS", Context.MODE_PRIVATE)
|
||||
return sharedPreferences.getString("FCM_TOKEN", null)
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.Store
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
@ -19,12 +20,10 @@ import com.alya.ecommerce_serang.ui.profile.mystore.product.ProductActivity
|
||||
import com.alya.ecommerce_serang.ui.profile.mystore.profile.DetailStoreProfileActivity
|
||||
import com.alya.ecommerce_serang.ui.profile.mystore.review.ReviewFragment
|
||||
import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsActivity
|
||||
import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsListFragment
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.MyStoreViewModel
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlin.getValue
|
||||
|
||||
class MyStoreActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityMyStoreBinding
|
||||
@ -68,7 +67,7 @@ class MyStoreActivity : AppCompatActivity() {
|
||||
binding.tvStoreType.text = store.storeType
|
||||
|
||||
if (store.storeImage != null && store.storeImage.toString().isNotEmpty() && store.storeImage.toString() != "null") {
|
||||
val imageUrl = "http://192.168.100.156:3000${store.storeImage}"
|
||||
val imageUrl = "$BASE_URL${store.storeImage}"
|
||||
Log.d("MyStoreActivity", "Loading store image from: $imageUrl")
|
||||
|
||||
Glide.with(this)
|
||||
|
@ -1,7 +1,12 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath ("androidx.navigation:navigation-safe-args-gradle-plugin:2.5.1")
|
||||
classpath ("com.google.gms:google-services:4.4.2")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
[versions]
|
||||
agp = "8.9.2"
|
||||
glide = "4.16.0"
|
||||
hiltAndroid = "2.48" # Updated from 2.44 for better compatibility
|
||||
hiltAndroid = "2.56.2" # Updated from 2.44 for better compatibility
|
||||
hiltLifecycleViewmodel = "1.0.0-alpha03"
|
||||
hiltCompiler = "2.48" # Added for consistency
|
||||
ksp = "1.9.0-1.0.13"
|
||||
kotlin = "1.9.0"
|
||||
hiltCompiler = "2.56.2" # Added for consistency
|
||||
ksp = "2.1.0-1.0.28"
|
||||
kotlin = "2.1.0"
|
||||
|
||||
coreKtx = "1.10.1"
|
||||
coreKtx = "1.16.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.2.1"
|
||||
espressoCore = "3.6.1"
|
||||
@ -19,7 +19,7 @@ constraintlayout = "2.1.4"
|
||||
legacySupportV4 = "1.0.0"
|
||||
lifecycleLivedataKtx = "2.8.7"
|
||||
lifecycleViewmodelKtx = "2.8.7"
|
||||
fragmentKtx = "1.5.6"
|
||||
fragmentKtx = "1.7.0"
|
||||
navigationFragmentKtx = "2.8.5"
|
||||
navigationUiKtx = "2.8.5"
|
||||
playServicesLocation = "21.3.0"
|
||||
|
Reference in New Issue
Block a user