Jump to content
KMarb

Self-updating android app in Alexandria

Recommended Posts

In 2015 or so I used Berlin to create some android apps. I'm primarily a Windows developer, so that was a painful process. I need to update those apps so they run on new android versions, but I'm running into several issues. I am happy I found this group, I have found a lot of good advice, but I have a feeling like answers change every couple of years, and so far I have not figured out a few things.

 

I know very little about android development, which is why I struggle, so any direction where I can learn more is much appreciated.

 

First thing is, I want my app to self-update by downloading an apk and running so user can install if wanted.

 

I have async download working, so that is good (threads are also new to me). My code that runs the update is below. I get the following error:

 

Project JCMTime.apk raised exception class EJNIException with message 'android.os.FileUriExposedException: file:///data/user/0/com.hello.world/files/BigTime64-4.1.apk exposed beyond app through Intent.getData()'.

 

Here is one version of the code I've tried:

procedure TDMCommon.initiateUpdate;

var
  F: Jfile;
  Intent: JIntent;
begin
  F := TJfile.JavaClass.init(stringtojstring(UpdateFolder),stringtojstring(UpdateFN));

  Intent := TJIntent.Create();
  Intent.setAction(TJIntent.JavaClass.ACTION_VIEW);
  Intent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  Intent.setDataAndType(TJnet_Uri.JavaClass.fromFile(F), StringToJString('application/vnd.android.package-archive'));

  TAndroidHelper.Context.startActivity(Intent);
end;
 

I've tried adding the below to manifest file:

        <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.hello.world.fileprovider"
        android:exported="false"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>
 

with following filepaths.xml file deploying to res\xml\:

 

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <root-path name="root" path="." />
</paths>

 

but I'm not even sure I need those things.

 

Any feedback is appreciated... I think the biggest problems I have are 1) lack of active delphi discussion groups, 2) programming that worked 2-3 years ago no longer works, so even when I find a relevant article/post it doesn't mean hooray, and 3) I am really uneducated about android development. Thanks for any feedback.

Share this post


Link to post
9 minutes ago, KMarb said:

Intent.setDataAndType(TJnet_Uri.JavaClass.fromFile(F), StringToJString('application/vnd.android.package-archive'));

This should be:

    Intent.setDataAndType(JFileToJURI(F)), StringToJString('application/vnd.android.package-archive'));

JFileToJURI uses the FileProvider class to create the correct URI

12 minutes ago, KMarb said:

I've tried adding the below to manifest file:

You won't need to do that if you check the Secure File Sharing option in the Entitlements List in the Project Options. This also creates and deploys the required xml file.

Share this post


Link to post

Thanks! I have removed the provider section from manifest. I do have secure file sharing checked in entitlements list.

 

With the change you suggested my error has changed:

 

Project JCMTime.apk raised exception class EJNIException with message 'java.lang.IllegalArgumentException: Couldn't find meta-data for provider with authority com.zunna.JCMTime.fileprovider'.

 

Unless you have a quicker solution I will try the steps laid out here: android - Couldn't find meta-data for provider with authority - Stack Overflow

Although... that seems almost like the same xml I had before?

Share this post


Link to post
3 hours ago, KMarb said:

Project JCMTime.apk raised exception class EJNIException with message 'java.lang.IllegalArgumentException: Couldn't find meta-data for provider with authority com.zunna.JCMTime.fileprovider'

Sorry.. missed a step. Delete AndroidManifest.template.xml in the project folder. The IDE will recreate this. If you have made any customisations to it, you will need to ensure you reinstate those after it is recreated.

Share this post


Link to post

Ok. I did that and saw how the new provider_paths.xml was placed in the Android64\Debug folder, and also added to deployment page.

 

Now when I run the app, the call to TAndroidHelper.Context.startActivity(Intent); seems to go to la-la land... No window pops up asking if I want to update, like it would if I ran the apk from the downloads folder manually.

 

