From 828112549077565a9b76e8dece8b9175002a48ce Mon Sep 17 00:00:00 2001
From: Skeletrobro <67814129+baronhsieh2005@users.noreply.github.com>
Date: Mon, 11 Nov 2024 20:37:00 -0500
Subject: [PATCH 1/6] Draft for notifs
---
PennMobile/build.gradle | 1 +
PennMobile/src/main/AndroidManifest.xml | 14 +++-
.../pennapps/labs/pennmobile/MainActivity.kt | 35 ++++++++++
.../notifications/PushNotificationService.kt | 67 +++++++++++++++++++
.../baseline_circle_notifications_24.xml | 5 ++
PennMobile/src/main/res/values/colors.xml | 1 +
gradle/libs.versions.toml | 4 +-
7 files changed, 125 insertions(+), 2 deletions(-)
create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/notifications/PushNotificationService.kt
create mode 100644 PennMobile/src/main/res/drawable/baseline_circle_notifications_24.xml
diff --git a/PennMobile/build.gradle b/PennMobile/build.gradle
index 77e706227..1c8b04e9b 100644
--- a/PennMobile/build.gradle
+++ b/PennMobile/build.gradle
@@ -99,6 +99,7 @@ dependencies {
testImplementation platform(libs.androidx.compose.bom)
androidTestImplementation platform(libs.androidx.compose.bom)
implementation platform(libs.firebase.bom)
+ implementation(libs.firebase.messaging)
implementation libs.bundles.compose
implementation libs.bundles.material
diff --git a/PennMobile/src/main/AndroidManifest.xml b/PennMobile/src/main/AndroidManifest.xml
index 27cc004da..a28b75aaa 100644
--- a/PennMobile/src/main/AndroidManifest.xml
+++ b/PennMobile/src/main/AndroidManifest.xml
@@ -18,6 +18,7 @@
+
-
+
+
+
+
+
@@ -65,6 +71,12 @@
+
+
+ if (isGranted) {
+ // FCM SDK (and your app) can post notifications.
+ } else {
+ // TODO: Inform user that that your app will not show notifications.
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
if (Build.VERSION.SDK_INT > 28) {
setTheme(R.style.DarkModeApi29)
@@ -91,6 +106,7 @@ class MainActivity : AppCompatActivity() {
setTheme(R.style.DarkBackground)
}
Utils.getCurrentSystemTime()
+ askNotificationPermission()
setSupportActionBar(binding.include.toolbar)
fragmentManager = supportFragmentManager
@@ -132,6 +148,25 @@ class MainActivity : AppCompatActivity() {
}
}
+ private fun askNotificationPermission() {
+ // This is only necessary for API level >= 33 (TIRAMISU)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) ==
+ PackageManager.PERMISSION_GRANTED
+ ) {
+ // FCM SDK (and your app) can post notifications.
+ } else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
+ // TODO: display an educational UI explaining to the user the features that will be enabled
+ // by them granting the POST_NOTIFICATION permission. This UI should provide the user
+ // "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission.
+ // If the user selects "No thanks," allow the user to continue without notifications.
+ } else {
+ // Directly ask for the permission
+ requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+ }
+ }
+ }
+
private fun onExpandableBottomNavigationItemSelected() {
binding.include.expandableBottomBar.setOnNavigationItemSelectedListener { item ->
val position =
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/notifications/PushNotificationService.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/notifications/PushNotificationService.kt
new file mode 100644
index 000000000..179a1ea0a
--- /dev/null
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/notifications/PushNotificationService.kt
@@ -0,0 +1,67 @@
+package com.pennapps.labs.pennmobile.notifications
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.graphics.BitmapFactory
+import android.util.Log
+import androidx.core.app.NotificationCompat
+import com.google.firebase.messaging.FirebaseMessagingService
+import com.google.firebase.messaging.RemoteMessage
+import com.pennapps.labs.pennmobile.MainActivity
+import com.pennapps.labs.pennmobile.R
+import java.net.URL
+
+class PushNotificationService : FirebaseMessagingService() {
+ override fun onNewToken(token: String) {
+ super.onNewToken(token)
+ // Update Server/Database
+ Log.d("FCM Registration", "Refreshed token: $token")
+ }
+
+ override fun onMessageReceived(message: RemoteMessage) {
+ super.onMessageReceived(message)
+ Log.d("Notification", "Notification received!")
+ val title = message.notification?.title
+ val body = message.notification?.body
+ val imageUrl = message.notification?.imageUrl
+
+ val notificationManager =
+ getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+
+ val notificationChannel =
+ NotificationChannel(
+ "MAIN_CHANNEL",
+ "Main Channel",
+ NotificationManager.IMPORTANCE_HIGH,
+ )
+ notificationManager.createNotificationChannel(notificationChannel)
+
+ val mainActivityIntent = Intent(this, MainActivity::class.java)
+ mainActivityIntent.apply {
+ flags += Intent.FLAG_ACTIVITY_NEW_TASK
+ flags += Intent.FLAG_ACTIVITY_CLEAR_TOP
+ }
+ val pendingIntent = PendingIntent.getActivity(this, 0, mainActivityIntent, PendingIntent.FLAG_IMMUTABLE)
+
+ val notificationBuilder =
+ NotificationCompat
+ .Builder(this, "MAIN_CHANNEL")
+ .setContentTitle(title)
+ .setContentText(body)
+ .setSmallIcon(R.drawable.baseline_circle_notifications_24)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setContentIntent(pendingIntent)
+ .setAutoCancel(true)
+
+ imageUrl?.let {
+ val bitmap = BitmapFactory.decodeStream(URL(imageUrl.toString()).openConnection().getInputStream())
+ notificationBuilder.setLargeIcon(bitmap)
+ notificationBuilder.setStyle(NotificationCompat.BigPictureStyle().bigPicture(bitmap))
+ }
+
+ notificationManager.notify(1, notificationBuilder.build())
+ }
+}
diff --git a/PennMobile/src/main/res/drawable/baseline_circle_notifications_24.xml b/PennMobile/src/main/res/drawable/baseline_circle_notifications_24.xml
new file mode 100644
index 000000000..68c2c90a0
--- /dev/null
+++ b/PennMobile/src/main/res/drawable/baseline_circle_notifications_24.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/PennMobile/src/main/res/values/colors.xml b/PennMobile/src/main/res/values/colors.xml
index 8613609bb..268201e90 100644
--- a/PennMobile/src/main/res/values/colors.xml
+++ b/PennMobile/src/main/res/values/colors.xml
@@ -72,5 +72,6 @@
#FF81D4FA
#FF039BE5
#FF01579B
+ #990000
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 98fb1f815..d19bd425d 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -18,9 +18,10 @@ coordinatorlayout = "1.2.0"
customalertviewdialogue = "a1fc69d54d"
espressoCore = "3.5.0"
exifinterface = "1.3.6"
-firebaseBom = "31.5.0"
+firebaseBom = "33.5.1"
firebaseCrashlyticsKtx = "18.6.0"
firebaseCrashalytics = "2.9.9"
+firebaseMessaging = "24.0.3"
glanceAppwidget = "1.1.0"
glide = "4.11.0"
googleMapsServices = "2.2.0"
@@ -103,6 +104,7 @@ firebase-analytics = { module = "com.google.firebase:firebase-analytics" }
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" }
firebase-crashlytics-ktx = { module = "com.google.firebase:firebase-crashlytics-ktx", version.ref = "firebaseCrashlyticsKtx" }
+firebase-messaging = { module = "com.google.firebase:firebase-messaging", version.ref = "firebaseMessaging" }
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
google-maps-services = { module = "com.google.maps:google-maps-services", version.ref = "googleMapsServices" }
joda-time = { module = "joda-time:joda-time", version.ref = "jodaTime" }
From 579101584958ebdad8c74090322aaffd398a685b Mon Sep 17 00:00:00 2001
From: Skeletrobro <67814129+baronhsieh2005@users.noreply.github.com>
Date: Mon, 18 Nov 2024 12:58:18 -0500
Subject: [PATCH 2/6] Notif Networking
---
PennMobile/src/main/AndroidManifest.xml | 11 ++-
.../pennapps/labs/pennmobile/MainActivity.kt | 27 ++++++++
.../labs/pennmobile/api/NotificationAPI.kt | 22 ++++++
.../api/fragments/LoginWebviewFragment.kt | 23 +++++++
.../api/viewmodels/LoginWebviewViewmodel.kt | 29 ++++++++
.../more/fragments/SettingsFragment.kt | 69 +++++++++++++------
.../more/viewmodels/SettingsViewModel.kt | 26 +++++++
.../notifications/PushNotificationService.kt | 30 +++++---
PennMobile/src/main/res/values/strings.xml | 4 ++
9 files changed, 205 insertions(+), 36 deletions(-)
create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/NotificationAPI.kt
create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/viewmodels/LoginWebviewViewmodel.kt
create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/SettingsViewModel.kt
diff --git a/PennMobile/src/main/AndroidManifest.xml b/PennMobile/src/main/AndroidManifest.xml
index a28b75aaa..c41732ad7 100644
--- a/PennMobile/src/main/AndroidManifest.xml
+++ b/PennMobile/src/main/AndroidManifest.xml
@@ -22,6 +22,7 @@
+ android:name="com.google.firebase.messaging.default_notification_channel_id"
+ android:value="default_channel_id"/>
+ android:resource="@mipmap/ic_launcher_foreground" />
+
+
+
+ @DELETE("user/notifications/tokens/android/{token}/")
+ suspend fun deleteNotificationToken(
+ @Header("Authorization") bearerToken: String,
+ @Path("token") token: String,
+ ): Response
+}
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/fragments/LoginWebviewFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/fragments/LoginWebviewFragment.kt
index 904c49cb3..0481418d7 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/fragments/LoginWebviewFragment.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/fragments/LoginWebviewFragment.kt
@@ -17,6 +17,8 @@ import android.widget.Button
import android.widget.LinearLayout
import android.widget.Toast
import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
@@ -29,6 +31,9 @@ import com.pennapps.labs.pennmobile.api.StudentLife
import com.pennapps.labs.pennmobile.api.classes.AccessTokenResponse
import com.pennapps.labs.pennmobile.api.classes.Account
import com.pennapps.labs.pennmobile.api.classes.GetUserResponse
+import com.pennapps.labs.pennmobile.api.viewmodels.LoginWebviewViewmodel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
import org.apache.commons.lang3.RandomStringUtils
import retrofit.Callback
import retrofit.RetrofitError
@@ -55,6 +60,7 @@ class LoginWebviewFragment : Fragment() {
lateinit var platformAuthUrl: String
lateinit var clientID: String
lateinit var redirectUri: String
+ private val loginWebviewViewmodel: LoginWebviewViewmodel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
@@ -206,6 +212,7 @@ class LoginWebviewFragment : Fragment() {
editor.putLong(getString(R.string.token_expires_at), currentTime + expiresInInt)
editor.apply()
getUser(accessToken)
+ sendNotifToken()
}
}
@@ -260,6 +267,22 @@ class LoginWebviewFragment : Fragment() {
}
}
+ private fun sendNotifToken() {
+ val mNotificationAPI = MainActivity.notificationAPIInstance
+
+ mActivity.mNetworkManager.getAccessToken {
+ val bearerToken = "Bearer " + sp.getString(getString(R.string.access_token), "").toString()
+ val notifToken = sp.getString(getString(R.string.notification_token), "").toString()
+
+ Log.d("Notification Token", notifToken)
+ val notGuest = !sp.getBoolean(mActivity.getString(R.string.guest_mode), false)
+
+ lifecycleScope.launch(Dispatchers.IO) {
+ loginWebviewViewmodel.sendToken(mNotificationAPI, notGuest, bearerToken, notifToken)
+ }
+ }
+ }
+
private fun getCodeChallenge(codeVerifier: String): String {
// Hash the code verifier
val md = MessageDigest.getInstance("SHA-256")
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/viewmodels/LoginWebviewViewmodel.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/viewmodels/LoginWebviewViewmodel.kt
new file mode 100644
index 000000000..b8bf6dfe0
--- /dev/null
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/viewmodels/LoginWebviewViewmodel.kt
@@ -0,0 +1,29 @@
+package com.pennapps.labs.pennmobile.api.viewmodels
+
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import com.pennapps.labs.pennmobile.api.NotificationAPI
+
+// Currently only include logic for notifications, would add more network handling afterwards (TBD)
+
+class LoginWebviewViewmodel : ViewModel() {
+ suspend fun sendToken(
+ mNotificationAPI: NotificationAPI,
+ notGuest: Boolean,
+ bearerToken: String,
+ notifToken: String,
+ ) {
+ try {
+ if (notGuest) {
+ val response = mNotificationAPI.sendNotificationToken(bearerToken, notifToken)
+ if (response.isSuccessful) {
+ Log.i("Notification Token", "Successfully updated token")
+ } else {
+ Log.i("Notification Token", "Error updating token: ${response.code()} ${response.message()}")
+ }
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+}
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/SettingsFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/SettingsFragment.kt
index 0760ed6ec..a0733d60b 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/SettingsFragment.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/SettingsFragment.kt
@@ -4,6 +4,7 @@ import android.app.AlertDialog
import android.app.Dialog
import android.content.Context
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -12,15 +13,21 @@ import android.webkit.CookieManager
import android.widget.Button
import android.widget.EditText
import androidx.appcompat.view.ContextThemeWrapper
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import com.pennapps.labs.pennmobile.MainActivity
import com.pennapps.labs.pennmobile.R
+import com.pennapps.labs.pennmobile.more.viewmodels.SettingsViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
class SettingsFragment : PreferenceFragmentCompat() {
private var accountSettings: Preference? = null
private var logInOutButton: Preference? = null
+ private val settingsViewModel: SettingsViewModel by viewModels()
private lateinit var mActivity: MainActivity
private lateinit var mContext: Context
@@ -49,6 +56,8 @@ class SettingsFragment : PreferenceFragmentCompat() {
) {
super.onViewCreated(view, savedInstanceState)
val sp = PreferenceManager.getDefaultSharedPreferences(mActivity)
+ val bearerToken = "Bearer " + sp.getString(getString(R.string.access_token), "").toString()
+ val notifToken = sp.getString(getString(R.string.notification_token), "").toString()
val editor = sp.edit()
accountSettings = findPreference("pref_account_edit")
accountSettings?.onPreferenceClickListener =
@@ -89,28 +98,31 @@ class SettingsFragment : PreferenceFragmentCompat() {
logInOutButton?.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
if (pennKey != null) {
- val dialog = AlertDialog.Builder(context).create()
- dialog.setTitle("Log out")
- dialog.setMessage("Are you sure you want to log out?")
- dialog.setButton("Logout") { dialog, _ ->
- CookieManager.getInstance().removeAllCookie()
- editor.remove(getString(R.string.penn_password))
- editor.remove(getString(R.string.penn_user))
- editor.remove(getString(R.string.first_name))
- editor.remove(getString(R.string.last_name))
- editor.remove(getString(R.string.email_address))
- editor.remove(getString(R.string.pennkey))
- editor.remove(getString(R.string.accountID))
- editor.remove(getString(R.string.access_token))
- editor.remove(getString(R.string.guest_mode))
- editor.remove(getString(R.string.campus_express_token))
- editor.remove(getString(R.string.campus_token_expires_in))
- editor.apply()
- dialog.cancel()
- mActivity.startLoginFragment()
- }
- dialog.setButton2("Cancel") { dialog, _ -> dialog.cancel() }
- dialog.show()
+ AlertDialog
+ .Builder(context)
+ .setTitle("Log out")
+ .setMessage("Are you sure you want to log out?")
+ .setPositiveButton("Logout") { dialog, _ ->
+ deleteNotifToken(bearerToken, notifToken)
+ CookieManager.getInstance().removeAllCookie()
+ editor.apply {
+ remove(getString(R.string.penn_password))
+ remove(getString(R.string.penn_user))
+ remove(getString(R.string.first_name))
+ remove(getString(R.string.last_name))
+ remove(getString(R.string.email_address))
+ remove(getString(R.string.pennkey))
+ remove(getString(R.string.accountID))
+ remove(getString(R.string.access_token))
+ remove(getString(R.string.guest_mode))
+ remove(getString(R.string.campus_express_token))
+ remove(getString(R.string.campus_token_expires_in))
+ }
+ dialog.dismiss()
+ mActivity.startLoginFragment()
+ }.setNegativeButton("Cancel") { dialog, _ -> dialog.cancel() }
+ .create()
+ .show()
} else {
mActivity.startLoginFragment()
}
@@ -124,4 +136,17 @@ class SettingsFragment : PreferenceFragmentCompat() {
mActivity.setTitle(R.string.action_settings)
mActivity.setSelectedTab(MainActivity.MORE)
}
+
+ private fun deleteNotifToken(
+ bearerToken: String,
+ notifToken: String,
+ ) {
+ val mNotificationAPI = MainActivity.notificationAPIInstance
+ Log.i("Notification Token", notifToken)
+
+ lifecycleScope.launch(Dispatchers.Main) {
+ settingsViewModel.deleteTokenResponse(mNotificationAPI, bearerToken, notifToken)
+ Log.i("TestLaunch", "Launched!")
+ }
+ }
}
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/SettingsViewModel.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/SettingsViewModel.kt
new file mode 100644
index 000000000..560cb573a
--- /dev/null
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/SettingsViewModel.kt
@@ -0,0 +1,26 @@
+package com.pennapps.labs.pennmobile.more.viewmodels
+
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import com.pennapps.labs.pennmobile.api.NotificationAPI
+
+// Currently only implemented the notification logic, other network logistics to be implemented
+
+class SettingsViewModel : ViewModel() {
+ suspend fun deleteTokenResponse(
+ mNotificationAPI: NotificationAPI,
+ bearerToken: String,
+ notifToken: String,
+ ) {
+ try {
+ val response = mNotificationAPI.deleteNotificationToken(bearerToken, notifToken)
+ if (response.isSuccessful) {
+ Log.i("Notification Token", "Successfully deleted token")
+ } else {
+ Log.i("Notification Token", "Error deleting token: ${response.code()} ${response.message()}")
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+}
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/notifications/PushNotificationService.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/notifications/PushNotificationService.kt
index 179a1ea0a..29c0d4cd6 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/notifications/PushNotificationService.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/notifications/PushNotificationService.kt
@@ -5,28 +5,39 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
+import android.content.SharedPreferences
import android.graphics.BitmapFactory
import android.util.Log
import androidx.core.app.NotificationCompat
+import androidx.core.content.ContextCompat
+import androidx.preference.PreferenceManager
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.pennapps.labs.pennmobile.MainActivity
import com.pennapps.labs.pennmobile.R
-import java.net.URL
class PushNotificationService : FirebaseMessagingService() {
+ private lateinit var mSharedPrefs: SharedPreferences
+
override fun onNewToken(token: String) {
super.onNewToken(token)
// Update Server/Database
- Log.d("FCM Registration", "Refreshed token: $token")
+ mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
+
+ with(mSharedPrefs.edit()) {
+ putString("Notification Token", token)
+ apply()
+ }
+
+ Log.d("FCM Registration", "Stored Notification token: $token")
}
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
- Log.d("Notification", "Notification received!")
+
+ Log.d("Notification Received", "Notification received!")
val title = message.notification?.title
val body = message.notification?.body
- val imageUrl = message.notification?.imageUrl
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@@ -45,22 +56,19 @@ class PushNotificationService : FirebaseMessagingService() {
flags += Intent.FLAG_ACTIVITY_CLEAR_TOP
}
val pendingIntent = PendingIntent.getActivity(this, 0, mainActivityIntent, PendingIntent.FLAG_IMMUTABLE)
+ val bitMap = BitmapFactory.decodeResource(this.resources, R.drawable.ic_icon)
val notificationBuilder =
NotificationCompat
.Builder(this, "MAIN_CHANNEL")
.setContentTitle(title)
.setContentText(body)
- .setSmallIcon(R.drawable.baseline_circle_notifications_24)
+ .setSmallIcon(R.mipmap.ic_launcher_foreground)
+ .setLargeIcon(bitMap)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
-
- imageUrl?.let {
- val bitmap = BitmapFactory.decodeStream(URL(imageUrl.toString()).openConnection().getInputStream())
- notificationBuilder.setLargeIcon(bitmap)
- notificationBuilder.setStyle(NotificationCompat.BigPictureStyle().bigPicture(bitmap))
- }
+ .setColor(ContextCompat.getColor(this, R.color.penn_red))
notificationManager.notify(1, notificationBuilder.build())
}
diff --git a/PennMobile/src/main/res/values/strings.xml b/PennMobile/src/main/res/values/strings.xml
index f79485c3b..547cd875b 100644
--- a/PennMobile/src/main/res/values/strings.xml
+++ b/PennMobile/src/main/res/values/strings.xml
@@ -239,4 +239,8 @@
EXAMPLE
Add widget
This is an app widget description
+
+
+ Notification Token
+
From 7e05cf80bc43ae08c88546b0e2ae9821a072d240 Mon Sep 17 00:00:00 2001
From: Skeletrobro <67814129+baronhsieh2005@users.noreply.github.com>
Date: Mon, 18 Nov 2024 13:18:44 -0500
Subject: [PATCH 3/6] DeleteToken Fix
---
.../more/fragments/PreferenceFragment.kt | 75 +++++++++++++------
.../more/fragments/SettingsFragment.kt | 25 +------
...gsViewModel.kt => PreferencesViewModel.kt} | 2 +-
3 files changed, 58 insertions(+), 44 deletions(-)
rename PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/{SettingsViewModel.kt => PreferencesViewModel.kt} (95%)
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt
index 4a7474557..47a47ba3e 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt
@@ -4,14 +4,18 @@ import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.content.Intent
+import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
+import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.webkit.CookieManager
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.FragmentTransaction
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
@@ -20,7 +24,10 @@ import com.pennapps.labs.pennmobile.R
import com.pennapps.labs.pennmobile.components.dialog.CustomAlertDialogue
import com.pennapps.labs.pennmobile.gsr.fragments.PottruckFragment
import com.pennapps.labs.pennmobile.home.fragments.NewsFragment
+import com.pennapps.labs.pennmobile.more.viewmodels.PreferencesViewModel
import com.pennapps.labs.pennmobile.showSneakerToast
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
/**
* Created by Davies Lumumba Spring 2021
@@ -29,6 +36,7 @@ class PreferenceFragment : PreferenceFragmentCompat() {
private lateinit var mContext: Context
private lateinit var mActivity: MainActivity
private lateinit var toolbar: Toolbar
+ private val preferencesViewModel: PreferencesViewModel by viewModels()
override fun onAttach(context: Context) {
super.onAttach(context)
@@ -72,27 +80,34 @@ class PreferenceFragment : PreferenceFragmentCompat() {
userLoginPref?.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
if (pennKey != null) {
- val dialog = AlertDialog.Builder(context).create()
- dialog.setTitle("Log out")
- dialog.setMessage("Are you sure you want to log out?")
- dialog.setButton("Logout") { dialog, _ ->
- CookieManager.getInstance().removeAllCookie()
- editor.remove(getString(R.string.penn_password))
- editor.remove(getString(R.string.penn_user))
- editor.remove(getString(R.string.first_name))
- editor.remove(getString(R.string.last_name))
- editor.remove(getString(R.string.email_address))
- editor.remove(getString(R.string.pennkey))
- editor.remove(getString(R.string.accountID))
- editor.remove(getString(R.string.access_token))
- editor.remove(getString(R.string.guest_mode))
- editor.remove(getString(R.string.initials))
- editor.apply()
- dialog.cancel()
- mActivity.startLoginFragment()
- }
- // dialog.setButton(2,"Cancel") { dialog, _ -> dialog.cancel() }
- dialog.show()
+ AlertDialog
+ .Builder(context)
+ .setTitle("Log out")
+ .setMessage("Are you sure you want to log out?")
+ .setPositiveButton("Logout") { dialog, _ ->
+ Log.d("SettingsFragment", "Logout button clicked in dialog.")
+ deleteNotifToken(sp)
+ CookieManager.getInstance().removeAllCookie()
+ editor.apply {
+ remove(getString(R.string.penn_password))
+ remove(getString(R.string.penn_user))
+ remove(getString(R.string.first_name))
+ remove(getString(R.string.last_name))
+ remove(getString(R.string.email_address))
+ remove(getString(R.string.pennkey))
+ remove(getString(R.string.accountID))
+ remove(getString(R.string.access_token))
+ remove(getString(R.string.guest_mode))
+ remove(getString(R.string.campus_express_token))
+ remove(getString(R.string.campus_token_expires_in))
+ }
+ dialog.dismiss()
+ Log.d("SettingsFragment", "SharedPreferences cleared, navigating to Login.")
+ mActivity.startLoginFragment()
+ }.setNegativeButton("Cancel") { dialog, _ -> dialog.cancel() }
+ .create()
+ .show()
+ Log.d("SettingsFragment", "Logout confirmation dialog displayed.")
} else {
mActivity.startLoginFragment()
}
@@ -261,6 +276,24 @@ class PreferenceFragment : PreferenceFragmentCompat() {
alert.show()
}
+ private fun deleteNotifToken(sp: SharedPreferences) {
+ val bearerToken = "Bearer " + sp.getString(getString(R.string.access_token), "").toString()
+ val notifToken = sp.getString(getString(R.string.notification_token), "").toString()
+ Log.d("SettingsFragment", "deleteNotifToken called with notifToken: $notifToken")
+ val mNotificationAPI = MainActivity.notificationAPIInstance
+ Log.i("Notification Token", notifToken)
+
+ lifecycleScope.launch(Dispatchers.IO) {
+ try {
+ preferencesViewModel.deleteTokenResponse(mNotificationAPI, bearerToken, notifToken)
+ Log.d("SettingsFragment", "deleteTokenResponse coroutine completed.")
+ } catch (e: Exception) {
+ Log.e("SettingsFragment", "Error in deleteNotifToken: ${e.message}")
+ e.printStackTrace()
+ }
+ }
+ }
+
companion object {
private const val PENNLABS = "https://pennlabs.org"
private const val FEEDBACK = "https://airtable.com/appFRa4NQvNMEbWsA/shrn4VbSQa8QDj8OG"
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/SettingsFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/SettingsFragment.kt
index a0733d60b..9b6b128f3 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/SettingsFragment.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/SettingsFragment.kt
@@ -13,21 +13,15 @@ import android.webkit.CookieManager
import android.widget.Button
import android.widget.EditText
import androidx.appcompat.view.ContextThemeWrapper
-import androidx.fragment.app.viewModels
-import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import com.pennapps.labs.pennmobile.MainActivity
import com.pennapps.labs.pennmobile.R
-import com.pennapps.labs.pennmobile.more.viewmodels.SettingsViewModel
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
class SettingsFragment : PreferenceFragmentCompat() {
private var accountSettings: Preference? = null
private var logInOutButton: Preference? = null
- private val settingsViewModel: SettingsViewModel by viewModels()
private lateinit var mActivity: MainActivity
private lateinit var mContext: Context
@@ -56,8 +50,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
) {
super.onViewCreated(view, savedInstanceState)
val sp = PreferenceManager.getDefaultSharedPreferences(mActivity)
- val bearerToken = "Bearer " + sp.getString(getString(R.string.access_token), "").toString()
- val notifToken = sp.getString(getString(R.string.notification_token), "").toString()
val editor = sp.edit()
accountSettings = findPreference("pref_account_edit")
accountSettings?.onPreferenceClickListener =
@@ -103,7 +95,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
.setTitle("Log out")
.setMessage("Are you sure you want to log out?")
.setPositiveButton("Logout") { dialog, _ ->
- deleteNotifToken(bearerToken, notifToken)
+ Log.d("SettingsFragment", "Logout button clicked in dialog.")
CookieManager.getInstance().removeAllCookie()
editor.apply {
remove(getString(R.string.penn_password))
@@ -119,10 +111,12 @@ class SettingsFragment : PreferenceFragmentCompat() {
remove(getString(R.string.campus_token_expires_in))
}
dialog.dismiss()
+ Log.d("SettingsFragment", "SharedPreferences cleared, navigating to Login.")
mActivity.startLoginFragment()
}.setNegativeButton("Cancel") { dialog, _ -> dialog.cancel() }
.create()
.show()
+ Log.d("SettingsFragment", "Logout confirmation dialog displayed.")
} else {
mActivity.startLoginFragment()
}
@@ -136,17 +130,4 @@ class SettingsFragment : PreferenceFragmentCompat() {
mActivity.setTitle(R.string.action_settings)
mActivity.setSelectedTab(MainActivity.MORE)
}
-
- private fun deleteNotifToken(
- bearerToken: String,
- notifToken: String,
- ) {
- val mNotificationAPI = MainActivity.notificationAPIInstance
- Log.i("Notification Token", notifToken)
-
- lifecycleScope.launch(Dispatchers.Main) {
- settingsViewModel.deleteTokenResponse(mNotificationAPI, bearerToken, notifToken)
- Log.i("TestLaunch", "Launched!")
- }
- }
}
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/SettingsViewModel.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/PreferencesViewModel.kt
similarity index 95%
rename from PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/SettingsViewModel.kt
rename to PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/PreferencesViewModel.kt
index 560cb573a..591fab4d3 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/SettingsViewModel.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/PreferencesViewModel.kt
@@ -6,7 +6,7 @@ import com.pennapps.labs.pennmobile.api.NotificationAPI
// Currently only implemented the notification logic, other network logistics to be implemented
-class SettingsViewModel : ViewModel() {
+class PreferencesViewModel : ViewModel() {
suspend fun deleteTokenResponse(
mNotificationAPI: NotificationAPI,
bearerToken: String,
From 6ea307f1bdf2dcbc9751e9e3c61a8c26856ac842 Mon Sep 17 00:00:00 2001
From: Skeletrobro <67814129+baronhsieh2005@users.noreply.github.com>
Date: Mon, 18 Nov 2024 13:32:49 -0500
Subject: [PATCH 4/6] Minor fixes
---
.../api/fragments/LoginWebviewFragment.kt | 14 ++++++--------
.../more/fragments/PreferenceFragment.kt | 9 ++-------
2 files changed, 8 insertions(+), 15 deletions(-)
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/fragments/LoginWebviewFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/fragments/LoginWebviewFragment.kt
index 0481418d7..f2c164fd2 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/fragments/LoginWebviewFragment.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/fragments/LoginWebviewFragment.kt
@@ -270,16 +270,14 @@ class LoginWebviewFragment : Fragment() {
private fun sendNotifToken() {
val mNotificationAPI = MainActivity.notificationAPIInstance
- mActivity.mNetworkManager.getAccessToken {
- val bearerToken = "Bearer " + sp.getString(getString(R.string.access_token), "").toString()
- val notifToken = sp.getString(getString(R.string.notification_token), "").toString()
+ val bearerToken = "Bearer " + sp.getString(getString(R.string.access_token), "").toString()
+ val notifToken = sp.getString(getString(R.string.notification_token), "").toString()
- Log.d("Notification Token", notifToken)
- val notGuest = !sp.getBoolean(mActivity.getString(R.string.guest_mode), false)
+ Log.d("Notification Token", notifToken)
+ val notGuest = !sp.getBoolean(mActivity.getString(R.string.guest_mode), false)
- lifecycleScope.launch(Dispatchers.IO) {
- loginWebviewViewmodel.sendToken(mNotificationAPI, notGuest, bearerToken, notifToken)
- }
+ lifecycleScope.launch(Dispatchers.IO) {
+ loginWebviewViewmodel.sendToken(mNotificationAPI, notGuest, bearerToken, notifToken)
}
}
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt
index 47a47ba3e..36801dec8 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt
@@ -82,10 +82,9 @@ class PreferenceFragment : PreferenceFragmentCompat() {
if (pennKey != null) {
AlertDialog
.Builder(context)
- .setTitle("Log out")
+ .setTitle("Log Out")
.setMessage("Are you sure you want to log out?")
.setPositiveButton("Logout") { dialog, _ ->
- Log.d("SettingsFragment", "Logout button clicked in dialog.")
deleteNotifToken(sp)
CookieManager.getInstance().removeAllCookie()
editor.apply {
@@ -100,14 +99,13 @@ class PreferenceFragment : PreferenceFragmentCompat() {
remove(getString(R.string.guest_mode))
remove(getString(R.string.campus_express_token))
remove(getString(R.string.campus_token_expires_in))
+ remove(getString(R.string.initials))
}
dialog.dismiss()
- Log.d("SettingsFragment", "SharedPreferences cleared, navigating to Login.")
mActivity.startLoginFragment()
}.setNegativeButton("Cancel") { dialog, _ -> dialog.cancel() }
.create()
.show()
- Log.d("SettingsFragment", "Logout confirmation dialog displayed.")
} else {
mActivity.startLoginFragment()
}
@@ -279,16 +277,13 @@ class PreferenceFragment : PreferenceFragmentCompat() {
private fun deleteNotifToken(sp: SharedPreferences) {
val bearerToken = "Bearer " + sp.getString(getString(R.string.access_token), "").toString()
val notifToken = sp.getString(getString(R.string.notification_token), "").toString()
- Log.d("SettingsFragment", "deleteNotifToken called with notifToken: $notifToken")
val mNotificationAPI = MainActivity.notificationAPIInstance
Log.i("Notification Token", notifToken)
lifecycleScope.launch(Dispatchers.IO) {
try {
preferencesViewModel.deleteTokenResponse(mNotificationAPI, bearerToken, notifToken)
- Log.d("SettingsFragment", "deleteTokenResponse coroutine completed.")
} catch (e: Exception) {
- Log.e("SettingsFragment", "Error in deleteNotifToken: ${e.message}")
e.printStackTrace()
}
}
From 2f35e4451ed2b854757f42507a78b742f1386ba4 Mon Sep 17 00:00:00 2001
From: Skeletrobro <67814129+baronhsieh2005@users.noreply.github.com>
Date: Mon, 18 Nov 2024 13:39:49 -0500
Subject: [PATCH 5/6] Fix MoreFragment initial bug
---
.../pennmobile/more/fragments/MoreFragment.kt | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/MoreFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/MoreFragment.kt
index b530d1514..5289832c2 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/MoreFragment.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/MoreFragment.kt
@@ -47,6 +47,21 @@ class MoreFragment : Fragment() {
savedInstanceState: Bundle?,
) {
super.onViewCreated(view, savedInstanceState)
+ val initials =
+ PreferenceManager
+ .getDefaultSharedPreferences(mActivity)
+ .getString(getString(R.string.initials), null)
+ if (initials != null && initials.isNotEmpty()) {
+ binding.initials.text = initials
+ } else {
+ binding.profileBackground.setImageDrawable(
+ ResourcesCompat.getDrawable(
+ resources,
+ R.drawable.ic_guest_avatar,
+ context?.theme,
+ ),
+ )
+ }
childFragmentManager
.beginTransaction()
.replace(R.id.more_frame, PreferenceFragment())
From 37ad439d18060413cb678f940d1578b29df7eb4c Mon Sep 17 00:00:00 2001
From: Skeletrobro <67814129+baronhsieh2005@users.noreply.github.com>
Date: Mon, 18 Nov 2024 13:46:15 -0500
Subject: [PATCH 6/6] Rename and Remove delete Authorization
---
.../com/pennapps/labs/pennmobile/api/NotificationAPI.kt | 1 -
.../labs/pennmobile/more/fragments/PreferenceFragment.kt | 7 +++----
.../{PreferencesViewModel.kt => PreferenceViewModel.kt} | 5 ++---
3 files changed, 5 insertions(+), 8 deletions(-)
rename PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/{PreferencesViewModel.kt => PreferenceViewModel.kt} (88%)
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/NotificationAPI.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/NotificationAPI.kt
index a12c851ed..d6b85d664 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/NotificationAPI.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/api/NotificationAPI.kt
@@ -16,7 +16,6 @@ interface NotificationAPI {
@DELETE("user/notifications/tokens/android/{token}/")
suspend fun deleteNotificationToken(
- @Header("Authorization") bearerToken: String,
@Path("token") token: String,
): Response
}
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt
index 36801dec8..f331ef6e2 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/fragments/PreferenceFragment.kt
@@ -24,7 +24,7 @@ import com.pennapps.labs.pennmobile.R
import com.pennapps.labs.pennmobile.components.dialog.CustomAlertDialogue
import com.pennapps.labs.pennmobile.gsr.fragments.PottruckFragment
import com.pennapps.labs.pennmobile.home.fragments.NewsFragment
-import com.pennapps.labs.pennmobile.more.viewmodels.PreferencesViewModel
+import com.pennapps.labs.pennmobile.more.viewmodels.PreferenceViewModel
import com.pennapps.labs.pennmobile.showSneakerToast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -36,7 +36,7 @@ class PreferenceFragment : PreferenceFragmentCompat() {
private lateinit var mContext: Context
private lateinit var mActivity: MainActivity
private lateinit var toolbar: Toolbar
- private val preferencesViewModel: PreferencesViewModel by viewModels()
+ private val preferenceViewModel: PreferenceViewModel by viewModels()
override fun onAttach(context: Context) {
super.onAttach(context)
@@ -275,14 +275,13 @@ class PreferenceFragment : PreferenceFragmentCompat() {
}
private fun deleteNotifToken(sp: SharedPreferences) {
- val bearerToken = "Bearer " + sp.getString(getString(R.string.access_token), "").toString()
val notifToken = sp.getString(getString(R.string.notification_token), "").toString()
val mNotificationAPI = MainActivity.notificationAPIInstance
Log.i("Notification Token", notifToken)
lifecycleScope.launch(Dispatchers.IO) {
try {
- preferencesViewModel.deleteTokenResponse(mNotificationAPI, bearerToken, notifToken)
+ preferenceViewModel.deleteTokenResponse(mNotificationAPI, notifToken)
} catch (e: Exception) {
e.printStackTrace()
}
diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/PreferencesViewModel.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/PreferenceViewModel.kt
similarity index 88%
rename from PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/PreferencesViewModel.kt
rename to PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/PreferenceViewModel.kt
index 591fab4d3..bb7b366bc 100644
--- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/PreferencesViewModel.kt
+++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/more/viewmodels/PreferenceViewModel.kt
@@ -6,14 +6,13 @@ import com.pennapps.labs.pennmobile.api.NotificationAPI
// Currently only implemented the notification logic, other network logistics to be implemented
-class PreferencesViewModel : ViewModel() {
+class PreferenceViewModel : ViewModel() {
suspend fun deleteTokenResponse(
mNotificationAPI: NotificationAPI,
- bearerToken: String,
notifToken: String,
) {
try {
- val response = mNotificationAPI.deleteNotificationToken(bearerToken, notifToken)
+ val response = mNotificationAPI.deleteNotificationToken(notifToken)
if (response.isSuccessful) {
Log.i("Notification Token", "Successfully deleted token")
} else {