I'm able to select file, selected pdf file got uri as content://somefile but when i'm sending the file using unirest getting error file not found exception below is my code
//file chooser
File file;
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(Intent.createChooser(chooseFile, "Select a File to Upload"),1);
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 1:
if (resultCode == -1) {
file = new File(data.getData().getPath());
}
break;
}
}
HttpResponse<String> response = Unirest.post("http://url/")
.field("file", file)
you have to do the typical method so please use retrofit for saving so much time
file upload example - How to Upload Image file in Retrofit 2
for networking, you have to also visit the official retrofit site
https://square.github.io/retrofit/
99% of people recommend retrofit for networking and saving so much time
import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
object SelectedFilePath {
fun getPath(context: Context, uri: Uri): String? {
//check here to KITKAT or new version
val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":").toTypedArray()
val type = split[0]
if ("primary".equals(type, ignoreCase = true)) {
return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
}
} else if (isDownloadsDocument(uri)) {
val id = DocumentsContract.getDocumentId(uri)
val contentUri = ContentUris.withAppendedId(
Uri.parse("content://<span id=\"IL_AD1\" class=\"IL_AD\">downloads</span>/public_downloads"),
java.lang.Long.valueOf(id)
)
return getDataColumn(context, contentUri, null, null)
} else if (isMediaDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":").toTypedArray()
val type = split[0]
var contentUri: Uri? = null
if ("image" == type) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
} else if ("video" == type) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
} else if ("audio" == type) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}
val selection = "_id=?"
val selectionArgs = arrayOf(
split[1]
)
return getDataColumn(context, contentUri, selection, selectionArgs)
}
} else if ("content".equals(uri.scheme, ignoreCase = true)) {
// Return the remote address
return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(
context,
uri,
null,
null
)
} else if ("file".equals(uri.scheme, ignoreCase = true)) {
return uri.path
}
return null
}
fun getDataColumn(
context: Context, uri: Uri?, selection: String?,
selectionArgs: Array<String>?
): String? {
var cursor: Cursor? = null
val column = "_data"
val projection = arrayOf(
column
)
try {
cursor = context.contentResolver.query(
uri!!, projection, selection, selectionArgs,
null
)
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(index)
}
} finally {
cursor?.close()
}
return null
}
fun isExternalStorageDocument(uri: Uri): Boolean {
return "com.android.externalstorage.documents" == uri.authority
}
fun isDownloadsDocument(uri: Uri): Boolean {
return "com.android.providers.downloads.documents" == uri.authority
}
fun isMediaDocument(uri: Uri): Boolean {
return "com.android.providers.media.documents" == uri.authority
}
fun isGooglePhotosUri(uri: Uri): Boolean {
return "com.google.android.apps.photos.content" == uri.authority
}
}
Now onActivityResult use this class
MY_RESULT_CODE_FILECHOOSER -> if (resultCode == Activity.RESULT_OK) {
if (data != null) {
val fileUri: Uri? = data.data
Log.d(LOG_TAG, "Uri: $fileUri")
var filePath: String? = null
try {
filePath = fileUri?.let { SelectedFilePath.getPath(this, it) }
uploadFile(filePath)
} catch (e: Exception) {
Log.e(LOG_TAG, "Error: $e")
showToast("Error: $e")
}
if (filePath != null) {
Log.e(LOG_TAG, filePath)
}
}
}
Full code is:
private fun askPermissionAndBrowseFile() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // Level 23
// Check if we have Call permission
val permisson: Int = ActivityCompat.checkSelfPermission(
this,
Manifest.permission.READ_EXTERNAL_STORAGE
)
if (permisson != PackageManager.PERMISSION_GRANTED) {
requestPermissions(
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
MY_REQUEST_CODE_PERMISSION
)
return
}
}
browseFiles()
}
private fun browseFiles() {
var chooseFileIntent = Intent(Intent.ACTION_GET_CONTENT)
chooseFileIntent.type = "*/*"
chooseFileIntent.addCategory(Intent.CATEGORY_OPENABLE)
chooseFileIntent = Intent.createChooser(chooseFileIntent, "Choose a file")
startActivityForResult(chooseFileIntent, MY_RESULT_CODE_FILECHOOSER)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
MY_REQUEST_CODE_PERMISSION -> {
// Note: If request is cancelled, the result arrays are empty.
// Permissions granted (CALL_PHONE).
if (grantResults.size > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
) {
Log.d(LOG_TAG, "Permission granted!")
showToast("Permission granted!")
browseFiles()
} else {
Log.i(LOG_TAG, "Permission denied!")
showToast("Permission denied!")
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
MY_RESULT_CODE_FILECHOOSER -> if (resultCode == Activity.RESULT_OK) {
if (data != null) {
val fileUri: Uri? = data.data
Log.d(LOG_TAG, "Uri: $fileUri")
var filePath: String? = null
try {
filePath = fileUri?.let { SelectedFilePath.getPath(this, it) }
uploadFile(filePath)
} catch (e: Exception) {
Log.e(LOG_TAG, "Error: $e")
showToast("Error: $e")
}
if (filePath != null) {
Log.e(LOG_TAG, filePath)
}
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private fun uploadFile(filePath: String?) {
val myData = Data.Builder().putString(AppConstants.FILE_TO_UPLOAD, filePath).build()
val oneTimeWorkRequest = OneTimeWorkRequest.Builder(UploadFileWorker::class.java)
.setInputData(myData)
.build()
WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)
Toast.makeText(this, resources.getString(R.string.file_will_be_uploaded), Toast.LENGTH_SHORT).show()
}
enter code here
Upload worker class :
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.thirdeyegen.mr_workspace.R
import com.thirdeyegen.mr_workspace.constants.AppConstants
import com.thirdeyegen.mr_workspace.network.ProgressRequestBody
import com.thirdeyegen.mr_workspace.network.RestClient
import okhttp3.MultipartBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.io.File
infix fun Int.hasReached(limit: Int): Boolean {
return this == limit
}
class UploadFileWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
private var isSuccess = true
private val TAG = "UPLOAD_FILE_WORKER==>"
val PROGRESS_MAX = 100
var PROGRESS_CURRENT = 0
val mContext = context
private lateinit var builder: NotificationCompat.Builder
private lateinit var notificationManager: NotificationManager
override fun doWork(): Result {
Log.d(TAG, "DO_WORK")
createNotificationChannel()
return startUploading()
}
private fun startUploading(): Result {
val filePath = inputData.getString(AppConstants.FILE_TO_UPLOAD)
if (filePath != null) {
val file = File(filePath)
val fileBody = ProgressRequestBody(file, "*", object : ProgressRequestBody.UploadCallbacks {
override fun onFinish() {
if (PROGRESS_CURRENT hasReached 100) {
notificationManager.cancel(1)
}
}
override fun onProgressUpdate(percentage: Int) {
PROGRESS_CURRENT = percentage
builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false)
notificationManager.notify(1, builder.build());
if (percentage hasReached 100) {
notificationManager.cancel(1)
}
Log.d(TAG, "UPLOAD_PROGRESS====> $percentage")
}
override fun onError() {
notificationManager.cancel(1)
Log.d(TAG, "UPLOAD_ERROR===>")
}
})
val filePart: MultipartBody.Part = MultipartBody.Part.createFormData("file", file.name, fileBody)
RestClient.apiInterfaceForUploader.uploadVideo(filePart, "record").enqueue(object : Callback<Any?> {
override fun onResponse(call: Call<Any?>, response: Response<Any?>) {
Toast.makeText(applicationContext, R.string.msg_file_uploaded_successfully, Toast.LENGTH_LONG).show()
isSuccess = true
}
override fun onFailure(call: Call<Any?>, t: Throwable) {
Toast.makeText(applicationContext, R.string.msg_error_while_uploading_file, Toast.LENGTH_SHORT).show()
isSuccess = false
}
})
}
return if (isSuccess) {
Result.success()
} else {
Result.failure()
}
}
private fun createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name: CharSequence = "My Channel"
val description = mContext.getString(R.string.upload_file_text)
val importance: Int = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel("upload_worker", name, importance)
channel.description = description
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager: NotificationManager = mContext.getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(channel)
}
setupNotification(mContext)
}
private fun setupNotification(context: Context) {
builder = NotificationCompat.Builder(context, "upload_worker").apply {
setContentTitle(context.getString(R.string.file_upload_text))
setOngoing(false)
setAutoCancel(true)
setContentText(context.getString(R.string.upload_in_progress))
setSmallIcon(R.drawable.ic_files)
setSound(null)
setNotificationSilent()
priority = NotificationCompat.PRIORITY_LOW
}
notificationManager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(1, builder.build())
builder.setOngoing(true)
}
}
Related
I have used ACTION_OPEN_DOCUMENT_TREE to let the user choose a folder [So to avoid using MANAGE_EXTERNAL_STORAGE] but couldn't view i.e. Read the files inside the hidden
Already tried all the file piker libraries
I cannot access files in this directory even after giving permission from Document Tree to a produced directory in android studio which is below:
'/storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/.Statuses'
below is the result I get when I list the files in the above directory but am unable to read the files using Image.file() in flutter URI permission also granted which are as follows
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
below is one of the results I get when I list the files, its accessible in native android code but not in flutter
'content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fmedia%2Fcom.whatsapp%2FWhatsApp%2FMedia%2F.Statuses/document/primary%3AAndroid%2Fmedia%2Fcom.whatsapp%2FWhatsApp%2FMedia%2F.Statuses%2Fa9e8797092cd4bfeb6e426dfa1be7a84.jpg'
following code is used to get permission from files using native android code in mainactivity file
private fun grantAndroid11StorageAccessPermission() {
println("mydhfhsdkfsd 00")
if (isMyPackedgeInstalled("com.whatsapp")) {
val intent: Intent
val storageManager: StorageManager =
getSystemService(Context.STORAGE_SERVICE) as StorageManager
val whatsappfolderdir: String = if (File(
Environment.getExternalStorageDirectory()
.toString() + "/Android/media/com.whatsapp/WhatsApp/Media/.Statuses"
).isDirectory()
) {
"Android%2Fmedia%2Fcom.whatsapp%2FWhatsApp%2FMedia%2F.Statuses"
} else {
"WhatsApp%2FMedia%2F.Statuses"
}
if (Build.VERSION.SDK_INT >= 29) {
intent = storageManager.primaryStorageVolume.createOpenDocumentTreeIntent()
val scheme: String =
(intent.getParcelableExtra<android.os.Parcelable>("android.provider.extra.INITIAL_URI") as Uri)
.toString()
.replace("/root/", "/document/")
val stringBuilder = "$scheme%3A$whatsappfolderdir"
intent.putExtra("android.provider.extra.INITIAL_URI", Uri.parse(stringBuilder))
println("mydhfhsdkfsd $stringBuilder")
} else {
intent = Intent("android.intent.action.OPEN_DOCUMENT_TREE")
intent.putExtra("android.provider.extra.INITIAL_URI", Uri.parse(whatsappfolderdir))
}
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
startActivityForResult(
intent,
OPEN_DOCOMENT_TREE_REQUEST_CODE
)
return
} else {
Toast.makeText(
this#MainActivity,
"Error getting ",
Toast.LENGTH_SHORT
).show()
}
}
following function is used to retrieve the files list
private fun getDataFromWhatsAppFolder(): Array<DocumentFile>? {
try {
val fromTreeUri: DocumentFile? = DocumentFile.fromTreeUri(
applicationContext,
Uri.parse(namedataprefs)
)
if (fromTreeUri!!.exists() && fromTreeUri.isDirectory() && fromTreeUri.canRead() && fromTreeUri.canWrite()) {
return fromTreeUri.listFiles()
}
} catch (e: Exception) {
e.printStackTrace()
return null
}
return null
}
if complete Mainactivity file is required I will add it below also
MainActivity
package com.example.flutterapp
import android.R.attr.path
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.storage.StorageManager
import android.util.Log
import android.widget.Toast
import androidx.annotation.Nullable
import androidx.documentfile.provider.DocumentFile
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
import java.io.File
class MainActivity : FlutterActivity() {
private val CHANNEL = "apilevel"
private val MY_PERMISSIONS_REQUEST_WRITE_STORAGE = 1235
private val OPEN_DOCOMENT_TREE_REQUEST_CODE = 1014
var filesList: ArrayList<String> = ArrayList<String>()
private var namedataprefs: String? = null
private var callResult: MethodChannel.Result? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(flutterEngine!!)
val prefs: SharedPreferences =
getSharedPreferences("whatsapp_pref", Context.MODE_PRIVATE)
namedataprefs = prefs.getString("whatsapp", "")
MethodChannel(
flutterEngine!!.dartExecutor.binaryMessenger,
CHANNEL
).setMethodCallHandler { call, result ->
callResult = result
if (call.method == "getapilevel") {
Log.d("fffffffffff", (Build.VERSION.SDK_INT).toString())
result.success((Build.VERSION.SDK_INT).toString())
} else if (call.method == "whatsapplist") {
var mylis: List<String> = ArrayList<String>()
if (getDataFromWhatsAppFolder() != null) {
Log.d("fffffffffff", "folder done")
mylis = getData()
Log.d("fffffffffff", "folder datalengthc " + File("/storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/.Statuses").canRead())
val directory: File = File("/storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/.Statuses")
val files = directory.listFiles()
Log.d("Files", "Size: " + files.size)
for (i in files.indices) {
Log.d("Files", "FileName:" + files[i].name)
}
}
result.success(mylis)
} else if (call.method == "whatsapp") {
Log.d("fffffffffff", "grantwhatsapp")
if (getDataFromWhatsAppFolder() == null) {
grantAndroid11StorageAccessPermission()
}
} else {
result.notImplemented()
}
}
}
private fun isMyPackedgeInstalled(packageName: String?): Boolean {
return try {
packageManager.getPackageInfo(packageName!!, 0)
true
} catch (e: PackageManager.NameNotFoundException) {
false
}
}
private fun grantAndroid11StorageAccessPermission() {
println("mydhfhsdkfsd 00")
if (isMyPackedgeInstalled("com.whatsapp")) {
val intent: Intent
val storageManager: StorageManager =
getSystemService(Context.STORAGE_SERVICE) as StorageManager
val whatsappfolderdir: String = if (File(
Environment.getExternalStorageDirectory()
.toString() + "/Android/media/com.whatsapp/WhatsApp/Media/.Statuses"
).isDirectory()
) {
"Android%2Fmedia%2Fcom.whatsapp%2FWhatsApp%2FMedia%2F.Statuses"
} else {
"WhatsApp%2FMedia%2F.Statuses"
}
if (Build.VERSION.SDK_INT >= 29) {
intent = storageManager.primaryStorageVolume.createOpenDocumentTreeIntent()
val scheme: String =
(intent.getParcelableExtra<android.os.Parcelable>("android.provider.extra.INITIAL_URI") as Uri)
.toString()
.replace("/root/", "/document/")
val stringBuilder = "$scheme%3A$whatsappfolderdir"
intent.putExtra("android.provider.extra.INITIAL_URI", Uri.parse(stringBuilder))
println("mydhfhsdkfsd $stringBuilder")
} else {
intent = Intent("android.intent.action.OPEN_DOCUMENT_TREE")
intent.putExtra("android.provider.extra.INITIAL_URI", Uri.parse(whatsappfolderdir))
}
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
startActivityForResult(
intent,
OPEN_DOCOMENT_TREE_REQUEST_CODE
)
return
} else {
Toast.makeText(
this#MainActivity,
"Error getting ",
Toast.LENGTH_SHORT
).show()
}
}
private fun getDataFromWhatsAppFolder(): Array<DocumentFile>? {
try {
val fromTreeUri: DocumentFile? = DocumentFile.fromTreeUri(
applicationContext,
Uri.parse(namedataprefs)
)
if (fromTreeUri!!.exists() && fromTreeUri.isDirectory() && fromTreeUri.canRead() && fromTreeUri.canWrite()) {
return fromTreeUri.listFiles()
}
} catch (e: Exception) {
e.printStackTrace()
return null
}
return null
}
private fun getData(): List<String> {
if (Build.VERSION.SDK_INT >= 30) {
if (filesList != null) {
filesList = ArrayList<String>()
}
try {
val allFileswhatsapp: Array<DocumentFile>? = getDataFromWhatsAppFolder()
///val allFileswhat: MutableList<String> = mutableListOf(Array<DocumentFile>)
val allFileswhat: Array<DocumentFile> = allFileswhatsapp!!
// allFileswhat.toMutableList().addAll(allFileswhatsapp)
// allFileswhat.addAll(allFileswhatsapp)
for (allFile in allFileswhat) {
// val file: Uri = allFile.getUri()
val file: Uri = allFile.getUri()
if (!allFile.getUri().toString().contains(".nomedia") && !allFile.getUri()
.toString().equals("")
) {
val fromTreeUri1 = DocumentFile.fromSingleUri(
this#MainActivity,
Uri.parse(allFile.getUri().toString())
)
if (fromTreeUri1 != null) {
// moveFile(fromTreeUri1.uri.toString());
filesList.add(fromTreeUri1.uri.toString())
Log.d("fffffffffff added list ", fromTreeUri1.uri.toString())
Log.d("fffffffffff added segm ", fromTreeUri1.uri.lastPathSegment.toString())
}
}
}
filesList.add("/storage/emulated/0/Download/wester.jpg")
} catch (e: Exception) {
e.printStackTrace()
}
}
return filesList
}
#SuppressLint("WrongConstant")
override fun onActivityResult(requestCode: Int, resultCode: Int, #Nullable data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == OPEN_DOCOMENT_TREE_REQUEST_CODE && resultCode == -1) {
val uri: Uri? = data!!.data
try {
contentResolver.takePersistableUriPermission(
uri!!,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
contentResolver.takePersistableUriPermission(
uri!!,
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
)
contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
} catch (e: Exception) {
e.printStackTrace()
}
namedataprefs = uri!!.toString()
val editor: SharedPreferences.Editor =
getSharedPreferences("whatsapp_pref", Context.MODE_PRIVATE)
.edit()
editor.putString("whatsapp", uri.toString())
editor.apply()
Log.d("fffffffffff", "my string location is " + uri.toString())
var mylis: List<String> = ArrayList<String>()
if (getDataFromWhatsAppFolder() != null) {
Log.d("fffffffffff", "folder done")
mylis = getData()
Log.d("fffffffffff", "folder datalength " + mylis.size)
}
callResult!!.success(mylis)
}
}
fun moveFile(sourceFile: String?): Boolean {
val despath =
"/storage/emulated/0/Download/tempwhatsapp/" + File(
sourceFile
).name
val destUri = Uri.fromFile(File(despath))
try {
val `is` = contentResolver.openInputStream(Uri.parse(sourceFile))
val os = contentResolver.openOutputStream(destUri, "w")
val buffer = ByteArray(1024)
while (true) {
val read = `is`!!.read(buffer)
if (read > 0) {
os!!.write(buffer, 0, read)
} else {
`is`.close()
os!!.flush()
os.close()
val intent = Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE")
intent.data = destUri
sendBroadcast(intent)
return true
}
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
return false
}
}
}
I am using beacon plugin. After I upgraded flutter to the version 3.0.0, it throwed this error:
Class 'BeaconsPlugin' is not abstract and does not implement abstract member public abstract fun onRequestPermissionsResult(p0: Int, p1: Array<(out) String!>, p2: IntArray): Boolean defined in io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener
e: C:\Software\HR-Management-Localization\mapping_tool\packages\simple_beacons_flutter\android\src\main\kotlin\com\umair\beacons_plugin\BeaconsPlugin.kt: (66, 32): Type mismatch: inferred type is Activity? but Context was expected
e: C:\Software\HR-Management-Localization\mapping_tool\packages\simple_beacons_flutter\android\src\main\kotlin\com\umair\beacons_plugin\BeaconsPlugin.kt: (428, 5): 'onRequestPermissionsResult' overrides nothing
I've solved it by removing question marks from lines
permissions: Array<out String>?,
grantResults: IntArray?
to this
permissions: Array<out String>,
grantResults: IntArray
in onRequestPermissionsResult method.
And than it throwed a different error: BeaconsPlugin.kt: (66, 32): Type mismatch: inferred type is Activity? but Context was expected
I don't know kotlin therefore unable to solve this.
Here is my whole BeaconsPlugin.kt:
package com.umair.beacons_plugin
import android.Manifest
import android.app.Activity
import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import androidx.annotation.NonNull
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.*
import timber.log.Timber
/** BeaconsPlugin */
class BeaconsPlugin : FlutterPlugin, ActivityAware,
PluginRegistry.RequestPermissionsResultListener {
private var context: Context? = null
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
Timber.i("onAttachedToEngine")
messenger = flutterPluginBinding.binaryMessenger
setUpPluginMethods(
flutterPluginBinding.applicationContext,
flutterPluginBinding.binaryMessenger
)
context = flutterPluginBinding.applicationContext
beaconHelper = BeaconHelper(flutterPluginBinding.applicationContext)
context?.let {
BeaconPreferences.init(it)
stopBackgroundService(it)
}
}
companion object {
private val TAG = "BeaconsPlugin"
private var REQUEST_LOCATION_PERMISSIONS = 1890
private var PERMISSION_REQUEST_BACKGROUND_LOCATION = 1891
private var channel: MethodChannel? = null
private var event_channel: EventChannel? = null
private var currentActivity: Activity? = null
private var beaconHelper: BeaconHelper? = null
private var defaultPermissionDialogTitle = "This app needs background location access"
private var defaultPermissionDialogMessage =
"Please grant location access so this app can detect beacons in the background."
#JvmStatic
internal var messenger: BinaryMessenger? = null
#JvmStatic
fun registerWith(registrar: PluginRegistry.Registrar) {
BeaconPreferences.init(registrar.context())
if (beaconHelper == null) {
this.beaconHelper = BeaconHelper(registrar.context())
}
val instance = BeaconsPlugin()
registrar.addRequestPermissionsResultListener(instance)
//requestPermission()
setUpPluginMethods(registrar.activity(), registrar.messenger())
}
#JvmStatic
fun registerWith(messenger: BinaryMessenger, context: Context) {
BeaconPreferences.init(context)
if (beaconHelper == null) {
this.beaconHelper = BeaconHelper(context)
}
val instance = BeaconsPlugin()
//requestPermission()
setUpPluginMethods(context, messenger)
}
#JvmStatic
fun registerWith(messenger: BinaryMessenger, beaconHelper: BeaconHelper, context: Context) {
BeaconPreferences.init(context)
this.beaconHelper = beaconHelper
val instance = BeaconsPlugin()
//requestPermission()
setUpPluginMethods(context, messenger)
}
#JvmStatic
private fun setUpPluginMethods(context: Context, messenger: BinaryMessenger) {
Timber.plant(Timber.DebugTree())
channel = MethodChannel(messenger, "beacons_plugin")
notifyIfPermissionsGranted(context)
channel?.setMethodCallHandler { call, result ->
when {
call.method == "startMonitoring" -> {
stopService = false
callBack?.startScanning()
result.success("Started scanning Beacons.")
}
call.method == "stopMonitoring" -> {
if (runInBackground) {
stopService = true
context.let {
stopBackgroundService(it)
}
}
callBack?.stopMonitoringBeacons()
result.success("Stopped scanning Beacons.")
}
call.method == "addRegion" -> {
callBack?.addRegion(call, result)
}
call.method == "clearRegions" -> {
callBack?.clearRegions(call, result)
}
call.method == "runInBackground" -> {
call.argument<Boolean>("background")?.let {
runInBackground = it
}
result.success("App will run in background? $runInBackground")
}
call.method == "clearDisclosureDialogShowFlag" -> {
call.argument<Boolean>("clearFlag")?.let {
if (it) {
clearPermissionDialogShownFlag()
result.success("clearDisclosureDialogShowFlag: Flag cleared!")
} else {
setPermissionDialogShown()
result.success("clearDisclosureDialogShowFlag: Flag Set!")
}
}
}
call.method == "setDisclosureDialogMessage" -> {
call.argument<String>("title")?.let {
defaultPermissionDialogTitle = it
}
call.argument<String>("message")?.let {
defaultPermissionDialogMessage = it
}
requestPermission()
result.success("Disclosure message Set: $defaultPermissionDialogMessage")
}
call.method == "addBeaconLayoutForAndroid" -> {
call.argument<String>("layout")?.let {
callBack?.addBeaconLayout(it)
result.success("Beacon layout added: $it")
}
}
call.method == "setForegroundScanPeriodForAndroid" -> {
var foregroundScanPeriod = 1100L
var foregroundBetweenScanPeriod = 0L
call.argument<Int>("foregroundScanPeriod")?.let {
if (it > foregroundScanPeriod) {
foregroundScanPeriod = it.toLong()
}
}
call.argument<Int>("foregroundBetweenScanPeriod")?.let {
if (it > foregroundBetweenScanPeriod) {
foregroundBetweenScanPeriod = it.toLong()
}
}
callBack?.setForegroundScanPeriod(
foregroundScanPeriod = foregroundScanPeriod,
foregroundBetweenScanPeriod = foregroundBetweenScanPeriod
)
result.success("setForegroundScanPeriod updated.")
}
call.method == "setBackgroundScanPeriodForAndroid" -> {
var backgroundScanPeriod = 1100L
var backgroundBetweenScanPeriod = 0L
call.argument<Int>("backgroundScanPeriod")?.let {
if (it > backgroundScanPeriod) {
backgroundScanPeriod = it.toLong()
}
}
call.argument<Int>("backgroundBetweenScanPeriod")?.let {
if (it > backgroundBetweenScanPeriod) {
backgroundBetweenScanPeriod = it.toLong()
}
}
callBack?.setBackgroundScanPeriod(
backgroundScanPeriod = backgroundScanPeriod,
backgroundBetweenScanPeriod = backgroundBetweenScanPeriod
)
result.success("setBackgroundScanPeriod updated.")
}
else -> result.notImplemented()
}
}
event_channel = EventChannel(messenger, "beacons_plugin_stream")
event_channel?.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
callBack?.setEventSink(events)
}
override fun onCancel(arguments: Any?) {
}
})
}
#JvmStatic
private fun notifyIfPermissionsGranted(context: Context) {
if (permissionsGranted(context)) {
doIfPermissionsGranted()
}
}
#JvmStatic
fun permissionsGranted(context: Context): Boolean {
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
#JvmStatic
private fun doIfPermissionsGranted() {
Timber.i("doIfPermissionsGranted")
if (beaconHelper == null) {
return
}
this.callBack = beaconHelper
sendBLEScannerReadyCallback()
}
#JvmStatic
private fun requestPermission() {
if (areBackgroundScanPermissionsGranted()) {
requestLocationPermissions()
} else {
requestBackgroundPermission()
}
}
private fun requestLocationPermissions() {
if (!arePermissionsGranted()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
currentActivity?.let {
ActivityCompat.requestPermissions(
it,
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
),
REQUEST_LOCATION_PERMISSIONS
)
}
} else {
currentActivity?.let {
ActivityCompat.requestPermissions(
it,
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
),
REQUEST_LOCATION_PERMISSIONS
)
}
}
} else {
doIfPermissionsGranted()
}
} else {
doIfPermissionsGranted()
}
}
#JvmStatic
private fun requestBackgroundPermission() {
if (!isPermissionDialogShown()) {
currentActivity?.let {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
//if (it.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
val builder: AlertDialog.Builder =
AlertDialog.Builder(it)
builder.setTitle(defaultPermissionDialogTitle)
builder.setMessage(defaultPermissionDialogMessage)
builder.setPositiveButton("Ok", null)
builder.setOnDismissListener {
setPermissionDialogShown()
requestLocationPermissions()
channel?.invokeMethod("isPermissionDialogShown", "true")
}
builder.show()
//}
}
}
}
}
#JvmStatic
private fun arePermissionsGranted(): Boolean {
currentActivity?.let {
return ContextCompat.checkSelfPermission(
it,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(
it,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
return false
}
#JvmStatic
private fun areBackgroundScanPermissionsGranted(): Boolean {
currentActivity?.let {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContextCompat.checkSelfPermission(
it,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
) == PackageManager.PERMISSION_GRANTED
} else {
return true
}
}
return true
}
#JvmStatic
var runInBackground = false
#JvmStatic
var stopService = false
interface PluginImpl {
fun startScanning()
fun stopMonitoringBeacons()
fun addRegion(call: MethodCall, result: MethodChannel.Result)
fun clearRegions(call: MethodCall, result: MethodChannel.Result)
fun setEventSink(events: EventChannel.EventSink?)
fun addBeaconLayout(layout: String)
fun setForegroundScanPeriod(
foregroundScanPeriod: Long,
foregroundBetweenScanPeriod: Long
)
fun setBackgroundScanPeriod(
backgroundScanPeriod: Long,
backgroundBetweenScanPeriod: Long
)
}
private var callBack: PluginImpl? = null
fun sendBLEScannerReadyCallback() {
channel?.invokeMethod("scannerReady", "")
}
fun startBackgroundService(context: Context) {
if (runInBackground && !stopService) {
val serviceIntent1 = Intent(context, BeaconsDiscoveryService::class.java)
context.startService(serviceIntent1)
}
}
fun stopBackgroundService(context: Context) {
if (runInBackground && !stopService) {
val serviceIntent = Intent(context, BeaconsDiscoveryService::class.java)
context.stopService(serviceIntent)
}
}
}
override fun onDetachedFromEngine(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
currentActivity = null
channel?.setMethodCallHandler(null)
event_channel?.setStreamHandler(null)
if (!BeaconsPlugin.runInBackground)
beaconHelper?.stopMonitoringBeacons()
context?.let {
stopBackgroundService(it)
}
context = null
}
override fun onAttachedToActivity(activityPluginBinding: ActivityPluginBinding) {
currentActivity = activityPluginBinding.activity
BeaconPreferences.init(currentActivity)
activityPluginBinding.addRequestPermissionsResultListener(this)
//requestPermission()
if (arePermissionsGranted()) {
sendBLEScannerReadyCallback()
}
}
override fun onDetachedFromActivityForConfigChanges() {
Timber.i("onDetachedFromActivityForConfigChanges")
}
override fun onReattachedToActivityForConfigChanges(activityPluginBinding: ActivityPluginBinding) {
Timber.i("onReattachedToActivityForConfigChanges")
currentActivity = activityPluginBinding.activity
activityPluginBinding.addRequestPermissionsResultListener(this)
}
override fun onDetachedFromActivity() {
Timber.i("onDetachedFromActivity")
currentActivity = null
context?.let {
startBackgroundService(it)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
): Boolean {
if (requestCode == REQUEST_LOCATION_PERMISSIONS && grantResults?.isNotEmpty()!! && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
doIfPermissionsGranted()
return true
}
if (requestCode == PERMISSION_REQUEST_BACKGROUND_LOCATION && grantResults?.isNotEmpty()!! && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//setPermissionDialogShown()
//requestPermission()
return true
}
return false
}
}
i have save problem in deferent class and solved by edit this one
#JvmStatic
fun registerWith(registrar: PluginRegistry.Registrar) {
val instance = BackgroundSttPlugin()
registrar.addRequestPermissionsResultListener(instance)
requestRecordPermission()
// this one "registrar.context()" make my problem solved
setUpPluginMethods(registrar.context(), registrar.messenger())
}
I need to fetch data inside WhatsApp folders on External Storage.
As i am targeting API Level 30 i am no longer able to access WhatsApp folders on External Storage. I have implemented Storage Access Framework and got Android/media folder Uri and Document File. And using listFiles() i am able to list files but with filter() and sortedByDescending() functions it becomes very slow.
What i have tried?
Used Cursor loader with Projection and Selection Arguments but it only
worked for non hidden folders like WhatsApp Images and WhatsApp Videos
It returns empty cursor for hidden folder .Statuses
Tried replacing MediaStore.Video.Media.EXTERNAL_CONTENT_URI with MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
What is required?
List images and videos from .Statuses folder same as i am listing WhatsApp Images using Media Store in HomeActivity.java
Below is my code
In this activity i get permision to Android/media and set all WhatsApp folders URIs for status fetching and other use, but fetched WhatsApp Images with projection and selection from WhatsApp Images folder
class HomeActivity : AppCompatActivity(), InternetListener, PurchasesUpdatedListener,
CoroutineScope {
private val exceptionHandler = CoroutineExceptionHandler { context, exception ->
Toast.makeText(this, exception.message, Toast.LENGTH_LONG).show()
}
private val dataRepository: DataRepository by inject()
val tinyDB: TinyDB by inject()
val REQUEST_CODE = 12123
init {
newNativeAdSetUp = null
}
val sharedViewModel by viewModel<SharedViewModel>()
val viewModel by viewModel<HomeViewModel>()
val handler = CoroutineExceptionHandler { _, exception ->
Log.d("CoroutineException", "$exception handled !")
}
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job + handler
private lateinit var job: Job
val sdk30PermissionListener = object : PermissionListener {
override fun onPermissionGranted() {
openDocumentTree()
}
override fun onPermissionDenied(deniedPermissions: MutableList<String>?) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
handlePermissionsByVersion()
}
private fun handlePermissionsByVersion() {
if (SDK_INT >= Build.VERSION_CODES.R) {
if ((ContextCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
== PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(
this,
Manifest.permission.READ_EXTERNAL_STORAGE
)
== PackageManager.PERMISSION_GRANTED)
) {
//if granted load whatsapp images and some uris setup to viewmodel
loadWhatsAppImages()
if (arePermissionsGranted()) {
if (dataRepository.mrWhatsAppImages == null || dataRepository.mrWhatsAppBusinessImages == null) {
setUpWAURIs()
}
}
} else {
TedPermission.with(this)
.setPermissionListener(sdk30PermissionListener)
.setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
.setPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
.check()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, #Nullable data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) {
if (data != null) {
//this is the uri user has provided us
val treeUri: Uri? = data.data
if (treeUri != null) {
sharedViewModel.treeUri = treeUri
val decoded = Uri.decode(treeUri.toString())
Log.i(LOGTAG, "got uri: ${treeUri.toString()}")
// here we should do some checks on the uri, we do not want root uri
// because it will not work on Android 11, or perhaps we have some specific
// folder name that we want, etc
if (Uri.decode(treeUri.toString()).endsWith(":")) {
showWrongFolderSelection()
return
}
if (!decoded.equals(Constants.WHATSAPP_MEDIA_URI_DECODED)) {
showWrongFolderSelection()
return
}
// here we ask the content resolver to persist the permission for us
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(
treeUri,
takeFlags
)
val treeUriAsString = treeUri.toString()
tinyDB.putString("FOLDER_URI", treeUriAsString)
if (SDK_INT >= Build.VERSION_CODES.R) {
setupPaths()
}
}
}
}
}
private fun setupPaths() {
setUpOverlay()
fetchWhatsAppRootURIs(
this,
sharedViewModel,
dataRepository,
tinyDB
) {
fetchWhatsAppBusinessRootURIs(
this,
sharedViewModel,
dataRepository,
tinyDB
) {
tinyDB.putBoolean("WARootPathsDone", true)
removeOverlay()
}
}
}
override fun onDestroy() {
dialogHandler.removeCallbacksAndMessages(null)
super.onDestroy()
}
val loadmanagerImages = object : LoaderManager.LoaderCallbacks<Cursor> {
val whatsAppImagesArrayList = arrayListOf<File>()
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
var location: File = File(
Environment.getExternalStorageDirectory()
.toString() + Constants.whatsapp_images_path
)
if (!location.exists()) {
location = File(
Environment.getExternalStorageDirectory()
.toString() + Constants.whatsapp_images_path11
)
}
if (location != null && location.exists()) {
whatsAppImagesArrayList.clear()
Timber.e("checkLoaded-onCreateLoader $id")
if (id == 0) {
var folder = location.absolutePath
val projection = arrayOf(
MediaStore.MediaColumns.DATA,
MediaStore.MediaColumns.DATE_MODIFIED
)
val selection = MediaStore.Images.Media.DATA + " like ? "
val selectionArgs: String = "%$folder%"
return CursorLoader(
this#HomeActivity,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
arrayOf(selectionArgs),
"${MediaStore.Images.Media.DATE_MODIFIED} DESC"
)
}
}
return null!!
}
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor?) {
Timber.e("checkLoaded-onLoadFinished")
var absolutePathOfImage: String
if (loader.id == 0) {
cursor?.let {
val columnIndexData = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)
GlobalScope.launch(Dispatchers.Main + exceptionHandler) {
async(Dispatchers.IO + exceptionHandler) {
while (!cursor.isClosed && cursor.moveToNext() == true) {
absolutePathOfImage = cursor.getString(columnIndexData!!)
whatsAppImagesArrayList.add(File(absolutePathOfImage))
}
}.await()
LoaderManager.getInstance(this#HomeActivity).destroyLoader(0)
Timber.e("checkLoaded-Completion")
galleryViewModel.whatsAppImagesList.postValue(whatsAppImagesArrayList)
}
}
}
}
override fun onLoaderReset(loader: Loader<Cursor>) {
}
}
fun loadWhatsAppImages() {
try {
tinyDB.putBoolean("whatsAppMediaLoadCalled", true)
LoaderManager.getInstance(this).initLoader(
0,
null,
loadmanagerImages
)
} catch (e: RuntimeException) {
Log.e("exVideos ", "ex : ${e.localizedMessage}")
}
}
companion object {
const val ANDROID_DOCID = "primary:Android/media/"
const val EXTERNAL_STORAGE_PROVIDER_AUTHORITY = "com.android.externalstorage.documents"
private val androidUri = DocumentsContract.buildDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY, ANDROID_DOCID
)
val androidTreeUri = DocumentsContract.buildTreeDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY, ANDROID_DOCID
)
}
private fun openDocumentTree() {
val uriString = tinyDB.getString("FOLDER_URI", "")
when {
uriString == "" -> {
Log.w(LOGTAG, "uri not stored")
askPermission()
}
arePermissionsGranted() -> {
}
else -> {
Log.w(LOGTAG, "uri permission not stored")
askPermission()
}
}
}
// this will present the user with folder browser to select a folder for our data
private fun askPermission() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, androidUri)
startActivityForResult(intent, REQUEST_CODE)
}
private fun arePermissionsGranted(): Boolean {
var uriString = tinyDB.getString("FOLDER_URI", "")
val list = contentResolver.persistedUriPermissions
for (i in list.indices) {
val persistedUriString = list[i].uri.toString()
if (persistedUriString == uriString && list[i].isWritePermission && list[i].isReadPermission) {
return true
}
}
return false
}
private fun showWrongFolderSelection() {
val layoutInflaterAndroid = LayoutInflater.from(this)
val mView = layoutInflaterAndroid.inflate(R.layout.layout_dialog_wrong_folder, null)
val builder = AlertDialog.Builder(this, R.style.ThemePageSearchDialog)
builder.setView(mView)
val alertDialog = builder.show()
alertDialog.setCancelable(false)
val btnOk = mView.findViewById(R.id.tvExit) as TextView
val tvCancel = mView.findViewById(R.id.tvCancel) as TextView
btnOk.setOnClickListener {
alertDialog.dismiss()
openDocumentTree()
}
tvCancel.setOnClickListener {
alertDialog.dismiss()
}
}
private fun setUpWAURIs() {
dataRepository.mrWhatsAppImages =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppImages")
)
dataRepository.mrWhatsAppVN =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppVN")
)
dataRepository.mrWhatsAppDocs =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppDocs")
)
dataRepository.mrWhatsAppVideo =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppVideo")
)
dataRepository.mrWhatsAppAudio =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppAudio")
)
dataRepository.WhatsAppStatuses =
getDocumentFileFromStringURIStatuses(
this,
tinyDB.getString("WhatsAppStatuses")
)
dataRepository.mrWhatsAppBusinessImages =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessImages")
)
dataRepository.mrWhatsAppBusinessVN =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessVN")
)
dataRepository.mrWhatsAppBusinessDocs =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessDocs")
)
dataRepository.mrWhatsAppBusinessVideo =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessVideo")
)
dataRepository.mrWhatsAppBusinessAudio =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessAudio")
)
dataRepository.WhatsAppBusinessStatuses =
getDocumentFileFromStringURIStatuses(
this,
tinyDB.getString("WhatsAppBusinessStatuses")
)
}
fun setUpOverlay() {
val dialogfragment = FullScreenLoadingDialog()
dialogfragment.isCancelable = false
dialogfragment.setisAdmobAd(true)
val ft: FragmentTransaction =
supportFragmentManager.beginTransaction()
ft.add(dialogfragment, "DialogFragment_FLAG")
ft.commitAllowingStateLoss()
}
fun removeOverlay() {
val fragment: Fragment? = supportFragmentManager.findFragmentByTag("DialogFragment_FLAG")
if (fragment != null && fragment is DialogFragment) {
fragment.dismissAllowingStateLoss()
}
}
fun fetchWhatsAppRootURIs(
context: Context,
sharedViewModel: SharedViewModel,
dataRepository: DataRepository,
tinyDB: TinyDB, completed: () -> Unit
) {
val selectedPackageName = Constants.WHATSAPP_PKG_NAME
val selectedRootName = Constants.WHATSAPP_ROOT_NAME
var waImages: DocumentFile? = null
var waVN: DocumentFile? = null
var waDocs: DocumentFile? = null
var waVideos: DocumentFile? = null
var waAudio: DocumentFile? = null
var waStatus: DocumentFile? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && sharedViewModel.treeUri != null) {
CoroutineScope(Dispatchers.Main).launch {
async(Dispatchers.IO) {
val dir = DocumentFile.fromTreeUri(
context,
sharedViewModel.treeUri!!
)
dir?.listFiles()?.forEach {
if (it.name.equals(selectedPackageName)) {
it.listFiles().forEach {
if (it.name.equals(selectedRootName)) {
it.listFiles().forEach {
if (it.name.equals(Constants.WHATSAPP_MEDIA_FOLDER_NAME)) {
it.listFiles().forEach {
if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_IMAGES)) {
waImages = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_VN)) {
waVN = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_DOCUMENTS)) {
waDocs = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_VIDEO)) {
waVideos = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_AUDIO)) {
waAudio = it
} else if (it.name.equals(Constants.FOLDER_NAME_STATUSES)) {
waStatus = it
}
}
}
}
}
}
}
}
}.await()
Timber.e("processStatusFetch:Done")
tinyDB.putString("mrWhatsAppImages", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppVN", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppDocs", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppVideo", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppAudio", waImages?.uri.toString())
tinyDB.putString("WhatsAppStatuses", waStatus?.uri.toString())
dataRepository.mrWhatsAppImages = waImages
dataRepository.mrWhatsAppVN = waVN
dataRepository.mrWhatsAppDocs = waDocs
dataRepository.mrWhatsAppVideo = waVideos
dataRepository.mrWhatsAppAudio = waAudio
dataRepository.WhatsAppStatuses = waStatus
completed()
}
}
}
Here i am using .Statuses folder URI to list DocumentFiles and display but this way it is slow
class StatusImageFragment : Fragment(), StatusListener, CoroutineScope {
companion object {
fun newInstance() = StatusImageFragment()
}
val handler = CoroutineExceptionHandler { _, exception ->
Log.d("CoroutineException", "$exception handled !")
}
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job + handler
private lateinit var job: Job
private var adapterSDK30 = StatusImageAdapterSDK30()
private var no_image: ImageView? = null
private var no_image_txt: TextView? = null
val tinyDB: TinyDB by inject()
val sharedViewModel by viewModel<SharedViewModel>()
private val dataRepository: DataRepository by inject()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
job = Job()
return inflater.inflate(R.layout.status_image_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
swipeRefresh(false, false)
}
public fun swipeRefresh(isReloadRequired: Boolean, isFromModeChanged: Boolean) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (isFromModeChanged) {
status_image_recycler.visibility = View.GONE
progressbar.visibility = View.VISIBLE
no_image?.let {
it.visibility = View.GONE
}
no_image_txt?.let {
it.visibility = View.GONE
}
go_to_app?.let {
it.visibility = View.GONE
}
} else {
if (adapterSDK30.listImages == null || adapterSDK30.listImages.size == 0) {
no_image?.let {
it.visibility = View.GONE
}
no_image_txt?.let {
it.visibility = View.GONE
}
go_to_app?.let {
it.visibility = View.GONE
}
progressbar.visibility = View.VISIBLE
}
}
if (isReloadRequired) {
processStatusFetchFromChild({
sharedViewModel.statusImages.observe(viewLifecycleOwner, Observer {
val arrayList = it
adapterSDK30.listImages = arrayList
postFetchingExecutionSDK30()
})
})
} else {
sharedViewModel.statusImages.observe(viewLifecycleOwner, Observer {
val arrayList = it
adapterSDK30.listImages = arrayList
adapterSDK30.listImages = it
postFetchingExecutionSDK30()
})
}
}
} catch (ex: Exception) {
ex.printStackTrace()
}
}
private fun postFetchingExecutionSDK30() {
progressbar.visibility = View.GONE
status_image_recycler.visibility = View.VISIBLE
if (adapterSDK30!!.listImages != null && adapterSDK30!!.listImages.size > 0) {
no_image?.let {
it.visibility = View.GONE
}
no_image_txt?.let {
it.visibility = View.GONE
}
go_to_app?.let {
it.visibility = View.GONE
}
} else {
no_image?.let {
it.visibility = View.VISIBLE
}
no_image_txt?.let {
it.visibility = View.VISIBLE
}
go_to_app?.let {
it.visibility = View.VISIBLE
}
}
adapterSDK30!!.notifyDataSetChanged()
status_img_swipe.isRefreshing = false
}
override fun onDestroyView() {
job.cancel()
super.onDestroyView()
}
fun processStatusFetchFromChild(completed: () -> Unit) {
val statusSelection = tinyDB.getInt(Constants.status_accounts)
if (statusSelection == 0 || statusSelection == 1) {
if (dataRepository.WhatsAppStatuses == null) {
(activity as StatusActivity).setUpWAURIs()
}
var documentFileStatuses: DocumentFile? = dataRepository.WhatsAppStatuses
if (statusSelection == 1) {
documentFileStatuses = dataRepository.WhatsAppBusinessStatuses
}
if (documentFileStatuses != null) {
launch(Dispatchers.Main) {
val statusImages1 = arrayListOf<DocumentFile>()
async(Dispatchers.IO) {
//this takes time ; want to fetch this same as WhatsApp Gallery
statusImages1.addAll(documentFileStatuses!!.listFiles().filter {
it.mimeType.equals(Constants.MIME_TYPE_IMG_PNG) || it.mimeType.equals(
Constants.MIME_TYPE_IMG_JPG
) || it.mimeType.equals(Constants.MIME_TYPE_IMG_JPEG)
}.sortedByDescending { it.lastModified() })
}.await()
Timber.e("processStatusFetch:Done")
sharedViewModel.statusImages.postValue(statusImages1)
completed()
}
} else {
Timber.e("processStatusFetch:Done")
sharedViewModel.statusImages.postValue(arrayListOf<DocumentFile>())
completed()
}
} else {
Timber.e("processStatusFetch:Done")
sharedViewModel.statusImages.postValue(arrayListOf<DocumentFile>())
completed()
}
}
}
Please note WhatsApp folder path which i used is
val whatsapp_images_path11 = "/Android/media/“ +"com.whatsapp" +"/WhatsApp/Media/WhatsAppImages/"
How i can use MediaStore in this case so that i don't need to use sort and filter functions of list? Its not important to get java.io File only i can work with URIs as well.
What I have finally implemented, in android 10+ you need to ask the user for your specific directory access. Then you can use this functions to fetch statuses:
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun readSDKFrom30(): ArrayList<String> {
val treeUri = DocumentsContract.buildTreeDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY,
"primary:Android/media/com.whatsapp/WhatsApp/Media/.Statuses"
)
val tree = DocumentFile.fromTreeUri(context, treeUri)!!
val pathList = ArrayList<String>()
listFolderContent(tree).forEach { uri ->
val file = createFileFromContentUri(uri)
pathList.add(file.toString())
}
return pathList
}
private fun listFolderContent(folder: DocumentFile): List<Uri> {
return if (folder.isDirectory) {
val files = folder.listFiles().toMutableList()
files.sortByDescending { it.lastModified() }
files.mapNotNull { file ->
if (file.name != null) file.uri else null
}
} else {
emptyList()
}
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createFileFromContentUri(fileUri: Uri): File {
var fileName = ""
fileUri.let { returnUri ->
context.contentResolver.query(returnUri, null, null, null)
}?.use { cursor ->
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor.moveToFirst()
fileName = cursor.getString(nameIndex)
}
val iStream: InputStream =
context.contentResolver.openInputStream(fileUri)!!
val outputDir: File = context.cacheDir!!
val outputFile = File(outputDir, fileName)
copyStreamToFile(iStream, outputFile)
iStream.close()
return outputFile
}
private fun copyStreamToFile(inputStream: InputStream, outputFile: File) {
inputStream.use { input ->
val outputStream = FileOutputStream(outputFile)
outputStream.use { output ->
val buffer = ByteArray(4 * 1024) // buffer size
while (true) {
val byteCount = input.read(buffer)
if (byteCount < 0) break
output.write(buffer, 0, byteCount)
}
output.flush()
}
}
}
Using DocumentFile to handle SAF uries is slow indeed.
Better use DocumentsContract to do so.
Its about twenty times as fast as DocumentFile and about as fast as classic File class stuff.
Using MediaStore for hidden folders should be possible. You cannot create hidden folders with the mediastore. But if you managed to make them not using mediastore you should be able to list files in them using mediastore. Well if they are scanned. And if they belong to your app.
I'm having a hard time getting an image URL from firebase storage, I got the photo URL from the database, but as I know, Glide can't get a picture from the link like this: com.google.firebase.storage.UploadTask#62873ce
Here are my Storage and database references:
private fun StorageReference.uploadUserPhoto(uid: String, photo: Uri,
onSuccess: (UploadTask.TaskSnapshot) -> Unit) {
child("users/$uid/photo").putFile(mImageUri).addOnCompleteListener {
if (it.isSuccessful) {
mStorage.child("users/$uid/photo").downloadUrl.addOnCompleteListener { task ->
if (it.isSuccessful) {
onSuccess(it.result!!)
} else {
showToast(it.exception!!.message!!)
}
}
}
}
}
private fun DatabaseReference.updateUserPhoto(uid: String, photoUrl: String,
onSuccess: () -> Unit){
child("users/$uid/photo").setValue(photoUrl)
.addOnCompleteListener {
if (it.isSuccessful) {
onSuccess()
} else {
showToast(it.exception!!.message!!)
}
}
}
here is the function:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == TAKE_PICTURE_REQUEST_CODE && resultCode == RESULT_OK) {
val uid = mAuth.currentUser!!.uid
mStorage.uploadUserPhoto(uid, mImageUri) {
val photoUrl = it.task.toString()
mDatabase.updateUserPhoto(uid, photoUrl) {
mUser = mUser.copy(photo = photoUrl)
profile_image.loadUserPhoto(mUser.photo)
}
}
}
}
I tried changing
val photoUrl = it.task.toString()
to
val photoUrl = it.downloadUrl.toString()
but it highlights 'downloadUrl' as unresolved reference
Full code:
package com.example.homeactivity.activities
import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.text.Editable
import android.util.Log
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import com.example.homeactivity.R
import com.example.homeactivity.models.User
import com.example.homeactivity.views.PasswordDialog
import com.google.firebase.auth.AuthCredential
import com.google.firebase.auth.EmailAuthProvider
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageReference
import com.google.firebase.storage.UploadTask
import kotlinx.android.synthetic.main.activity_edit_profile.*
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
class EditProfileActivity : AppCompatActivity(), PasswordDialog.Listener {
private lateinit var mImageUri: Uri
private lateinit var mStorage: StorageReference
private val TAG = "EditProfileActivity"
private lateinit var mUser: com.example.homeactivity.models.User
private lateinit var mAuth: FirebaseAuth
private lateinit var mDatabase: DatabaseReference
private lateinit var mPendingUser: com.example.homeactivity.models.User
private val TAKE_PICTURE_REQUEST_CODE = 1
val simpleDateFormat = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_profile)
Log.d(TAG, "onCreate")
close_image.setOnClickListener { finish() }
save_image.setOnClickListener { updateProfile() }
change_photo_text.setOnClickListener { takeCameraPicture() }
mAuth = FirebaseAuth.getInstance()
mDatabase = FirebaseDatabase.getInstance().reference
mStorage = FirebaseStorage.getInstance().reference
mDatabase.child("users").child(mAuth.currentUser!!.uid)
.addListenerForSingleValueEvent(ValueEventListenerAdapter {
mUser = it.getValue(com.example.homeactivity.models.User::class.java)!!
name_input.setText(mUser.name, TextView.BufferType.EDITABLE)
username_input.setText(mUser.username, TextView.BufferType.EDITABLE)
website_input.setText(mUser.website, TextView.BufferType.EDITABLE)
bio_input.setText(mUser.bio, TextView.BufferType.EDITABLE)
email_input.setText(mUser.email, TextView.BufferType.EDITABLE)
phone_input.setText(mUser.phone?.toString(), TextView.BufferType.EDITABLE)
profile_image.loadUserPhoto(mUser.photo)
})
}
private fun takeCameraPicture() {
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (intent.resolveActivity(packageManager) != null) {
val imageFile = createImageFile()
mImageUri = FileProvider.getUriForFile(
this,
"com.example.homeactivity.fileprovider",
imageFile
)
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri)
startActivityForResult(intent, TAKE_PICTURE_REQUEST_CODE)
}
}
private fun createImageFile(): File {
val storageDir: File? = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
"JPEG_${simpleDateFormat.format(Date())}_",
".jpg",
storageDir
)
}
#SuppressLint("MissingSuperCall")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == TAKE_PICTURE_REQUEST_CODE && resultCode == RESULT_OK) {
val uid = mAuth.currentUser!!.uid
mStorage.uploadUserPhoto(uid, mImageUri) {
val photoUrl = it.task.toString()
mDatabase.updateUserPhoto(uid, photoUrl) {
mUser = mUser.copy(photo = photoUrl)
profile_image.loadUserPhoto(mUser.photo)
}
}
}
}
private fun updateProfile() {
mPendingUser = readInputs()
val error = validate(mPendingUser)
if (error == null) {
if (mPendingUser.email == mUser.email) {
updateUser(mPendingUser)
} else {
PasswordDialog().show(supportFragmentManager, "password_dialog")
}
} else {
showToast(error)
}
}
private fun readInputs(): User {
return User(
name = name_input.text.toString(),
username = username_input.text.toString(),
email = email_input.text.toString(),
website = website_input.text.toStringOrNull(),
bio = bio_input.text.toStringOrNull(),
phone = phone_input.text.toStringOrNull()
)
}
override fun onPasswordConfirm(password: String) {
if (password.isNotEmpty()) {
val credential = EmailAuthProvider.getCredential(mUser.email, password)
mAuth.currentUser!!.reauthenticate(credential) {
mAuth.currentUser!!.updateEmail(mPendingUser.email) {
updateUser(mPendingUser)
}
}
} else {
showToast("You must enter your password")
}
}
private fun updateUser(user: com.example.homeactivity.models.User) {
val updatesMap = mutableMapOf<String, Any?>()
if (user.name != mUser.name) updatesMap["name"] = user.name
if (user.username != mUser.username) updatesMap["username"] = user.username
if (user.website != mUser.website) updatesMap["website"] = user.website
if (user.bio != mUser.bio) updatesMap["bio"] = user.bio
if (user.email != mUser.email) updatesMap["email"] = user.email
if (user.phone != mUser.phone) updatesMap["phone"] = user.phone
mDatabase.updateUser(mAuth.currentUser!!.uid, updatesMap) {
showToast("Profile saved")
finish()
}
}
private fun validate(user: com.example.homeactivity.models.User): String? =
when {
user.name.isEmpty() -> "Please enter name"
user.username.isEmpty() -> "Please enter username"
user.email.isEmpty() -> "Please enter email"
else -> null
}
private fun FirebaseUser.updateEmail(email: String, onSuccess: () -> Unit) {
updateEmail(email).addOnCompleteListener {
if (it.isSuccessful) {
onSuccess()
} else {
showToast(it.exception!!.message!!)
}
}
}
private fun StorageReference.uploadUserPhoto(uid: String, photo: Uri,
onSuccess: (UploadTask.TaskSnapshot) -> Unit) {
child("users/$uid/photo").putFile(mImageUri).addOnCompleteListener {
if (it.isSuccessful) {
mStorage.child("users/$uid/photo").downloadUrl.addOnCompleteListener { task ->
if (it.isSuccessful) {
onSuccess(it.result!!)
} else {
showToast(it.exception!!.message!!)
}
}
}
}
}
private fun DatabaseReference.updateUserPhoto(uid: String, photoUrl: String,
onSuccess: () -> Unit){
child("users/$uid/photo").setValue(photoUrl)
.addOnCompleteListener {
if (it.isSuccessful) {
onSuccess()
} else {
showToast(it.exception!!.message!!)
}
}
}
private fun DatabaseReference.updateUser(
uid: String, updates: Map<String, Any?>,
onSuccess: () -> Unit
) {
child("users").child(uid).updateChildren(updates)
.addOnCompleteListener {
if (it.isSuccessful) {
onSuccess()
} else {
showToast(it.exception!!.message!!)
}
}
}
private fun FirebaseUser.reauthenticate(credential: AuthCredential, onSuccess: () -> Unit) {
reauthenticate(credential).addOnCompleteListener {
if (it.isSuccessful) {
onSuccess()
} else {
showToast(it.exception!!.message!!)
}
}
}
}
Change your uploadUserPhoto method like this
private fun StorageReference.uploadUserPhoto(uid: String, photo: Uri,
onSuccess: (String) -> Unit) {
val uTask = mStorage.child("users/$uid/photo").putFile(mImageUri)
child("users/$uid/photo").putFile(mImageUri).addOnCompleteListener {
if (it.isSuccessful) {
uTask.continueWithTask { task ->
mStorage.child("users/$uid/photo").downloadUrl
}.addOnCompleteListener{
if (it.isSuccessful && it.result != null) {
onSuccess(it.result!!.toString)
} else {
showToast(it.exception!!.message!!)
}
}
}
}
}
In your onActivityResult use url like
val photoUrl = it
I am getting a class not found exception when trying to click an image from the camera or while fetching it from the directory. The code is in kotlin and has been given below.
This is the class which implements the functionality to capture an image or picks them from the gallery.
The methods _openCamera() and openFileSelector() are implemented.
The main motive was to capture the images and upload them in the server but the implemented methods doesn't give the proper results.
class MainActivity : AppCompatActivity() {
private var drawerResult: Drawer? = null
private var jobschedular: JobScheduler? = null
private var jobschedularCode: Int = 1
private var phoneNumber: String? = null
private var toolbar: Toolbar? = null
private var familyId: String? = null
val TAG: String? = "Activity_Name"
val REQUEST_IMAGE_CAPTURE = 1
val REQUEST_CODE_FOR_GALLERY_CAPTURE = 2
var photoFile: File? = null
var progressDialog: Dialog? = null
private var doubleBackToExitPressedOnce = false
#SuppressLint("PrivateResource")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
Log.d(TAG, "Inside MainActivity")
//onclick listener for open camera
onclickListenerForOpenCamera()
//starting the services here . .
val service_checkAddedtoFamily = Intent(this, checkAddedToFamily::class.java)
startService(service_checkAddedtoFamily)
val service_checkDocsToBeVerified = Intent(this, checkDocsToBeVerified::class.java)
startService(service_checkDocsToBeVerified)
/*findViewById<Button>(R.id.scan).setOnClickListener {
val i = Intent(this, Testers::class.java)
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
startActivity(i)
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
}*/
//onclick listener for select image button
attach_onclick_listener_to_add_photos_from_gallery()
//onclick listener for select pdf files
onclickListenerForSelectPdfFile()
//get toolbar for drawer
toolbar = findViewById(R.id.toolbar_tabs)
//get phone number
val loginInfo = applicationContext.getSharedPreferences("loginInfo", Context.MODE_PRIVATE)
phoneNumber = loginInfo.getString("phoneNumber", "")
//onclick listener for upload button
//onclickListenerForUploadButton()
//onclick listener for retrieve button
onclickListenerForRetrieveButton()
//on click permanent diseases button
//onclickPermanentDiseasesButtton()
//navigation drawer
left_drawer(this, this#MainActivity, toolbar!!).createNavigationDrawer()
//verify auto upload
verifyAutoLoginInformation()
//create Sqlite database
DB_HELPER(this#MainActivity).writableDatabase
//get job schedular service
jobschedular = applicationContext.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
schedulTheJobForHealthGoals()
schedulTheJobForHealthInsurance()
setPreferencesForNutrition()
schedulTheJobForNutrition()
schedulTheJobForSyncNutritionOnline()
}
/*override fun onBackPressed() {
if (doubleBackToExitPressedOnce) {
super.onBackPressed()
return
}
this.doubleBackToExitPressedOnce = true
Toast.makeText(this, "Press back again to exit", Toast.LENGTH_SHORT).show()
Handler().postDelayed(Runnable { doubleBackToExitPressedOnce = false }, 2000)
}*/
//job schedular
fun schedulTheJobForHealthGoals() {
val builder = JobInfo.Builder(jobschedularCode, ComponentName(this#MainActivity, health_goals_services::class.java))
.setPersisted(true)
.setPeriodic(5000)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(false)
.setRequiresDeviceIdle(false)
val bundle = PersistableBundle()
bundle.putString("key", "value")
builder.setExtras(bundle)
val s_response = jobschedular!!.schedule(builder.build())
if (s_response <= 0) {
//something goes wrong
}
}
fun schedulTheJobForHealthInsurance() {
val builder = JobInfo.Builder(jobschedularCode, ComponentName(this#MainActivity, health_insurance_service::class.java))
.setPersisted(true)
.setPeriodic(5000)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(false)
.setRequiresDeviceIdle(false)
val bundle = PersistableBundle()
bundle.putString("key", "value")
builder.setExtras(bundle)
val s_response = jobschedular!!.schedule(builder.build())
if (s_response <= 0) {
//something goes wrong
}
}
fun schedulTheJobForNutrition() {
val builder = JobInfo.Builder(jobschedularCode, ComponentName(this#MainActivity, nutrition_service::class.java))
.setPersisted(true)
.setPeriodic(5000) //change to 1 hour
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(false)
.setRequiresDeviceIdle(false)
val bundle = PersistableBundle()
bundle.putString("key", "value")
builder.setExtras(bundle)
val s_response = jobschedular!!.schedule(builder.build())
if (s_response <= 0) {
//something goes wrong
}
}
fun setPreferencesForNutrition() {
val nutritionInfo = getSharedPreferences("nutrition", Context.MODE_PRIVATE)
val editor = nutritionInfo.edit()
editor.putString("breakFastTime_Hour", "7")
editor.putString("lunchTime_Hour", "14") //TODO: change to 13
editor.putString("DinnerTime_Hour", "20")
editor.apply()
}
fun schedulTheJobForSyncNutritionOnline() {
val builder = JobInfo.Builder(jobschedularCode, ComponentName(this#MainActivity, sync_nutrition_online::class.java))
.setPersisted(true)
.setPeriodic(5000)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(false)
.setRequiresDeviceIdle(false)
val bundle = PersistableBundle()
bundle.putString("key", "value")
builder.setExtras(bundle)
val s_response = jobschedular!!.schedule(builder.build())
if (s_response <= 0) {
//something goes wrong
}
}
//buttons on home screen
/*fun onclickListenerForUploadButton(){
findViewById<ImageView>(R.id.uploadButton).setOnClickListener{
openModeOfUploadActivity()
}
}*/
fun onclickListenerForRetrieveButton() {
findViewById<Button>(R.id.retrieveButton).setOnClickListener {
openHistoryActivity()
}
}
/*fun onclickPermanentDiseasesButtton(){
findViewById<Button>(R.id.permanentDiseasesButton).setOnClickListener{
openPermanentDiseases()
}
}*/
/*fun openModeOfUploadActivity(){
val intent = Intent(this,MainActivity::class.java)
startActivity(intent)
}*/
fun openHistoryActivity() {
val intent = Intent(this, history_pickFamilyMember::class.java)
startActivity(intent)
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
}
/*fun openPermanentDiseases(){
val intent = Intent(this,permanentDiseaese::class.java)
startActivity(intent)
}
*/
//verify auto login information
fun verifyAutoLoginInformation() {
val loginInfo = applicationContext.getSharedPreferences("loginInfo", Context.MODE_PRIVATE)
if (loginInfo.contains("familyOrIndividual") == true) {
//for family
if (loginInfo.getString("familyOrIndividual", "").toString() == "f") {
if (loginInfo.contains("phoneNumber") == true && loginInfo.contains("password") == true) {
val phoneNumber = loginInfo.getString("phoneNumber", "")
val password = loginInfo.getString("password", "")
individual_family_login(this#MainActivity).makeFamilyLoginApiRequest(phoneNumber, password)
} else {
left_drawer(this, this#MainActivity, toolbar!!).makeUserLogOut()
}
}
//for individual
if (loginInfo.getString("familyOrIndividual", "").toString() == "i") {
if (loginInfo.contains("phoneNumber") == true && loginInfo.contains("password") == true) {
val phoneNumber = loginInfo.getString("phoneNumber", "")
val password = loginInfo.getString("password", "")
individual_family_login(this#MainActivity).makeLoginApiRequest(phoneNumber, password)
} else {
left_drawer(this, this#MainActivity, toolbar!!).makeUserLogOut()
}
}
//for security
if (loginInfo.getString("familyOrIndividual", "").toString() != "i" && loginInfo.getString("familyOrIndividual", "").toString() != "f") {
left_drawer(this, this#MainActivity, toolbar!!).makeUserLogOut()
}
} else {
left_drawer(this, this#MainActivity, toolbar!!).makeUserLogOut()
}
}
//camera scan
fun onclickListenerForOpenCamera() {
findViewById<ImageView>(R.id.openCamera).setOnClickListener {
get_permissions_camera()
}
}
fun _openCamera() {
Log.d("Errors__", "inside _openCamera()")
try {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
try {
photoFile = createImageFile()
} catch (ex: Exception) {
Log.d("Errors__", "inside: " + ex.toString())
}
if (photoFile != null) {
val builder: StrictMode.VmPolicy.Builder = StrictMode.VmPolicy.Builder()
StrictMode.setVmPolicy(builder.build())
val photoURI: Uri = Uri.fromFile(photoFile!!)
Log.d("Path__", "photoURI: $photoURI")
Log.d("Path__", "photoURI.path: " + photoURI.path)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
}
}
} catch (e: Exception) {
Log.d("Errors__", "_openCamera" + e.toString())
}
}
fun createImageFile(): File {
Log.d("Errors__", "inside createImageFile()")
val mCurrentPhotoPath: String
val imageFileName = "camera"
val storageDir: File = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
)
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath()
Log.d("Path__", "Image: $image")
return image
}
//file selector
fun onclickListenerForSelectPdfFile() {
findViewById<ImageView>(R.id.selectPdfFile).setOnClickListener {
get_permissions_fileExplorer()
}
}
#SuppressLint("SdCardPath")
fun openFileSelector() {
val properties = DialogProperties()
properties.selection_mode = DialogConfigs.MULTI_MODE;
properties.selection_type = DialogConfigs.FILE_SELECT;
properties.root = File(DialogConfigs.DEFAULT_DIR);
properties.error_dir = File(DialogConfigs.DEFAULT_DIR);
properties.offset = File(DialogConfigs.DEFAULT_DIR);
properties.extensions = null;
val dialog: FilePickerDialog = FilePickerDialog(this#MainActivity, properties)
dialog.setTitle("Select a File")
dialog.setDialogSelectionListener(object : DialogSelectionListener {
override fun onSelectedFilePaths(files: Array<out String>?) {
convertPdfToImages(files!!)
}
})
dialog.show()
}
fun convertPdfToImages(files: Array<out String>) {
showProcessProgress()
doAsync {
var uriList: MutableList<Uri>? = mutableListOf()
val no_of_files = files.size
var counter = 0
while (counter < no_of_files) {
var pdfFile = File(files[counter])
val decodeService = DecodeServiceBase(PdfContext())
decodeService.setContentResolver(applicationContext.getContentResolver())
decodeService.open(Uri.fromFile(pdfFile))
val pageCount: Int = decodeService.getPageCount()
var i = 0
while (i < pageCount) {
val page: PdfPage = decodeService.getPage(i) as PdfPage
val rectF = RectF(0.toFloat(), 0.toFloat(), 1.toFloat(), 1.toFloat())
// do a fit center to 1920x1080
val scaleBy = 1
val with: Int = (page.getWidth() * scaleBy)
val height: Int = (page.getHeight() * scaleBy)
val bitmap: Bitmap = page.renderBitmap(with, height, rectF)
try {
val outputFile = File(applicationContext.externalCacheDir,
System.currentTimeMillis().toString() + ".jpg")
val outputStream = FileOutputStream(outputFile)
// a bit long running
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
uriList!!.add(Uri.fromFile(outputFile))
outputStream.close()
} catch (e: IOException) {
}
i++
}
counter++
}
uiThread {
progressDialog!!.hide()
openPreview(uriList!!)
Log.d("mess", "size: " + uriList.size + " " + uriList.toString())
}
}
}
//select image
fun attach_onclick_listener_to_add_photos_from_gallery() {
findViewById<ImageView>(R.id.selectImage).setOnClickListener {
get_permissions_gallery()
}
}
fun open_selector() {
Matisse.from(this)
.choose(MimeType.allOf())
.countable(true)
.maxSelectable(200)
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
.thumbnailScale(0.85f)
.imageEngine(PicassoEngine())
.forResult(REQUEST_CODE_FOR_GALLERY_CAPTURE)
}
//activity results
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
var uriOfImage = Uri.fromFile(photoFile)
Log.d("fileCapturing__", "URI Image: $uriOfImage")
//start croper
CropImage.activity(uriOfImage)
.start(this)
}
if (requestCode == REQUEST_CODE_FOR_GALLERY_CAPTURE && resultCode == Activity.RESULT_OK) {
var selected_images = Matisse.obtainResult(data)
openPreview(selected_images!!)
}
//for croper
if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
val result: CropImage.ActivityResult = CropImage.getActivityResult(data)
if (resultCode == RESULT_OK) {
doAsync {
val builder: StrictMode.VmPolicy.Builder = StrictMode.VmPolicy.Builder()
StrictMode.setVmPolicy(builder.build())
var resultUri: Uri = result.getUri()
//save cropped image for persisitance
val croppedImage = createImageFile() //empty
val outputStream = FileOutputStream(croppedImage)
// a bit long running
(Picasso.with(this#MainActivity)
.load(resultUri)
.get()
).compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
Log.d("fileCapturing__", "outputStream: $outputStream")
resultUri = Uri.fromFile(croppedImage)
outputStream.close()
uiThread {
//add to mu list
var mu_list = ArrayList<Uri>(1)
mu_list.add(resultUri)
Log.d("fileCapturing__", "camera uri" + resultUri.toString())
openPreview(mu_list)
}
}
} else if (resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) {
val error: Exception = result.getError()
Log.d("fileCapturing__", "Error: $error")
}
}
}
//preview
fun openPreview(list: MutableList<Uri>) {
val _object = list
val i = Intent(this, typeOfDocument::class.java)
val args = Bundle()
args.putSerializable("ARRAYLIST", _object as java.io.Serializable)
i.putExtra("BUNDLE", args)
startActivity(i)
finish()
}
//get permissions
//Camera
fun get_permissions_camera() {
if (ContextCompat.checkSelfPermission(this#MainActivity, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
MaterialDialog.Builder(this#MainActivity)
.title("Camera permission")
.content("Camera permissions are required for opening Camera")
.negativeText("Cancel")
.onNegative(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
}
})
.positiveText("Give Permissions")
.onPositive(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
getPermissionsUsingDexter_camera(
android.Manifest.permission.CAMERA
)
}
})
.show()
} else {
_openCamera()
}
}
fun getPermissionsUsingDexter_camera(permissionString: String) {
Dexter.withActivity(this)
.withPermissions(
permissionString
).withListener(object : MultiplePermissionsListener {
override fun onPermissionRationaleShouldBeShown(permissions: MutableList<PermissionRequest>?, token: PermissionToken?) {
}
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
if (report!!.areAllPermissionsGranted() == true) {
_openCamera()
Log.d("mess", "permission given")
} else {
Log.d("mess", "permission not granted")
}
}
})
.check()
}
//gallery
fun get_permissions_gallery() {
if (ContextCompat.checkSelfPermission(this#MainActivity, android.Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
MaterialDialog.Builder(this#MainActivity)
.title("Storage permission")
.content("Storage permissions are required for opening the Gallery")
.negativeText("Cancel")
.onNegative(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
}
})
.positiveText("Give Permissions")
.onPositive(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
getPermissionsUsingDexter_gallery(
android.Manifest.permission.READ_EXTERNAL_STORAGE
)
}
})
.show()
} else {
open_selector()
}
}
fun getPermissionsUsingDexter_gallery(permissionString: String) {
Dexter.withActivity(this)
.withPermissions(
permissionString
).withListener(object : MultiplePermissionsListener {
override fun onPermissionRationaleShouldBeShown(permissions: MutableList<PermissionRequest>?, token: PermissionToken?) {
}
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
if (report!!.areAllPermissionsGranted() == true) {
open_selector()
Log.d("mess", "permission given")
} else {
Log.d("mess", "permission not granted")
}
}
})
.check()
}
//file exploer
fun get_permissions_fileExplorer() {
if (ContextCompat.checkSelfPermission(this#MainActivity, android.Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
MaterialDialog.Builder(this#MainActivity)
.title("Storage permission")
.content("Storage access permissions are required for opening File Explorer")
.negativeText("Cancel")
.onNegative(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
}
})
.positiveText("Give Permissions")
.onPositive(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
getPermissionsUsingDexter_fileExplores(
android.Manifest.permission.READ_EXTERNAL_STORAGE
)
}
})
.show()
} else {
openFileSelector()
}
}
fun getPermissionsUsingDexter_fileExplores(permissionString: String) {
Dexter.withActivity(this)
.withPermissions(
permissionString
).withListener(object : MultiplePermissionsListener {
override fun onPermissionRationaleShouldBeShown(permissions: MutableList<PermissionRequest>?, token: PermissionToken?) {
}
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
if (report!!.areAllPermissionsGranted() == true) {
openFileSelector()
Log.d("mess", "permission given")
} else {
Log.d("mess", "permission not granted")
}
}
})
.check()
}
//progress bar
fun showProcessProgress() {
progressDialog = MaterialDialog.Builder(this)
.title("Please Wait")
.content("Converting Pdf to Images")
.progress(true, 0)
.show()
}
}
The error shows when I try to click an image or fetch from the library.
The provider path used is given below:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_files"
path="." />
</paths>
I have tried changing the path to "/" but it didn't work. The error wasn't showing earlier but the error exists now.
Here is the snapshot of the logs.
All suggestions are accepted. Thanks in advance.