From 46fcfdf0a5ad9b00a517ce5231c827649c85be67 Mon Sep 17 00:00:00 2001 From: Vivian Li <112584985+strawberrybread@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:12:10 -0400 Subject: [PATCH 1/7] actions: less warnings is more (fixes #3772) (#3773) Co-authored-by: dogi --- app/build.gradle | 4 +- .../planet/myplanet/utilities/FileUtils.kt | 98 ++++++++++++------- 2 files changed, 65 insertions(+), 37 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f712ee31ab..68f3233ff4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "org.ole.planet.myplanet" minSdkVersion 21 targetSdkVersion 34 - versionCode 1655 - versionName "0.16.55" + versionCode 1656 + versionName "0.16.56" ndkVersion '21.3.6528147' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/org/ole/planet/myplanet/utilities/FileUtils.kt b/app/src/main/java/org/ole/planet/myplanet/utilities/FileUtils.kt index d1436a27a1..f8f48ebf93 100644 --- a/app/src/main/java/org/ole/planet/myplanet/utilities/FileUtils.kt +++ b/app/src/main/java/org/ole/planet/myplanet/utilities/FileUtils.kt @@ -1,5 +1,6 @@ package org.ole.planet.myplanet.utilities +import android.app.PendingIntent import android.app.usage.StorageStatsManager import android.content.Context import android.content.Intent @@ -13,9 +14,8 @@ import android.provider.MediaStore import android.text.TextUtils import android.webkit.MimeTypeMap import androidx.annotation.RequiresApi -import androidx.core.content.FileProvider -import org.ole.planet.myplanet.BuildConfig import org.ole.planet.myplanet.MainApplication +import android.content.pm.PackageInstaller import org.ole.planet.myplanet.R import java.io.BufferedReader import java.io.File @@ -116,29 +116,42 @@ object FileUtils { @JvmStatic fun installApk(activity: Context, file: String?) { + if (!file?.endsWith("apk")!!) return + val toInstall = File(file) + if (!toInstall.exists()) return try { - if (!file?.endsWith("apk")!!) return - val toInstall = getSDPathFromUrl(file) - toInstall.setReadable(true, false) - val apkUri: Uri - val intent: Intent - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - apkUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".provider", toInstall) - intent = Intent(Intent.ACTION_INSTALL_PACKAGE) - intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - intent.setData(apkUri) - } else { - apkUri = Uri.fromFile(toInstall) - intent = Intent(Intent.ACTION_VIEW) - intent.setDataAndType(apkUri, "application/vnd.android.package-archive") - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - activity.startActivity(intent) + val packageInstaller = activity.packageManager.packageInstaller + val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL) + val sessionId = packageInstaller.createSession(params) + val session = packageInstaller.openSession(sessionId) + addApkToInstallSession(toInstall, session) + val intent = Intent(activity, activity.javaClass) + val pendingIntent = PendingIntent.getActivity(activity, 0, intent, + PendingIntent.FLAG_IMMUTABLE) + val intentSender = pendingIntent.intentSender + session.commit(intentSender) + session.close() } catch (e: Exception) { e.printStackTrace() } } + @Throws(IOException::class) + private fun addApkToInstallSession(apkFile: File, session: PackageInstaller.Session) { + val out: OutputStream = session.openWrite("my_app_session", 0, -1) + val fis = FileInputStream(apkFile) + fis.use { input -> + out.use { output -> + val buffer = ByteArray(4096) + var length: Int + while (input.read(buffer).also { length = it } != -1) { + output.write(buffer, 0, length) + } + session.fsync(out) + } + } + } + private fun getMimeType(url: String): String? { var type: String? = null val extension = MimeTypeMap.getFileExtensionFromUrl(url) @@ -223,24 +236,39 @@ object FileUtils { @JvmStatic fun getImagePath(context: Context, uri: Uri?): String? { - var cursor = uri?.let { context.contentResolver.query(it, null, null, null, null) } - return if (cursor != null && cursor.moveToFirst()) { - var document_id = cursor.getString(0) - document_id = document_id.substring(document_id.lastIndexOf(":") + 1) - cursor.close() - cursor = context.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Images.Media._ID + " = ? ", arrayOf(document_id), null) + if (uri == null) return null + val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA) + var cursor: Cursor? = null + try { + cursor = context.contentResolver.query(uri, projection, null, null, null) if (cursor != null && cursor.moveToFirst()) { - val path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)) - cursor.close() - path - } else { - // Handle the case when the cursor is empty or null - null // or return an appropriate default value or handle the error accordingly + val documentIdIndex = cursor.getColumnIndex(MediaStore.Images.Media._ID) + if (documentIdIndex >= 0) { + val documentId = cursor.getString(documentIdIndex) + cursor.close() + val selection = "${MediaStore.Images.Media._ID} = ?" + val selectionArgs = arrayOf(documentId) + cursor = context.contentResolver.query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + projection, + selection, + selectionArgs, + null + ) + if (cursor != null && cursor.moveToFirst()) { + val dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA) + if (dataIndex >= 0) { + val path = cursor.getString(dataIndex) + cursor.close() + return path + } + } + } } - } else { - // Handle the case when the cursor is empty or null - null // or return an appropriate default value or handle the error accordingly + } finally { + cursor?.close() } + return null } @JvmStatic @@ -417,7 +445,7 @@ object FileUtils { fun nameWithoutExtension(fileName: String?): String?{ extractFileName(fileName) - val nameWithExtension = FileUtils.extractFileName(fileName) + val nameWithExtension = extractFileName(fileName) val nameWithoutExtension = nameWithExtension?.substringBeforeLast(".") return nameWithoutExtension } From 3a65ee9e6f4f8e913d5b5aabebe0d70658f847fb Mon Sep 17 00:00:00 2001 From: Elijah Whang <59347000+ewhang5@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:38:14 -0500 Subject: [PATCH 2/7] actions: less warnings is more (fixes #3777) (#3781) Co-authored-by: dogi --- app/build.gradle | 4 +- .../myplanet/ui/courses/CourseStepFragment.kt | 4 +- .../myplanet/ui/exam/TakeExamFragment.kt | 4 +- .../planet/myplanet/utilities/CameraUtils.kt | 77 ++++++++++++------- 4 files changed, 56 insertions(+), 33 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 68f3233ff4..1997250c14 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "org.ole.planet.myplanet" minSdkVersion 21 targetSdkVersion 34 - versionCode 1656 - versionName "0.16.56" + versionCode 1657 + versionName "0.16.57" ndkVersion '21.3.6528147' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt b/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt index fd61014b61..e2a44d1c8d 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt @@ -25,7 +25,7 @@ import org.ole.planet.myplanet.model.RealmSubmission import org.ole.planet.myplanet.model.RealmUserModel import org.ole.planet.myplanet.service.UserProfileDbHandler import org.ole.planet.myplanet.ui.exam.TakeExamFragment -import org.ole.planet.myplanet.utilities.CameraUtils.CapturePhoto +import org.ole.planet.myplanet.utilities.CameraUtils.capturePhoto import org.ole.planet.myplanet.utilities.CameraUtils.ImageCaptureCallback import org.ole.planet.myplanet.utilities.Constants import org.ole.planet.myplanet.utilities.Constants.showBetaFeature @@ -164,7 +164,7 @@ class CourseStepFragment : BaseContainerFragment(), ImageCaptureCallback { takeExam.arguments = b homeItemClickListener?.openCallFragment(takeExam) context?.let { it1 -> - CapturePhoto(it1, object : ImageCaptureCallback { + capturePhoto(it1, object : ImageCaptureCallback { override fun onImageCapture(fileUri: String?) { } }) diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/exam/TakeExamFragment.kt b/app/src/main/java/org/ole/planet/myplanet/ui/exam/TakeExamFragment.kt index b698a9102f..e47cb79318 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/exam/TakeExamFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/exam/TakeExamFragment.kt @@ -21,7 +21,7 @@ import org.ole.planet.myplanet.model.RealmExamQuestion import org.ole.planet.myplanet.model.RealmSubmission import org.ole.planet.myplanet.model.RealmSubmission.Companion.createSubmission import org.ole.planet.myplanet.service.UserProfileDbHandler -import org.ole.planet.myplanet.utilities.CameraUtils.CapturePhoto +import org.ole.planet.myplanet.utilities.CameraUtils.capturePhoto import org.ole.planet.myplanet.utilities.CameraUtils.ImageCaptureCallback import org.ole.planet.myplanet.utilities.JsonParserUtils.getStringAsJsonArray import org.ole.planet.myplanet.utilities.JsonUtils.getString @@ -210,7 +210,7 @@ class TakeExamFragment : BaseExamFragment(), View.OnClickListener, CompoundButto try { if (isCertified && !isMySurvey) { context?.let { it1 -> - CapturePhoto(it1, object : ImageCaptureCallback { + capturePhoto(it1, object : ImageCaptureCallback { override fun onImageCapture(fileUri: String?) { } }) diff --git a/app/src/main/java/org/ole/planet/myplanet/utilities/CameraUtils.kt b/app/src/main/java/org/ole/planet/myplanet/utilities/CameraUtils.kt index 59efc46ee8..b9caaf7d66 100644 --- a/app/src/main/java/org/ole/planet/myplanet/utilities/CameraUtils.kt +++ b/app/src/main/java/org/ole/planet/myplanet/utilities/CameraUtils.kt @@ -6,24 +6,28 @@ import android.content.pm.PackageManager import android.graphics.ImageFormat import android.graphics.SurfaceTexture import android.hardware.camera2.* +import android.hardware.camera2.params.OutputConfiguration +import android.hardware.camera2.params.SessionConfiguration import android.media.ImageReader +import android.os.Build import android.os.Handler import android.os.HandlerThread -import android.util.Size import android.view.Surface import androidx.core.content.ContextCompat import java.io.File import java.io.FileOutputStream import java.util.* +import java.util.concurrent.Executors object CameraUtils { private var cameraDevice: CameraDevice? = null private var captureSession: CameraCaptureSession? = null private var imageReader: ImageReader? = null private var backgroundHandler: Handler - private var backgroundThread: HandlerThread + private var backgroundThread: HandlerThread = HandlerThread("CameraBackground") + @JvmStatic - fun CapturePhoto(context: Context, callback: ImageCaptureCallback) { + fun capturePhoto(context: Context, callback: ImageCaptureCallback) { if (ContextCompat.checkSelfPermission( context, Manifest.permission.CAMERA @@ -48,11 +52,7 @@ object CameraUtils { captureBuilder?.addTarget(imageReader!!.surface) captureBuilder?.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) - val captureCallback = object : CameraCaptureSession.CaptureCallback() { - override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) { - super.onCaptureCompleted(session, request, result) - } - } + val captureCallback = object : CameraCaptureSession.CaptureCallback() {} captureSession?.stopRepeating() captureSession?.abortCaptures() @@ -83,13 +83,6 @@ object CameraUtils { val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager try { val cameraId = manager.cameraIdList[0] // Assuming we want to use the first (rear) camera - val characteristics = manager.getCameraCharacteristics(cameraId) - val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) - val largest = Collections.max( - listOf(*map!!.getOutputSizes(ImageFormat.JPEG)), - CompareSizesByArea() - ) - val reader = ImageReader.newInstance(largest.width, largest.height, ImageFormat.JPEG, 2) manager.openCamera(cameraId, object : CameraDevice.StateCallback() { override fun onOpened(camera: CameraDevice) { cameraDevice = camera @@ -117,9 +110,10 @@ object CameraUtils { val surface = Surface(texture) val captureRequestBuilder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) captureRequestBuilder.addTarget(surface) - cameraDevice!!.createCaptureSession( - listOf(surface), - object : CameraCaptureSession.StateCallback() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + val outputConfigurations = listOf(OutputConfiguration(surface)) + val executor = Executors.newSingleThreadExecutor() + val stateCallback = object : CameraCaptureSession.StateCallback() { override fun onConfigured(session: CameraCaptureSession) { if (cameraDevice == null) return captureSession = session @@ -139,9 +133,44 @@ object CameraUtils { } override fun onConfigureFailed(session: CameraCaptureSession) {} - }, - backgroundHandler - ) + } + + val sessionConfiguration = SessionConfiguration( + SessionConfiguration.SESSION_REGULAR, + outputConfigurations, + executor, + stateCallback + ) + + cameraDevice!!.createCaptureSession(sessionConfiguration) + } else { + @Suppress("DEPRECATION") + cameraDevice!!.createCaptureSession( + listOf(surface), + object : CameraCaptureSession.StateCallback() { + override fun onConfigured(session: CameraCaptureSession) { + if (cameraDevice == null) return + captureSession = session + try { + captureRequestBuilder.set( + CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE + ) + captureSession!!.setRepeatingRequest( + captureRequestBuilder.build(), + null, + backgroundHandler + ) + } catch (e: CameraAccessException) { + e.printStackTrace() + } + } + + override fun onConfigureFailed(session: CameraCaptureSession) {} + }, + backgroundHandler + ) + } } catch (e: CameraAccessException) { e.printStackTrace() } @@ -151,13 +180,7 @@ object CameraUtils { fun onImageCapture(fileUri: String?) } - private class CompareSizesByArea : Comparator { - override fun compare(lhs: Size, rhs: Size): Int { - return java.lang.Long.signum(lhs.width.toLong() * lhs.height - rhs.width.toLong() * rhs.height) - } - } init { - backgroundThread = HandlerThread("CameraBackground") backgroundThread.start() backgroundHandler = Handler(backgroundThread.looper) } From 54f59ed8d3d92f5f864e0031ff7fe2603163e4c0 Mon Sep 17 00:00:00 2001 From: rlam20 <52076121+rlam20@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:46:37 -0400 Subject: [PATCH 3/7] feedback: smoother empty list (fixes #3726) (#3783) Co-authored-by: dogi --- app/build.gradle | 4 +- .../myplanet/base/BaseRecyclerFragment.kt | 1 + .../ui/feedback/FeedbackListFragment.kt | 37 ++++++++++++++----- .../res/layout/fragment_feedback_list.xml | 11 ++++-- app/src/main/res/values-ar/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-ne/strings.xml | 1 + app/src/main/res/values-so/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 10 files changed, 44 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 1997250c14..798be1c95d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "org.ole.planet.myplanet" minSdkVersion 21 targetSdkVersion 34 - versionCode 1657 - versionName "0.16.57" + versionCode 1658 + versionName "0.16.58" ndkVersion '21.3.6528147' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/org/ole/planet/myplanet/base/BaseRecyclerFragment.kt b/app/src/main/java/org/ole/planet/myplanet/base/BaseRecyclerFragment.kt index cfb2abc90a..64dfbbb1f9 100644 --- a/app/src/main/java/org/ole/planet/myplanet/base/BaseRecyclerFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/base/BaseRecyclerFragment.kt @@ -298,6 +298,7 @@ abstract class BaseRecyclerFragment
  • : BaseRecyclerParentFragment(), On "submission" -> (v as TextView).setText(R.string.no_submissions) "teams" -> (v as TextView).setText(R.string.no_teams) "chatHistory" -> (v as TextView).setText(R.string.no_chats) + "feedback" -> (v as TextView).setText(R.string.no_feedback) else -> (v as TextView).setText(R.string.no_data_available_please_check_and_try_again) } } diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/feedback/FeedbackListFragment.kt b/app/src/main/java/org/ole/planet/myplanet/ui/feedback/FeedbackListFragment.kt index 09d5b33970..bf49250c18 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/feedback/FeedbackListFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/feedback/FeedbackListFragment.kt @@ -14,6 +14,7 @@ import org.ole.planet.myplanet.model.RealmFeedback import org.ole.planet.myplanet.model.RealmUserModel import org.ole.planet.myplanet.service.UserProfileDbHandler import org.ole.planet.myplanet.ui.feedback.FeedbackFragment.OnFeedbackSubmittedListener +import org.ole.planet.myplanet.base.BaseRecyclerFragment.Companion.showNoData class FeedbackListFragment : Fragment(), OnFeedbackSubmittedListener { private lateinit var fragmentFeedbackListBinding: FragmentFeedbackListBinding @@ -25,6 +26,7 @@ class FeedbackListFragment : Fragment(), OnFeedbackSubmittedListener { fragmentFeedbackListBinding = FragmentFeedbackListBinding.inflate(inflater, container, false) mRealm = DatabaseService(requireActivity()).realmInstance userModel = UserProfileDbHandler(requireContext()).userModel + fragmentFeedbackListBinding.fab.setOnClickListener { val feedbackFragment = FeedbackFragment() feedbackFragment.setOnFeedbackSubmittedListener(this) @@ -33,15 +35,13 @@ class FeedbackListFragment : Fragment(), OnFeedbackSubmittedListener { } } - mRealm.executeTransactionAsync( - Realm.Transaction { }, - Realm.Transaction.OnSuccess { - feedbackList = mRealm.where(RealmFeedback::class.java) - .equalTo("owner", userModel?.name).findAllAsync() - feedbackList?.addChangeListener { results -> - updatedFeedbackList(results) - } - }) + feedbackList = mRealm.where(RealmFeedback::class.java) + .equalTo("owner", userModel?.name).findAllAsync() + + feedbackList?.addChangeListener { results -> + updatedFeedbackList(results) + } + return fragmentFeedbackListBinding.root } @@ -53,6 +53,11 @@ class FeedbackListFragment : Fragment(), OnFeedbackSubmittedListener { if (userModel?.isManager() == true) list = mRealm.where(RealmFeedback::class.java).findAll() val adapterFeedback = AdapterFeedback(requireActivity(), list) fragmentFeedbackListBinding.rvFeedback.adapter = adapterFeedback + + val itemCount = feedbackList?.size ?: 0 + showNoData(fragmentFeedbackListBinding.tvMessage, itemCount, "feedback") + + updateTextViewsVisibility(itemCount) } override fun onDestroy() { @@ -74,11 +79,25 @@ class FeedbackListFragment : Fragment(), OnFeedbackSubmittedListener { updatedFeedbackList(updatedList) }) } + private fun updatedFeedbackList(updatedList: RealmResults?) { activity?.runOnUiThread { val adapterFeedback = updatedList?.let { AdapterFeedback(requireActivity(), it) } fragmentFeedbackListBinding.rvFeedback.adapter = adapterFeedback adapterFeedback?.notifyDataSetChanged() + + val itemCount = updatedList?.size ?: 0 + showNoData(fragmentFeedbackListBinding.tvMessage, itemCount, "feedback") + updateTextViewsVisibility(itemCount) } } + + private fun updateTextViewsVisibility(itemCount: Int) { + val visibility = if (itemCount == 0) View.GONE else View.VISIBLE + fragmentFeedbackListBinding.tvTitle.visibility = visibility + fragmentFeedbackListBinding.tvType.visibility = visibility + fragmentFeedbackListBinding.tvPriority.visibility = visibility + fragmentFeedbackListBinding.tvStatus.visibility = visibility + fragmentFeedbackListBinding.tvOpenDate.visibility = visibility + } } diff --git a/app/src/main/res/layout/fragment_feedback_list.xml b/app/src/main/res/layout/fragment_feedback_list.xml index b430b2fde6..a566f95885 100644 --- a/app/src/main/res/layout/fragment_feedback_list.xml +++ b/app/src/main/res/layout/fragment_feedback_list.xml @@ -22,7 +22,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - - - \ No newline at end of file + + diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 228ad83a45..1ff4fea205 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1051,6 +1051,7 @@ كوكب %s التقديمات غير متاحة لا توجد محادثات سابقة + لا توجد تعليقات متاحة %s diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 47d2cb2eec..cc2c828a20 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1051,6 +1051,7 @@ %s Planeta envíos no disponibles no hay chats anteriores + No hay comentarios aquí %s diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 1c26669b65..e9f31a26a5 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1051,6 +1051,7 @@ %s planète soumissions non disponibles aucune discussion précédente + aucun commentaire disponible %s diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml index c75d0b674e..855313a00a 100644 --- a/app/src/main/res/values-ne/strings.xml +++ b/app/src/main/res/values-ne/strings.xml @@ -1051,6 +1051,7 @@ %s ग्रह पेशाहरू उपलब्ध छैनन् अघिल्ला कुराकानीहरू छैनन् + कुनै प्रतिक्रिया उपलब्ध छैन %s diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index 38c3ada43d..ccedfc34df 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -1051,6 +1051,7 @@ %s Meerah soo gudbin lama heli karo ma jiraan wada sheekaysi hore + wax jawaab celin ah lama hayo %s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 599bfa36ff..f35f36b6e2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1051,6 +1051,7 @@ %s\'s Planet submissions not available no previous chats + no feedback available %s From 8f4c631d035e469e34f409289f309ad1eb9de554 Mon Sep 17 00:00:00 2001 From: Gideon Okuro Date: Mon, 8 Jul 2024 22:12:33 +0300 Subject: [PATCH 4/7] resources: safer transactions (fixes #3645) (#3785) Co-authored-by: dogi --- app/build.gradle | 4 +- .../ui/resources/ResourcesFragment.kt | 181 +++++++++--------- 2 files changed, 97 insertions(+), 88 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 798be1c95d..52eec789e6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "org.ole.planet.myplanet" minSdkVersion 21 targetSdkVersion 34 - versionCode 1658 - versionName "0.16.58" + versionCode 1659 + versionName "0.16.59" ndkVersion '21.3.6528147' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/resources/ResourcesFragment.kt b/app/src/main/java/org/ole/planet/myplanet/ui/resources/ResourcesFragment.kt index ce94d8315c..06afdf6c7c 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/resources/ResourcesFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/resources/ResourcesFragment.kt @@ -39,21 +39,22 @@ import java.util.UUID class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItemSelected, ChipDeletedListener, TagClickListener, OnFilterListener { - private var tvAddToLib: TextView? = null - private var tvSelected: TextView? = null - var etSearch: EditText? = null - private var etTags: EditText? = null - var adapterLibrary: AdapterResource? = null - private var flexBoxTags: FlexboxLayout? = null - lateinit var searchTags: MutableList - var config: ChipCloudConfig? = null - private var clearTags: Button? = null - private var orderByTitle: Button? = null - private var orderByDate: Button? = null - private var selectAll: CheckBox? = null + private lateinit var tvAddToLib: TextView + private lateinit var tvSelected: TextView + private lateinit var etSearch: EditText + private lateinit var etTags: EditText + private lateinit var flexBoxTags: FlexboxLayout + private lateinit var searchTags: MutableList + private lateinit var config: ChipCloudConfig + private lateinit var clearTags: Button + private lateinit var orderByTitle: Button + private lateinit var orderByDate: Button + private lateinit var selectAll: CheckBox + private lateinit var filter: ImageButton + private lateinit var adapterLibrary: AdapterResource var map: HashMap? = null private var confirmation: AlertDialog? = null - var filter: ImageButton? = null + override fun getLayout(): Int { return R.layout.fragment_my_library } @@ -62,34 +63,34 @@ class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItem map = getRatings(mRealm, "resource", model?.id) val libraryList: List = getList(RealmMyLibrary::class.java).filterIsInstance() adapterLibrary = AdapterResource(requireActivity(), libraryList, map!!, mRealm) - adapterLibrary?.setRatingChangeListener(this) - adapterLibrary?.setListener(this) - return adapterLibrary!! + adapterLibrary.setRatingChangeListener(this) + adapterLibrary.setListener(this) + return adapterLibrary } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) searchTags = ArrayList() config = Utilities.getCloudConfig().showClose(R.color.black_overlay) - tvAddToLib = requireView().findViewById(R.id.tv_add) - etSearch = requireView().findViewById(R.id.et_search) - etTags = requireView().findViewById(R.id.et_tags) - clearTags = requireView().findViewById(R.id.btn_clear_tags) - tvSelected = requireView().findViewById(R.id.tv_selected) - flexBoxTags = requireView().findViewById(R.id.flexbox_tags) - selectAll = requireView().findViewById(R.id.selectAll) - tvDelete = requireView().findViewById(R.id.tv_delete) - filter = requireView().findViewById(R.id.filter) + tvAddToLib = view.findViewById(R.id.tv_add) + etSearch = view.findViewById(R.id.et_search) + etTags = view.findViewById(R.id.et_tags) + clearTags = view.findViewById(R.id.btn_clear_tags) + tvSelected = view.findViewById(R.id.tv_selected) + flexBoxTags = view.findViewById(R.id.flexbox_tags) + selectAll = view.findViewById(R.id.selectAll) + filter = view.findViewById(R.id.filter) + initArrays() updateTvDelete() - tvAddToLib?.setOnClickListener { + tvAddToLib.setOnClickListener { if ((selectedItems?.size ?: 0) > 0) { confirmation = createAlertDialog() confirmation?.show() addToMyList() selectedItems?.clear() - tvAddToLib?.isEnabled = false // After clearing selectedItems size is always 0 + tvAddToLib.isEnabled = false checkList() } } @@ -97,45 +98,54 @@ class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItem tvDelete?.setOnClickListener { AlertDialog.Builder(this.context) .setMessage(R.string.confirm_removal) - .setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int -> + .setPositiveButton(R.string.yes) { _, _ -> deleteSelected(true) val newFragment = ResourcesFragment() recreateFragment(newFragment) } .setNegativeButton(R.string.no, null).show() } - etSearch?.addTextChangedListener(object : TextWatcher { + + etSearch.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - adapterLibrary?.setLibraryList(applyFilter(filterLibraryByTag(etSearch?.text.toString().trim { it <= ' ' }, searchTags))) - showNoData(tvMessage, adapterLibrary?.itemCount, "resources") + adapterLibrary.setLibraryList( + applyFilter( + filterLibraryByTag( + etSearch.text.toString().trim(), searchTags + ) + ) + ) + showNoData(tvMessage, adapterLibrary.itemCount, "resources") } override fun afterTextChanged(s: Editable) {} }) - requireView().findViewById(R.id.btn_collections).setOnClickListener { + + view.findViewById(R.id.btn_collections).setOnClickListener { val f = CollectionsFragment.getInstance(searchTags, "resources") f.setListener(this@ResourcesFragment) f.show(childFragmentManager, "") } - showNoData(tvMessage, adapterLibrary?.itemCount, "resources") + showNoData(tvMessage, adapterLibrary.itemCount, "resources") clearTagsButton() - setupUI(requireView().findViewById(R.id.my_library_parent_layout), requireActivity()) + setupUI(view.findViewById(R.id.my_library_parent_layout), requireActivity()) changeButtonStatus() additionalSetup() - tvFragmentInfo = requireView().findViewById(R.id.tv_fragment_info) + tvFragmentInfo = view.findViewById(R.id.tv_fragment_info) if (isMyCourseLib) tvFragmentInfo.setText(R.string.txt_myLibrary) checkList() - selectAll?.setOnClickListener { + + selectAll.setOnClickListener { updateTvDelete() - val allSelected = selectedItems?.size == adapterLibrary?.getLibraryList()?.size - adapterLibrary?.selectAllItems(!allSelected) + val allSelected = selectedItems?.size == adapterLibrary.getLibraryList().size + adapterLibrary.selectAllItems(!allSelected) if (allSelected) { - selectAll?.isChecked = false - selectAll?.text = getString(R.string.select_all) + selectAll.isChecked = false + selectAll.text = getString(R.string.select_all) } else { - selectAll?.isChecked = true - selectAll?.text = getString(R.string.unselect_all) + selectAll.isChecked = true + selectAll.text = getString(R.string.unselect_all) } } } @@ -149,14 +159,14 @@ class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItem } private fun checkList() { - if (adapterLibrary?.getLibraryList()?.isEmpty() == true) { - selectAll?.visibility = View.GONE - etSearch?.visibility = View.GONE - tvAddToLib?.visibility = View.GONE - tvSelected?.visibility = View.GONE + if (adapterLibrary.getLibraryList().isEmpty()) { + selectAll.visibility = View.GONE + etSearch.visibility = View.GONE + tvAddToLib.visibility = View.GONE + tvSelected.visibility = View.GONE requireView().findViewById(R.id.btn_collections).visibility = View.GONE requireView().findViewById(R.id.filter).visibility = View.GONE - clearTags?.visibility = View.GONE + clearTags.visibility = View.GONE tvDelete?.visibility = View.GONE } } @@ -193,17 +203,17 @@ class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItem } private fun clearTagsButton() { - clearTags?.setOnClickListener { + clearTags.setOnClickListener { saveSearchActivity() searchTags.clear() - etSearch?.setText("") - tvSelected?.text = "" + etSearch.setText("") + tvSelected.text = "" levels.clear() mediums.clear() subjects.clear() languages.clear() - adapterLibrary?.setLibraryList(applyFilter(filterLibraryByTag("", searchTags))) - showNoData(tvMessage, adapterLibrary?.itemCount, "resources") + adapterLibrary.setLibraryList(applyFilter(filterLibraryByTag("", searchTags))) + showNoData(tvMessage, adapterLibrary.itemCount, "resources") } } @@ -214,30 +224,30 @@ class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItem } override fun onTagClicked(realmTag: RealmTag) { - flexBoxTags?.removeAllViews() + flexBoxTags.removeAllViews() val chipCloud = ChipCloud(activity, flexBoxTags, config) chipCloud.setDeleteListener(this) if (!searchTags.contains(realmTag)) searchTags.add(realmTag) chipCloud.addChips(searchTags) - adapterLibrary?.setLibraryList(applyFilter(filterLibraryByTag(etSearch?.text.toString(), searchTags))) + adapterLibrary.setLibraryList(applyFilter(filterLibraryByTag(etSearch.text.toString(), searchTags))) showTagText(searchTags, tvSelected) - showNoData(tvMessage, adapterLibrary?.itemCount, "resources") + showNoData(tvMessage, adapterLibrary.itemCount, "resources") } override fun onTagSelected(tag: RealmTag) { val li: MutableList = ArrayList() li.add(tag) searchTags = li - tvSelected?.text = "${getString(R.string.selected)}${tag.name}" - adapterLibrary?.setLibraryList(applyFilter(filterLibraryByTag(etSearch?.text.toString(), li))) - showNoData(tvMessage, adapterLibrary?.itemCount, "resources") + tvSelected.text = "${getString(R.string.selected)}${tag.name}" + adapterLibrary.setLibraryList(applyFilter(filterLibraryByTag(etSearch.text.toString(), li))) + showNoData(tvMessage, adapterLibrary.itemCount, "resources") } override fun onOkClicked(list: List?) { if (list?.isEmpty() == true) { searchTags.clear() - adapterLibrary?.setLibraryList(applyFilter(filterLibraryByTag(etSearch?.text.toString(), searchTags))) - showNoData(tvMessage, adapterLibrary?.itemCount, "resources") + adapterLibrary.setLibraryList(applyFilter(filterLibraryByTag(etSearch.text.toString(), searchTags))) + showNoData(tvMessage, adapterLibrary.itemCount, "resources") } else { for (tag in list ?: emptyList()) { onTagClicked(tag) @@ -246,20 +256,20 @@ class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItem } private fun changeButtonStatus() { - tvAddToLib?.isEnabled = (selectedItems?.size ?: 0) > 0 - if (adapterLibrary?.areAllSelected() == true) { - selectAll?.isChecked = true - selectAll?.text = getString(R.string.unselect_all) + tvAddToLib.isEnabled = (selectedItems?.size ?: 0) > 0 + if (adapterLibrary.areAllSelected()) { + selectAll.isChecked = true + selectAll.text = getString(R.string.unselect_all) } else { - selectAll?.isChecked = false - selectAll?.text = getString(R.string.select_all) + selectAll.isChecked = false + selectAll.text = getString(R.string.select_all) } } override fun chipDeleted(i: Int, s: String) { searchTags.removeAt(i) - adapterLibrary?.setLibraryList(applyFilter(filterLibraryByTag(etSearch?.text.toString(), searchTags))) - showNoData(tvMessage, adapterLibrary?.itemCount, "resources") + adapterLibrary.setLibraryList(applyFilter(filterLibraryByTag(etSearch.text.toString(), searchTags))) + showNoData(tvMessage, adapterLibrary.itemCount, "resources") } override fun filter(subjects: MutableSet, languages: MutableSet, mediums: MutableSet, levels: MutableSet) { @@ -267,14 +277,14 @@ class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItem this.languages = languages this.mediums = mediums this.levels = levels - adapterLibrary?.setLibraryList(applyFilter(filterLibraryByTag(etSearch?.text.toString().trim { it <= ' ' }, searchTags))) - showNoData(tvMessage, adapterLibrary?.itemCount, "resources") + adapterLibrary.setLibraryList(applyFilter(filterLibraryByTag(etSearch.text.toString().trim { it <= ' ' }, searchTags))) + showNoData(tvMessage, adapterLibrary.itemCount, "resources") } override fun getData(): Map> { - val libraryList = adapterLibrary?.getLibraryList()?.filterNotNull() + val libraryList = adapterLibrary.getLibraryList().filterNotNull() val b: MutableMap> = HashMap() - b["languages"] = libraryList?.let { getArrayList(it, "languages").filterNotNull().toSet() }!! + b["languages"] = libraryList.let { getArrayList(it, "languages").filterNotNull().toSet() } b["subjects"] = libraryList.let { getSubjects(it).toList().toSet() } b["mediums"] = getArrayList(libraryList, "mediums").filterNotNull().toSet() b["levels"] = getLevels(libraryList).toList().toSet() @@ -299,7 +309,7 @@ class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItem private fun filterApplied(): Boolean { return !(subjects.isEmpty() && languages.isEmpty() && mediums.isEmpty() && levels.isEmpty() - && searchTags.isEmpty() && "${etSearch?.text}".isEmpty()) + && searchTags.isEmpty() && "${etSearch.text}".isEmpty()) } private fun saveSearchActivity() { @@ -310,7 +320,7 @@ class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItem activity.time = Calendar.getInstance().timeInMillis activity.createdOn = model?.planetCode!! activity.parentCode = model?.parentCode!! - activity.text = etSearch?.text.toString() + activity.text = "${etSearch.text}" activity.type = "resources" val filter = JsonObject() filter.add("tags", getTagsArray(searchTags)) @@ -324,22 +334,21 @@ class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItem } private fun recreateFragment(fragment: Fragment) { - if (isMyCourseLib) { - val args = Bundle() - args.putBoolean("isMyCourseLib", true) - fragment.arguments = args - val transaction = parentFragmentManager.beginTransaction() - transaction.replace(R.id.fragment_container, fragment) - transaction.addToBackStack(null) - transaction.commit() - } else { + if (isAdded && activity != null && !requireActivity().isFinishing) { val transaction = parentFragmentManager.beginTransaction() + if (isMyCourseLib) { + val args = Bundle().apply { + putBoolean("isMyCourseLib", true) + } + fragment.arguments = args + } transaction.replace(R.id.fragment_container, fragment) transaction.addToBackStack(null) transaction.commit() } } + private fun additionalSetup() { val bottomSheet = requireView().findViewById(R.id.card_filter) requireView().findViewById(R.id.filter).setOnClickListener { @@ -353,7 +362,7 @@ class ResourcesFragment : BaseRecyclerFragment(), OnLibraryItem f.show(childFragmentManager, "") bottomSheet.visibility = View.GONE } - orderByDate?.setOnClickListener { adapterLibrary?.toggleSortOrder() } - orderByTitle?.setOnClickListener { adapterLibrary?.toggleTitleSortOrder() } + orderByDate.setOnClickListener { adapterLibrary.toggleSortOrder() } + orderByTitle.setOnClickListener { adapterLibrary.toggleTitleSortOrder() } } } \ No newline at end of file From d37137aab907825562c4c468d2b5fe493e8e0e7c Mon Sep 17 00:00:00 2001 From: Gideon Okuro Date: Mon, 8 Jul 2024 22:22:24 +0300 Subject: [PATCH 5/7] chat: smoother refresh (fixes #3784) (#3786) Co-authored-by: dogi --- app/build.gradle | 4 +- .../planet/myplanet/model/RealmChatHistory.kt | 2 + .../myplanet/ui/chat/ChatDetailFragment.kt | 64 ++++++------------- .../ui/chat/ChatHistoryListAdapter.kt | 20 +----- .../ui/chat/ChatHistoryListFragment.kt | 59 ++++++++--------- .../ui/dashboard/DashboardActivity.kt | 7 ++ .../ui/dashboard/DashboardActivity.kt.lite | 7 ++ 7 files changed, 68 insertions(+), 95 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 52eec789e6..965ab1eb33 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "org.ole.planet.myplanet" minSdkVersion 21 targetSdkVersion 34 - versionCode 1659 - versionName "0.16.59" + versionCode 1660 + versionName "0.16.60" ndkVersion '21.3.6528147' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/org/ole/planet/myplanet/model/RealmChatHistory.kt b/app/src/main/java/org/ole/planet/myplanet/model/RealmChatHistory.kt index 34cc5055d2..5e034ef2a1 100644 --- a/app/src/main/java/org/ole/planet/myplanet/model/RealmChatHistory.kt +++ b/app/src/main/java/org/ole/planet/myplanet/model/RealmChatHistory.kt @@ -18,6 +18,7 @@ open class RealmChatHistory : RealmObject() { var aiProvider: String? = null var title: String? = null var createdDate: String? = null + var updatedDate: String? = null var conversations: RealmList? = null companion object { @JvmStatic @@ -33,6 +34,7 @@ open class RealmChatHistory : RealmObject() { chatHistory._id = JsonUtils.getString("_id", act) chatHistory.title = JsonUtils.getString("title", act) chatHistory.createdDate = JsonUtils.getString("createdDate", act) + chatHistory.updatedDate = JsonUtils.getString("updatedDate", act) chatHistory.user = JsonUtils.getString("user", act) chatHistory.aiProvider = JsonUtils.getString("aiProvider", act) chatHistory.conversations = parseConversations(mRealm, JsonUtils.getJsonArray("conversations", act)) diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatDetailFragment.kt b/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatDetailFragment.kt index f5cc74baec..e2d0bc44d7 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatDetailFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatDetailFragment.kt @@ -16,10 +16,12 @@ import org.ole.planet.myplanet.datamanager.* import org.ole.planet.myplanet.model.* import org.ole.planet.myplanet.model.RealmChatHistory.Companion.addConversationToChatHistory import org.ole.planet.myplanet.service.UserProfileDbHandler +import org.ole.planet.myplanet.ui.dashboard.DashboardActivity import org.ole.planet.myplanet.utilities.Utilities import retrofit2.Call import retrofit2.Callback import retrofit2.Response +import java.util.Date class ChatDetailFragment : Fragment() { lateinit var fragmentChatDetailBinding: FragmentChatDetailBinding @@ -30,6 +32,7 @@ class ChatDetailFragment : Fragment() { private var aiName: String = "" private var aiModel: String = "" private lateinit var mRealm: Realm + var user: RealmUserModel? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -43,7 +46,7 @@ class ChatDetailFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mRealm = DatabaseService(requireContext()).realmInstance - val user = UserProfileDbHandler(requireContext()).userModel + user = UserProfileDbHandler(requireContext()).userModel mAdapter = ChatAdapter(ArrayList(), requireContext(), fragmentChatDetailBinding.recyclerGchat) fragmentChatDetailBinding.recyclerGchat.adapter = mAdapter val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(requireContext()) @@ -268,6 +271,7 @@ class ChatDetailFragment : Fragment() { override fun onResponse(call: Call, response: Response) { val responseBody = response.body() if (response.isSuccessful && responseBody != null) { + val jsonObject = JsonObject() if (responseBody.status == "Success") { val chatResponse = response.body()?.chat if (chatResponse != null) { @@ -275,12 +279,13 @@ class ChatDetailFragment : Fragment() { mAdapter.addResponse(chatResponse) _id = "${response.body()?.couchDBResponse?.id}" _rev = "${response.body()?.couchDBResponse?.rev}" - val jsonObject = JsonObject() jsonObject.addProperty("_rev", "${response.body()?.couchDBResponse?.rev}") jsonObject.addProperty("_id", "${response.body()?.couchDBResponse?.id}") - jsonObject.addProperty("time", "") - jsonObject.addProperty("title", "") - jsonObject.addProperty("updatedTime", "") + jsonObject.addProperty("aiProvider", aiName) + jsonObject.addProperty("user", user?.name) + jsonObject.addProperty("title", query) + jsonObject.addProperty("createdTime", Date().time) + jsonObject.addProperty("updatedDate", "") val conversationsArray = JsonArray() val conversationObject = JsonObject() @@ -289,16 +294,22 @@ class ChatDetailFragment : Fragment() { conversationsArray.add(conversationObject) jsonObject.add("conversations", conversationsArray) + + requireActivity().runOnUiThread { + RealmChatHistory.insert(mRealm, jsonObject) + } + (requireActivity() as? DashboardActivity)?.refreshChatHistoryList() } } else { fragmentChatDetailBinding.textGchatIndicator.visibility = View.VISIBLE fragmentChatDetailBinding.textGchatIndicator.text = context?.getString(R.string.message_placeholder, responseBody.message) - val jsonObject = JsonObject() jsonObject.addProperty("_rev", "") jsonObject.addProperty("_id", "") - jsonObject.addProperty("time", "") - jsonObject.addProperty("title", "") - jsonObject.addProperty("updatedTime", "") + jsonObject.addProperty("aiProvider", aiName) + jsonObject.addProperty("user", user?.name) + jsonObject.addProperty("title", query) + jsonObject.addProperty("createdTime", Date().time) + jsonObject.addProperty("updatedDate", "") val conversationsArray = JsonArray() val conversationObject = JsonObject() @@ -310,6 +321,7 @@ class ChatDetailFragment : Fragment() { requireActivity().runOnUiThread { RealmChatHistory.insert(mRealm, jsonObject) } + (requireActivity() as? DashboardActivity)?.refreshChatHistoryList() } } else { fragmentChatDetailBinding.textGchatIndicator.visibility = View.VISIBLE @@ -318,23 +330,6 @@ class ChatDetailFragment : Fragment() { } else { response.message() } - val jsonObject = JsonObject() - jsonObject.addProperty("_rev", "") - jsonObject.addProperty("_id", "") - jsonObject.addProperty("time", "") - jsonObject.addProperty("title", "") - jsonObject.addProperty("updatedTime", "") - - val conversationsArray = JsonArray() - val conversationObject = JsonObject() - conversationObject.addProperty("query", query) - conversationObject.addProperty("response", "") - conversationsArray.add(conversationObject) - - jsonObject.add("conversations", conversationsArray) - requireActivity().runOnUiThread { - RealmChatHistory.insert(mRealm, jsonObject) - } } fragmentChatDetailBinding.buttonGchatSend.isEnabled = true @@ -343,23 +338,6 @@ class ChatDetailFragment : Fragment() { } override fun onFailure(call: Call, t: Throwable) { - val jsonObject = JsonObject() - jsonObject.addProperty("_rev", "") - jsonObject.addProperty("_id", "") - jsonObject.addProperty("time", "") - jsonObject.addProperty("title", "") - jsonObject.addProperty("updatedTime", "") - - val conversationsArray = JsonArray() - val conversationObject = JsonObject() - conversationObject.addProperty("query", query) - conversationObject.addProperty("response", "") - conversationsArray.add(conversationObject) - - jsonObject.add("conversations", conversationsArray) - requireActivity().runOnUiThread { - RealmChatHistory.insert(mRealm, jsonObject) - } fragmentChatDetailBinding.textGchatIndicator.visibility = View.VISIBLE fragmentChatDetailBinding.textGchatIndicator.text = context?.getString(R.string.message_placeholder, t.message) fragmentChatDetailBinding.buttonGchatSend.isEnabled = true diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatHistoryListAdapter.kt b/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatHistoryListAdapter.kt index 75b590868b..82e78c7f30 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatHistoryListAdapter.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatHistoryListAdapter.kt @@ -55,10 +55,10 @@ class ChatHistoryListAdapter(var context: Context, private var chatHistory: List if (chat.conversations != null && chat.conversations?.isNotEmpty() == true) { chat.conversations?.get(0)?.query?.contains(query, ignoreCase = true) == true } else { - chat.title?.contains(query, ignoreCase = true) == true + chat.title?.contains(query, ignoreCase = true) ==true } } - notifyItemRangeChanged(0, filteredChatHistory.size) + notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -77,23 +77,9 @@ class ChatHistoryListAdapter(var context: Context, private var chatHistory: List } fun updateChatHistory(newChatHistory: List) { - val oldListSize = chatHistory.size - val newListSize = newChatHistory.size chatHistory = newChatHistory filteredChatHistory = newChatHistory - if(oldListSizenewListSize){ - notifyItemRangeRemoved(newListSize,oldListSize-newListSize) - } - else{ - for(i in 0 until newListSize){ - if(chatHistory[i] != newChatHistory[i]){ - notifyItemChanged(i) - } - } - } + notifyDataSetChanged() } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatHistoryListFragment.kt b/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatHistoryListFragment.kt index 513d47cb2f..b740263e5e 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatHistoryListFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatHistoryListFragment.kt @@ -54,42 +54,13 @@ class ChatHistoryListFragment : Fragment() { } } - val mRealm = DatabaseService(requireActivity()).realmInstance - val chats = mRealm.where(RealmChatHistory::class.java).findAll() - - val list = mRealm.where(RealmChatHistory::class.java).equalTo("user", user?.name) - .sort("id", Sort.DESCENDING) - .findAll() - - val filteredHistoryList = ArrayList() - for (chat in chats) { - val model = list.find { it.id == chat.id } - if (model != null && !filteredHistoryList.contains(model)) { - filteredHistoryList.add(model) - } - } - showNoData(fragmentChatHistoryListBinding.noChats, filteredHistoryList.size, "chatHistory") - if (filteredHistoryList.isEmpty()) { - fragmentChatHistoryListBinding.searchBar.visibility = View.GONE - fragmentChatHistoryListBinding.recyclerView.visibility = View.GONE - } - val adapter = ChatHistoryListAdapter(requireContext(), list, this) - adapter.setChatHistoryItemClickListener(object : ChatHistoryListAdapter.ChatHistoryItemClickListener { - override fun onChatHistoryItemClicked(conversations: RealmList?, id: String, rev:String?) { - conversations?.let { sharedViewModel.setSelectedChatHistory(it) } - sharedViewModel.setSelected_id(id) - rev?.let { sharedViewModel.setSelected_rev(it) } - - fragmentChatHistoryListBinding.slidingPaneLayout.openPane() - } - }) - fragmentChatHistoryListBinding.recyclerView.adapter = adapter + refreshChatHistoryList() fragmentChatHistoryListBinding.searchBar.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - adapter.filter(s.toString()) + (fragmentChatHistoryListBinding.recyclerView.adapter as? ChatHistoryListAdapter)?.filter(s.toString()) } override fun afterTextChanged(s: Editable?) {} @@ -102,8 +73,30 @@ class ChatHistoryListFragment : Fragment() { .sort("id", Sort.DESCENDING) .findAll() - val adapter = fragmentChatHistoryListBinding.recyclerView.adapter as ChatHistoryListAdapter - adapter.updateChatHistory(list) + val adapter = fragmentChatHistoryListBinding.recyclerView.adapter as? ChatHistoryListAdapter + if (adapter == null) { + val newAdapter = ChatHistoryListAdapter(requireContext(), list, this) + newAdapter.setChatHistoryItemClickListener(object : ChatHistoryListAdapter.ChatHistoryItemClickListener { + override fun onChatHistoryItemClicked(conversations: RealmList?, id: String, rev: String?) { + conversations?.let { sharedViewModel.setSelectedChatHistory(it) } + sharedViewModel.setSelected_id(id) + rev?.let { sharedViewModel.setSelected_rev(it) } + + fragmentChatHistoryListBinding.slidingPaneLayout.openPane() + } + }) + fragmentChatHistoryListBinding.recyclerView.adapter = newAdapter + } else { + adapter.updateChatHistory(list) + fragmentChatHistoryListBinding.searchBar.visibility = View.VISIBLE + fragmentChatHistoryListBinding.recyclerView.visibility = View.VISIBLE + } + + showNoData(fragmentChatHistoryListBinding.noChats, list.size, "chatHistory") + if (list.isEmpty()) { + fragmentChatHistoryListBinding.searchBar.visibility = View.GONE + fragmentChatHistoryListBinding.recyclerView.visibility = View.GONE + } } } 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 054e33a82a..ff7986587f 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 @@ -204,6 +204,13 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N }) } + fun refreshChatHistoryList() { + val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container) + if (fragment is ChatHistoryListFragment) { + fragment.refreshChatHistoryList() + } + } + private fun hideWifi() { val nav_Menu = activityDashboardBinding.appBarBell.bellToolbar.menu nav_Menu.findItem(R.id.menu_goOnline) 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 3f3a9cf013..adc9634237 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 @@ -204,6 +204,13 @@ class DashboardActivity : DashboardElementActivity(), OnHomeItemClickListener, N }) } + fun refreshChatHistoryList() { + val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container) + if (fragment is ChatHistoryListFragment) { + fragment.refreshChatHistoryList() + } + } + private fun hideWifi() { val nav_Menu = activityDashboardBinding.appBarBell.bellToolbar.menu nav_Menu.findItem(R.id.menu_goOnline) From 2f0d82998b1af7b8d2057ac4e4d6d9baf1847fa4 Mon Sep 17 00:00:00 2001 From: Vivian Li <112584985+strawberrybread@users.noreply.github.com> Date: Tue, 9 Jul 2024 11:36:54 -0400 Subject: [PATCH 6/7] courses: smoother steps (fixes #3789) (#3790) Co-authored-by: dogi --- app/build.gradle | 4 ++-- .../planet/myplanet/ui/courses/CourseStepFragment.kt | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 965ab1eb33..3fb4d54968 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "org.ole.planet.myplanet" minSdkVersion 21 targetSdkVersion 34 - versionCode 1660 - versionName "0.16.60" + versionCode 1661 + versionName "0.16.61" ndkVersion '21.3.6528147' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt b/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt index e2a44d1c8d..8e6cd8be51 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/courses/CourseStepFragment.kt @@ -1,5 +1,6 @@ package org.ole.planet.myplanet.ui.courses +import android.os.Build import android.os.Bundle import android.text.Spannable import android.text.method.LinkMovementMethod @@ -7,6 +8,7 @@ import android.text.style.URLSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.RequiresApi import androidx.fragment.app.Fragment import io.realm.Case import io.realm.Realm @@ -51,7 +53,6 @@ class CourseStepFragment : BaseContainerFragment(), ImageCaptureCallback { stepId = requireArguments().getString("stepId") stepNumber = requireArguments().getInt("stepNumber") } - userVisibleHint = false } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -89,6 +90,7 @@ class CourseStepFragment : BaseContainerFragment(), ImageCaptureCallback { } } + @RequiresApi(Build.VERSION_CODES.O) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) step = cRealm.where(RealmCourseStep::class.java).equalTo("id", stepId).findFirst()!! @@ -115,6 +117,9 @@ class CourseStepFragment : BaseContainerFragment(), ImageCaptureCallback { textWithSpans.removeSpan(urlSpan) } } + if (isVisible && isMyCourse(user?.id, step.courseId, cRealm)) { + saveCourseProgress() + } } private fun hideTestIfNoQuestion() { @@ -152,6 +157,7 @@ class CourseStepFragment : BaseContainerFragment(), ImageCaptureCallback { } } + @RequiresApi(Build.VERSION_CODES.O) private fun setListeners() { val notDownloadedResources: List = cRealm.where(RealmMyLibrary::class.java).equalTo("stepId", stepId).equalTo("resourceOffline", false).isNotNull("resourceLocalAddress").findAll() setResourceButton(notDownloadedResources, fragmentCourseStepBinding.btnResources) @@ -175,6 +181,7 @@ class CourseStepFragment : BaseContainerFragment(), ImageCaptureCallback { setOpenResourceButton(downloadedResources, fragmentCourseStepBinding.btnOpen) } + @RequiresApi(Build.VERSION_CODES.O) override fun onDownloadComplete() { super.onDownloadComplete() setListeners() @@ -184,7 +191,7 @@ class CourseStepFragment : BaseContainerFragment(), ImageCaptureCallback { companion object { fun prependBaseUrlToImages(markdownContent: String?, baseUrl: String): String { - val pattern = "!\\[.*?\\]\\((.*?)\\)" + val pattern = "!\\[.*?]\\((.*?)\\)" val imagePattern = Pattern.compile(pattern) val matcher = markdownContent?.let { imagePattern.matcher(it) } val result = StringBuffer() From 020db62b641e356822bf33a7d7964756de75beae Mon Sep 17 00:00:00 2001 From: Elijah Whang <59347000+ewhang5@users.noreply.github.com> Date: Tue, 9 Jul 2024 10:43:12 -0500 Subject: [PATCH 7/7] community: smoother tabs (fixes #3797) (#3798) Co-authored-by: dogi --- app/build.gradle | 4 ++-- .../ole/planet/myplanet/ui/community/AdapterLeader.kt | 11 ++++++----- .../myplanet/ui/community/CommunityTabFragment.kt | 1 - 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 3fb4d54968..d4bf5b9ceb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "org.ole.planet.myplanet" minSdkVersion 21 targetSdkVersion 34 - versionCode 1661 - versionName "0.16.61" + versionCode 1662 + versionName "0.16.62" ndkVersion '21.3.6528147' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/community/AdapterLeader.kt b/app/src/main/java/org/ole/planet/myplanet/ui/community/AdapterLeader.kt index c340cdf688..f3e6a0cf1a 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/community/AdapterLeader.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/community/AdapterLeader.kt @@ -4,10 +4,11 @@ import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import org.ole.planet.myplanet.R import org.ole.planet.myplanet.databinding.RowJoinedUserBinding import org.ole.planet.myplanet.model.RealmUserModel -class AdapterLeader(var context: Context, var leaders: List) : +class AdapterLeader(var context: Context, private var leaders: List) : RecyclerView.Adapter() { private lateinit var rowJoinedUserBinding: RowJoinedUserBinding override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -23,15 +24,15 @@ class AdapterLeader(var context: Context, var leaders: List) : if (leaders[position].firstName == null) { holder.title.text = leaders[position].name } else { - holder.title.text = "${leaders[position]}" + holder.title.text = context.getString(R.string.message_placeholder, leaders[position]) } - holder.tv_description.text = leaders[position].email + holder.tvDescription.text = leaders[position].email } } - internal inner class ViewHolderLeader(private val rowJoinedUserBinding: RowJoinedUserBinding) : RecyclerView.ViewHolder(rowJoinedUserBinding.root){ + internal inner class ViewHolderLeader(rowJoinedUserBinding: RowJoinedUserBinding) : RecyclerView.ViewHolder(rowJoinedUserBinding.root){ var title= rowJoinedUserBinding.tvTitle - var tv_description= rowJoinedUserBinding.tvDescription + var tvDescription= rowJoinedUserBinding.tvDescription var icon= rowJoinedUserBinding.icMore } } \ No newline at end of file diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/community/CommunityTabFragment.kt b/app/src/main/java/org/ole/planet/myplanet/ui/community/CommunityTabFragment.kt index 36c550e783..200fa92e22 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/community/CommunityTabFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/community/CommunityTabFragment.kt @@ -9,7 +9,6 @@ import androidx.fragment.app.Fragment import com.google.android.material.tabs.TabLayoutMediator import org.ole.planet.myplanet.databinding.FragmentTeamDetailBinding import org.ole.planet.myplanet.service.UserProfileDbHandler -import org.ole.planet.myplanet.ui.sync.SyncActivity import org.ole.planet.myplanet.utilities.Constants.PREFS_NAME import org.ole.planet.myplanet.utilities.TimeUtils import java.util.Date