Qt/C++/Android - How to install an .APK file programmatically? - android

I am implementing my own auto-updater within my application. I was able to successfully download the .apk file of the newer version into the /Download folder on the sdcard, but I can't figure out how to open/run that file so the user is presented with the new installation dialog.
The only thing I could come up with:
QString downloadedAPK = "/storage/emulated/0/Download/latest.apk"; // Not hardcoded, but wrote it here this way for simplicity
QDesktopServices::openUrl(QUrl(downloadedAPK));
Debugger output:
D/Instrumentation(26418): checkStartActivityResult :Intent { act=android.intent.action.VIEW dat=file:///storage/emulated/0/Download/latest.apk }
D/Instrumentation(26418): checkStartActivityResult inent is instance of inent:
W/System.err(26418): android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=file:///storage/emulated/0/Download/latest.apk }
W/System.err(26418): at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1660)
W/System.err(26418): at android.app.Instrumentation.execStartActivity(Instrumentation.java:1430)
W/System.err(26418): at android.app.Activity.startActivityForResult(Activity.java:3532)
W/System.err(26418): at android.app.Activity.startActivityForResult(Activity.java:3493)
W/System.err(26418): at android.app.Activity.startActivity(Activity.java:3735)
W/System.err(26418): at android.app.Activity.startActivity(Activity.java:3703)
W/System.err(26418): at org.qtproject.qt5.android.QtNative.openURL(QtNative.java:110)
W/System.err(26418): at dalvik.system.NativeStart.run(Native Method)
I have looked everywhere but never found anything regarding opening APKs from Qt. The only thing I found was a solutoin using JNI ( which I don't want to use because it's simpler to just do it with C++ and because I have zero experience with the whole C++/JNI thing ) and it was not well documented so I didn't understand how to make it work.
So, what would be the easiest way to open the downloaded apk?
Edit:
I have followed Tumbus's answer, but because of some compiling errors I had to make a few modifications on his JNI code as follows:
void Updater::InstallApp(const QString &appPackageName)
{
qDebug() << "[+] APP: " << appPackageName; // Which is the string ("/storage/emulated/0/Download/latest.apk")
QAndroidJniObject app = QAndroidJniObject::fromString(appPackageName);
QAndroidJniObject::callStaticMethod<jint>("AndroidIntentLauncher",
"installApp",
"(Ljava/lang/String;)I",
app.object<jstring>());
}
When I run my application on my android device, it pulls the newest .apk file from my server, then nothing happens. Why? (I have not made any changes on the AndroidManifest.xml until now).

You have to make a custom intent to install APK. See this question for details.
I'm afraid such platform-specific think must require calls to platform-specific API. The good news are Qt framework has simplified wrap-up on JNI and you can include a Java class into Android project. Therefore I would make my own static java function called from Qt.
Example
Java class
package io.novikov.androidintentlauncher;
import org.qtproject.qt5.android.QtNative;
import java.lang.String;
import java.io.File;
import android.content.Intent;
import android.util.Log;
import android.net.Uri;
import android.content.ContentValues;
import android.content.Context;
public class AndroidIntentLauncher
{
protected AndroidIntentLauncher()
{
}
public static int installApp(String appPackageName) {
if (QtNative.activity() == null)
return -1;
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(appPackageName)),
"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
QtNative.activity().startActivity(intent);
return 0;
} catch (android.content.ActivityNotFoundException anfe) {
return -3;
}
}
}
Notice that startActivity() should be called as a method from *QtNative.activity(). We have to maintain special directory structures for java according to conventional rules. The example is at Makefile section below.
JNI
The C++ code to call this method is a bit tricky.
const static char* MY_JAVA_CLASS = "io/novikov/androidintentlauncher/AndroidIntentLauncher";
static void InstallApp(const QString &appPackageName) {
QAndroidJniObject jsText = QAndroidJniObject::fromString(appPackageName);
QAndroidJniObject::callStaticMethod<jint>(MY_JAVA_CLASS,
"installApp",
"(Ljava/lang/String;)I",
jsText.object<jstring>());
}
The string literal "(Ljava/lang/String;)I" is the signature of java method.
The name of the Java class must be at a complete form "my/domain/my_app/MyClass"
Makefile
The last challenge is to include the java code to your project properly. Below the corresponding fragment of the .pro file.
android {
QT += androidextras
OTHER_FILES += android_src/src/io/novikov/androidintentlauncher/AndroidIntentLauncher.java
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android_src
}
QMake has a special variable ANDROID_PACKAGE_SOURCE_DIR for this job. Java sources must reside in ANDROID_PACKAGE_SOURCE_DIR/src/my/domain directories.
Also don't forget to add java source to OTHER_FILES and include androidextras QT option.

