2021-06-19 09:06:52 +02:00
|
|
|
// (c) 2017 Ekkehard Gentz (ekke)
|
|
|
|
// this project is based on ideas from
|
|
|
|
// http://blog.lasconic.com/share-on-ios-and-android-using-qml/
|
|
|
|
// see github project https://github.com/lasconic/ShareUtils-QML
|
|
|
|
// also inspired by:
|
|
|
|
// https://www.androidcode.ninja/android-share-intent-example/
|
|
|
|
// https://www.calligra.org/blogs/sharing-with-qt-on-android/
|
|
|
|
// https://stackoverflow.com/questions/7156932/open-file-in-another-app
|
|
|
|
// http://www.qtcentre.org/threads/58668-How-to-use-QAndroidJniObject-for-intent-setData
|
|
|
|
// OpenURL in At Android: got ideas from:
|
|
|
|
// https://github.com/BernhardWenzel/open-url-in-qt-android
|
|
|
|
// https://github.com/tobiatesan/android_intents_qt
|
|
|
|
//
|
|
|
|
// see also /COPYRIGHT and /LICENSE
|
|
|
|
|
|
|
|
package de.itsblue.blueROCK;
|
|
|
|
|
|
|
|
import org.qtproject.qt5.android.QtNative;
|
|
|
|
|
|
|
|
import org.qtproject.qt5.android.bindings.QtActivity;
|
|
|
|
import android.os.*;
|
|
|
|
import android.content.*;
|
|
|
|
import android.app.*;
|
|
|
|
|
|
|
|
import java.lang.String;
|
|
|
|
import android.content.Intent;
|
|
|
|
import java.io.File;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.content.ContentResolver;
|
|
|
|
import android.webkit.MimeTypeMap;
|
|
|
|
|
|
|
|
import org.ekkescorner.utils.*;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class MainActivity extends QtActivity
|
|
|
|
{
|
|
|
|
// native - must be implemented in Cpp via JNI
|
|
|
|
// 'file' scheme or resolved from 'content' scheme:
|
|
|
|
public static native void setFileUrlReceived(String url);
|
2021-06-20 19:04:36 +02:00
|
|
|
//
|
|
|
|
public static native void setOtherUrlReceived(String url, String scheme);
|
2021-06-19 09:06:52 +02:00
|
|
|
// InputStream from 'content' scheme:
|
|
|
|
public static native void setFileReceivedAndSaved(String url);
|
|
|
|
//
|
|
|
|
public static native void fireActivityResult(int requestCode, int resultCode);
|
|
|
|
//
|
|
|
|
public static native boolean checkFileExits(String url);
|
|
|
|
|
|
|
|
public static boolean isIntentPending;
|
|
|
|
public static boolean isInitialized;
|
|
|
|
public static String workingDirPath;
|
|
|
|
|
|
|
|
// Use a custom Chooser without providing own App as share target !
|
|
|
|
// see QShareUtils.java createCustomChooserAndStartActivity()
|
|
|
|
// Selecting your own App as target could cause AndroidOS to call
|
|
|
|
// onCreate() instead of onNewIntent()
|
|
|
|
// and then you are in trouble because we're using 'singleInstance' as LaunchMode
|
|
|
|
// more details: my blog at Qt
|
|
|
|
@Override
|
|
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
Log.d("ekkescorner", "onCreate QShareActivity");
|
|
|
|
// now we're checking if the App was started from another Android App via Intent
|
|
|
|
Intent theIntent = getIntent();
|
|
|
|
if (theIntent != null){
|
|
|
|
String theAction = theIntent.getAction();
|
|
|
|
if (theAction != null){
|
|
|
|
Log.d("ekkescorner onCreate ", theAction);
|
|
|
|
// QML UI not ready yet
|
|
|
|
// delay processIntent();
|
|
|
|
isIntentPending = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // onCreate
|
|
|
|
|
|
|
|
// WIP - trying to find a solution to survive a 2nd onCreate
|
|
|
|
// ongoing discussion in QtMob (Slack)
|
|
|
|
// from other Apps not respecting that you only have a singleInstance
|
|
|
|
// there are problems per ex. sharing a file from Google Files App,
|
|
|
|
// but working well using Xiaomi FileManager App
|
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
|
|
|
Log.d("ekkescorner", "onDestroy QShareActivity");
|
|
|
|
// super.onDestroy();
|
|
|
|
// System.exit() closes the App before doing onCreate() again
|
|
|
|
// then the App was restarted, but looses context
|
|
|
|
// This works for Samsung My Files
|
|
|
|
// but Google Files doesn't call onDestroy()
|
|
|
|
System.exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// we start Activity with result code
|
|
|
|
// to test JNI with QAndroidActivityResultReceiver you must comment or rename
|
|
|
|
// this method here - otherwise you'll get wrong request or result codes
|
|
|
|
@Override
|
|
|
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
|
|
// Check which request we're responding to
|
|
|
|
Log.d("ekkescorner onActivityResult", "requestCode: "+requestCode);
|
|
|
|
if (resultCode == RESULT_OK) {
|
|
|
|
Log.d("ekkescorner onActivityResult - resultCode: ", "SUCCESS");
|
|
|
|
} else {
|
|
|
|
Log.d("ekkescorner onActivityResult - resultCode: ", "CANCEL");
|
|
|
|
}
|
|
|
|
// hint: result comes back too fast for Action SEND
|
|
|
|
// if you want to delete/move the File add a Timer w 500ms delay
|
|
|
|
// see Example App main.qml - delayDeleteTimer
|
|
|
|
// if you want to revoke permissions for older OS
|
|
|
|
// it makes sense also do this after the delay
|
|
|
|
fireActivityResult(requestCode, resultCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we are opened from other apps:
|
|
|
|
@Override
|
|
|
|
public void onNewIntent(Intent intent) {
|
|
|
|
Log.d("ekkescorner", "onNewIntent");
|
|
|
|
super.onNewIntent(intent);
|
|
|
|
setIntent(intent);
|
|
|
|
// Intent will be processed, if all is initialized and Qt / QML can handle the event
|
|
|
|
if(isInitialized) {
|
|
|
|
processIntent();
|
|
|
|
} else {
|
|
|
|
isIntentPending = true;
|
|
|
|
}
|
|
|
|
} // onNewIntent
|
|
|
|
|
|
|
|
public void checkPendingIntents(String workingDir) {
|
|
|
|
isInitialized = true;
|
|
|
|
workingDirPath = workingDir;
|
|
|
|
Log.d("ekkescorner", workingDirPath);
|
|
|
|
if(isIntentPending) {
|
|
|
|
isIntentPending = false;
|
|
|
|
Log.d("ekkescorner", "checkPendingIntents: true");
|
|
|
|
processIntent();
|
|
|
|
} else {
|
|
|
|
Log.d("ekkescorner", "nothingPending");
|
|
|
|
}
|
|
|
|
} // checkPendingIntents
|
|
|
|
|
|
|
|
// process the Intent if Action is SEND or VIEW
|
|
|
|
private void processIntent(){
|
|
|
|
Intent intent = getIntent();
|
|
|
|
|
|
|
|
Uri intentUri;
|
|
|
|
String intentScheme;
|
|
|
|
String intentAction;
|
|
|
|
// we are listening to android.intent.action.SEND or VIEW (see Manifest)
|
|
|
|
if (intent.getAction().equals("android.intent.action.VIEW")){
|
|
|
|
intentAction = "VIEW";
|
|
|
|
intentUri = intent.getData();
|
|
|
|
} else if (intent.getAction().equals("android.intent.action.SEND")){
|
|
|
|
intentAction = "SEND";
|
|
|
|
Bundle bundle = intent.getExtras();
|
|
|
|
intentUri = (Uri)bundle.get(Intent.EXTRA_STREAM);
|
|
|
|
} else {
|
|
|
|
Log.d("ekkescorner Intent unknown action:", intent.getAction());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Log.d("ekkescorner action:", intentAction);
|
|
|
|
if (intentUri == null){
|
|
|
|
Log.d("ekkescorner Intent URI:", "is null");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.d("ekkescorner Intent URI:", intentUri.toString());
|
|
|
|
|
|
|
|
// content or file
|
|
|
|
intentScheme = intentUri.getScheme();
|
|
|
|
if (intentScheme == null){
|
|
|
|
Log.d("ekkescorner Intent URI Scheme:", "is null");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(intentScheme.equals("file")){
|
|
|
|
// URI as encoded string
|
|
|
|
Log.d("ekkescorner Intent File URI: ", intentUri.toString());
|
|
|
|
setFileUrlReceived(intentUri.toString());
|
|
|
|
// we are done Qt can deal with file scheme
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(!intentScheme.equals("content")){
|
|
|
|
Log.d("ekkescorner Intent URI unknown scheme: ", intentScheme);
|
2021-06-20 19:04:36 +02:00
|
|
|
setOtherUrlReceived(intentUri.toString(), intentScheme);
|
2021-06-19 09:06:52 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// ok - it's a content scheme URI
|
|
|
|
// we will try to resolve the Path to a File URI
|
|
|
|
// if this won't work or if the File cannot be opened,
|
|
|
|
// we'll try to copy the file into our App working dir via InputStream
|
|
|
|
// hopefully in most cases PathResolver will give a path
|
|
|
|
|
|
|
|
// you need the file extension, MimeType or Name from ContentResolver ?
|
|
|
|
// here's HowTo get it:
|
|
|
|
Log.d("ekkescorner Intent Content URI: ", intentUri.toString());
|
|
|
|
ContentResolver cR = this.getContentResolver();
|
|
|
|
MimeTypeMap mime = MimeTypeMap.getSingleton();
|
|
|
|
String fileExtension = mime.getExtensionFromMimeType(cR.getType(intentUri));
|
|
|
|
Log.d("ekkescorner","Intent extension: "+fileExtension);
|
|
|
|
String mimeType = cR.getType(intentUri);
|
|
|
|
Log.d("ekkescorner"," Intent MimeType: "+mimeType);
|
|
|
|
String name = QShareUtils.getContentName(cR, intentUri);
|
|
|
|
if(name != null) {
|
|
|
|
Log.d("ekkescorner Intent Name:", name);
|
|
|
|
} else {
|
|
|
|
Log.d("ekkescorner Intent Name:", "is NULL");
|
|
|
|
}
|
|
|
|
String filePath;
|
|
|
|
filePath = QSharePathResolver.getRealPathFromURI(this, intentUri);
|
|
|
|
if(filePath == null) {
|
|
|
|
Log.d("ekkescorner QSharePathResolver:", "filePath is NULL");
|
|
|
|
} else {
|
|
|
|
Log.d("ekkescorner QSharePathResolver:", filePath);
|
|
|
|
// to be safe check if this File Url really can be opened by Qt
|
|
|
|
// there were problems with MS office apps on Android 7
|
|
|
|
if (checkFileExits(filePath)) {
|
|
|
|
setFileUrlReceived(filePath);
|
|
|
|
// we are done Qt can deal with file scheme
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// trying the InputStream way:
|
|
|
|
filePath = QShareUtils.createFile(cR, intentUri, workingDirPath);
|
|
|
|
if(filePath == null) {
|
|
|
|
Log.d("ekkescorner Intent FilePath:", "is NULL");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setFileReceivedAndSaved(filePath);
|
|
|
|
} // processIntent
|
|
|
|
|
|
|
|
} // class QShareActivity
|