diff --git a/app/MainActivity.kt b/app/MainActivity.kt
new file mode 100644
index 0000000..a90c857
--- /dev/null
+++ b/app/MainActivity.kt
@@ -0,0 +1,71 @@
+package ru.nsu.concertmate
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.core.content.ContextCompat
+import ru.nsu.concertmate.ui.screens.FavoriteCitiesScreen
+import ru.nsu.concertmate.ui.theme.ConcertMateTheme
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ enableEdgeToEdge()
+
+ // Запрос разрешения на уведомления для Android 13 и выше
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ requestNotificationPermission()
+ }
+
+ setContent {
+ FavoriteCitiesScreen()
+ }
+ }
+
+ private fun requestNotificationPermission() {
+ if (ContextCompat.checkSelfPermission(
+ this,
+ Manifest.permission.POST_NOTIFICATIONS
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ val requestPermissionLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ Log.d("Permission", "Разрешение на уведомления предоставлено")
+ } else {
+ Log.d("Permission", "Разрешение на уведомления отклонено")
+ }
+ }
+ requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+ }
+ }
+}
+
+@Composable
+fun Greeting(name: String, modifier: Modifier = Modifier) {
+ Text(
+ text = "Hello $name!",
+ modifier = modifier
+ )
+}
+
+@Preview(showBackground = true)
+@Composable
+fun GreetingPreview() {
+ ConcertMateTheme {
+ Greeting("Android")
+ }
+}
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index e250eb0..caa33c8 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,6 +1,7 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.jetbrains.kotlin.android)
+ alias(libs.plugins.google.gms.google.services)
}
android {
@@ -61,6 +62,8 @@ dependencies {
implementation(libs.androidx.material3)
implementation(libs.landscapist)
implementation(libs.landscapist.coil3.android)
+ implementation(libs.firebase.auth)
+ implementation(libs.firebase.messaging.ktx)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
diff --git a/app/google-services.json b/app/google-services.json
new file mode 100644
index 0000000..8c12f46
--- /dev/null
+++ b/app/google-services.json
@@ -0,0 +1,29 @@
+{
+ "project_info": {
+ "project_number": "320206274480",
+ "project_id": "concert-mate-ba47b",
+ "storage_bucket": "concert-mate-ba47b.firebasestorage.app"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:320206274480:android:6c650272877b1d9e472075",
+ "android_client_info": {
+ "package_name": "ru.nsu.concertmate"
+ }
+ },
+ "oauth_client": [],
+ "api_key": [
+ {
+ "current_key": "AIzaSyCVBbqQn3ESdsCJOn6BRj8f5Xj7VNvK01k"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": []
+ }
+ }
+ }
+ ],
+ "configuration_version": "1"
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0e1c19e..aec8010 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,11 @@
+
+
+
+
+
+ tools:targetApi="31">
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/app/src/main/java/ru/nsu/concertmate/MainActivity.kt b/app/src/main/java/ru/nsu/concertmate/MainActivity.kt
index 8577da4..a90c857 100644
--- a/app/src/main/java/ru/nsu/concertmate/MainActivity.kt
+++ b/app/src/main/java/ru/nsu/concertmate/MainActivity.kt
@@ -1,17 +1,71 @@
package ru.nsu.concertmate
+import android.Manifest
+import android.content.pm.PackageManager
+import android.os.Build
import android.os.Bundle
+import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
-import ru.nsu.concertmate.ui.screens.ConcertInfoScreen
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.core.content.ContextCompat
+import ru.nsu.concertmate.ui.screens.FavoriteCitiesScreen
+import ru.nsu.concertmate.ui.theme.ConcertMateTheme
+
class MainActivity : ComponentActivity() {
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
+
+ // Запрос разрешения на уведомления для Android 13 и выше
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ requestNotificationPermission()
+ }
+
setContent {
- ConcertInfoScreen()
+ FavoriteCitiesScreen()
}
}
+
+ private fun requestNotificationPermission() {
+ if (ContextCompat.checkSelfPermission(
+ this,
+ Manifest.permission.POST_NOTIFICATIONS
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ val requestPermissionLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ Log.d("Permission", "Разрешение на уведомления предоставлено")
+ } else {
+ Log.d("Permission", "Разрешение на уведомления отклонено")
+ }
+ }
+ requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+ }
+ }
+}
+
+@Composable
+fun Greeting(name: String, modifier: Modifier = Modifier) {
+ Text(
+ text = "Hello $name!",
+ modifier = modifier
+ )
+}
+
+@Preview(showBackground = true)
+@Composable
+fun GreetingPreview() {
+ ConcertMateTheme {
+ Greeting("Android")
+ }
}
diff --git a/app/src/main/java/ru/nsu/concertmate/MyFirebaseMessagingService.kt b/app/src/main/java/ru/nsu/concertmate/MyFirebaseMessagingService.kt
new file mode 100644
index 0000000..9549aad
--- /dev/null
+++ b/app/src/main/java/ru/nsu/concertmate/MyFirebaseMessagingService.kt
@@ -0,0 +1,111 @@
+package ru.nsu.concertmate
+
+import android.Manifest
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Build
+import android.util.Log
+import androidx.core.app.ActivityCompat
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.content.ContextCompat
+import com.google.firebase.messaging.FirebaseMessagingService
+import com.google.firebase.messaging.RemoteMessage
+
+class MyFirebaseMessagingService : FirebaseMessagingService() {
+
+ companion object {
+ private const val TAG = "MyFirebaseMessaging"
+ private const val CHANNEL_ID = "FCM_Channel"
+ private const val CHANNEL_NAME = "Default Channel"
+ private const val CHANNEL_DESCRIPTION = "Firebase Cloud Messaging Notifications"
+ }
+
+ override fun onNewToken(token: String) {
+ super.onNewToken(token)
+ Log.d(TAG, "New token: $token")
+
+ // Сохраняем токен с помощью Preferences
+ Preferences.setAccessToken(applicationContext, token)
+
+ // Отправляем токен на сервер
+ sendTokenToServer(token)
+ }
+
+ override fun onMessageReceived(remoteMessage: RemoteMessage) {
+ super.onMessageReceived(remoteMessage)
+
+ // Обрабатываем уведомление
+ remoteMessage.notification?.let { notification ->
+ Log.d(TAG, "Notification received: ${notification.title}, ${notification.body}")
+ showNotification(notification.title, notification.body)
+ }
+
+ // Обрабатываем пользовательские данные
+ if (remoteMessage.data.isNotEmpty()) {
+ Log.d(TAG, "Data payload: ${remoteMessage.data}")
+ handleDataPayload(remoteMessage.data)
+ }
+ }
+
+ private fun sendTokenToServer(token: String) {
+ // Пример API вызова или обновления на сервере
+ Log.d(TAG, "Token sent to server: $token")
+ }
+
+ private fun handleDataPayload(data: Map) {
+ // Обрабатываем пользовательские данные
+ Log.d(TAG, "Handling custom data payload: $data")
+ }
+
+ private fun showNotification(title: String?, message: String?) {
+ val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+
+ // Создаём канал уведомлений для Android 8.0 и выше
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channel = NotificationChannel(
+ CHANNEL_ID,
+ CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_HIGH
+ ).apply {
+ description = CHANNEL_DESCRIPTION
+ }
+ notificationManager.createNotificationChannel(channel)
+ }
+
+ // Создаём интент для открытия MainActivity
+ val intent = Intent(this, MainActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ }
+ val pendingIntent = PendingIntent.getActivity(
+ this, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+
+ // Строим уведомление
+ val notification = NotificationCompat.Builder(this, CHANNEL_ID)
+ .setContentTitle(title ?: "New Notification")
+ .setContentText(message ?: "You have a new message.")
+ .setSmallIcon(R.drawable.ic_launcher_foreground) // Замените на иконку вашего приложения
+ .setColor(ContextCompat.getColor(this, R.color.teal_200)) // Необязательный цвет
+ .setAutoCancel(true)
+ .setContentIntent(pendingIntent)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setDefaults(NotificationCompat.DEFAULT_ALL)
+ .build()
+
+ // Показываем уведомление
+ if (ActivityCompat.checkSelfPermission(
+ this,
+ Manifest.permission.POST_NOTIFICATIONS
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ return
+ }
+ NotificationManagerCompat.from(this).notify(System.currentTimeMillis().toInt(), notification)
+ }
+}
diff --git a/app/src/main/java/ru/nsu/concertmate/Preferences.kt b/app/src/main/java/ru/nsu/concertmate/Preferences.kt
new file mode 100644
index 0000000..210293f
--- /dev/null
+++ b/app/src/main/java/ru/nsu/concertmate/Preferences.kt
@@ -0,0 +1,24 @@
+package ru.nsu.concertmate
+
+import android.content.Context
+import android.content.SharedPreferences
+
+object Preferences {
+
+ private const val PREF_NAME = "MySharedPref"
+ private const val ACCESS_TOKEN_KEY = "ACCESSTOKEN"
+
+ // Сохранить токен
+ fun setAccessToken(context: Context, token: String?) {
+ val sharedPreferences: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
+ val editor = sharedPreferences.edit()
+ editor.putString(ACCESS_TOKEN_KEY, token)
+ editor.apply()
+ }
+
+ // Извлечь токен
+ fun getAccessToken(context: Context): String? {
+ val sharedPreferences: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
+ return sharedPreferences.getString(ACCESS_TOKEN_KEY, null)
+ }
+}
diff --git a/build.gradle.kts b/build.gradle.kts
index f74b04b..932a7ae 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,4 +2,5 @@
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.jetbrains.kotlin.android) apply false
+ alias(libs.plugins.google.gms.google.services) apply false
}
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 482d682..dd4b048 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -11,6 +11,9 @@ lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3"
composeBom = "2024.04.01"
uiTextGoogleFonts = "1.7.6"
+googleGmsGoogleServices = "4.4.2"
+firebaseAuth = "23.1.0"
+firebaseMessagingKtx = "24.1.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -30,8 +33,11 @@ androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
landscapist = { module = "com.github.skydoves:landscapist", version.ref = "landscapist" }
landscapist-coil3-android = { module = "com.github.skydoves:landscapist-coil3-android", version.ref = "landscapistCoil3Android" }
+firebase-auth = { group = "com.google.firebase", name = "firebase-auth", version.ref = "firebaseAuth" }
+firebase-messaging-ktx = { group = "com.google.firebase", name = "firebase-messaging-ktx", version.ref = "firebaseMessagingKtx" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+google-gms-google-services = { id = "com.google.gms.google-services", version.ref = "googleGmsGoogleServices" }