Related

Running an android service in kivy written in java - too much?

I'm trying to build an NFC card emulator in kivy (similar to https://github.com/okanatas/NFCCardEmulator).
I can't simply subclass HostApduService (i.e.)
from jnius import autoclass
HostApduService=autoclass(android.nfc.cardemulation.HostApduService)
class NfcService(HostApduService):
def processCommandApdu(self, commandApdu, extras):
# Process the incoming APDU command
# and return a response APDU
# Example: Respond with the same data as received
return self.getBytes(commandApdu)
so instead I've tried to include some java files (similar to to the ones in the github) i.e.
package nfccardemulator;
import android.nfc.cardemulation.HostApduService;
import android.os.Bundle;
public class HostCardEmulatorService extends HostApduService {
#Override
public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
...
}
}
and some other utilities files in Java.
I've put them in the services/nfccardemulator folder and added the following to buildozer:
android.add_src = services
I've checked the apk, and the classes are compiled and present. But I'm struggling to access them via autoclass
If I say
HostCardEmulatorService = autoclass("nfccardemulator.HostCardEmulatorService")
I get an error instantiating it.
I'm not sure if I'm even setting up a Service like this correctly.
I've seen an example of PythonService i.e.
PythonService = autoclass('org.kivy.android.PythonService')
and then having to use multiprocessing on the service while the kivy process runs separately.
Is what I'm trying to do even possible currently?
Thanks

PCL working with shared code (cross platform library)

I'm new with Xamarin and have to create a cross platform library (iOS, Android, Win Phone). i have so much difficult to create a pcl who works for those 3 platforms.
For example, this pcl needs establish a socket connection with a printer.
In pcl i can't use System.Net.Sockets so my idea is create a method and tried call his code with shared project .
when i execute the code, is stepped over the code inside #if __ANDROID__ .
PCL
public class Class1
{
public void socket()
{
Conecta.Class oi = new Conecta.Class();
oi.conecta();
}
}
Shared code
#if __ANDROID__
using System.Net.Sockets;
#endif
namespace Conecta
{
class Class
{
public void conecta()
{
#if __ANDROID__
System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();
clientSocket.Connect("192.168.210.171", 4002);
NetworkStream serverStream = clientSocket.GetStream();
byte[] outStream = System.Text.Encoding.ASCII.GetBytes("teste \n");
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
#endif
}
}
}
It's just a test, the code isn't sophisticated
Someone can help me?
Any other idea?
I believe PCL is not yet finished.
Even if you can resolve your printer issue in future you will have a lot of other problems.
In our project we prefer to share a code from one directory with different .csproj files.
PCL benefits really not worth powder and shot.

AIDL unable to find the definition of a Parcelable class

