Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions app/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -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")
}
}
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.jetbrains.kotlin.android)
alias(libs.plugins.google.gms.google.services)
}

android {
Expand Down Expand Up @@ -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)
Expand Down
29 changes: 29 additions & 0 deletions app/google-services.json
Original file line number Diff line number Diff line change
@@ -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"
}
28 changes: 19 additions & 9 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<!-- Required permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -11,23 +16,28 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ConcertMate"
tools:targetApi="31"
android:name="ru.nsu.concertmate.App">
tools:targetApi="31">


<!-- Main activity -->
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.ConcertMate">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:host="myownpersonaldomain.com" />
</intent-filter>
</activity>

<!-- Firebase Messaging Service -->
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>

</manifest>
</manifest>
58 changes: 56 additions & 2 deletions app/src/main/java/ru/nsu/concertmate/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -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")
}
}
111 changes: 111 additions & 0 deletions app/src/main/java/ru/nsu/concertmate/MyFirebaseMessagingService.kt
Original file line number Diff line number Diff line change
@@ -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<String, String>) {
// Обрабатываем пользовательские данные
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)
}
}
24 changes: 24 additions & 0 deletions app/src/main/java/ru/nsu/concertmate/Preferences.kt
Original file line number Diff line number Diff line change
@@ -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)
}
}
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading