I'm writing unit tests for a use case that basically creates a file with the contents of an InputStream.
However, with the lines:
val path = context.filesDir.path + "/issues.csv"
val fos = FileOutputStream(path)
I'm having trouble, as the when trying to create the FileOutputStream from the path it always results in a FileNotFoundException.
This is the complete code:
class SaveFileUseCase #Inject constructor(
private val context: Context,
#DefaultCoroutineDispatcher private val defaultCoroutineDispatcher: CoroutineDispatcher
) {
suspend operator fun invoke(body: ResponseBody) =
withContext(defaultCoroutineDispatcher) {
saveFile(body)
}
private fun saveFile(body: ResponseBody?) {
if (body == null) {
return
}
var input: InputStream? = null
try {
input = body.byteStream()
val path = context.filesDir.path + "/issues.csv"
val fos = FileOutputStream(path)
fos.use { output ->
val buffer = ByteArray(4 * 1024) // or other buffer size
var read: Int
while (input.read(buffer).also { read = it } != -1) {
output.write(buffer, 0, read)
}
output.flush()
}
} catch (e: Exception) {
Log.e("Error saving file", e.toString())
} finally {
input?.close()
}
}
}
How can I test this?
Thanks a lot in advance!
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)
)
}
}
}
}
}
This is my following code.
I'm having a issue display image from Google Drive.
Source code from https://www.section.io/engineering-education/backup-services-with-google-drive-api-in-android/
I have also worked with this image url https://drive.google.com/uc?id=FILE_ID but only worked in anyone with the link access not restricted images.
fun downloadFileFromGDrive(id: String) {
getDriveService()?.let { googleDriveService ->
CoroutineScope(Dispatchers.IO).launch {
val gDriveFile = googleDriveService.Files().get(id).execute()
Log.e("gDriveFile", gDriveFile.toString())
val outputStream: OutputStream = ByteArrayOutputStream()
googleDriveService.files()[id].executeMediaAndDownloadTo(outputStream)
}
} ?: Toast.makeText(context, "Please Log In first!", LENGTH_SHORT).show()
}
Use the following function It will download Google drive Image and save it to the app private files folder.
fun downloadFileFromGDrive(id: String) {
getDriveService()?.let { googleDriveService ->
CoroutineScope(Dispatchers.IO).launch {
Log.e("idDownload", id)
val file = File(context.filesDir, "${id}.jpg")
if (!file.exists()) {
try {
val gDriveFile = googleDriveService.Files().get(id).execute()
saveImageInFilesDir(gDriveFile.id)
} catch (e: Exception) {
println("!!! Handle Exception $e")
}
}
}
} ?: ""
}
private fun saveImageInFilesDir(id: String?) {
getDriveService()?.let { googleDriveService ->
CoroutineScope(Dispatchers.IO).launch {
val file = File(context.filesDir, "${id}.jpg")
try {
val outputStream = FileOutputStream(file)
googleDriveService.files()[id]
.executeMediaAndDownloadTo(outputStream)
if (id != null) {
googleDriveService.readFile(id)
}
outputStream.flush()
outputStream.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
I want to load rtf files with webview using raw folder. Below is my code;
val urlWebView = findViewById<WebView>(R.id.webview)
readTextFromResource(R.raw.lettemp_369)?.let { urlWebView.loadData(it, "text/rtf", "utf-8") };
Below is function;
private fun readTextFromResource(resourceID: Int): String? {
val raw: InputStream = resources.openRawResource(resourceID)
val stream = ByteArrayOutputStream()
var i: Int
try {
i = raw.read()
while (i != -1) {
stream.write(i)
i = raw.read()
}
raw.close()
} catch (e: IOException) {
e.printStackTrace()
}
return stream.toString()
}
I want to load rtf files with webview using raw folder. Below is my code;
val urlWebView = findViewById<WebView>(R.id.webview)
readTextFromResource(R.raw.lettemp_369)?.let { urlWebView.loadData(it, "text/rtf", "utf-8") };
Below is function;
private fun readTextFromResource(resourceID: Int): String? {
val raw: InputStream = resources.openRawResource(resourceID)
val stream = ByteArrayOutputStream()
var i: Int
try {
i = raw.read()
while (i != -1) {
stream.write(i)
i = raw.read()
}
raw.close()
} catch (e: IOException) {
e.printStackTrace()
}
return stream.toString()
}
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
}