I have the following project structure.
My StockInfo.java is perfectly fine.
StockInfo.java (No error)
package org.yccheok.jstock.engine;
import android.os.Parcel;
import android.os.Parcelable;
public class StockInfo implements Parcelable {
...
...
StockInfo.aidl (No error)
package org.yccheok.jstock.engine;
parcelable StockInfo;
StockInfoObserver.aidl (Error!)
package org.yccheok.jstock.engine;
interface StockInfoObserver {
void update(StockInfo stockInfo);
}
AutoCompleteApi.aidl (Error!)
package org.yccheok.jstock.engine;
interface AutoCompleteApi {
void handle(String string);
void attachStockInfoObserver(StockInfoObserver stockInfoObserver);
}
However, Eclipse complains in StockInfoObserver.aidl (It does complain AutoCompleteApi.aidl too, as it cannot process StockInfoObserver.aidl),
parameter stockInfo (1) unknown type StockInfo
I tried for an hour, but still not able to find out, why in aidl, StockInfo is not being recognized although I had
Provided StockInfo.aidl
Provided StockInfo.java
Any idea?
Here are the complete errors.
Note, AutoCompleteApi.aidl is very much dependent on StockInfoObserver.aidl. That's why you will see the error.
I share the entire project for your reference purpose : https://www.dropbox.com/s/0k5pe75jolv5mtq/jstock-android.zip
According to Android documentation You must include an import statement for each additional type not listed above, even if they are defined in the same package as your interface
Try to add this line to StockInfoObserver.aidl
import org.yccheok.jstock.engine.StockInfo;

Calling native method twice of third party library in an Activity causes the Android application to close down

I have integrated two native libraries (.so ) in my application. The libraries compile fine and I can load them in my application too. The first time I invoke a native method of a library it works fine, but if I call the same method again in the Activity the application shuts down.
The problem I am facing is exactly the same as mentioned in here :
http://grokbase.com/t/gg/android-ndk/1226m68ydm/app-exit-on-second-native-call
The solution that works is to invoke the native method in another Activity and shut it down forcefully via System.exit(0). Following the article I tried setting the pointers to NULL of the called method after a successful operation, but this too didn't help me. Also its not possible to unload a library once its loaded by System.loadLibrary().
I want to call the native methods more than once without creating a new Activity. Any ideas how to solve this issue ?
(I FINALLY FOUND A SOLUTION ... HERE IT IS)
Okay, I have finally found a way to resolve this issue. The solution is actually pretty simple. Build another independent native library (utility library) to load and unload the other libraries. What we need to do is use dlopen() and dlclose() in the native method of the utility. We can load the utility library like before via System.loadLibrary().
So in the native method of the utility library what we need to do is:
Use#include <dlfcn.h> // this is required to call dlopen() and dlclose() functions.
Provide handler and function prototype:
void *handle;
typedef int (*func)(int); // define function prototype
func myFunctionName; // some name for the function
Open the library via dlopen() :
handle = dlopen("/data/data/my.package.com/lib/somelibrary.so", RTLD_LAZY);
Get and Call the function of the library:
myFunctionName = (func)dlsym(handle, "actualFunctionNameInLibrary");
myFunctionName(1); // passing parameters if needed in the call
Now that the call is done. Close it via dlclose():
dlclose(handle);
Hope this will help others facing the same issue.
So ... my solution was starting a service that runs the shared library code, this service has a different process name ( you can set it in the Android Manifest ), as it is a different process you can kill it ( Using Process.killProcess(Process.myPid()) when it finishes running, without affecting your application in any way.
Worked very well for me, hope it helps someone else.
As this is the top hit for this issue and as the issue itself still exists, it seems that the approach that ZakiMak shared with us is still the most popular solution.
For others who may want to implement it and would like a little more detail for the latest Android releases, here are some notes I made as I stumbled through this:
Firstly, there is a solution which implements this approach on GitHub now. I have not tried it personally, but I have used it as a reference. It is very useful to see how the Android.mk file is structured and how the library is opened and methods called. Link is here: https://github.com/jhotovy/android-ffmpeg
The path to the native library folder changes over Android releases and it also appears to change every time you run the app (although this may be just in debug mode). Either way, it is best to pass the path in from the calling Java method if possible. For example:
In the Java wrapping class:
import android.content.Context;
import android.util.Log;
public class FfmpegJNIWrapper {
//This class provides a Java wrapper around the exposed JNI ffmpeg functions.
static {
//Load the 'first' or 'outer' JNI library so this activity can use it
System.loadLibrary("ffmpeg_wraper_multi_invoke_jni");
}
public static int call_ffmpegWrapper(Context appContext, String[] ffmpegArgs) {
//Get the native libary path
String nativeLibPath = appContext.getApplicationInfo().nativeLibraryDir;
//Call the method in the first or 'outer' library, passing it the
//native library past as well as the original args
return ffmpegWrapper(nativeLibPath, ffmpegArgs);
}
// Native methods for ffmpeg functions
public static native int ffmpegWrapper(String nativeLibPath, String[] argv);
}
In the 'first' or 'outer' native library:
JNIEXPORT jint JNICALL Java_com_yourpackage_androidffmpegwrapper_FfmpegJNIWrapper_ffmpegWrapper(JNIEnv *pEnv, jobject pObj, jstring nativeLibPath, jobjectArray javaArgv) {
//Get the second or 'inner' native library path
char* nativePathPassedIn = (char *)(*pEnv)->GetStringUTFChars(pEnv, nativeLibPath, NULL);
char ourNativeLibraryPath[256];
snprintf(ourNativeLibraryPath, sizeof (ourNativeLibraryPath), "%s%s", nativePathPassedIn, "/libffmpeg_wraper_jni.so"); //the name of your ffmpeg library
//Open the so library
void *handle;
typedef int (*func)(JNIEnv*, jobject, jobjectArray);
handle = dlopen(ourNativeLibraryPath, RTLD_LAZY);
if (handle == NULL) {
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "could not open library: %s", dlerror());
printf("Could not dlopen(\"libbar.so\"): %s\n", dlerror());
return(-1);
}
//Call the ffmpeg wrapper functon in the second or 'inner' library
func reenterable_ffmpegWrapperFunction;
reenterable_ffmpegWrapperFunction = (func)dlsym(handle, "Java_com_yourpackage_androidffmpegwrapper_FfmpegJNIWrapper_ffmpegWrapper");
reenterable_ffmpegWrapperFunction(pEnv, pObj, javaArgv); //the original arguments
//Close the library
dlclose(handle);
// return
return(1);
}
The Android.mk file is a little 'flaky' to put it politely. Because you are building two separate libraries in one Android.mk file, this may be a little more complex that other NDK make files so if you get some strange errors do some searching before you start taking your project apart. For example: https://stackoverflow.com/a/6243727/334402

Android - JNI guidelines

I want to incorporate small, lean and mean C-based parser into my Android project. I've done JNI programming in the past but not any type of native (C) development on Android. My plan is to compile C lib into SO and create JNI wrapper around it which I'm going to use in my app. Is this how it can/should be done? Second and most important question - how can I include .so into my APK? Where would it go?
Depending on how much data you pass and how often I seriously doubt a Java/JNI/C would perform faster than a native java implementation.
Passing anything other than a "Java Int" to a "C long" invokes the JNI data conversion routines which are anything but lean and mean.
So unless your JNI routine fits the pattern:
Pass small amount of data.
Do lots and lots of work in C.
Pass small result set back.
You will be considerably slower than a native java implementation. If you stick with the basic "C" like java operations (+,-,*,==,>) using the "native" data types (int, char, String) and avoid the fancy libraries Java will perform nearly as fast as C.
The remaining bug-bear of java performance is the time taken to fire up the JVM and get everything going, but as you are starting off from a Java program this is a non issue.
The other reason for "slow" java performance is people insist on unnecessary factories, containers, xml beans etc. etc. where plain, simple methods would do the job better.
Create a JNI folder in your Android root Application folder(where there is src, res) .Place the code (1) there name it as someshared-lib.c.
(1)
Java_YourPackageName_YourClassNameWhereYoudeclareYourNativeFunction_NativeFuntionName(JNIEnv* env,jobject thiz)
{
//your c code , the JNI will act as a wrapper for it
return (*env)->NewStringUTF(env, "<string to pass or you can mention jchar * type string >");
}
(2)IN java file
package YourPackageName;
public class YourClassNameWhereYoudeclareYourNativeFunction extends Activity
{
public native String NativeFuntionName();
String returnValue = NativeFuntionName();
}
(3)IN Android.mk do this :
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := someshared-lib //note the libname same as c file name
LOCAL_SRC_FILES := someshared-lib.c //this is the file where you placed the code (1)
include $(BUILD_SHARED_LIBRARY)
export your ndk-build
(do export PATH=:$PATH
go to the JNI folder as created above :
execute ndk-build command
you will get a library formed someshared-lib in the lib folder formed in the Application root folder.While building and running the application this will get bundled up with the apk and will get installed in the device.To verify this you can go to the
/data/data/your_package_name/lib folder.
The app searched this lib in the /data/data/your_package_name/lib ( also /system/lib as well ) folder and use it for the dynamic calls(JNI) being made from the Android application.
Now if you want to return anything other than string you have to change the above method declration in c file as below :
Java_YourPackageName_YourClassNameWhereYoudeclareYourNativeFunction_NativeFuntionName(JNIEnv* env,jclass obj,jobject thiz)
{
jfieldID fid;
jboolean enable_flag;
//Pass the class object having all the variable from the android app to the JNI in the jclass obj and access its members using the field ID and using Get and Set firld ID.
clazz = (*env)->GetObjectClass(env, info);
fid = (*env)->GetFieldID(env,clazz,"some_variable","X"); //"X" - type of variable for boolean it //is Z , for INtezer it is I, for double it is D,
refer this document for detailed explaination
//for getting teh value fomr the JNI
enable_flag = (*env)->GetBooleanField(env, **thiz**, fid);
//for setting the value
fid = (*env)->GetFieldID(env,clazz,"other_float_variable","D");
(*env)->SetDoubleField(env,**thiz**,fid,other_float_variable);
}
Also in the Android Application you have to pass the Class object of the structure.
e.g
(2) will become now :
package YourPackageName;
public class YourClassNameWhereYoudeclareYourNativeFunction extends Activity
{
public native String NativeFuntionName();
String returnValue = NativeFuntionName( exampleStruct exst);
where exampleStruct :
public class exampleStruct {
protected boolean some_variable = 0;//no log saving by default
protected float other_float_variable = 0;
}
}
Hope this helps.
use Android NDK
Download n docs Android NDK 1.6
This will save you from writing JNI layer for lib and also will install the app in the lib folder of your app data folder.
Someone said that JNI in android sucks : http://www.koushikdutta.com/2009/01/jni-in-android-and-foreword-of-why-jni.html

Categories

Resources