diff --git a/app/build.gradle b/app/build.gradle index 03d0a7b1d9..36a8454a78 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "org.ole.planet.myplanet" minSdkVersion 26 targetSdkVersion 34 - versionCode 2099 - versionName "0.20.99" + versionCode 2107 + versionName "0.21.7" ndkVersion '21.3.6528147' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/org/ole/planet/myplanet/MainApplication.kt b/app/src/main/java/org/ole/planet/myplanet/MainApplication.kt index b0939e1ce0..2ff8e10bde 100644 --- a/app/src/main/java/org/ole/planet/myplanet/MainApplication.kt +++ b/app/src/main/java/org/ole/planet/myplanet/MainApplication.kt @@ -24,6 +24,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import org.ole.planet.myplanet.MainApplication import org.ole.planet.myplanet.base.BaseResourceFragment.Companion.backgroundDownload import org.ole.planet.myplanet.base.BaseResourceFragment.Companion.getAllLibraryList import org.ole.planet.myplanet.callback.TeamPageListener @@ -139,6 +140,40 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks { false } } + + fun handleUncaughtException(e: Throwable) { + e.printStackTrace() + applicationScope.launch(Dispatchers.IO) { + try { + val realm = Realm.getDefaultInstance() + try { + realm.executeTransaction { r -> + val log = r.createObject(RealmApkLog::class.java, "${UUID.randomUUID()}") + val model = UserProfileDbHandler(context).userModel + if (model != null) { + log.parentCode = model.parentCode + log.createdOn = model.planetCode + log.userId = model.id + } + log.time = "${Date().time}" + log.page = "" + log.version = getVersionName(context) + log.type = RealmApkLog.ERROR_TYPE_CRASH + log.setError(e) + } + } finally { + realm.close() + } + } catch (ex: Exception) { + ex.printStackTrace() + } + } + + val homeIntent = Intent(Intent.ACTION_MAIN) + homeIntent.addCategory(Intent.CATEGORY_HOME) + homeIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(homeIntent) + } } private var activityReferences = 0 @@ -288,40 +323,6 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks { private fun onAppClosed() {} - private fun handleUncaughtException(e: Throwable) { - e.printStackTrace() - applicationScope.launch(Dispatchers.IO) { - try { - val realm = Realm.getDefaultInstance() - try { - realm.executeTransaction { r -> - val log = r.createObject(RealmApkLog::class.java, "${UUID.randomUUID()}") - val model = UserProfileDbHandler(this@MainApplication).userModel - if (model != null) { - log.parentCode = model.parentCode - log.createdOn = model.planetCode - log.userId = model.id - } - log.time = "${Date().time}" - log.page = "" - log.version = getVersionName(this@MainApplication) - log.type = RealmApkLog.ERROR_TYPE_CRASH - log.setError(e) - } - } finally { - realm.close() - } - } catch (ex: Exception) { - ex.printStackTrace() - } - } - - val homeIntent = Intent(Intent.ACTION_MAIN) - homeIntent.addCategory(Intent.CATEGORY_HOME) - homeIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK - startActivity(homeIntent) - } - override fun onTerminate() { super.onTerminate() onAppClosed() diff --git a/app/src/main/java/org/ole/planet/myplanet/model/RealmMyCourse.kt b/app/src/main/java/org/ole/planet/myplanet/model/RealmMyCourse.kt index 0dcd436966..ed55b4e3c0 100644 --- a/app/src/main/java/org/ole/planet/myplanet/model/RealmMyCourse.kt +++ b/app/src/main/java/org/ole/planet/myplanet/model/RealmMyCourse.kt @@ -205,15 +205,15 @@ open class RealmMyCourse : RealmObject() { } else { mutableListOf() } - + val linksToProcess: List synchronized(concatenatedLinks) { - for (link in concatenatedLinks) { - if (!existingConcatenatedLinks.contains(link)) { - existingConcatenatedLinks.add(link) - } + linksToProcess = concatenatedLinks.toList() + } + for (link in linksToProcess) { + if (!existingConcatenatedLinks.contains(link)) { + existingConcatenatedLinks.add(link) } } - val jsonConcatenatedLinks = gson.toJson(existingConcatenatedLinks) settings.edit().putString("concatenated_links", jsonConcatenatedLinks).apply() } diff --git a/app/src/main/java/org/ole/planet/myplanet/service/AudioRecorderService.kt b/app/src/main/java/org/ole/planet/myplanet/service/AudioRecorderService.kt index f752db1e2a..24f23be3fe 100644 --- a/app/src/main/java/org/ole/planet/myplanet/service/AudioRecorderService.kt +++ b/app/src/main/java/org/ole/planet/myplanet/service/AudioRecorderService.kt @@ -4,6 +4,7 @@ import android.media.MediaRecorder import android.os.Build import android.os.Environment import androidx.annotation.RequiresApi +import org.ole.planet.myplanet.MainApplication import org.ole.planet.myplanet.MainApplication.Companion.context import java.io.File import java.util.UUID @@ -73,11 +74,18 @@ class AudioRecorderService { } fun stopRecording() { - if (myAudioRecorder != null) { - myAudioRecorder?.stop() - myAudioRecorder?.release() - myAudioRecorder = null - audioRecordListener?.onRecordStopped(outputFile) + myAudioRecorder?.let { recorder -> + try { + if (isRecording()) { + recorder.stop() + recorder.release() + } + } catch (e: RuntimeException) { + MainApplication.handleUncaughtException(e) + } finally { + myAudioRecorder = null + audioRecordListener?.onRecordStopped(outputFile) + } } } diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/courses/AdapterCourses.kt b/app/src/main/java/org/ole/planet/myplanet/ui/courses/AdapterCourses.kt index 49752abbe8..83709f82c4 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/courses/AdapterCourses.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/courses/AdapterCourses.kt @@ -137,11 +137,23 @@ class AdapterCourses(private val context: Context, private var courseList: List< } holder.rowCourseBinding.title.text = course.courseTitle - holder.rowCourseBinding.description.text = course.description - val markdownContentWithLocalPaths = prependBaseUrlToImages( - course.description, "file://" + MainApplication.context.getExternalFilesDir(null) + "/ole/" - ) - setMarkdownText(holder.rowCourseBinding.description, markdownContentWithLocalPaths) + holder.rowCourseBinding.description.apply { + text = course.description + val markdownContentWithLocalPaths = AdapterCourses.prependBaseUrlToImages( + course.description, "file://" + MainApplication.context.getExternalFilesDir(null) + "/ole/" + ) + setMarkdownText(this, markdownContentWithLocalPaths) + + setOnClickListener { + homeItemClickListener?.openCallFragment(TakeCourseFragment().apply { + arguments = Bundle().apply { + putString("id", course.courseId) + putInt("position", position) + } + }) + } + } + if (course.gradeLevel.isNullOrEmpty() && course.subjectLevel.isNullOrEmpty()) { holder.rowCourseBinding.holder.visibility = View.VISIBLE holder.rowCourseBinding.tvDate2.visibility = View.VISIBLE @@ -182,12 +194,18 @@ class AdapterCourses(private val context: Context, private var courseList: List< holder.rowCourseBinding.checkbox.setOnClickListener { view: View -> holder.rowCourseBinding.checkbox.contentDescription = context.getString(R.string.select_res_course, course.courseTitle) Utilities.handleCheck((view as CheckBox).isChecked, position, selectedItems, courseList) - if (listener != null) listener!!.onSelectedListChange(selectedItems) + listener?.onSelectedListChange(selectedItems) } } else { holder.rowCourseBinding.checkbox.visibility = View.GONE } showProgressAndRating(position, holder) + + holder.rowCourseBinding.root.setOnClickListener { + if (position != RecyclerView.NO_POSITION) { + openCourse(courseList[position], 0) + } + } } } } @@ -350,4 +368,4 @@ class AdapterCourses(private val context: Context, private var courseList: List< return result.toString() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/courses/TakeCourseFragment.kt b/app/src/main/java/org/ole/planet/myplanet/ui/courses/TakeCourseFragment.kt index 79e64139e7..b12fe54d35 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/courses/TakeCourseFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/courses/TakeCourseFragment.kt @@ -77,12 +77,7 @@ class TakeCourseFragment : Fragment(), ViewPager.OnPageChangeListener, View.OnCl currentStep = getCourseProgress() - position = when { - position > 0 -> position - 1 - currentStep > 0 -> currentStep - 1 - else -> 0 - } - + position = if (currentStep > 0) currentStep - 1 else 0 fragmentTakeCourseBinding.viewPager2.currentItem = position updateStepDisplay(position) @@ -93,6 +88,9 @@ class TakeCourseFragment : Fragment(), ViewPager.OnPageChangeListener, View.OnCl setListeners() fragmentTakeCourseBinding.viewPager2.currentItem = position checkSurveyCompletion() + fragmentTakeCourseBinding.backButton.setOnClickListener { + requireActivity().supportFragmentManager.popBackStack() + } } private fun setListeners() { diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/dashboard/DashboardActivity.kt b/app/src/main/java/org/ole/planet/myplanet/ui/dashboard/DashboardActivity.kt index 11903828a9..eeb3f8d28a 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/dashboard/DashboardActivity.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/dashboard/DashboardActivity.kt @@ -273,6 +273,27 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N false } + val allCommVoiceResults = mRealm.where(RealmNews::class.java) + .greaterThanOrEqualTo("time", startTime) + .findAll() + + val allCommVoice = allCommVoiceResults.filter { realmNews -> + realmNews.viewIn?.let { viewInStr -> + try { + val viewInArray = JSONArray(viewInStr) + for (i in 0 until viewInArray.length()) { + val viewInObj = viewInArray.getJSONObject(i) + if (viewInObj.optString("section") == "community") { + return@filter true + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + false + } + fun getDateFromTimestamp(timestamp: Long): String { val dateFormat = SimpleDateFormat("yyyy-MM-dd") return dateFormat.format(Date(timestamp)) @@ -282,6 +303,10 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N .map { getDateFromTimestamp(it.time) } .distinct() + val allUniqueDates = allCommVoice + .map { getDateFromTimestamp(it.time) } + .distinct() + val courseData = fetchCourseData(mRealm, user?.id) val courseId = "9517e3b45a5bb63e69bb8f269216974d" @@ -314,16 +339,16 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N } else { "Ingresa al curso $courseName completalo ($current de $max hecho)" } - challengeDialog(uniqueDates.size, courseStatus) + challengeDialog(uniqueDates.size, courseStatus, allUniqueDates.size) } else { - challengeDialog(uniqueDates.size, "$courseName no iniciado") + challengeDialog(uniqueDates.size, "$courseName no iniciado", allUniqueDates.size) } } } } } - fun challengeDialog(voiceCount: Int, courseStatus: String) { + fun challengeDialog(voiceCount: Int, courseStatus: String, allVoiceCount: Int) { val voiceTaskDone = if (voiceCount >= 5) "✅" else "[ ]" val prereqsMet = courseStatus.contains("terminado", ignoreCase = true) && voiceCount >= 5 val syncTaskDone = if (prereqsMet) { @@ -345,10 +370,10 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N if (isCompleted && !hasShownCongrats) { editor.putBoolean("has_shown_congrats", true).apply() val markdownContent = """ - ![issues challenge](file:///android_asset/images/november_challenge.jpeg)
- ### ¡Felicidades Reto Completado! + Ganancias totales: **$${calculateProgress(allVoiceCount)}** + ### ¡Felicidades! Reto Completado
""".trimIndent() - MarkdownDialog.newInstance(markdownContent, courseStatus, voiceCount) + MarkdownDialog.newInstance(markdownContent, courseStatus, voiceCount, allVoiceCount) .show(supportFragmentManager, "markdown_dialog") } else { val voicesText = if (voiceCount > 0) { @@ -357,16 +382,20 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N "" } val markdownContent = """ - ![issues challenge](file:///android_asset/images/november_challenge.jpeg)
+ Ganancias totales: **$${calculateProgress(allVoiceCount)}** ### $courseTaskDone
### $voiceTaskDone Comparte tu opinión en Nuestras Voces. $voicesText
- ### $syncTaskDone Recuerda sincronizar la aplicacion movil. + ### $syncTaskDone Recuerda sincronizar la aplicación móvil.
""".trimIndent() - MarkdownDialog.newInstance(markdownContent, courseStatus, voiceCount) + MarkdownDialog.newInstance(markdownContent, courseStatus, voiceCount, allVoiceCount) .show(supportFragmentManager, "markdown_dialog") } } + private fun calculateProgress(allVoiceCount: Int): Int { + return (allVoiceCount) * 5 + } + private fun setupRealmListeners() { setupListener { mRealm.where(RealmMyLibrary::class.java).findAllAsync() diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/dashboard/DashboardActivity.kt.lite b/app/src/main/java/org/ole/planet/myplanet/ui/dashboard/DashboardActivity.kt.lite index 85a9a6f420..68feb330d7 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/dashboard/DashboardActivity.kt.lite +++ b/app/src/main/java/org/ole/planet/myplanet/ui/dashboard/DashboardActivity.kt.lite @@ -273,6 +273,27 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N false } + val allCommVoiceResults = mRealm.where(RealmNews::class.java) + .greaterThanOrEqualTo("time", startTime) + .findAll() + + val allCommVoice = allCommVoiceResults.filter { realmNews -> + realmNews.viewIn?.let { viewInStr -> + try { + val viewInArray = JSONArray(viewInStr) + for (i in 0 until viewInArray.length()) { + val viewInObj = viewInArray.getJSONObject(i) + if (viewInObj.optString("section") == "community") { + return@filter true + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + false + } + fun getDateFromTimestamp(timestamp: Long): String { val dateFormat = SimpleDateFormat("yyyy-MM-dd") return dateFormat.format(Date(timestamp)) @@ -282,6 +303,10 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N .map { getDateFromTimestamp(it.time) } .distinct() + val allUniqueDates = allCommVoice + .map { getDateFromTimestamp(it.time) } + .distinct() + val courseData = fetchCourseData(mRealm, user?.id) val courseId = "9517e3b45a5bb63e69bb8f269216974d" @@ -314,16 +339,16 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N } else { "Ingresa al curso $courseName completalo ($current de $max hecho)" } - challengeDialog(uniqueDates.size, courseStatus) + challengeDialog(uniqueDates.size, courseStatus, allUniqueDates.size) } else { - challengeDialog(uniqueDates.size, "$courseName no iniciado") + challengeDialog(uniqueDates.size, "$courseName no iniciado", allUniqueDates.size) } } } } } - fun challengeDialog(voiceCount: Int, courseStatus: String) { + fun challengeDialog(voiceCount: Int, courseStatus: String, allVoiceCount: Int) { val voiceTaskDone = if (voiceCount >= 5) "✅" else "[ ]" val prereqsMet = courseStatus.contains("terminado", ignoreCase = true) && voiceCount >= 5 val syncTaskDone = if (prereqsMet) { @@ -345,10 +370,10 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N if (isCompleted && !hasShownCongrats) { editor.putBoolean("has_shown_congrats", true).apply() val markdownContent = """ - ![issues challenge](file:///android_asset/images/november_challenge.jpeg)
- ### ¡Felicidades Reto Completado! + Ganancias totales: **$${calculateProgress(allVoiceCount)}** + ### ¡Felicidades! Reto Completado
""".trimIndent() - MarkdownDialog.newInstance(markdownContent, courseStatus, voiceCount) + MarkdownDialog.newInstance(markdownContent, courseStatus, voiceCount, allVoiceCount) .show(supportFragmentManager, "markdown_dialog") } else { val voicesText = if (voiceCount > 0) { @@ -357,16 +382,20 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N "" } val markdownContent = """ - ![issues challenge](file:///android_asset/images/november_challenge.jpeg)
+ Ganancias totales: **$${calculateProgress(allVoiceCount)}** ### $courseTaskDone
### $voiceTaskDone Comparte tu opinión en Nuestras Voces. $voicesText
- ### $syncTaskDone Recuerda sincronizar la aplicacion movil. + ### $syncTaskDone Recuerda sincronizar la aplicación móvil.
""".trimIndent() - MarkdownDialog.newInstance(markdownContent, courseStatus, voiceCount) + MarkdownDialog.newInstance(markdownContent, courseStatus, voiceCount, allVoiceCount) .show(supportFragmentManager, "markdown_dialog") } } + private fun calculateProgress(allVoiceCount: Int): Int { + return (allVoiceCount) * 5 + } + private fun setupRealmListeners() { setupListener { mRealm.where(RealmMyLibrary::class.java).findAllAsync() diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/team/teamMember/AdapterMemberRequest.kt b/app/src/main/java/org/ole/planet/myplanet/ui/team/teamMember/AdapterMemberRequest.kt index 4c5c958877..dfd257a4cc 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/team/teamMember/AdapterMemberRequest.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/team/teamMember/AdapterMemberRequest.kt @@ -23,16 +23,25 @@ class AdapterMemberRequest(private val context: Context, private val list: Mutab } override fun onBindViewHolder(holder: ViewHolderUser, position: Int) { - rowMemberRequestBinding.tvName.text = list[position].name ?: list[position].toString() + val currentItem = list.getOrNull(position) ?: return + rowMemberRequestBinding.tvName.text = currentItem.name ?: currentItem.toString() if (isTeamLeader) { rowMemberRequestBinding.btnAccept.isEnabled = true rowMemberRequestBinding.btnReject.isEnabled = true + rowMemberRequestBinding.btnAccept.setOnClickListener { - acceptReject(list[position], true, position) + val adapterPosition = holder.bindingAdapterPosition + if (adapterPosition != RecyclerView.NO_POSITION && adapterPosition < list.size) { + acceptReject(list[adapterPosition], true, adapterPosition) + } } + rowMemberRequestBinding.btnReject.setOnClickListener { - acceptReject(list[position], false, position) + val adapterPosition = holder.bindingAdapterPosition + if (adapterPosition != RecyclerView.NO_POSITION && adapterPosition < list.size) { + acceptReject(list[adapterPosition], false, adapterPosition) + } } } else { rowMemberRequestBinding.btnAccept.isEnabled = false diff --git a/app/src/main/java/org/ole/planet/myplanet/utilities/MarkdownDialog.kt b/app/src/main/java/org/ole/planet/myplanet/utilities/MarkdownDialog.kt index 7614c66f99..8da283fb40 100644 --- a/app/src/main/java/org/ole/planet/myplanet/utilities/MarkdownDialog.kt +++ b/app/src/main/java/org/ole/planet/myplanet/utilities/MarkdownDialog.kt @@ -27,18 +27,21 @@ class MarkdownDialog : DialogFragment() { private var markdownContent: String = "" private var courseStatus: String = "" private var voiceCount: Int = 0 + private var allVoiceCount: Int = 0 companion object { private const val ARG_MARKDOWN_CONTENT = "markdown_content" private const val ARG_COURSE_STATUS = "course_status" private const val ARG_VOICE_COUNT = "voice_count" + private const val ARG_ALL_VOICE_COUNT = "all_voice_count" - fun newInstance(markdownContent: String, courseStatus: String, voiceCount: Int): MarkdownDialog { + fun newInstance(markdownContent: String, courseStatus: String, voiceCount: Int, allVoiceCount: Int): MarkdownDialog { val fragment = MarkdownDialog() val args = Bundle().apply { putString(ARG_MARKDOWN_CONTENT, markdownContent) putString(ARG_COURSE_STATUS, courseStatus) putInt(ARG_VOICE_COUNT, voiceCount) + putInt(ARG_ALL_VOICE_COUNT, allVoiceCount) } fragment.arguments = args return fragment @@ -50,6 +53,7 @@ class MarkdownDialog : DialogFragment() { markdownContent = arguments?.getString(ARG_MARKDOWN_CONTENT) ?: "" courseStatus = arguments?.getString(ARG_COURSE_STATUS) ?: "" voiceCount = arguments?.getInt(ARG_VOICE_COUNT, 0) ?: 0 + allVoiceCount = arguments?.getInt(ARG_ALL_VOICE_COUNT, 0) ?: 0 } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -75,6 +79,11 @@ class MarkdownDialog : DialogFragment() { } } setupCloseButton() + + val earnedDollars = allVoiceCount * 5 + val progressValue = ((earnedDollars.toDouble() / 500) * 100).toInt() + dialogCampaignChallengeBinding.progressBar.progress = progressValue + } override fun onStart() { diff --git a/app/src/main/res/drawable/november_challenge.jpeg b/app/src/main/res/drawable/november_challenge.jpeg new file mode 100644 index 0000000000..885e65f056 Binary files /dev/null and b/app/src/main/res/drawable/november_challenge.jpeg differ diff --git a/app/src/main/res/layout/dialog_campaign_challenge.xml b/app/src/main/res/layout/dialog_campaign_challenge.xml index 0df4d78e27..30acef1db3 100644 --- a/app/src/main/res/layout/dialog_campaign_challenge.xml +++ b/app/src/main/res/layout/dialog_campaign_challenge.xml @@ -3,8 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="16dp" - android:background="@color/secondary_bg"> + android:background="@color/secondary_bg" + android:padding="16dp"> - + + - + app:layout_constraintTop_toBottomOf="@id/progressBar" />