diff --git a/app/build.gradle b/app/build.gradle index 494a6b9..9343354 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -83,7 +83,7 @@ dependencies { implementation 'androidx.preference:preference-ktx:1.2.1' implementation 'androidx.preference:preference-ktx:1.2.1' implementation 'com.google.android.play:core:1.10.3' - + implementation 'io.nats:jnats:2.11.2' @@ -142,5 +142,7 @@ dependencies { implementation("androidx.work:work-runtime-ktx:2.9.0") - implementation("io.nats:jnats:2.11.2") + // implementation("io.nats:jnats:2.11.2") + implementation 'com.google.android.play:core:1.10.3' + } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 153965f..abc813f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,8 @@ + + @@ -81,7 +83,17 @@ android:exported="false" android:label="@string/title_activity_dashboard" android:theme="@style/Theme.HPOS.NoActionBar" - tools:ignore="MissingClass" /> + tools:ignore="AppLinkUrlError,MissingClass"> + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/adapter/OfflineUserListAdapter.kt b/app/src/main/java/com/example/hpostesting/presentation/adapter/OfflineUserListAdapter.kt index c190408..f1481a5 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/adapter/OfflineUserListAdapter.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/adapter/OfflineUserListAdapter.kt @@ -64,7 +64,7 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int) if (false) { Toast.makeText( view.context, - context?.getString(R.string.low_battery_warning), + view.context.getString(R.string.low_battery_warning), Toast.LENGTH_SHORT ).show() return@setOnClickListener @@ -73,7 +73,7 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int) if (userList.testStatus!!) { Toast.makeText( view.context, - context?.getString(R.string.test_already_conducted), + view.context.getString(R.string.test_already_conducted), Toast.LENGTH_SHORT ).show() } else { @@ -81,13 +81,14 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int) if (isBetween15And30Minutes(userList.incubationTime) < Constants.INCUBATION_TIME_MIN) { Toast.makeText( view.context, - context?.getString(R.string.incubation_not_completed), + view.context.getString(R.string.incubation_not_completed), Toast.LENGTH_SHORT ).show() - } else if (isBetween15And30Minutes(userList.incubationTime) > Constants.INCUBATION_TIME_MAX) { + } else + if (isBetween15And30Minutes(userList.incubationTime) > Constants.INCUBATION_TIME_MAX) { Toast.makeText( view.context, - context?.getString(R.string.incubation_crossed_30_minutes), + view.context.getString(R.string.incubation_crossed_30_minutes), Toast.LENGTH_SHORT ).show() } else { @@ -102,7 +103,7 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int) } else { Toast.makeText( view.context, - context?.getString(R.string.incubation_not_started), + view.context.getString(R.string.incubation_not_started), Toast.LENGTH_SHORT ).show() } diff --git a/app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt index 4741cd2..6591f33 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt @@ -1,29 +1,42 @@ package com.example.hpostesting.presentation.dashboard +import android.app.DownloadManager +import android.content.BroadcastReceiver import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.net.Uri import android.os.Bundle import android.util.Log import android.view.Menu import android.widget.Toast +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.FileProvider import androidx.drawerlayout.widget.DrawerLayout import androidx.navigation.findNavController import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.navigateUp import androidx.navigation.ui.setupActionBarWithNavController import androidx.navigation.ui.setupWithNavController +import com.example.hpostesting.data.Result import com.example.hpostesting.data.constant.LanguageManager import com.example.hpostesting.presentation.NatsManager import com.google.android.material.navigation.NavigationView import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.play.core.appupdate.AppUpdateManager import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel + import com.google.firebase.appdistribution.FirebaseAppDistribution import com.google.firebase.appdistribution.FirebaseAppDistributionException import com.google.firebase.crashlytics.FirebaseCrashlytics import dagger.hilt.android.AndroidEntryPoint import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.databinding.ActivityDashboardBinding +import okhttp3.ResponseBody + +import java.io.File open interface IDataCollector { fun setConnect(connect: Boolean) @@ -36,6 +49,10 @@ class DashboardActivity : AppCompatActivity(), IDataCollector { private lateinit var binding: ActivityDashboardBinding var responses: String = "" lateinit var nats: NatsManager + private var downloadId: Long = 0 + // TODO: Remove hemocube viewmodel + private val hemocubeViewModel: HemoCubeViewModel by viewModels() + override fun attachBaseContext(newBase: Context?) { val languageCode = LanguageManager.getSavedLanguage(newBase!!) LanguageManager.setLocale(newBase, languageCode) @@ -52,6 +69,42 @@ class DashboardActivity : AppCompatActivity(), IDataCollector { nats.connect() nats.pub("server.hpos.HCV-000-3001.ping", "THIS IS A TEST MSG") + hemocubeViewModel.deviceUpdate.observe(this) { result -> + when (result) { + is Result.Success -> { + // Handle success + val apkUrl = result.data +// val apkUrl = "https://dl.dropboxusercontent.com/s/fi/1c3nn7t0co431hicl3hrt/app-debug.apk?rlkey=e4uf13ty1dpcked614vy1aaqp&dl=0" + initiateUpdate(apkUrl.toString()) + Log.d("ApI", "APK URL: $apkUrl") + Toast.makeText( + this, + "APK UPLOAD ${result.data}", + Toast.LENGTH_SHORT + ).show() + // The APK URL LiveData will be updated automatically + } + + is Result.Error -> { + result.exception.let { message -> + Toast.makeText(this, "An error occurred On Updating App: $message", Toast.LENGTH_LONG) + .show() + + } + + } + + is Result.Loading -> { + + } + + else -> { + + } + } + } + + val drawerLayout: DrawerLayout = binding.drawerLayout val navView: NavigationView = binding.navView val navController = findNavController(R.id.nav_host_fragment_content_dashboard) @@ -63,7 +116,6 @@ class DashboardActivity : AppCompatActivity(), IDataCollector { ) setupActionBarWithNavController(navController, appBarConfiguration) navView.setupWithNavController(navController) - } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -77,6 +129,82 @@ class DashboardActivity : AppCompatActivity(), IDataCollector { return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() } + private fun initiateUpdate(responseBody: String) { + // Extract the URL from the ResponseBody + val apkUrl = responseBody + + // Check if the extracted APK URL is valid + if (!isValidHttpUrl(apkUrl)) { + // Handle the case where the URL is not valid + return + } + + val request = DownloadManager.Request(Uri.parse(apkUrl)) + request.setTitle("App Update") + request.setDescription("Downloading update...") + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) + request.setDestinationInExternalFilesDir(this, "Updates", "update.apk") + + val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + downloadId = downloadManager.enqueue(request) + + // Register a BroadcastReceiver to receive the download complete event + val filter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE) + registerReceiver(downloadReceiver, filter) + } + + // Function to check if a URL has a valid HTTP/HTTPS scheme + // Function to extract the APK URL from the ResponseBody + private fun extractApkUrl(responseBody: ResponseBody): String { + // Assuming your ResponseBody contains the APK URL as a string + return responseBody.string() + } + + // Function to check if a URL has a valid HTTP/HTTPS scheme + private fun isValidHttpUrl(url: String): Boolean { + return url.startsWith("http://") || url.startsWith("https://") + } + private val downloadReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) + if (id == downloadId) { + // Install the downloaded APK + installApk() + } + } + } + + private fun installApk() { + val file = File(getExternalFilesDir("Updates"), "update.apk") + file.setReadable(true, false) // Ensure the file is readable + + val uri: Uri = FileProvider.getUriForFile( + this, + "com.example.hpostesting.fileprovider", + file + ) + + // Create an intent to install the APK + val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE) + installIntent.data = uri + installIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or + Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_CLEAR_TOP + installIntent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true) + + // Start the installation + startActivity(installIntent) + + Log.d("InstallApk", "Install Intent URI: $uri") + Log.d("InstallApk", "Package Name: $packageName") + } + + override fun onDestroy() { + super.onDestroy() + unregisterReceiver(downloadReceiver) + } + + override fun onResume() { super.onResume() diff --git a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt index 9fa3604..2e89400 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt @@ -169,7 +169,7 @@ class HemoCubeViewModel @Inject constructor( val logFile = logFileManager.createLogFile().let { file -> val requestBody = file?.asRequestBody("multipart/form-data".toMediaTypeOrNull()) val multipartFile = - requestBody?.let { MultipartBody.Part.createFormData("logFile", file.name, it) } + requestBody?.let { MultipartBody.Part.createFormData("logFile", file?.name, it) } multipartFile?.let { partFile -> databaseRepository.uploadLogs(partFile).let { result -> uploadLogs.postValue(result) diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..6e9c380 --- /dev/null +++ b/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,3 @@ + + +