v0.1
This commit is contained in:
parent
591d55f43a
commit
fb560b54b1
115 changed files with 5537 additions and 0 deletions
|
@ -0,0 +1,23 @@
|
|||
package androidnative;
|
||||
import android.content.Intent;
|
||||
|
||||
/** An alternative Activity class for Qt applicaiton.
|
||||
|
||||
Remarks: It is recommended but not a must to use this class as the main activity.
|
||||
*/
|
||||
|
||||
public class AndroidNativeActivity extends org.qtproject.qt5.android.bindings.QtActivity {
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
SystemDispatcher.onActivityResult(requestCode,resultCode,data);
|
||||
}
|
||||
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
SystemDispatcher.onActivityResume();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
package androidnative;
|
||||
import org.qtproject.qt5.android.QtNative;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import android.app.Activity;
|
||||
import java.util.Map;
|
||||
import android.net.Uri;
|
||||
import java.util.HashMap;
|
||||
import android.database.Cursor;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import android.os.Environment;
|
||||
import android.content.ClipData;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ImagePicker {
|
||||
|
||||
// Random
|
||||
public static final int PICK_IMAGE_ACTION = 0x245285a3;
|
||||
public static final int TAKE_PHOTO_ACTION = 0x29fe8748;
|
||||
|
||||
public static final String PICK_IMAGE_MESSAGE = "androidnative.ImagePicker.pickImage";
|
||||
public static final String TAKE_PHOTO_MESSAGE = "androidnative.ImagePicker.takePhoto";
|
||||
public static final String CHOSEN_MESSAGE = "androidnative.ImagePicker.chosen";
|
||||
|
||||
private static final String TAG = "androidnative.ImagePicker";
|
||||
|
||||
private static Uri mPhotoUri;
|
||||
private static Boolean broadcast = false;
|
||||
|
||||
static {
|
||||
SystemDispatcher.addListener(new SystemDispatcher.Listener() {
|
||||
public void onDispatched(String type , Map message) {
|
||||
if (type.equals(PICK_IMAGE_MESSAGE)) {
|
||||
pickImage(message);
|
||||
} else if (type.equals(TAKE_PHOTO_MESSAGE)) {
|
||||
takePhoto(message);
|
||||
} else if (type.equals(SystemDispatcher.ACTIVITY_RESULT_MESSAGE)) {
|
||||
onActivityResult(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void pickImage(Map message) {
|
||||
Boolean multiple = false;
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||
|
||||
if (message.containsKey("multiple")) {
|
||||
multiple = (Boolean) message.get("multiple");
|
||||
}
|
||||
|
||||
if (multiple) {
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
||||
}
|
||||
|
||||
// >= API 18
|
||||
activity.startActivityForResult(intent, PICK_IMAGE_ACTION);
|
||||
}
|
||||
|
||||
static void takePhoto(Map message) {
|
||||
if (message.containsKey("broadcast")) {
|
||||
broadcast = (Boolean) message.get("broadcast");
|
||||
}
|
||||
|
||||
String timeStamp = new SimpleDateFormat("yyyy-MM-dd HH.mm.ss").format(new Date());
|
||||
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
|
||||
if(!storageDir.exists() && !storageDir.mkdir())
|
||||
return;
|
||||
|
||||
File image = new File(storageDir.getAbsolutePath() + "/" + timeStamp + ".jpg");
|
||||
mPhotoUri = Uri.fromFile(image);
|
||||
|
||||
Log.d(TAG,"takePhoto : " + mPhotoUri);
|
||||
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, mPhotoUri);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
activity.startActivityForResult(intent,TAKE_PHOTO_ACTION);
|
||||
}
|
||||
|
||||
static private void onActivityResult(Map message) {
|
||||
int resultCode = (Integer) message.get("resultCode");
|
||||
if (resultCode != Activity.RESULT_OK)
|
||||
return;
|
||||
int requestCode = (Integer) message.get("requestCode");
|
||||
Intent data = (Intent) message.get("data");
|
||||
|
||||
if (requestCode == PICK_IMAGE_ACTION) {
|
||||
importImage(data);
|
||||
} else if (requestCode == TAKE_PHOTO_ACTION) {
|
||||
// Android 4.x. data will be null.
|
||||
// Android 6.x. data is not null
|
||||
importImageFromFileUri(mPhotoUri);
|
||||
if (broadcast)
|
||||
broadcastToMediaScanner(mPhotoUri);
|
||||
}
|
||||
}
|
||||
|
||||
static private void importImage(Intent data) {
|
||||
Uri uri = data.getData();
|
||||
|
||||
Log.d(TAG,"importImage: uri:" + uri);
|
||||
Log.d(TAG,"importImage: type: " + data.getType());
|
||||
|
||||
if (data.getClipData() != null) {
|
||||
importImageFromClipData(data);
|
||||
} else if (uri != null ) {
|
||||
if (uri.getScheme().equals("file")) {
|
||||
importImageFromFileUri(uri);
|
||||
} else {
|
||||
importImageFromContentUri(uri);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static private void importImageFromFileUri(Uri uri) {
|
||||
ArrayList<Uri> list = new ArrayList(1);
|
||||
list.add(uri);
|
||||
importImageFromFileUri(list);
|
||||
}
|
||||
|
||||
static private void importImageFromFileUri(List uris) {
|
||||
Map reply = new HashMap();
|
||||
ArrayList<String> list = new ArrayList(uris.size());
|
||||
for (int i = 0 ; i < uris.size() ; i++) {
|
||||
list.add(uris.get(i).toString());
|
||||
}
|
||||
reply.put("imageUrls",list);
|
||||
SystemDispatcher.dispatch(CHOSEN_MESSAGE,reply);
|
||||
}
|
||||
|
||||
|
||||
static private void importImageFromContentUri(Uri uri) {
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
|
||||
String[] columns = { MediaStore.Images.Media.DATA, MediaStore.Images.ImageColumns.ORIENTATION };
|
||||
|
||||
Cursor cursor = activity.getContentResolver().query(uri, columns, null, null, null);
|
||||
if (cursor == null) {
|
||||
Log.d(TAG,"importImageFromContentUri: Query failed");
|
||||
return;
|
||||
}
|
||||
|
||||
cursor.moveToFirst();
|
||||
ArrayList<Uri> uris = new ArrayList(cursor.getCount());
|
||||
|
||||
for (int i = 0 ; i < cursor.getCount(); i++) {
|
||||
int columnIndex;
|
||||
columnIndex = cursor.getColumnIndex(columns[0]);
|
||||
String path = cursor.getString(columnIndex);
|
||||
|
||||
/*
|
||||
columnIndex = cursor.getColumnIndex(columns[1]);
|
||||
int orientation = cursor.getInt(columnIndex);
|
||||
*/
|
||||
|
||||
if (path == null) {
|
||||
Log.d(TAG,"importImageFromContentUri: The path of image is null. The image may not on the storage.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Uri fileUri = Uri.fromParts("file",path,"");
|
||||
uris.add(fileUri);
|
||||
|
||||
Log.d(TAG,"importImageFromContentUri: " + fileUri.toString());
|
||||
}
|
||||
|
||||
importImageFromFileUri(uris);
|
||||
cursor.close();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static void importImageFromClipData(Intent data) {
|
||||
ClipData clipData = data.getClipData();
|
||||
|
||||
Log.d(TAG,"importFromClipData");
|
||||
|
||||
if (clipData.getItemCount() == 0)
|
||||
return;
|
||||
|
||||
ArrayList<Uri> uris = new ArrayList(clipData.getItemCount());
|
||||
|
||||
for (int i = 0 ; i < clipData.getItemCount() ; i++ ){
|
||||
Uri uri = clipData.getItemAt(i).getUri();
|
||||
uris.add(resolveUri(uri));
|
||||
}
|
||||
importImageFromFileUri(uris);
|
||||
}
|
||||
|
||||
static private Uri resolveUri(Uri uri) {
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
|
||||
String[] columns = { MediaStore.Images.Media.DATA, MediaStore.Images.ImageColumns.ORIENTATION};
|
||||
|
||||
Cursor cursor = activity.getContentResolver().query(uri, columns, null, null, null);
|
||||
if (cursor == null) {
|
||||
Log.d(TAG,"Query failed");
|
||||
return Uri.parse("");
|
||||
}
|
||||
|
||||
cursor.moveToFirst();
|
||||
int columnIndex;
|
||||
|
||||
columnIndex = cursor.getColumnIndex(columns[0]);
|
||||
String path = cursor.getString(columnIndex);
|
||||
|
||||
// columnIndex = cursor.getColumnIndex(columns[1]);
|
||||
// int orientation = cursor.getInt(columnIndex);
|
||||
cursor.close();
|
||||
return Uri.fromParts("file",path,"");
|
||||
}
|
||||
|
||||
private static void broadcastToMediaScanner(Uri uri) {
|
||||
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
mediaScanIntent.setData(uri);
|
||||
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
activity.sendBroadcast(mediaScanIntent);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package androidnative;
|
||||
|
||||
import android.app.Activity;
|
||||
import org.qtproject.qt5.android.QtNative;
|
||||
import java.util.Map;
|
||||
import android.media.MediaScannerConnection;
|
||||
|
||||
/**
|
||||
* Created by benlau on 19/2/2017.
|
||||
*/
|
||||
|
||||
public class MediaScanner {
|
||||
|
||||
|
||||
public static final String SCAN_FILE = "androidnative.MediaScanner.scanFile";
|
||||
|
||||
static {
|
||||
SystemDispatcher.addListener(new SystemDispatcher.Listener() {
|
||||
public void onDispatched(String type, Map message) {
|
||||
if (type.equals(SCAN_FILE)) {
|
||||
scanFile(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void scanFile(Map message) {
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
|
||||
String path = (String) message.get("path");
|
||||
|
||||
MediaScannerConnection.scanFile(activity, new String[] {path}, null, null);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
// 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 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";
|
||||
|
||||
/** 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);
|
||||
|
||||
dispatch(ACTIVITY_RESULT_MESSAGE,message);
|
||||
}
|
||||
|
||||
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<Payload> queue = new LinkedList();
|
||||
|
||||
private static List<Listener> listeners = new ArrayList<Listener>();
|
||||
|
||||
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<Map.Entry>) 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);
|
||||
// Log.d(TAG,"Class Loaded: " + className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.e(TAG,"Failed to load class: " + className);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package androidnative;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
import java.util.Map;
|
||||
|
||||
import org.qtproject.qt5.android.QtNative;
|
||||
|
||||
public class Toast {
|
||||
public static final String TOAST_MESSAGE = "androidnative.Toast.showToast";
|
||||
|
||||
private static final String TAG = "androidnative.Toast";
|
||||
|
||||
static {
|
||||
SystemDispatcher.addListener(new SystemDispatcher.Listener() {
|
||||
public void onDispatched(String type, Map message) {
|
||||
if (type.equals(TOAST_MESSAGE)) {
|
||||
showToast(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void showToast(Map message) {
|
||||
if (!message.containsKey("text")) {
|
||||
Log.d(TAG, "showToast: no text");
|
||||
return;
|
||||
}
|
||||
|
||||
int duration = android.widget.Toast.LENGTH_SHORT;
|
||||
if (message.containsKey("longLength")) {
|
||||
Boolean isLong = (Boolean)message.get("longLength");
|
||||
if (isLong != null && isLong == true)
|
||||
duration = android.widget.Toast.LENGTH_LONG;
|
||||
}
|
||||
|
||||
Activity activity = QtNative.activity();
|
||||
activity.runOnUiThread(new ToastRunnable(activity, (String)message.get("text"), duration));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package androidnative;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class ToastRunnable implements Runnable {
|
||||
private Activity activity;
|
||||
private String text;
|
||||
private int duration;
|
||||
|
||||
public ToastRunnable(Activity activity, String text, int duration) {
|
||||
this.activity = activity;
|
||||
this.text = text;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(activity, text, duration).show();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package androidnative;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import org.qtproject.qt5.android.QtNative;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class Util {
|
||||
|
||||
private static final String TAG = "androidnative.Util";
|
||||
|
||||
public static final String SET_TRANSLUCENT_STATUS_BAR = "androidnative.Util.setTranslucentStatusBar";
|
||||
public static final String SET_FULL_SCREEN = "androidnative.Util.setFullScreen";
|
||||
|
||||
|
||||
static {
|
||||
SystemDispatcher.addListener(new SystemDispatcher.Listener() {
|
||||
public void onDispatched(String type, Map message) {
|
||||
if (type.equals(SET_TRANSLUCENT_STATUS_BAR)) {
|
||||
setTranslucentStatusBar(message);
|
||||
} else if (type.equals(SET_FULL_SCREEN)) {
|
||||
setFullScreen(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void setTranslucentStatusBar(Map message) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Boolean value = (Boolean) message.get("value");
|
||||
final Activity activity = QtNative.activity();
|
||||
|
||||
Runnable runnable = new Runnable () {
|
||||
public void run() {
|
||||
Window w = activity.getWindow(); // in Activity's onCreate() for instance
|
||||
|
||||
if (value) {
|
||||
// w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
|
||||
w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
} else {
|
||||
// w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
|
||||
w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
activity.runOnUiThread(runnable);
|
||||
|
||||
}
|
||||
|
||||
static void setFullScreen(Map message) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Boolean value = (Boolean) message.get("value");
|
||||
final Activity activity = QtNative.activity();
|
||||
|
||||
Runnable runnable = new Runnable () {
|
||||
public void run() {
|
||||
Window w = activity.getWindow(); // in Activity's onCreate() for instance
|
||||
View decorView = w.getDecorView();
|
||||
|
||||
int config = decorView.getSystemUiVisibility();
|
||||
|
||||
if (value) {
|
||||
config &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
} else {
|
||||
config |= View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||
}
|
||||
decorView.setSystemUiVisibility(config);
|
||||
}
|
||||
};
|
||||
|
||||
activity.runOnUiThread(runnable);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,344 @@
|
|||
package androidnative;
|
||||
import org.qtproject.qt5.android.QtNative;
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.app.Activity;
|
||||
import java.util.Map;
|
||||
import android.net.Uri;
|
||||
import java.util.HashMap;
|
||||
import android.database.Cursor;
|
||||
import android.content.ContentResolver ;
|
||||
import android.content.ContentValues ;
|
||||
import android.provider.MediaStore;
|
||||
import android.media.ThumbnailUtils ;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.io.FileNotFoundException ;
|
||||
import java.io.IOException ;
|
||||
import java.util.Date;
|
||||
import android.os.Environment;
|
||||
import android.content.ClipData;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import android.graphics.Bitmap ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class VideoPicker {
|
||||
|
||||
// Random , generated using : $ openssl rand -hex 3
|
||||
public static final int PICK_VIDEO_ACTION = 0x7c43f0;
|
||||
public static final int TAKE_VIDEO_ACTION = 0x4df0bc;
|
||||
|
||||
|
||||
public static final String PICK_VIDEO_MESSAGE = "androidnative.VideoPicker.pickVideo";
|
||||
public static final String TAKE_VIDEO_MESSAGE = "androidnative.VideoPicker.takeVideo";
|
||||
public static final String CHOSEN_MESSAGE = "androidnative.VideoPicker.chosen";
|
||||
|
||||
private static final String TAG = "androidnative.VideoPicker";
|
||||
|
||||
private static Uri mVideoUri;
|
||||
private static Boolean broadcast = false;
|
||||
|
||||
static {
|
||||
SystemDispatcher.addListener(new SystemDispatcher.Listener() {
|
||||
public void onDispatched(String type , Map message) {
|
||||
Log.d(TAG,"onDispatched called with type :" + type );
|
||||
if (type.equals(PICK_VIDEO_MESSAGE)) {
|
||||
Log.d(TAG,"type is PICK_VIDEO_MESSAGE" );
|
||||
pickVideo(message);
|
||||
} else if (type.equals(TAKE_VIDEO_MESSAGE)) {
|
||||
Log.d(TAG,"type is TAKE_VIDEO_MESSAGE" );
|
||||
takeVideo(message);
|
||||
} else if (type.equals(SystemDispatcher.ACTIVITY_RESULT_MESSAGE)) {
|
||||
Log.d(TAG,"type is ACTIVITY_RESULT_MESSAGE" );
|
||||
onActivityResult(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void pickVideo(Map message) {
|
||||
Boolean multiple = false;
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
|
||||
intent.setType("video/*");
|
||||
|
||||
if (message.containsKey("multiple")) {
|
||||
multiple = (Boolean) message.get("multiple");
|
||||
}
|
||||
|
||||
if (multiple) {
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
||||
}
|
||||
|
||||
// >= API 18
|
||||
activity.startActivityForResult(intent, PICK_VIDEO_ACTION);
|
||||
}
|
||||
|
||||
static void takeVideo(Map message) {
|
||||
if (message.containsKey("broadcast")) {
|
||||
broadcast = (Boolean) message.get("broadcast");
|
||||
}
|
||||
|
||||
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
if (takeVideoIntent.resolveActivity(activity.getPackageManager()) != null) {
|
||||
activity.startActivityForResult(takeVideoIntent, TAKE_VIDEO_ACTION );
|
||||
}
|
||||
}
|
||||
|
||||
static private void onActivityResult(Map message) {
|
||||
|
||||
int resultCode = (Integer) message.get("resultCode");
|
||||
if (resultCode != Activity.RESULT_OK)
|
||||
return;
|
||||
int requestCode = (Integer) message.get("requestCode");
|
||||
Intent data = (Intent) message.get("data");
|
||||
|
||||
if (requestCode == PICK_VIDEO_ACTION) {
|
||||
importVideo(data);
|
||||
} else if (requestCode == TAKE_VIDEO_ACTION) {
|
||||
// Android 4.x. data will be null.
|
||||
// Android 6.x. data is not null
|
||||
|
||||
Log.d(TAG,"request code is TAKE_VIDEO_ACTION:" + TAKE_VIDEO_ACTION );
|
||||
mVideoUri = data.getData();
|
||||
Log.d(TAG, "after TAKE_VIDEO_ACTION mVideoUri is: " + mVideoUri );
|
||||
if (mVideoUri != null ) {
|
||||
importVideoFromContentUri(mVideoUri);
|
||||
}
|
||||
|
||||
if (broadcast)
|
||||
broadcastToMediaScanner(mVideoUri);
|
||||
}
|
||||
}
|
||||
|
||||
static private void importVideo(Intent data) {
|
||||
Uri uri = data.getData();
|
||||
|
||||
Log.d(TAG,"importVideo: uri:" + uri);
|
||||
Log.d(TAG,"importVideo: type: " + data.getType());
|
||||
|
||||
if (data.getClipData() != null) {
|
||||
importVideoFromClipData(data);
|
||||
} else if (uri != null ) {
|
||||
if (uri.getScheme().equals("file")) {
|
||||
importVideoFromFileUri(uri);
|
||||
} else {
|
||||
importVideoFromContentUri(uri);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static private void importVideoFromFileUri(Uri uri) {
|
||||
ArrayList<Uri> list = new ArrayList(1);
|
||||
list.add(uri);
|
||||
importVideoFromFileUri(list);
|
||||
}
|
||||
|
||||
static private void importVideoFromFileUri(List uris) {
|
||||
Map reply = new HashMap();
|
||||
ArrayList<String> list = new ArrayList(uris.size());
|
||||
ArrayList<String> thumbList = new ArrayList(uris.size());
|
||||
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
for (int i = 0 ; i < uris.size() ; i++) {
|
||||
|
||||
Uri uri = Uri.parse( Uri.decode(uris.get(i).toString()));
|
||||
String thumbPath = getVideoThumbnail(activity , uri.getPath() );
|
||||
Uri thumbUri = Uri.fromParts("file", thumbPath ,"");
|
||||
thumbList.add(thumbUri.toString());
|
||||
list.add(uris.get(i).toString());
|
||||
}
|
||||
reply.put("videoUrls",list);
|
||||
reply.put("videoThumbUrls",thumbList);
|
||||
Log.d(TAG , "dispatching to systemdispatcher with CHOSEN_MESSAGE" );
|
||||
SystemDispatcher.dispatch(CHOSEN_MESSAGE,reply);
|
||||
}
|
||||
|
||||
|
||||
static private void importVideoFromContentUri(Uri uri) {
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
|
||||
String[] columns = { MediaStore.Video.Media.DATA };
|
||||
|
||||
Cursor cursor = activity.getContentResolver().query(uri, columns, null, null, null);
|
||||
if (cursor == null) {
|
||||
Log.d(TAG,"importVideoFromContentUri: Query failed");
|
||||
return;
|
||||
}
|
||||
|
||||
cursor.moveToFirst();
|
||||
ArrayList<Uri> uris = new ArrayList(cursor.getCount());
|
||||
|
||||
for (int i = 0 ; i < cursor.getCount(); i++) {
|
||||
int columnIndex;
|
||||
columnIndex = cursor.getColumnIndex(columns[0]);
|
||||
String path = cursor.getString(columnIndex);
|
||||
|
||||
if (path == null) {
|
||||
Log.d(TAG,"importVideoFromContentUri: The path of video is null. The video may not on the storage.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Uri fileUri = Uri.fromParts("file",path,"");
|
||||
uris.add(fileUri);
|
||||
|
||||
Log.d(TAG,"importVideoFromContentUri: " + fileUri.toString());
|
||||
}
|
||||
|
||||
importVideoFromFileUri(uris);
|
||||
cursor.close();
|
||||
|
||||
}
|
||||
|
||||
public static String getVideoThumbnail(Context context, String filePath ) {
|
||||
|
||||
Log.d(TAG , "getVideoThumbnail called for filePath:" + filePath);
|
||||
// first get the VideoId from the path
|
||||
|
||||
String[] columns = {
|
||||
MediaStore.Video.Media._ID
|
||||
};
|
||||
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
|
||||
Cursor cursor = cr.query(
|
||||
android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI , columns,
|
||||
MediaStore.Video.VideoColumns.DATA + " LIKE ?", new String[] { filePath }, null);
|
||||
cursor.moveToFirst();
|
||||
|
||||
if (cursor == null) {
|
||||
Log.e(TAG,"query for videoId failed");
|
||||
return null;
|
||||
}
|
||||
cursor.moveToFirst();
|
||||
long videoId = cursor.getLong(0);
|
||||
|
||||
Log.d(TAG , "getVideoThumbnail videoId: " + videoId);
|
||||
|
||||
// use videoId to get the thumbnail path
|
||||
try {
|
||||
|
||||
String[] projection = {
|
||||
MediaStore.Video.Thumbnails.DATA,
|
||||
};
|
||||
cursor = cr.query(
|
||||
MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI,
|
||||
projection,
|
||||
MediaStore.Video.Thumbnails.VIDEO_ID + "=?",
|
||||
new String[] { String.valueOf(videoId) },
|
||||
null);
|
||||
if (cursor.getCount() > 0 ) {
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(0);
|
||||
}
|
||||
|
||||
Log.w(TAG, "Sorry no thumbnail found with video id: " + videoId);
|
||||
Log.d(TAG, "Generating new thumbnail." );
|
||||
Bitmap bMap = ThumbnailUtils.createVideoThumbnail( filePath , MediaStore.Video.Thumbnails.MICRO_KIND);
|
||||
|
||||
if (bMap != null) {
|
||||
Log.d(TAG, "storing new thumbnail." );
|
||||
storeThumbnail(cr , bMap , videoId , MediaStore.Video.Thumbnails.MICRO_KIND);
|
||||
}
|
||||
|
||||
// Query again after storing the Thumbnail in MediaStrore.Video.Thumbnails
|
||||
Log.d(TAG, "Querying again for thumbnail" );
|
||||
cursor = cr.query(
|
||||
MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI,
|
||||
projection,
|
||||
MediaStore.Video.Thumbnails.VIDEO_ID + "=?",
|
||||
new String[] { String.valueOf(videoId) },
|
||||
null);
|
||||
|
||||
if (cursor.getCount() > 0 ) {
|
||||
Log.d(TAG, "Got thumbnail in second query." );
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(0);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void importVideoFromClipData(Intent data) {
|
||||
ClipData clipData = data.getClipData();
|
||||
|
||||
Log.d(TAG,"Video importFromClipData");
|
||||
|
||||
if (clipData.getItemCount() == 0)
|
||||
return;
|
||||
|
||||
ArrayList<Uri> uris = new ArrayList(clipData.getItemCount());
|
||||
|
||||
for (int i = 0 ; i < clipData.getItemCount() ; i++ ){
|
||||
Uri uri = clipData.getItemAt(i).getUri();
|
||||
uris.add(resolveUri(uri));
|
||||
}
|
||||
importVideoFromFileUri(uris);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static private Uri resolveUri(Uri uri) {
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
|
||||
String[] columns = { MediaStore.Video.Media.DATA };
|
||||
|
||||
Cursor cursor = activity.getContentResolver().query(uri, columns, null, null, null);
|
||||
if (cursor == null) {
|
||||
Log.d(TAG,"Query failed");
|
||||
return Uri.parse("");
|
||||
}
|
||||
|
||||
cursor.moveToFirst();
|
||||
int columnIndex;
|
||||
|
||||
columnIndex = cursor.getColumnIndex(columns[0]);
|
||||
String path = cursor.getString(columnIndex);
|
||||
|
||||
cursor.close();
|
||||
return Uri.fromParts("file",path,"");
|
||||
}
|
||||
|
||||
private static void broadcastToMediaScanner(Uri uri) {
|
||||
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
mediaScanIntent.setData(uri);
|
||||
Activity activity = org.qtproject.qt5.android.QtNative.activity();
|
||||
activity.sendBroadcast(mediaScanIntent);
|
||||
}
|
||||
|
||||
private static void storeThumbnail(ContentResolver cr, Bitmap thumb, long id, int kind) {
|
||||
|
||||
ContentValues values = new ContentValues(4);
|
||||
values.put(MediaStore.Video.Thumbnails.KIND,kind);
|
||||
values.put(MediaStore.Video.Thumbnails.VIDEO_ID,(int)id );
|
||||
values.put(MediaStore.Video.Thumbnails.HEIGHT,thumb.getHeight());
|
||||
values.put(MediaStore.Video.Thumbnails.WIDTH,thumb.getWidth());
|
||||
|
||||
Uri url = cr.insert(MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI, values);
|
||||
|
||||
try {
|
||||
OutputStream thumbOut = cr.openOutputStream(url);
|
||||
thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
|
||||
thumbOut.close();
|
||||
} catch (FileNotFoundException ex) {
|
||||
Log.e("IMAGE_COMPRESSION_ERROR", "File not found");
|
||||
ex.printStackTrace();
|
||||
} catch (IOException ex) {
|
||||
Log.e("IMAGE_COMPRESSION_ERROR", "IO Exception");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue