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
12 changes: 12 additions & 0 deletions app/src/main/java/crux/bphc/cms/activities/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.MenuItem
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
Expand Down Expand Up @@ -120,6 +121,17 @@ class MainActivity : AppCompatActivity() {

resolveIntent()
resolveModuleLinkShare()

//checks if the attempt to unenrol was successful and instructs the MyCoursesFragment() to refresh
val fragment = MyCoursesFragment()
val unenrolResult = intent.getBooleanExtra(CourseContentFragment.INTENT_UNENROL_RESULT,false)
if(unenrolResult){
val bundle = Bundle()
bundle.putBoolean(getString(R.string.refreshRequired),true)
fragment.arguments = bundle
supportFragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit()
}

}

override fun onBackPressed() {
Expand Down
164 changes: 122 additions & 42 deletions app/src/main/java/crux/bphc/cms/fragments/CourseContentFragment.kt

Large diffs are not rendered by default.

98 changes: 65 additions & 33 deletions app/src/main/java/crux/bphc/cms/fragments/MyCoursesFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.setFragmentResultListener
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
Expand Down Expand Up @@ -46,9 +47,9 @@ class MyCoursesFragment : Fragment() {

// Activity result launchers
private val courseDetailActivityLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
mAdapter.filterCoursesByName(courses, searchCourseET.text.toString())
}
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
mAdapter.filterCoursesByName(courses, searchCourseET.text.toString())
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -60,8 +61,10 @@ class MyCoursesFragment : Fragment() {
requireActivity().title = "My Courses"
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
realm = Realm.getDefaultInstance()
return inflater.inflate(R.layout.fragment_my_courses, container, false)
}
Expand All @@ -82,7 +85,8 @@ class MyCoursesFragment : Fragment() {
realm.close()

CoroutineScope(Dispatchers.Main).launch {
Toast.makeText(requireActivity(), "Marked all as read", Toast.LENGTH_SHORT).show()
Toast.makeText(requireActivity(), "Marked all as read", Toast.LENGTH_SHORT)
.show()
mAdapter.courses = this@MyCoursesFragment.courseDataHandler.courseList
}
}
Expand All @@ -109,18 +113,29 @@ class MyCoursesFragment : Fragment() {
return@ClickListener true
}

//ensures refreshing of courses after user unenrols from one
val refreshRequired = arguments?.getBoolean(getString(R.string.refreshRequired), false)
if (refreshRequired == true) {
swipeRefreshLayout.isRefreshing = true
refreshCourses()
}


mAdapter.downloadClickListener = ClickListener { `object`: Any, position: Int ->
val course = `object` as Course
if (course.downloadStatus != -1) return@ClickListener false
course.downloadStatus = 0
mAdapter.notifyItemChanged(position)
val courseDownloader = CourseDownloader(activity, courseDataHandler.getCourseName(course.id))

val courseDownloader =
CourseDownloader(activity, courseDataHandler.getCourseName(course.id))
courseDownloader.setDownloadCallback(object : CourseDownloader.DownloadCallback {
override fun onCourseDataDownloaded() {
course.downloadedFiles = courseDownloader.getDownloadedContentCount(course.id)
course.totalFiles = courseDownloader.getTotalContentCount(course.id)
if (course.totalFiles == course.downloadedFiles) {
Toast.makeText(activity, "All files already downloaded", Toast.LENGTH_SHORT).show()
Toast.makeText(activity, "All files already downloaded", Toast.LENGTH_SHORT)
.show()
course.downloadStatus = -1
} else {
course.downloadStatus = 1
Expand All @@ -140,7 +155,7 @@ class MyCoursesFragment : Fragment() {

override fun onFailure() {
Toast.makeText(activity, "Check your internet connection", Toast.LENGTH_SHORT)
.show()
.show()
course.downloadStatus = -1
mAdapter.notifyItemChanged(position)
courseDownloader.unregisterReceiver()
Expand All @@ -165,12 +180,15 @@ class MyCoursesFragment : Fragment() {
searchCourseET.setText("")
searchIcon.setImageResource(R.drawable.ic_search)
searchIcon.setOnClickListener(null)
val inputManager = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE)
as? InputMethodManager
val inputManager =
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE)
as? InputMethodManager
val currentFocus = requireActivity().currentFocus
if (currentFocus != null) {
inputManager?.hideSoftInputFromWindow(currentFocus.windowToken,
InputMethodManager.HIDE_NOT_ALWAYS)
inputManager?.hideSoftInputFromWindow(
currentFocus.windowToken,
InputMethodManager.HIDE_NOT_ALWAYS
)
}
}
} else {
Expand Down Expand Up @@ -209,12 +227,15 @@ class MyCoursesFragment : Fragment() {
val courseDataHandler = CourseDataHandler(realm)
courseDataHandler.replaceCourses(courseList)
realm.close()
checkEmpty()
withContext(Dispatchers.Main){
checkEmpty()
}
updateCourseContent()
} catch (e: Exception) {
Log.e(TAG, "", e)
withContext(Dispatchers.Main) {
Toast.makeText(requireActivity(), "Error: ${e.message}", Toast.LENGTH_SHORT).show()
Toast.makeText(requireActivity(), "Error: ${e.message}", Toast.LENGTH_SHORT)
.show()
if (e is InvalidTokenException) {
UserUtils.logout()
UserUtils.clearBackStackAndLaunchTokenActivity(requireActivity())
Expand Down Expand Up @@ -264,8 +285,10 @@ class MyCoursesFragment : Fragment() {
val message: String = if (coursesUpdated == 0) {
getString(R.string.upToDate)
} else {
resources.getQuantityString(R.plurals.noOfCoursesUpdated, coursesUpdated,
coursesUpdated)
resources.getQuantityString(
R.plurals.noOfCoursesUpdated, coursesUpdated,
coursesUpdated
)
}
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
}
Expand All @@ -278,8 +301,8 @@ class MyCoursesFragment : Fragment() {
}

private inner class Adapter constructor(
val context: Context,
courseList: List<Course>
val context: Context,
courseList: List<Course>
) : RecyclerView.Adapter<Adapter.MyViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)

Expand Down Expand Up @@ -336,27 +359,30 @@ class MyCoursesFragment : Fragment() {
val count = courseDataHandler.getUnreadCount(course.id)
itemView.course_number.text = course.courseName[0]
itemView.course_name.text = name
itemView.unread_count.text = DecimalFormat.getIntegerInstance().format(count.toLong())
itemView.unread_count.text =
DecimalFormat.getIntegerInstance().format(count.toLong())
itemView.unread_count.isVisible = count != 0
itemView.mark_as_read_button.isVisible = count != 0
itemView.favorite_button.setImageResource(if (course.isFavorite) R.drawable.ic_fav_filled else R.drawable.ic_fav_outlined)
}

fun confirmDownloadCourse() {
MaterialAlertDialogBuilder(context)
.setTitle("Confirm Download")
.setMessage("Are you sure you want to all the contents of this course?")
.setPositiveButton("Yes") { _: DialogInterface?, _: Int ->
if (downloadClickListener != null) {
val pos = layoutPosition
if (!downloadClickListener!!.onClick(courses[pos], pos)) {
Toast.makeText(activity, "Download already in progress",
Toast.LENGTH_SHORT).show()
}
.setTitle("Confirm Download")
.setMessage("Are you sure you want to all the contents of this course?")
.setPositiveButton("Yes") { _: DialogInterface?, _: Int ->
if (downloadClickListener != null) {
val pos = layoutPosition
if (!downloadClickListener!!.onClick(courses[pos], pos)) {
Toast.makeText(
activity, "Download already in progress",
Toast.LENGTH_SHORT
).show()
}
}
.setNegativeButton("Cancel", null)
.show()
}
.setNegativeButton("Cancel", null)
.show()
}

fun markCourseAsRead() {
Expand All @@ -373,7 +399,8 @@ class MyCoursesFragment : Fragment() {
course.isFavorite = isFavourite
courses = sortCourses(courses)
notifyDataSetChanged()
val toast = if (isFavourite) getString(R.string.added_to_favorites) else getString(R.string.removed_from_favorites)
val toast =
if (isFavourite) getString(R.string.added_to_favorites) else getString(R.string.removed_from_favorites)
Toast.makeText(activity, toast, Toast.LENGTH_SHORT).show()
}

Expand All @@ -386,7 +413,12 @@ class MyCoursesFragment : Fragment() {
}

itemView.mark_as_read_button.setOnClickListener { markCourseAsRead() }
itemView.favorite_button.setOnClickListener { setFavoriteStatus(layoutPosition, !courses[layoutPosition].isFavorite) }
itemView.favorite_button.setOnClickListener {
setFavoriteStatus(
layoutPosition,
!courses[layoutPosition].isFavorite
)
}
itemView.download_image.setOnClickListener { confirmDownloadCourse() }
}
}
Expand Down
52 changes: 52 additions & 0 deletions app/src/main/java/crux/bphc/cms/helper/CourseManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package crux.bphc.cms.helper

import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.annotation.UiThread
import crux.bphc.cms.models.course.Course
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class CourseManager(val courseId: Int, val courseRequestHandler: CourseRequestHandler) {

var startedUnenrol: Boolean = false

@UiThread
suspend fun unenrolCourse(context: Context): Boolean {
/* Guard against multiple presses */
if (startedUnenrol) return false
startedUnenrol = true

if (!UserSessionManager.hasValidSession()) {
if (!UserSessionManager.createUserSession()) {
Toast.makeText(
context,
"Failed to create session. Try logging out and back in!",
Toast.LENGTH_LONG
).show()
startedUnenrol = false
return false
}
}

val ret = withContext(Dispatchers.IO) {
val idsess = courseRequestHandler.getEnrolIdSessKey(courseId) ?:
return@withContext false
val enrolId = idsess.first ?: return@withContext false
val sessKey = idsess.second ?: return@withContext false

courseRequestHandler.unenrolSelf(enrolId, sessKey)
}

if (ret) {
Toast.makeText(context, "Successfully unenroled from course!", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(context, "Failed to unenrol from course!", Toast.LENGTH_LONG ).show()
}

startedUnenrol = false
return ret
}

}
38 changes: 37 additions & 1 deletion app/src/main/java/crux/bphc/cms/helper/CourseRequestHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.content.Context;
import android.util.Log;
import android.util.Pair;
import android.widget.Toast;

import androidx.annotation.NonNull;
Expand Down Expand Up @@ -29,6 +30,7 @@
import crux.bphc.cms.models.forum.ForumData;
import crux.bphc.cms.network.APIClient;
import crux.bphc.cms.network.MoodleServices;
import kotlin.text.Regex;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
Expand Down Expand Up @@ -246,7 +248,6 @@ public List<Discussion> getForumDiscussions(int moduleId) {
return null;
}


@NotNull
public List<Discussion> getForumDicussionsSync(int moduleId) throws IOException {
Call<ForumData> call = moodleServices.getForumDiscussions(userAccount.getToken(), moduleId, 0, 0);
Expand Down Expand Up @@ -276,6 +277,41 @@ public void onFailure(@NotNull Call<ForumData> call, @NotNull Throwable t) {
});
}

@Nullable
public Pair<String,String> getEnrolIdSessKey(int courseId) {
String sessionCookie = UserSessionManager.INSTANCE.getFormattedSessionCookie();
Call<ResponseBody> call = moodleServices.viewCoursePage(sessionCookie, courseId);
try {
Response<ResponseBody> response = call.execute();
String rawHtml = response.body().string();
if (rawHtml == null) {
return null;
}

String enrolId = new Regex("enrolid=([0-9]+)").find(rawHtml, 0)
.getGroupValues().get(1);
String sessKey = new Regex("sesskey=(\\w+)").find(rawHtml, 0)
.getGroupValues().get(1);
return new Pair(enrolId, sessKey);
} catch (IOException | IndexOutOfBoundsException | NullPointerException e) {
Log.e(TAG, "Failed in getEnrolIdSessKey", e);
return null;
}
}

public boolean unenrolSelf(String enrolId, String sessKey) {
String sessionCookie = UserSessionManager.INSTANCE.getFormattedSessionCookie();
Call<ResponseBody> call = moodleServices.selfUnenrolCourse(sessionCookie, enrolId, sessKey);
try{
Response<ResponseBody> response = call.execute();
/* We have no way of verifying unenrolment success */
return true;
} catch (IOException e) {
Log.e(TAG, "Failed in unenrolSelf", e);
return false;
}
}

//This method resolves the names of files with same names
private List<CourseSection> resolve(List<CourseSection> courseSections) {
List<Content> contents = new ArrayList<>();
Expand Down
Loading