0

I'm currently working on a solution how to update and install APK file programmatically - very similar like this issue. My app doesn't use Google Services.

For updating purposes, APK-files with ascending versions are stored on an internal server and a C# Web service returns the latest version of the APK file.

I do this with Retrofit2:

    @Streaming
    @GET("/ws/webservice.svc/getUpdate")
    fun getUpdate(
        @Query("Programm") program: String,
        @Query("version") version: String,
        @Query("test") test: Boolean
    ): Single<String>

and LiveData:

override fun getUpdate() {
        disposables += api.getUpdate(
            program = context.getString(R.string.app_name),
            version = context.getString(R.string.app_version),
            test = isTest
        )
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeBy(
                onSuccess = {
                    liveData.value = GetUpdate(it)
                },
                onError = {
                    liveData.value = Error("Error getUpdate: " + it.message)
                }
            )
    }

The problem that I'm facing, is that the response of that API call (which means - the latest APK file) has a base64String representation like shown in the image below - this is for example only a part of the server response when the API call is made in browser.

APK file as String

Is it somehow possible to generate a "real" APK file from this String representation after downloading it, so after that I can probably be able to install it on the device? I know that this is weird, but the customer wants me to re-use the same web service for this purposes.

I found a similar issue here. How can be this done in Kotlin? Thanks in advance.

BWappsAndmore
  • 491
  • 3
  • 17

2 Answers2

1

Yes you need to decode the base64 into a ByteArray then write the bytes to a location with the postfix .apk. What you have is a String where the bytes are encoded using the base64 encoding scheme.

Since your using kotlin you might what to look here to get the ByteArray![1] from the String. Then just ensure the file you write has .apk extension.

[1] https://developer.android.com/reference/kotlin/java/util/Base64.Decoder#decode(kotlin.String)

JBirdVegas
  • 10,855
  • 2
  • 44
  • 50
  • Thank you so much for your help. I am on the way to implement the solution of this issue and I'll post it here as soon as possible. – BWappsAndmore Jan 28 '20 at 09:17
0

AndroidManifest.xml

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

MVVM with LiveData:

    override fun getUpdate() {
        disposables += api.getUpdate(
            program = context.getString(R.string.app_name) + ".apk",
            version = context.getString(R.string.app_version),
            test = isTest
        )
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .map(::decodeToAPK)
            .subscribe({
                liveData.value = GetUpdate 
            }, {
                liveData.value = Error("Error getUpdate: " + it.message)
            })
    }


    private fun decodeToAPK(base64String: String) {

        val byteArrayAPK = Base64.decode(base64String, Base64.DEFAULT)

        val file =
            File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "yourApp.apk")
        try {
            val fileOutputStream = FileOutputStream(file)
            fileOutputStream.write(byteArrayAPK)
            fileOutputStream.flush()
            fileOutputStream.close()
        } catch (e: UnsupportedEncodingException) {
            Log.d(null, "error writing APK")
            e.printStackTrace()
        }
   }

   sealed class SomeAction : Action {
     data class Error(val message: String) : SomeAction()
     object GetUpdate : SomeAction()
   }
BWappsAndmore
  • 491
  • 3
  • 17