Compare commits
8 Commits
preprod-te
...
preprod-te
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ba0124f1a | ||
|
|
f9b8df970d | ||
|
|
46d2c2f57b | ||
|
|
18ed6c4e11 | ||
|
|
b0d348cf6d | ||
|
|
214a834740 | ||
|
|
dfbcb4af6a | ||
|
|
ba45346fba |
@@ -19,8 +19,8 @@ android {
|
|||||||
applicationId "in.sminnovations.hpostesting"
|
applicationId "in.sminnovations.hpostesting"
|
||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 56
|
versionCode 26
|
||||||
versionName "2.1.56"
|
versionName "2.1.26"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ dependencies {
|
|||||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||||
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 'com.google.android.play:core:1.10.3'
|
||||||
|
implementation 'io.nats:jnats:2.11.2'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -139,4 +139,6 @@ dependencies {
|
|||||||
|
|
||||||
implementation "androidx.preference:preference-ktx:1.2.1"
|
implementation "androidx.preference:preference-ktx:1.2.1"
|
||||||
implementation 'io.nats:jnats:2.11.2'
|
implementation 'io.nats:jnats:2.11.2'
|
||||||
|
implementation 'com.google.android.play:core:1.10.3'
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.BATTERY_STATS"
|
android:name="android.permission.BATTERY_STATS"
|
||||||
tools:ignore="ProtectedPermissions" />
|
tools:ignore="ProtectedPermissions" />
|
||||||
@@ -62,7 +64,17 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/title_activity_dashboard"
|
android:label="@string/title_activity_dashboard"
|
||||||
android:theme="@style/Theme.HPOS.NoActionBar"
|
android:theme="@style/Theme.HPOS.NoActionBar"
|
||||||
tools:ignore="MissingClass" />
|
tools:ignore="AppLinkUrlError,MissingClass">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:scheme="content" />
|
||||||
|
<data android:scheme="file" />
|
||||||
|
<data android:mimeType="application/vnd.android.package-archive" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="com.example.hpostesting.presentation.testRight.UsbService"
|
android:name="com.example.hpostesting.presentation.testRight.UsbService"
|
||||||
@@ -114,6 +126,17 @@
|
|||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:stateNotNeeded="true"
|
android:stateNotNeeded="true"
|
||||||
tools:replace="android:screenOrientation" />
|
tools:replace="android:screenOrientation" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="com.example.hpostesting.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -64,7 +64,7 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int)
|
|||||||
if (batLevel < 40) {
|
if (batLevel < 40) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
view.context,
|
view.context,
|
||||||
context?.getString(R.string.low_battery_warning),
|
view.context.getString(R.string.low_battery_warning),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
@@ -73,7 +73,7 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int)
|
|||||||
if (userList.testStatus!!) {
|
if (userList.testStatus!!) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
view.context,
|
view.context,
|
||||||
context?.getString(R.string.test_already_conducted),
|
view.context.getString(R.string.test_already_conducted),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
} else {
|
} else {
|
||||||
@@ -81,13 +81,14 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int)
|
|||||||
if (isBetween15And30Minutes(userList.incubationTime) < Constants.INCUBATION_TIME_MIN) {
|
if (isBetween15And30Minutes(userList.incubationTime) < Constants.INCUBATION_TIME_MIN) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
view.context,
|
view.context,
|
||||||
context?.getString(R.string.incubation_not_completed),
|
view.context.getString(R.string.incubation_not_completed),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
} else if (isBetween15And30Minutes(userList.incubationTime) > Constants.INCUBATION_TIME_MAX) {
|
} else
|
||||||
|
if (isBetween15And30Minutes(userList.incubationTime) > Constants.INCUBATION_TIME_MAX) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
view.context,
|
view.context,
|
||||||
context?.getString(R.string.incubation_crossed_30_minutes),
|
view.context.getString(R.string.incubation_crossed_30_minutes),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
} else {
|
} else {
|
||||||
@@ -102,7 +103,7 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int)
|
|||||||
} else {
|
} else {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
view.context,
|
view.context,
|
||||||
context?.getString(R.string.incubation_not_started),
|
view.context.getString(R.string.incubation_not_started),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,47 @@
|
|||||||
package com.example.hpostesting.presentation.dashboard
|
package com.example.hpostesting.presentation.dashboard
|
||||||
|
|
||||||
|
import android.app.DownloadManager
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.ui.AppBarConfiguration
|
import androidx.navigation.ui.AppBarConfiguration
|
||||||
import androidx.navigation.ui.navigateUp
|
import androidx.navigation.ui.navigateUp
|
||||||
import androidx.navigation.ui.setupActionBarWithNavController
|
import androidx.navigation.ui.setupActionBarWithNavController
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
|
import com.example.hpostesting.data.Result
|
||||||
import com.example.hpostesting.data.constant.LanguageManager
|
import com.example.hpostesting.data.constant.LanguageManager
|
||||||
|
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
|
||||||
import com.google.android.material.navigation.NavigationView
|
import com.google.android.material.navigation.NavigationView
|
||||||
import com.google.android.play.core.appupdate.AppUpdateManager
|
|
||||||
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
|
|
||||||
import com.google.firebase.appdistribution.FirebaseAppDistribution
|
import com.google.firebase.appdistribution.FirebaseAppDistribution
|
||||||
import com.google.firebase.appdistribution.FirebaseAppDistributionException
|
import com.google.firebase.appdistribution.FirebaseAppDistributionException
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import `in`.sminnovations.hpostesting.R
|
import `in`.sminnovations.hpostesting.R
|
||||||
import `in`.sminnovations.hpostesting.databinding.ActivityDashboardBinding
|
import `in`.sminnovations.hpostesting.databinding.ActivityDashboardBinding
|
||||||
|
import okhttp3.ResponseBody
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class DashboardActivity : AppCompatActivity() {
|
class DashboardActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||||
private lateinit var binding: ActivityDashboardBinding
|
private lateinit var binding: ActivityDashboardBinding
|
||||||
|
private var downloadId: Long = 0
|
||||||
|
private val hemocubeViewModel: HemoCubeViewModel by viewModels()
|
||||||
|
|
||||||
override fun attachBaseContext(newBase: Context?) {
|
override fun attachBaseContext(newBase: Context?) {
|
||||||
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
|
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
|
||||||
LanguageManager.setLocale(newBase, languageCode)
|
LanguageManager.setLocale(newBase, languageCode)
|
||||||
@@ -40,6 +55,42 @@ class DashboardActivity : AppCompatActivity() {
|
|||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
setSupportActionBar(binding.appBarDashboard.toolbar)
|
setSupportActionBar(binding.appBarDashboard.toolbar)
|
||||||
|
|
||||||
|
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)
|
||||||
|
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: $message", Toast.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
is Result.Loading -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val drawerLayout: DrawerLayout = binding.drawerLayout
|
val drawerLayout: DrawerLayout = binding.drawerLayout
|
||||||
val navView: NavigationView = binding.navView
|
val navView: NavigationView = binding.navView
|
||||||
val navController = findNavController(R.id.nav_host_fragment_content_dashboard)
|
val navController = findNavController(R.id.nav_host_fragment_content_dashboard)
|
||||||
@@ -52,6 +103,8 @@ class DashboardActivity : AppCompatActivity() {
|
|||||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||||
navView.setupWithNavController(navController)
|
navView.setupWithNavController(navController)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
@@ -65,6 +118,82 @@ class DashboardActivity : AppCompatActivity() {
|
|||||||
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
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() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
@@ -104,4 +233,7 @@ class DashboardActivity : AppCompatActivity() {
|
|||||||
// Toast.makeText(this, "App Update: Success!", Toast.LENGTH_SHORT).show()
|
// Toast.makeText(this, "App Update: Success!", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ import com.example.hpostesting.data.model.login.LoginResponse
|
|||||||
import com.example.hpostesting.data.model.patient.HemoCubeTestData
|
import com.example.hpostesting.data.model.patient.HemoCubeTestData
|
||||||
import com.example.hpostesting.data.model.patient.UserData
|
import com.example.hpostesting.data.model.patient.UserData
|
||||||
import com.example.hpostesting.data.model.updates.CheckUpdateRequest
|
import com.example.hpostesting.data.model.updates.CheckUpdateRequest
|
||||||
|
import com.example.hpostesting.data.model.updates.DeviceUpdateRequest
|
||||||
import com.example.hpostesting.presentation.KitScanActivity
|
import com.example.hpostesting.presentation.KitScanActivity
|
||||||
import com.example.hpostesting.presentation.adapter.OfflineUserListAdapter
|
import com.example.hpostesting.presentation.adapter.OfflineUserListAdapter
|
||||||
import com.example.hpostesting.presentation.adapter.UserListAdapter
|
import com.example.hpostesting.presentation.adapter.UserListAdapter
|
||||||
@@ -150,6 +151,7 @@ class HomeFragment : Fragment() {
|
|||||||
} else {
|
} else {
|
||||||
hemoCubeViewModel.checkUpdate(createCheckUpdateRequestData())
|
hemoCubeViewModel.checkUpdate(createCheckUpdateRequestData())
|
||||||
hemoCubeViewModel.uploadLogs()
|
hemoCubeViewModel.uploadLogs()
|
||||||
|
hemoCubeViewModel.deviceUpdate(createDeviceRequestData())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hemoCubeViewModel.loginResponse.observe(viewLifecycleOwner) { response ->
|
hemoCubeViewModel.loginResponse.observe(viewLifecycleOwner) { response ->
|
||||||
@@ -260,6 +262,13 @@ class HomeFragment : Fragment() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createDeviceRequestData(): DeviceUpdateRequest {
|
||||||
|
return DeviceUpdateRequest(
|
||||||
|
serial_no = Constants.serialNumber
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setUserId() {
|
private fun setUserId() {
|
||||||
binding.btnSubmit.setOnClickListener {
|
binding.btnSubmit.setOnClickListener {
|
||||||
val userId = binding.userId.text.toString()
|
val userId = binding.userId.text.toString()
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ class HemoCubeViewModel @Inject constructor(
|
|||||||
val logFile = logFileManager.createLogFile().let { file ->
|
val logFile = logFileManager.createLogFile().let { file ->
|
||||||
val requestBody = file?.asRequestBody("multipart/form-data".toMediaTypeOrNull())
|
val requestBody = file?.asRequestBody("multipart/form-data".toMediaTypeOrNull())
|
||||||
val multipartFile =
|
val multipartFile =
|
||||||
requestBody?.let { MultipartBody.Part.createFormData("logFile", file.name, it) }
|
requestBody?.let { MultipartBody.Part.createFormData("logFile", file?.name, it) }
|
||||||
multipartFile?.let { partFile ->
|
multipartFile?.let { partFile ->
|
||||||
databaseRepository.uploadLogs(partFile).let { result ->
|
databaseRepository.uploadLogs(partFile).let { result ->
|
||||||
uploadLogs.postValue(result)
|
uploadLogs.postValue(result)
|
||||||
|
|||||||
3
app/src/main/res/xml/file_paths.xml
Normal file
3
app/src/main/res/xml/file_paths.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<external-files-path name="Updates" path="." />
|
||||||
|
</paths>
|
||||||
Reference in New Issue
Block a user