i want to install .apk from assets directory.For installing first i copy .apk file from assets directory to internal storage or to sdCard then try to install it but because of unknown app install permission application didn't install
val assetManager: AssetManager = assets
try {
var file = File(path)
file.mkdir()
var outputFile = File(path, "testing_app.apk")
if (outputFile.exists()) {
outputFile.delete()
}
var inputStream: InputStream = assetManager.open("testing_app.apk")
var outputStream: OutputStream
outputStream = FileOutputStream(outputFile)
var byteArray = ByteArray(1024)
while (true) {
val read: Int = inputStream.read(byteArray)
if (read < 0) {
break
}
outputStream.write(byteArray, 0, read)
}
inputStream.close()
outputStream.flush()
outputStream.close()
var intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(
Uri.fromFile(File("${path}/testing_app.apk")),
"application/vnd.android.package-archive"
)
startActivity(intent)
} catch (e: Exception) {
Log.d("AppError", e.message)
}
With the current API, you have to indicate this in the AndroidManifest.xml:
<!-- required for installing other packages on Android O -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
... and the user also needs to enable "install from unknown sources", in the settings.
Related
I'm trying to download file via Retrfoit and everything goes well, until I'm opening it, because after that the only things that I see either a black screen (for images) or some kid of error about wrong file format. Can you tell me what am I doing wrong?
File creation sources:
val file = File(getExternalFilesDir(null), event.fileName)
ResponseBody converting sources:
private fun writeResponseBodyToDisk(body: ResponseBody, file: File): Boolean {
return try {
// todo change the file location/name according to your needs
val futureStudioIconFile: File = file
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try {
val fileReader = ByteArray(4096)
val fileSize: Long = body.contentLength()
var fileSizeDownloaded: Long = 0
inputStream = body.byteStream()
outputStream = FileOutputStream(futureStudioIconFile)
while (true) {
val read: Int = inputStream.read(fileReader)
if (read == -1) {
break
}
outputStream.write(fileReader, 0, read)
fileSizeDownloaded += read.toLong()
Timber.d("%s%s", "file download: $fileSizeDownloaded of ", fileSize)
}
outputStream.flush()
viewState.value = ViewState.ShowFile(file)
true
} catch (e: IOException) {
false
} finally {
inputStream?.close()
outputStream?.close()
}
} catch (e: IOException) {
false
}
}
Viewwing sources:
val fileUri = FileProvider.getUriForFile(
this,
"${BuildConfig.APPLICATION_ID}.fileprovider",
state.file
)
startActivity(
Intent.createChooser(
Intent().apply {
action = Intent.ACTION_VIEW
putExtra(Intent.EXTRA_STREAM, fileUri)
clipData = ClipData.newRawUri("", fileUri)
type = "*/*"
addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
},
null
)
)
Logs:
file download: 4096 of 21688
file download: 8192 of 21688
file download: 12288 of 21688
file download: 16384 of 21688
file download: 20480 of 21688
file download: 21688 of 21688
This question already has answers here:
exposed beyond app through ClipData.Item.getUri
(4 answers)
Closed 8 months ago.
I want to share my application using a share button inside it. Once the button is clicked it should get the base.apk from the package manager and then share it using Intents.
Here is what I have so far:
All UI is ready and working
I have the following code to get the app and share it
try {
val pm = packageManager
val ai = pm.getApplicationInfo(packageName, 0)
val srcFile = File(ai.publicSourceDir)
val share = Intent()
share.action = Intent.ACTION_SEND
share.type = "application/vnd.android.package-archive"
share.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(srcFile))
startActivity(Intent.createChooser(share, "Sharing"))
} catch (e: Exception) {
UtilityMethods(this).toast("Failed To Share The App", "e")
e.printStackTrace()
}
But I get an error with this procedure.
android.os.FileUriExposedException: file:///data/app/~~BC-clKZDViP_O7n44ooPbQ%3D%3D/MyAppPublicSourceDirectory/base.apk exposed beyond app through ClipData.Item.getUri()
Is there any help I can get regarding this? I tried a lot of solutions, but they don't work for me.
EDIT:: Updated Code, Copy the base.apk to Downloads Folder and Rename it. Then try to share it (which is where the error invokes from).
try {
// get the base apk of the app
val pm = packageManager
val ai = pm.getApplicationInfo(packageName, 0)
val srcFile = File(ai.publicSourceDir)
// save the file in Downloads folder
val dstFile = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"LogsCalculator.apk"
)
dstFile.createNewFile()
val input = FileInputStream(srcFile)
val output = FileOutputStream(dstFile)
val buffer = ByteArray(1024)
var length: Int = input.read(buffer)
while (length > 0) {
output.write(buffer, 0, length)
length = input.read(buffer)
}
output.flush()
output.close()
input.close()
// share the apk file now
val intent = Intent(Intent.ACTION_SEND)
intent.type = "application/vnd.android.package-archive"
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(dstFile))
startActivity(Intent.createChooser(intent, getString(R.string.sharing)))
} catch (e: Exception) {
UtilityMethods(this).toast("Failed To Share The App", "e")
e.printStackTrace()
}
It Still Does Not Work.
On later API versions, not even backup tools can obtain the APK anymore, therefore the approach is literally pointless. Instead use Firebase Dynamic Links, in order to permit user to user sharing of your application. This way they can install from Google Play Store, instead up installing some APK of unknown origin, which may not update well.
I got the solution.
try {
// get the base.apk
val baseApkLocation =
applicationContext.packageManager.getApplicationInfo(
applicationContext.packageName,
PackageManager.GET_META_DATA
).sourceDir
// get the file
val baseApk = File(baseApkLocation)
// the path
val path = Environment.getExternalStorageDirectory().toString() + "/Download/"
// make the directory
val dir = File(path)
// if the directory doesn't exist, make it
if (!dir.exists()) {
dir.mkdirs()
}
// Copy the .apk file to downloads directory
val destination = File(
path + "MyAppName.apk"
)
if (destination.exists()) {
destination.delete()
}
destination.createNewFile()
val input = FileInputStream(baseApk)
val output = FileOutputStream(destination)
val buffer = ByteArray(1024)
var length: Int = input.read(buffer)
while (length > 0) {
output.write(buffer, 0, length)
length = input.read(buffer)
}
output.flush()
output.close()
input.close()
// get content uri for the file
val uri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID + ".provider",
destination
)
// share the file
val intent = Intent(Intent.ACTION_SEND)
intent.type = "application/vnd.android.package-archive"
intent.putExtra(Intent.EXTRA_STREAM, uri)
startActivity(Intent.createChooser(intent, getString(R.string.share_app)))
} catch (e: Exception) {
Lib(this).toast("Failed To Share The App", "e")
e.printStackTrace()
}
Here is how it works
Get the base.apk file of the app from its source dir.
Copy the file to the new location and give it a meaningful name.
Get the content URI of the new file. This is what was missing.
// get content uri for the file
val uri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID + ".provider",
destination
)
Share the file.
Anyways thanks for all the help.
my App collecting some data from the user including an optional picture. To getting a High-Res picture i'm using following code:
https://medium.com/codex/how-to-use-the-android-activity-result-api-for-selecting-and-taking-images-5dbcc3e6324b
Getting the picture works as expected. If the user click on a save button, all data shall be written to an CSV-File on the SD-Card, and if latestTmpUri not null the user made a picture as well, and should be saved to the SD-Card, also.
I tried some snippets to move a file on Android, but everytime i'll get an error "File not exists". Maybe it has to do with path in provider_paths.xml, but i'm not sure.
By the way, i'm newbee on programming in Kotlin for Android.
EDIT:
If you take a look in the code from the URL above, there is an deleteOnExit()
private fun getTmpFileUri(): Uri {
val tmpFile = File.createTempFile("tmp_image_file", ".png", cacheDir).apply {
createNewFile()
deleteOnExit()
}
return FileProvider.getUriForFile(applicationContext, "${BuildConfig.APPLICATION_ID}.provider", tmpFile)
}
And if you look in provider_paths.xml
<cache-path name="cached_files" path="." />
<files-path name="images" path="." />
This is the path of the picture
content://com.company.contacts.provider/cached_files/tmp_image_file580022157706292749.png
To give an other path in <cache-path name="cached_files" path="." /> is not the solution i guess, because the SD-CARD's got a unique identifier, like E534-12F6
After a bit of research and thinking about FileInputStream and FileOutputStream and reading this post
https://stackoverflow.com/a/11327789/10155771
i got my solution. Depending on the Code in my first post to take a High-Res picture i modified it in this way:
private lateinit var tmpFile: File
private fun getTmpFileUri(): Uri {
tmpFile = File.createTempFile("tmp_image_file", ".png", cacheDir).apply {
createNewFile()
deleteOnExit()
}
return FileProvider.getUriForFile(applicationContext, "${BuildConfig.APPLICATION_ID}.provider", tmpFile)
}
to make the variable tmpFile global.
In my function to save the CSV and the optional picture i did this:
var imageName = ""
if(latestTmpUri != null) { // There was taken a picture if not null
val folder = getExternalFilesDirs(Environment.DIRECTORY_PICTURES)
val root = java.lang.String.valueOf(folder[1]).toString() // folder[1] is my SD-Card while folder[0] is internal storage
val filets: String = java.lang.String.valueOf(
TimeUnit.MILLISECONDS.toSeconds(
System.currentTimeMillis()
)
) // Unix Timestamp
imageName = companyContact +"_$filets.png"
var instream: InputStream? = null
var outstream: OutputStream? = null
try {
val dir: File = File(root, imageName.replace(" ", "_"))
instream = FileInputStream(tmpFile.path)
outstream = FileOutputStream(dir.path)
val buffer = ByteArray(1024)
var read: Int
while (instream!!.read(buffer).also { read = it } != -1) {
outstream!!.write(buffer, 0, read)
}
instream.close()
instream = null
outstream!!.flush()
outstream.close()
outstream = null
} catch (fnfe1: FileNotFoundException) {
fnfe1.message?.let { it1 -> Log.e("FileNotFoundException", it1) }
} catch (e: java.lang.Exception) {
Log.e("Exception", e.message!!)
}
}
Now i have my picture as png on my SD-Card.
Here is a simple solution for Kotlin 1.7.X
// move Uri file from cache to app's external files
// - from: data/data/com.example.myapp/cache/videos/file.mp4
// - to: sdcard/Android/data/com.example.myapp/files/Movies
// Note: not selectable by the user.
fun moveUriFileToAppMovies(fromUri: Uri, context: Context) {
val inputStream = context.contentResolver.openInputStream(fromUri)!!
val file = File(context.getExternalFilesDir(Environment.DIRECTORY_MOVIES),
fromUri.lastPathSegment ?: "video.mp4")
val outputStream = FileOutputStream(file)
inputStream.copyTo(outputStream)
inputStream.close()
outputStream.close()
}
Context:
My project is in the space of WiFi P2P/ WiFi Direct. I have found and followed some useful tutorials and questions and have a working app for sending Images between phones.
A socket is opened and an OutputStream sent to the other device. Device 2 receives the stream and has to assume the mime Type in order to compress and save to a file.*
EDIT: *Only has to compress for images, but needs to know the File type to save it to storage.
I would now like to open up this feature to transfer a file of any (common) type and have the other device happily receive it and stash it in the appropriate location (Or just all to Downloads might make it simpler).
My question:
What can I use to determine the MIME type for all (image, text, calendar events, contacts, videos) files coming in as an InputStream.
Current code attempts:
I have tried using BitmapFactory.decodeStream() to receive options and mimeType. This only works for Images.
guessContentTypeFromStream() hasn't worked at all and I think it might only be suitable for streaming from internet source?
Any clarifications/tips on how Storage access framework should be used here, or techniques to determine mimeType are appreciated!
Below is code used on the Receive side:
inner class RxClass: Thread() {
lateinit var socket: Socket
lateinit var serverSocket: ServerSocket
override fun run() {
try {
serverSocket = ServerSocket(8888)
socket = serverSocket.accept()
//If this line is reached, then a connection has been established between server and client
Log.d("RECEIVE", "Connection established")
lateinit var bitmap: Bitmap
lateinit var inputStream: InputStream
lateinit var outputStream: OutputStream
var mimeType: String? = null
lateinit var path: String
try {
inputStream = socket.getInputStream()
Log.d("RECEIVE", "got InputStream")
runOnUiThread { Toast.makeText(applicationContext, "Receiving file...", Toast.LENGTH_SHORT).show() }
//mimeType = guessContentTypeFromStream(inputStream)
val options = BitmapFactory.Options()
bitmap = BitmapFactory.decodeStream(inputStream, null, options)!! //TODO: this needs to be for all file types
mimeType = options.outMimeType
Log.d("RECEIVE", "Decoded input, Type: $mimeType")
} catch (e: IOException) {
e.printStackTrace()
}
when(mimeType){ //I was hoping that my attempt would let me know what MIME type was and then here I can act on it differently if neccessary
"image/jpeg" -> path = Environment.DIRECTORY_PICTURES
"image/jpg" -> path = Environment.DIRECTORY_PICTURES
"image/bmp" -> path = Environment.DIRECTORY_PICTURES
"image/gif" -> path = Environment.DIRECTORY_PICTURES
"image/png" -> path = Environment.DIRECTORY_PICTURES
"audio/mpeg" -> path = Environment.DIRECTORY_MUSIC
"audio/x-wav" -> path = Environment.DIRECTORY_MUSIC
"video/wav" -> path = Environment.DIRECTORY_MOVIES
"video/mp4" -> path = Environment.DIRECTORY_MOVIES
"text/plain" -> path = Environment.DIRECTORY_DOCUMENTS
"text/html" -> path = Environment.DIRECTORY_DOCUMENTS
"text/x-vcard" -> path = Environment.DIRECTORY_DOCUMENTS
"text/x-vcalendar" -> path = Environment.DIRECTORY_DOCUMENTS
null -> Log.d("RECEIVE", "MIMETYPE is NULL")
}
Log.d("RECEIVE", "Save file in location: $path")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val resolver: ContentResolver = contentResolver
val contentValues = ContentValues()
//contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, name)
//contentValues.put(MediaStore.Images.Media.MIME_TYPE, mimeType)
//contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, path)
val rxUri: Uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)!!
val inputStream = socket.getInputStream()
outputStream = resolver.openOutputStream(rxUri)!!
Log.d("RECEIVE", "Sending File: $rxUri")
inputStream.copyTo(outputStream)
if(bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)) {
runOnUiThread { Toast.makeText(applicationContext, "File Saved", Toast.LENGTH_SHORT).show() }
}
Log.d("RECEIVE", "close streams")
outputStream.flush()
outputStream.close()
inputStream.close()
runOnUiThread {
ReceivedImage.setImageURI(rxUri)
}
} else { //Version less than Android Q is not implemented
}
} catch (e: IOException){
e.printStackTrace()
}
}
}
Useful links
https://developer.android.com/training/data-storage
https://developer.android.com/training/data-storage/shared/media#add-item
https://developer.android.com/reference/kotlin/android/graphics/BitmapFactory
Thanks in advance
I am trying to save files from the asset folder to the android device's public directory e.g. "Downloads".
Normal file read-write doesn't seem to work.
How to do it?
I have tried this
How to copy files from 'assets' folder to sdcard?
but this didn't work.
fun copy() {
val bufferSize = 1024
val assetManager = context.assets
val assetFiles = assetManager.list("")
assetFiles.forEach {
val inputStream = assetManager.open(it)
val outputStream = FileOutputStream(File(this.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), it))
try {
inputStream.copyTo(outputStream, bufferSize)
} finally {
inputStream.close()
outputStream.flush()
outputStream.close()
}
}
}
Make sure you have enabled runtime read/write permissions and after that simply you can use this code to save any file to a directory.
fun saveImageToExternalStorage(image:Bitmap) {
val fullPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/downloads"
try
{
val dir = File(fullPath)
if (!dir.exists())
{
dir.mkdirs()
}
val fOut:OutputStream = null
val file = File(fullPath, "image.png")
if (file.exists())
file.delete()
file.createNewFile()
fOut = FileOutputStream(file)
// 100 means no compression, the lower you go, the stronger the compression
image.compress(Bitmap.CompressFormat.PNG, 100, fOut)
fOut.flush()
fOut.close()
}
catch (e:Exception) {
Log.e("saveToExternalStorage()", e.message)
}
}