I have a programme which needs to show off photos occasionally. The problem is that is always asks which programme to use to display the file. The delphi picture ReadFromFile is no use, as it uses a filename and the modern android system only gives you a uri. Using the folllowing code works, but asks you which programme you wish to use every blasted time. I am unwilling to specify “always” in case that wrecks it for every other programme on the system!
Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW);
Intent.setData(fp.uri);
// intent.setPackage(StringToJString('PHOTOS')); //Neither photos nor com.photos works here.
// intent.putExtra(StringToJString('com.PHOTOS'), 0); //Doesn't work either.
Intent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
TAndroidHelper.Activity.startActivity(Intent);
Any ideas please?
If you’re targeting a specific app, in later versions of Android, you will need to have an entry inside the <queries> tag inAndroidManifest.template.xml, e.g for the “default” Photos app:
..and you will need to use setPackageName in the Intent , as well as call resolveActivity to make sure that the app really is installed (apparently it may not be). This code assumes there has been a file called Example.jpg deployed with a Remote Path value of .\assets\internal:
uses
System.IOUtils,
Androidapi.JNI.GraphicsContentViewText, Androidapi.Helpers, Androidapi.JNI.Net, Androidapi.JNI.JavaTypes;
procedure TForm1.Button1Click(Sender: TObject);
var
LIntent: JIntent;
LFile: JFile;
LFileName: string;
begin
LFileName := TPath.Combine(TPath.GetDocumentsPath, 'Example.jpg');
LFile := TJFile.JavaClass.init(StringToJString(LFileName));
LIntent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW);
LIntent.setPackage(StringToJString('com.google.android.apps.photos'));
LIntent.setDataAndType(TAndroidHelper.JFileToJURI(LFile), StringToJString('image/*'));
LIntent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
if LIntent.resolveActivity(TAndroidHelper.Context.getPackageManager) <> nil then
TAndroidHelper.Activity.startActivity(LIntent);
// else Photos is not installed
end;
Thanks. That was helpful. As I have no way of knowing which app a particular user may have, and will need them to set up their own defaults if desired, attacking the manifest file is not an option. Using my defaults, I ended up trying
PeteALookupForm.ParseFilename(FileName, FileOnly, ExtOnly);
ExtOnly := LowerCase(ExtOnly);
player := '';
if ExtOnly = 'wav' then
begin
player := 'VLC';
mime := 'audio/x-wav'
end;
if ExtOnly = 'jpg' then
begin
player := 'photos';
mime := 'image/*';
end;
fp := PeteALookupForm.GetFileUri(LastDirectory, filename);
if fp <> nil then
begin
try
Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW);
Intent.setDataAndType(fp.uri, StringToJString(mime));
intent.setPackage(StringToJString('com.google.android.apps.' + player));
Intent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
TAndroidHelper.Activity.startActivity(Intent);
except
Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW);
Intent.setData(fp.uri);
Intent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
TAndroidHelper.Activity.startActivity(Intent);
end;
which works well on the photos, but cannot cope with the sound recordings (.wav files). The second half (the “except” block) provides the sound OK, but the “try” section before the “except” causes
EJNIE activity not found Exception
No activity found to handle Intent
{act = android.intent.action.VIEW
dat = content://com.android.externalstorage.documents/…
typ = image/*flg=0x1 xflg=0x4
pkg = com.google.android.apps.VLC }
Google/Android will tell you otherwise. resolveActivity and startActivity will fail in later versions of Android unless you have the package item in queries, regardless of whether or not the app is installed.
The package name for VLC is: org.videolan.vlc
Also, a Delphi try/except will not help you here. To avoid the Java exception you need to use resolveActivity to determine whether or not the app is installed, as described earlier.
Since I can’t do it that way, I have changed to copying the sound or photo files to the allowed android subdirectories.
I have tried copying the photos/audio files across from the .uri area to either
System.IOUtils.tPath.GetHomePath (giving /data/user/0/com.Embarcadero.PeteA/files) or
System.IOUtils.tPath.GetTempPath (giving /storage/emulated/0/Android/data/com.Embarcadero.PeteA/files/tmp)
The copying appears to go OK:
(Extract from CopyFileToTemp)
JBuffer := TJavaArray<Byte>.Create(2048);
JFileReader := TAndroidHelper.Context.getContentResolver.openInputStream(inputfileURI;
home := System.IOUtils.tPath.GetTempPath + '/';
AssignFile(f, home + FileName);
ReWrite(f);
while true do
begin
Transferred := JFileReader.Read(JBuffer);
if Transferred <= 0 then break; // -1 means EOF
BlockWrite(f, JBuffer.data^, Transferred);
end;
CloseFile(f);
JFileReader.close;
No problems there, but trying to use the computer to look for the written files does not work. I can look at Android/data/com.Embarcadero.PeteA/files/tmp, but none of the files written there show up on the computer, so I cannot test them.
When it tries to play the sound file (using home = filename) through MediaPlayer, I get
"Access violation at address 70933CC738 accessing 000000000000"
and if I try to read the photo file using Image1.Bitmap.LoadFromFile (ibid), I get
"java.io.IOExxception: IPrepare failed.: status = 0x1"
Would it be helpful if I sat down and wrote a complete demo programme for this? The
programme in which it exists is about 40 forms and rather large.
I have added the code to the FileAccess programme, so you can see what mucks up. You will need to have a picture (jpg) and a sound (wav) file in the permitted directory to play with. Select one of these files, then hit the “Copy To Temp” button. After that, click on the relevant “Pic from Temp” or “Sound from Temp” to see the problem.
Dave, I have finally got it all working, without the user having to select any programmes at all. It took a lot of experimenting to find out which technique would work to copy files from an “outside” subdirectory to the hidden android directory for the programme, with much help from the Marc Files app which lets you see those hidden files! Thanks for putting up with my problems, but there is nobody who speaks Delphi within 250km of here, so you get all the queries, for which I apologise. I could not do it without you!