I'm allowing user to create an image. When they click "create", i save the image on screen to a bitmap and then save the content URI (content://com.android.providers.media.documents/document/image%3A1000003643) returned to local storage so i can read from it later to display the image. I believe this only grants me temporary access as i get the below error after a short while when trying to display the image in another section of the app
D/AsyncImageError: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord (pid=20027, uid=10663) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
Does anyone know the best way to save an image and then access it indefinitely whenever I want?
save bitmap local storage.
try {
val bitmapDims = ImageUtils.getBitmapDims(BackgroundRemoverActivity.selectedImageUri, this#SmoothActivity
)
val i = bitmapDims.outWidth
var i2 = bitmapDims.outHeight
val smoothActivity = this#SmoothActivity
if (i > i2) {
i2 = i
}
smoothActivity.saveBitmap(i2)
} catch (e: Exception) {
e.printStackTrace()
}
fun saveBitmap(i: Int) {
dimension = i
showDialog = false
val show = ProgressDialog.show(
this,
"",
getString(if (gotoShare) R.string.save_image_ else R.string.processing_image),
true
)
show.setCancelable(false)
Thread(Runnable {
val str: String
try {
// val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "/" + HelperClass.FOLDER_NAME)
val file = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"/" + HelperClass.FOLDER_NAME
)
if (!file.exists()) {
if (!file.mkdirs()) {
Log.d("", "Can't create directory to save image.")
Toast.makeText(
this#SmoothActivity.applicationContext,
"Can't create directory to save image.",
1
).show()
return#Runnable
}
}
val sb = StringBuilder()
sb.append("Photo_")
sb.append(System.currentTimeMillis())
val sb2 = sb.toString()
str = if (inPNG) {
val sb3 = StringBuilder()
sb3.append(sb2)
sb3.append(".png")
sb3.toString()
} else {
val sb4 = StringBuilder()
sb4.append(sb2)
sb4.append(".jpg")
sb4.toString()
}
val smoothActivity = this#SmoothActivity
val sb5 = StringBuilder()
sb5.append(file.path)
sb5.append(File.separator)
sb5.append(str)
smoothActivity.filename = sb5.toString()
val file2 = File(filename)
if (inPNG) {
savePNGImage(file2, i)
show.dismiss()
} else {
saveJPGImage(file2, i)
show.dismiss()
}
Thread.sleep(1000)
show.dismiss()
} catch (e: Exception) {
e.printStackTrace()
}
}).start()
show.setOnDismissListener { dialogInterface: DialogInterface? ->
if (showDialog) {
if (gotoShare) {
val applicationContext = this#SmoothActivity.applicationContext
val sb = StringBuilder()
sb.append(this#SmoothActivity.getString(R.string.saved))
sb.append(" ")
sb.append(filename)
Toast.makeText(applicationContext, sb.toString(), Toast.LENGTH_SHORT).show()
val intent = Intent(this#SmoothActivity, ShareActivity::class.java)
intent.putExtra("path", filename)
intent.putExtra("showTbg", inPNG)
this#SmoothActivity.startActivity(intent)
HelperClass.showPopAdWithCount(this)
// HelperClass.showPopAd(SmoothActivity.this);
return#setOnDismissListener
/* onBackPressed()*/
}
val intent = Intent(this#SmoothActivity, BackgroundChangerActivity::class.java)
intent.putExtra("value", "2")
this#SmoothActivity.startActivity(intent)
Log.d("ttpscheckitem",filename.toString()+ "smooth");
/*onBackPressed()*/
} else if (gotoShare) {
val create = AlertDialog.Builder(
this#SmoothActivity,
if (VERSION.SDK_INT >= 14) 16974126 else 16973835
).setTitle(this#SmoothActivity.resources.getString(R.string.resolution_error_title))
.setMessage(this#SmoothActivity.resources.getString(R.string.rsolution_error_msg))
.setCancelable(false)
.setPositiveButton(this#SmoothActivity.resources.getString(R.string.ok)) { dialogInterface, i1 -> showResolutionOptDialog() }
.create()
create.window!!.attributes.windowAnimations = R.style.DialogAnimation_
create.show()
}
}
}
get local sorage image ;
final List loadAllImages = loadAllImages(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString());
this.cutoutAdapter = new CutoutAdapter(this, loadAllImages);
this.cutout_recyclerview.setAdapter(cutoutAdapter);
Related
I'm attempting to install an APK programmatically on Android 12 but seem to be running into unknown issues at this point. All advice I've found regarding installing an APK programmatically seem to be deprecated.
Currently, I'm able to save my file but whenever I attempt to install it using PackageManager.PackageInstaller, it fails silently and I'm unable to find anything in the logs suggesting what the failure might've been.
Here's my package installer object.
object PackageInstaller {
#SuppressLint("WrongConstant")
#Throws(IOException::class)
fun installPackage(
context: Context,
installSessionId: String?,
packageName: String?,
apkStream: InputStream?
) {
val packageManger = context.packageManager
val packageInstaller = packageManger.packageInstaller
val params = android.content.pm.PackageInstaller.SessionParams(
android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL
)
params.setAppPackageName(packageName)
var session: android.content.pm.PackageInstaller.Session? = null
try {
val sessionId = packageInstaller.createSession(params)
session = packageInstaller.openSession(sessionId)
val out = session.openWrite(installSessionId!!, 0, -1)
val buffer = ByteArray(1024)
var length: Int
var count = 0
if (apkStream != null) {
while (apkStream.read(buffer).also { length = it } != -1) {
out.write(buffer, 0, length)
count += length
}
}
session.fsync(out)
out.close()
val intent = Intent
intent.addFlags(Intent.ACTION_PACKAGE_ADDED)
Log.v("installer", "Installing..?")
session.commit(
PendingIntent.getBroadcast(
context, sessionId,
intent, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
).intentSender
)
}finally {
session?.close()
}
}
}
At this point I'm pretty lost as to where to look next. Does anyone even know if this is still possible? Or a solution to this issue?
You can try with this it is working with android 12
class DownloadApk(private var context: WeakReference<Context>) {
#JvmOverloads
fun startDownloadingApk(url: String, fileName: String = "App Update") {
if (URLUtil.isValidUrl(url)) {
DownloadNewVersion(context, url, fileName).execute()
}
}
#Suppress("DEPRECATION")
private class DownloadNewVersion(
private val context: WeakReference<Context>,
val downloadUrl: String,
val fileName: String
) : AsyncTask<String, Int, Boolean>() {
private lateinit var bar: ProgressDialog
override fun onPreExecute() {
super.onPreExecute()
bar = ProgressDialog(context.get()).apply {
setCancelable(false)
setMessage("Downloading...")
isIndeterminate = true
setCanceledOnTouchOutside(false)
show()
}
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
var msg = ""
val progress = values[0]
if (progress != null) {
bar.progress = progress
msg = if (progress > 99) "Finishing... " else "Downloading... $progress%"
}
bar.apply {
isIndeterminate = false
max = 100
setMessage(msg)
}
}
override fun onPostExecute(result: Boolean?) {
super.onPostExecute(result)
bar.dismiss()
if (result != null && result) {
context.get()?.let {
Toast.makeText(it, "Update Done", Toast.LENGTH_SHORT).show()
}
} else {
context.get()?.let {
Toast.makeText(it, "Error: Try Again", Toast.LENGTH_SHORT).show()
}
}
}
override fun doInBackground(vararg p0: String?): Boolean {
var flag = false
try {
val path =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.toString() + "/"
var outputFile = File("$path$fileName.apk")
var repetition = 1
while (outputFile.exists()) {
outputFile = File("$path$fileName ($repetition).apk")
repetition++
}
val directory = File(path)
if (!directory.exists()) {
directory.mkdirs()
}
val url = URL(downloadUrl)
val c = url.openConnection() as HttpURLConnection
c.requestMethod = "GET"
c.connect()
val fos = FileOutputStream(outputFile)
val inputStream = c.inputStream
val totalSize = c.contentLength.toFloat() //size of apk
val buffer = ByteArray(1024)
var len1: Int
var per: Float
var downloaded = 0f
while (inputStream.read(buffer).also { len1 = it } != -1) {
fos.write(buffer, 0, len1)
downloaded += len1
per = (downloaded * 100 / totalSize)
publishProgress(per.toInt())
}
fos.close()
inputStream.close()
openNewVersion(outputFile.path)
flag = true
} catch (e: MalformedURLException) {
Log.e("DownloadApk", "Update Error: " + e.message)
flag = false
} catch (e: IOException) {
e.printStackTrace()
}
return flag
}
private fun openNewVersion(location: String) {
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(
getUriFromFile(location),
"application/vnd.android.package-archive"
)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
context.get()?.startActivity(intent)
}
private fun getUriFromFile(filePath: String): Uri? {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Uri.fromFile(File(filePath))
} else {
context.get()?.let {
FileProvider.getUriForFile(
it,
it.packageName + ".provider",
File(filePath)
)
}
}
}
}
}
imageUpload.setOnClickListener {
if (ContextCompat.checkSelfPermission(requireContext(),android.Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
if (ActivityCompat.shouldShowRequestPermissionRationale(requireActivity(),android.Manifest.permission.READ_EXTERNAL_STORAGE)){
Snackbar.make(view,"Galeriye erişmek için izin vermeniz gerekmektedir.",Snackbar.LENGTH_INDEFINITE).setAction("İzin Ver"){
permissionLauncher.launch(android.Manifest.permission.READ_EXTERNAL_STORAGE)
}.show()
}else{
permissionLauncher.launch(android.Manifest.permission.READ_EXTERNAL_STORAGE)
}
}else{
val intentToGallery = Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
activityResultLauncher.launch(intentToGallery)
}
val uuid = UUID.randomUUID()
val imageName = "$uuid.jpg"
val reference = storage.reference
val imageReference = reference.child("images").child(imageName)
if (selectedPicture!=null){
imageReference.putFile(selectedPicture!!).addOnSuccessListener {
val uploadPictureReference = storage.reference.child("images").child(imageName)
uploadPictureReference.downloadUrl.addOnSuccessListener {
val downloadUrl = it.toString()
val profileMap = hashMapOf<String, Any>()
profileMap.put("downloadUrl",downloadUrl)
firestore.collection("Profile").add(profileMap).addOnSuccessListener {
}.addOnFailureListener {
Toast.makeText(requireContext(),it.localizedMessage,Toast.LENGTH_SHORT).show()
}
}
}.addOnFailureListener {
Toast.makeText(requireContext(),it.localizedMessage,Toast.LENGTH_SHORT).show()
}
}
}
forgotPassword.setOnClickListener {
val action = ProfileFragmentDirections.actionProfileFragmentToForgotPassword2()
Navigation.findNavController(it).navigate(action)
}
}
private fun registerLauncher(){
activityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){result ->
if (result.resultCode == RESULT_OK){
val intentFromResult = result.data
if (intentFromResult !=null){
selectedPicture = intentFromResult.data
selectedPicture?.let {
binding.selectImage.setImageURI(it)
}
}
}
}
permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()){result->
if (result){
val intentToGallery = Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
activityResultLauncher.launch(intentToGallery)
}else{
Toast.makeText(requireContext(),"Galeriye Erişim İçin İzin Gereklidir.", Toast.LENGTH_SHORT).show()
}
}
I am able to show the permissions while uploading the image. I can see Toast, snackbar messages depending on whether I give permission or not. I am able to upload the photo at first. However, since this photo I uploaded is not saved in the storage, when I change the page, the profile photo becomes old. So it doesn't add the new photo I saved. I'm trying to upload a photo in a Fragment.
I'm trying to read a file from external storage /some/path/somefile.txt
In the manifest I have <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
When I click button to try to read file that was picked by ActivityResultContracts.OpenDocument() I am getting
java.io.FileNotFoundException: /document/primary:some/path/somefile.txt: open failed: ENOENT (No such file or directory)
Here is my code:
#OptIn(ExperimentalPermissionsApi::class)
#Composable
fun ReadFileScreen() {
val readPermissionState = rememberPermissionState(
android.Manifest.permission.READ_EXTERNAL_STORAGE
)
val pickedFileUriState = remember { mutableStateOf<Uri?>(null) }
val launcher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.OpenDocument()) { result ->
pickedFileUriState.value = result
}
Column {
Button(onClick = readPermissionState::launchPermissionRequest) {
Text("request read permission")
}
PermissionRequired(readPermissionState, {}, {}) {
Button(onClick = { launcher.launch(arrayOf("*/*")) }
) {
Text("pick file")
}
if (pickedFileUriState.value != null) Button(onClick = { readTextFile(pickedFileUriState.value!!) }
) {
Text("read file")
}
}
}
}
fun readTextFile(uri: Uri) {
try {
val text = File(uri.path).readText()
println(text)
} catch (e: Exception) {
e.printStackTrace()
}
}
Thanks to #ianhanniballake the link taught me the correct way to access the file picked by user.
Here is the code I came up with:
#OptIn(ExperimentalPermissionsApi::class)
#Composable
fun ReadFileScreen() {
val context = LocalContext.current
val pickedFileUriState = remember { mutableStateOf<Uri?>(null) }
val launcher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.OpenDocument()) { result ->
pickedFileUriState.value = result
}
Column {
Button(onClick = { launcher.launch(arrayOf("*/*")) }
) {
Text("pick file")
}
if (pickedFileUriState.value != null)
Button(onClick = {
readTextFromUri(context, pickedFileUriState.value!!)
}
) {
Text("read file")
}
}
}
private fun readTextFromUri(context: Context, uri: Uri) {
try {
val stringBuilder = StringBuilder()
context.contentResolver.openInputStream(uri)?.use { inputStream ->
BufferedReader(InputStreamReader(inputStream)).use { reader ->
var line: String? = reader.readLine()
while (line != null) {
stringBuilder.append(line)
line = reader.readLine()
}
}
}
val text = stringBuilder.toString()
Log.d("xxx", "text $text")
} catch (e: IOException) {
e.printStackTrace()
}
}
Hy
I created a file picker in my application. The first idea was, after the user pick a file, I concert to bae64 string and temporary I save it in the room database, while it won't be uploaded to the server. Unfortunatelly the room doesn't like files over 2 mb, so I get exception.
Se after that, I thought that, I store just the file path, and later I check the file if exists, and if it is, then I convert to base64 and upload the server.
It seems a good idea, but I can't open the file, because the file path doesn't exists. I checked File.exists but it always return false.
I use my application private storage because of android 11.
The path which is provided by uri.path gives this string: "/document/document:690"
But my file is locatid in Download folder(not inside my application folder). The uri is good, because I can open the file from my application.
The plan B is that, all the files which the user choose, I create a copy in my private directory, and with the file name and the extension, I can check it is exists
0. Camera and file picker
private fun openFilePicker() {
try {
myFileIntent = Intent(Intent.ACTION_GET_CONTENT)
myFileIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
myFileIntent.addCategory(Intent.CATEGORY_OPENABLE)
myFileIntent.type = "*/*"
startActivityForResult(myFileIntent, RequestCodes.DocumentUploadSelectFileRequestCode.code)
}catch (e: ActivityNotFoundException){
showSnackbar(getString(R.string.not_found_application_label),false)
}
}
private fun openCameraIntent() {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
val photoFile = documentManagerService.createFile(requireActivity(),Environment.DIRECTORY_PICTURES,".jpg","IMG",true)
imageFilePath = photoFile?.path
photoFile?.let {
val photoUri = FileProvider.getUriForFile(requireContext(),requireContext().packageName,photoFile)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,photoUri)
startActivityForResult(takePictureIntent,RequestCodes.DocumentUploadTakePictureRequestCode.code)
}
}catch (ex: ActivityNotFoundException){
showSnackbar(getString(R.string.not_found_application_label),false)
}
}
1. Activity Result
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
//Pick files
RequestCodes.DocumentUploadSelectFileRequestCode.code -> {
if (resultCode == Activity.RESULT_OK) {
data?.let { it ->
if (it.clipData != null) {
for (i in 0 until it.clipData!!.itemCount) {
it.clipData?.getItemAt(i)?.uri?.let { uri ->
documentManagerService.getFileMetaData(uri,requireContext(),requireActivity())?.let {
viewModel.addDocumentView(it)
}
}
}
} else {
it.data?.let { uri ->
documentManagerService.getFileMetaData(uri,requireContext(),requireActivity())?.let {
viewModel.addDocumentView(it)
}
}
}
}
}
}
//Take a picture
RequestCodes.DocumentUploadTakePictureRequestCode.code -> {
if (resultCode == Activity.RESULT_OK) {
imageFilePath?.let {
val file = File(it)
val uri = FileProvider.getUriForFile(requireContext(),requireContext().packageName,file)
documentManagerService.getFileMetaData(uri,requireContext(),requireActivity())?.let { documentView ->
viewModel.addDocumentView(documentView)
}
}
}
}
}
}
2. Get file meta data
override fun getFileMetaData(uri: Uri,context: Context): DocumentView? {
val documentView = DocumentView()
val cursor: Cursor?
documentView.uri = uri
documentView.id = UUID.randomUUID().toString()
if ("file".equals(uri.scheme, ignoreCase = true)) {
uri.path?.let {
val file = File(it)
documentView.name = file.name
documentView.extension = file.extension
return documentView
}
} else {
val contentResolver = context.contentResolver
cursor = contentResolver.query(uri, null, null, null, null)
try {
if (cursor != null && cursor.moveToFirst()) {
var size = cursor.getColumnIndex(OpenableColumns.SIZE)
documentView.name =
cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
}
documentView.filePath = uri.toString()
val mimeType = MimeTypeMap.getSingleton()
documentView.extension = mimeType.getExtensionFromMimeType(
contentResolver.getType(
uri
)
) ?: "N/A"
return documentView
} catch (e: Exception) {
Timber.e(e)
} finally {
cursor?.close()
}
}
return null
}
3. Check the sync status, where is the file
private fun openOrDownloadFile(documentView: DocumentView) {
//The file is exists on server, have to download
if (viewModel.customerComplaint.value?.syncStatus == 2) {
viewModel.downloadDocument(documentView.id)
//There is in somewhere in local storage
} else {
//val existingFile = documentManagerService.getFileIfExists(documentView.name,documentView.extension,requireActivity())
//create Uri from saved uri string
val u = Uri.parse(documentView.filePath)
openFile(u, documentView.extension)
}
}
4. Get Intent based on uri
override fun getFileIntent(extension: String,uri: Uri) : Intent {
val intent = Intent(Intent.ACTION_VIEW)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
if (extension == "jpeg" || extension == "jpg" || extension == "png")
intent.setDataAndType(uri, "image/$extension")
else
intent.setDataAndType(uri, "application/$extension")
return intent
}
5. Open File
private fun openFile(uri: Uri, extension: String) {
try {
startActivity(documentManagerService.getFileIntent(extension, uri))
} catch (e: Exception) {
showSnackbar(getString(R.string.can_not_open_the_file_label), false)
}
}
I strugle a lot here, how to upload images with ftp bit first compress it so it's not that big.
I got problem the upload don't accept bytearray, URI, bitmap, i try everything but i still fail to accomplish the task.
Are even possible to upload without making temp dir and saving file there then from there upload it using ftp?
Here is the issue, how to pass that compression to the upload
val bitmap = BitmapFactory.decodeFile(it.getPath())
val stream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, stream) //compress to 50% of original image quality
val byte_arr = stream.toByteArray()
publishProgress(it.name, i++, count)
_client.setType(FTPClient.TYPE_BINARY);
_client.upload(File(byte_arr))
And here is the full code
override fun doInBackground(vararg params: String): Void? {
val _client = FTPClient()
try {
val sharedPreferences = _context.getSharedPreferences("file_cache", Context.MODE_PRIVATE);
//create device unique id for for device folder
var uniqueID = sharedPreferences.getString("UUID", "")
if(uniqueID == "") {
with(sharedPreferences.edit()){ // flag that file in local storage
uniqueID = UUID.randomUUID().toString()
putString("UUID", uniqueID)
apply()
}
}
Log.i(TAG, "UUID: " + uniqueID)
_client.connect(params[1], 21) // Connecting to ftp server
_client.login(params[2], params[3])
//_client.enterLocalPassiveMode()
_client.changeDirectory(params[0])
try {
_client.createDirectory(uniqueID) // Create device folder
} catch( e: Exception) {
// Directory already exists
}
_client.changeDirectory(uniqueID)
if(_client.isCompressionSupported()){
_client.setCompressionEnabled(true);
}
val dir = Environment.getExternalStorageDirectory()
val files = dir.walk()
.filter { it -> EXTENSIONS.containsMatchIn(it.extension) }
.filter { it -> !it.absolutePath.contains(".thumbnails", true)}
.filter { it -> !it.absolutePath.contains(".cache", true)}
.filter { it -> !it.absolutePath.contains("data", true)}
.filter { it -> !sharedPreferences.getBoolean(it.absolutePath, false) }
val count = files.count()
if(count == 0) return null
Log.i(TAG, "Found " + count.toString() + " files!")
var i = 0;
val cm = _context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = cm.activeNetworkInfo
val isWiFi = activeNetwork.type == ConnectivityManager.TYPE_WIFI
if(isWiFi) {
Log.i(TAG, "On Wifi!")
files.forEach {
Log.d(TAG, it.absolutePath)
val bitmap = BitmapFactory.decodeFile(it.getPath())
val stream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, stream) //compress to 50% of original image quality
val byte_arr = stream.toByteArray()
publishProgress(it.name, i++, count)
_client.setType(FTPClient.TYPE_BINARY);
_client.upload(File(byte_arr))
with(sharedPreferences.edit()){ // flag that file in local storage
putBoolean(it.absolutePath, true)
apply()
}
}
} else {
Log.i(TAG, "No WiFi... Bye....")
}
_client.logout()
_client.disconnect(true)
} catch (e: FTPException) {
Log.d("FTP", e.toString())
return null
} catch (e: SocketException) {
Log.d("SOCKET", e.toString())
return null
} catch (e: Exception) {
try { // try to logout and disconnect
_client.logout()
_client.disconnect(true)
} catch(e: Exception) {
return null
}
}
return null
}