I do see my app state go through WillBecomeInactive, then BecameActive, but no prompt to install/update and no error message.

 

Here is an important detail maybe... the apk I'm launching is the same version I'm running... it is not actually an update. Could that be why it doesn't show the install window? Or could it be a permission problem still with launching an apk from my app?

 

Your help is really appreciated.

Share this post


Link to post

This is a complete routine that should work:

procedure OpenApplication(const AFileName: string);
var
  LMimeType: JString;
  LIntent: JIntent;
begin
  LMimeType := TJMimeTypeMap.JavaClass.getSingleton.getMimeTypeFromExtension(StringToJString('apk'));
  LIntent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW);
  LIntent.setDataAndType(JFileToJURI(TJFile.JavaClass.init(StringToJString(AFileName))), LMimeType);
  LIntent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
  TAndroidHelper.Activity.startActivityForResult(LIntent, 999); // 999 is just a random value - it won't matter what it is
end;

Note the extra line regarding FLAG_GRANT_READ_URI_PERMISSION.

Share this post


Link to post

Thank you very much. Progress, but still problems.

 

On my Samsung Galaxy S6 running 64-bit Android 7 - IT WORKS! The "Do you want to install an update..." window pops up like it should. Oddly it is full screen, not just a portion of window like when run manually.

 

On two other 64-bit android 11 devices - same as before. the update prompt window never shows. If I go to shared downloads folder and run the just-downloaded apk manually, it works as expected, so the apk file is downloading correctly.

 

Lastly, on a 32-bit android 9 phone - same as the android 11's - no window pops up.

 

I had only been testing on android 11, but I wanted to test on more devices before reporting back, so it might have worked on the S6 (android 7) with prior versions of code.

 

I'm wondering if since my original work on this was in Berlin if there are app or main form settings that are preventing the intent from running properly on newer versions of android. Can you think of any other things to look at?

 

And a broader question, I see people who write log messages somehow/somewhere which I would think would be a great help sometimes to figure what's wrong. Is there an article that talks about setting that up?

Share this post


Link to post

Question: I know in recent versions of android, to install an app manually that does not come from play store, you have to enable permissions on the device for the app in question (like Files app, or Opera or whatever) so that you can even install the app.

 

How do I know if my app has permission to install an apk?

 

Does it matter if the apk is in shared downloads or if it is in my sandboxed documents?

 

Also, can my app install any apk, or can I only initiate the install for apk for same app as what I'm running?

 

If this is all explained in android docs please let me know a good starting point. Thank you again.

Share this post


Link to post
6 hours ago, KMarb said:

If I go to shared downloads folder

Is there any reason why you can't download to an app-specific folder? There are a number of changes in Android 11 that make it difficult to manage storage in shared folders:

 

https://developer.android.com/about/versions/11/privacy/storage

 

Which would explain why you are having issues.

6 hours ago, KMarb said:

I see people who write log messages somehow/somewhere which I would think would be a great help sometimes to figure what's wrong. Is there an article that talks about setting that up?

If you're logging from an app, the easiest way is to use Log.d from the FMX.Types unit. You can view the log messages using a logcat viewer - I have one called Device Lens.

3 hours ago, KMarb said:

How do I know if my app has permission to install an apk?

In Delphi, there's a permission in the Uses Permissions > Signature section of the Project Options called "Request install packages". Even with that checked, the OS might request approval from the user.

Share this post


Link to post

 "Request install packages" was the final piece! saving the apk to app-specific folder works but so does downloading to shared documents folder. I prefer the shared doc folder because user can find those downloads if something goes wrong.

 

With that setting, when the app calls the intent to install update, I get a window that says the app does not have that permission, but a click takes me to the settings where I can turn on the permission, then go back to app and click install.

 

This is a major win for my app! Thank you so much.

 

I will definitely check out Device Lens. Thanks again, happy weekend!

  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×