Skip to content

Commit

Permalink
Admin can now add attachments to Announcements using Firebase Storage
Browse files Browse the repository at this point in the history
  • Loading branch information
JustAGhost23 committed Jan 6, 2023
1 parent b9c5aee commit c33becd
Show file tree
Hide file tree
Showing 19 changed files with 514 additions and 40 deletions.
17 changes: 17 additions & 0 deletions .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions .idea/dictionaries/jasonag23.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ plugins {
}

android {
compileSdk 32
compileSdk 33

defaultConfig {
applicationId "com.example.cmsclonelite"
minSdk 21
targetSdk 32
targetSdk 33
versionCode 1
versionName "1.0"

Expand Down Expand Up @@ -47,11 +47,19 @@ android {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
configurations {
all {
exclude module: 'httpclient'
exclude module: 'commons-logging'
}
}
}

dependencies {
def nav_version = "2.5.2"

implementation platform('com.google.firebase:firebase-bom:31.1.1')
implementation 'com.google.firebase:firebase-storage-ktx'
implementation 'com.google.firebase:firebase-messaging-ktx:23.0.8'
implementation 'com.google.firebase:firebase-firestore-ktx:24.3.1'
implementation 'com.google.firebase:firebase-auth-ktx:21.0.8'
Expand Down
7 changes: 6 additions & 1 deletion app/src/main/java/com/example/cmsclonelite/Course.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.example.cmsclonelite

import android.net.Uri
import android.os.Parcelable
import com.google.firebase.storage.StorageMetadata
import com.google.firebase.storage.ktx.storageMetadata
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
import java.util.*
Expand All @@ -20,5 +23,7 @@ data class Course (
@Parcelize
data class Announcement (
var title: String? = null,
var body: String? = null
var body: String? = null,
var fileName: String = "",
var downloadUri: Uri? = null,
): Parcelable
1 change: 1 addition & 0 deletions app/src/main/java/com/example/cmsclonelite/Screen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ sealed class Screen(val route: String) {
object EditCourseDetails: Screen(route = "editCourseDetailsScreen")
object Announcements: Screen(route = "announcementsScreen")
object AddAnnouncements: Screen(route = "addAnnouncementsScreen")
object DetailedAnnouncement: Screen(route = "detailedAnnouncement")
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.example.cmsclonelite.Announcement
import com.example.cmsclonelite.Course
import com.example.cmsclonelite.Screen
import com.example.cmsclonelite.screens.*
Expand Down Expand Up @@ -65,5 +66,11 @@ fun SetupNavGraph(
AddAnnouncementsScreen(navController = navController, course = course, announcementsViewModel = announcementsViewModel)
}
}
composable(route = Screen.DetailedAnnouncement.route) {
val announcement = navController.previousBackStackEntry?.savedStateHandle?.get<Announcement>("announcement")
if (announcement != null) {
DetailedAnnouncementScreen(navController = navController, announcement = announcement)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package com.example.cmsclonelite.repository
import android.content.ContentValues
import android.content.ContentValues.TAG
import android.content.Context
import android.net.Uri
import android.provider.CalendarContract
import android.provider.OpenableColumns
import android.util.Log
import androidx.core.net.toUri
import com.example.cmsclonelite.Announcement
import com.example.cmsclonelite.Course
import com.google.auth.oauth2.AccessToken
Expand All @@ -13,7 +16,10 @@ import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FieldValue
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.SetOptions
import com.google.firebase.ktx.Firebase
import com.google.firebase.messaging.FirebaseMessaging
import com.google.firebase.storage.StorageMetadata
import com.google.firebase.storage.ktx.storage
import kotlinx.coroutines.*
import kotlinx.coroutines.tasks.await
import okhttp3.*
Expand All @@ -29,6 +35,8 @@ import kotlin.collections.HashMap
class CourseRepository {
private val client = OkHttpClient()
private val ioCoroutineScope = CoroutineScope(Dispatchers.IO)
private val storage = Firebase.storage
var storageRef = storage.reference

private suspend fun getCourses(db: FirebaseFirestore): List<DocumentSnapshot> {
val coursesRef = db.collection("courses")
Expand Down Expand Up @@ -87,6 +95,8 @@ class CourseRepository {
val announcement = Announcement()
announcement.title = announcementMap[i]!!.getValue("title")
announcement.body = announcementMap[i]!!.getValue("body")
announcement.fileName = announcementMap[i]!!.getValue("fileName")
announcement.downloadUri = announcementMap[i]!!.getValue("downloadUri").toUri()
list.add(announcement)
}
}
Expand Down Expand Up @@ -219,7 +229,7 @@ class CourseRepository {
calName = calCursor.getString(nameCol)
calID = calCursor.getString(idCol)

Log.d(ContentValues.TAG, "Calendar name = $calName Calendar ID = $calID")
Log.d(TAG, "Calendar name = $calName Calendar ID = $calID")

calCursor.close()
return calID.toLong()
Expand All @@ -233,7 +243,7 @@ class CourseRepository {
FirebaseMessaging.getInstance().subscribeToTopic(course.id!!)
db.collection("users").document(uid)
.update("enrolled", FieldValue.arrayUnion(course.id))
.addOnSuccessListener { Log.d(ContentValues.TAG, "DocumentSnapshot successfully updated!") }
.addOnSuccessListener { Log.d(TAG, "DocumentSnapshot successfully updated!") }
.addOnFailureListener { e: Exception? -> Log.w(ContentValues.TAG, "Error updating document", e) }
}
}
Expand All @@ -250,6 +260,12 @@ class CourseRepository {

fun deleteCourse(db: FirebaseFirestore, course: Course) {
ioCoroutineScope.launch {
val fileRef = storageRef.child("files/${course.id}")
fileRef.listAll().addOnCompleteListener { dir ->
for (item in dir.result.items) {
item.delete()
}
}
db.collection("users").whereArrayContains("enrolled", course.id!!)
.get()
.addOnCompleteListener { task ->
Expand Down Expand Up @@ -326,7 +342,9 @@ class CourseRepository {
for(i in newAnnouncementList) {
announcementHashMap["key${count}"] = hashMapOf(
"title" to i.title!!,
"body" to i.body!!
"body" to i.body!!,
"fileName" to i.fileName,
"downloadUri" to i.downloadUri.toString()
)
count -= 1
}
Expand All @@ -335,6 +353,37 @@ class CourseRepository {
}
}

suspend fun uploadFileToFirebase(course: Course, fileUri: Uri, context: Context): Uri? {
val returnCursor = context.contentResolver.query(fileUri, null, null, null, null)
val nameIndex = returnCursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
returnCursor?.moveToFirst()
val fileName = nameIndex?.let { returnCursor.getString(it) }
returnCursor?.close()
val fileRef = storageRef.child("files/${course.id}/${fileName}")
fileRef.putFile(fileUri).await()
return fileRef.downloadUrl.await()
}

suspend fun getFileMetadataFromFirebase(course: Course, fileUri: Uri, context: Context): StorageMetadata {
val returnCursor = context.contentResolver.query(fileUri, null, null, null, null)
val nameIndex = returnCursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
returnCursor?.moveToFirst()
val fileName = nameIndex?.let { returnCursor.getString(it) }
returnCursor?.close()
val fileRef = storageRef.child("files/${course.id}/${fileName}")
return fileRef.metadata.await()
}

suspend fun deleteFileFromFirebase(course: Course, fileUri: Uri, context: Context) {
val returnCursor = context.contentResolver.query(fileUri, null, null, null, null)
val nameIndex = returnCursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
returnCursor?.moveToFirst()
val fileName = nameIndex?.let { returnCursor.getString(it) }
returnCursor?.close()
val fileRef = storageRef.child("files/${course.id}/${fileName}")
fileRef.delete().await()
}

fun sendPushNotification(course: Course, announcement: Announcement) {
ioCoroutineScope.launch {
val url = "https://fcm.googleapis.com/v1/projects/cmsclonelite/messages:send"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fun AboutScreen(
}
)
},
) {
) { padding ->
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Top,
Expand Down
Loading

0 comments on commit c33becd

Please sign in to comment.