// Author: Ben Lau (https://github.com/benlau) package androidnative; import android.app.Activity; import org.qtproject.qt5.android.QtNative; import java.lang.String; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.ArrayList; import java.util.Queue; import java.util.LinkedList; import java.lang.ClassLoader; import java.lang.reflect.Method; import java.lang.Thread; import android.util.Log; import android.os.Handler; import android.os.Looper; import android.content.Intent; import android.os.*; import java.util.concurrent.Semaphore; import java.io.StringWriter; import java.io.PrintWriter; public class SystemDispatcher { public interface Listener { /** Every messages posted on SystemMessenger will trigger this function. @return true if the message is handled. Otherwise, it should be false. */ public void onDispatched(String type , Map message); } /** @threadsafe @remarks: The function may not be running from the UI thread. It is listener's duty to handle multiple threading issue. */ public static void dispatch(String name) { dispatch(name,null); } /** Dispatch a message. @threadsafe @remarks: The function may not be running from the UI thread. It is listener's duty to handle multiple threading issue. */ public static void dispatch(String type,Map message) { try { Payload payload; mutex.acquire(); if (dispatching) { payload = new Payload(); payload.type = type; payload.message = message; queue.add(payload); mutex.release(); return; } dispatching = true; mutex.release(); emit(type,message); // Emit mutex.acquire(); // Process queued message while (queue.size() > 0 ) { payload = queue.poll(); mutex.release(); emit(payload.type,payload.message); mutex.acquire(); } dispatching = false; mutex.release(); } catch (Exception e) { Log.e(TAG,"exception",e); } } public static void addListener(Listener listener ) { try { mutex.acquire(); listeners.add(listener); mutex.release(); } catch (Exception e) { Log.e(TAG,"exception",e); } } public static void removeListener(Listener listener ) { try { mutex.acquire(); listeners.remove(listener); mutex.release(); } catch (Exception e) { Log.e(TAG,"exception",e); } } public static String ACTIVITY_RESUME_MESSAGE = "androidnative.Activity.onResume"; public static String ACTIVITY_RESULT_MESSAGE = "androidnative.Activity.onActivityResult"; public static String SYSTEM_DISPATCHER_LOAD_CLASS_MESSAGE = "androidnative.SystemDispatcher.loadClass"; public static String SYSTEM_DISPATCHER_SET_INITIALIZED_MESSAGE = "androidnative.SystemDispatcher.setInitialized"; public static boolean isIntentPending=false; public static boolean isInitialized=false; private static Map waitingIntent; /** A helper function to dispatch a massage when onResume is invoked in the Activity class */ public static void onActivityResume() { dispatch(ACTIVITY_RESUME_MESSAGE); } /** A helper function to dispatch a message based on the input argument fron Activity.onActivityResult */ public static void onActivityResult (int requestCode, int resultCode, Intent data) { Map message = new HashMap(); message.put("requestCode",requestCode); message.put("resultCode",resultCode); message.put("data",data); if(isInitialized) { dispatch(ACTIVITY_RESULT_MESSAGE,message); waitingIntent=null; isIntentPending=false; } else { //onIntent start Log.e(TAG,"notInitialized: onIntentStart "+data); message.put("text",data.getStringExtra(Intent.EXTRA_TEXT)); message.put("subject",data.getStringExtra(Intent.EXTRA_SUBJECT)); waitingIntent = message; isIntentPending = true; } //onIntent end } private static class Payload { public String type; public Map message; } private static String TAG = "AndroidNative"; private static final Semaphore mutex = new Semaphore(1); private static Queue queue = new LinkedList(); private static List listeners = new ArrayList(); private static boolean dispatching = false; private static native void jniEmit(String name,Map message); /** Emit onDispatched signal to registered listenter */ private static void emit(String name,Map message) { for (int i = 0 ; i < listeners.size() ; i++ ) { Listener listener = listeners.get(i); try { listener.onDispatched(name,message); } catch (Exception e) { Log.d(TAG, Log.getStackTraceString(e)); } } jniEmit(name,message); } private static void printMap(Map data) { if (data == null) return; try { for (Map.Entry entry : (Set) data.entrySet()) { String key = (String) entry.getKey(); Object value = entry.getValue(); if (value == null) continue; if (value instanceof String) { String stringValue = (String) value; Log.d(TAG,String.format("%s : %s",key,stringValue)); } else if (value instanceof Integer) { int intValue = (Integer) value; Log.d(TAG,String.format("%s : %d",key,intValue)); } else if (value instanceof Boolean) { Boolean booleanValue = (Boolean) value; Log.d(TAG,String.format("%s : %b",key,booleanValue)); } else { Log.d(TAG,String.format("%s : Non-supported data type[%s] is passed",key,value.getClass().getName())); } } } catch (Exception e) { Log.d(TAG,e.getMessage()); } } public static void loadClass(String className) { try { ClassLoader classLoader = SystemDispatcher.class.getClassLoader(); Class aClass = Class.forName(className,true,classLoader); } catch (ClassNotFoundException e) { Log.e(TAG,"Failed to load class: " + className); e.printStackTrace(); } } public static void setInitialized() { isInitialized = true; if(isIntentPending) { isIntentPending = false; dispatch(ACTIVITY_RESULT_MESSAGE,waitingIntent); } } public static void init() { SystemDispatcher.addListener(new SystemDispatcher.Listener() { public void onDispatched(String type , Map message) { // Log.d(TAG,String.format("%s %b",type ,type.equals(SYSTEM_DISPATCHER_LOAD_CLASS_MESSAGE))); if (type.equals(SYSTEM_DISPATCHER_LOAD_CLASS_MESSAGE)) { String className = (String) message.get("className"); loadClass(className); } if (type.equals(SYSTEM_DISPATCHER_SET_INITIALIZED_MESSAGE)) { setInitialized(); } } }); } }