diff --git a/frameworks/cocos2d-x/cocos/base/CCDirector.cpp b/frameworks/cocos2d-x/cocos/base/CCDirector.cpp index 7f2920f..ba58765 100644 --- a/frameworks/cocos2d-x/cocos/base/CCDirector.cpp +++ b/frameworks/cocos2d-x/cocos/base/CCDirector.cpp @@ -1437,6 +1437,21 @@ void Director::mainLoop() } else if (! _invalid) { +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) + // version: 1.55.0 + // reason: 为实现拍照功能而添加,JAVA层的拍照功能在子线程中完成,而V8不允许跨线程调用Isolate,无法直接执行回调函数,因此添加此缓存,待下一帧执行。 + // 已通过Context.runOnUIThread和主线程创建Handler尝试,无效。 + if (_cachedFunctionsNextUpdate.size()) + { + for (auto iterator : _cachedFunctionsNextUpdate) + { + std::function method = iterator; + method(); + } + _cachedFunctionsNextUpdate.clear(); + } +#endif + drawScene(); // release the objects @@ -1459,5 +1474,12 @@ void Director::setAnimationInterval(float interval) } } +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) +void Director::runInNextUpdate(std::function method) +{ + _cachedFunctionsNextUpdate.push_back(method); +} +#endif + NS_CC_END diff --git a/frameworks/cocos2d-x/cocos/base/CCDirector.h b/frameworks/cocos2d-x/cocos/base/CCDirector.h index 3c881aa..c3e6ca8 100644 --- a/frameworks/cocos2d-x/cocos/base/CCDirector.h +++ b/frameworks/cocos2d-x/cocos/base/CCDirector.h @@ -513,7 +513,10 @@ class CC_DLL Director : public Ref void setCullingEnabled (bool enable) { _isCullingEnabled = enable; } bool isCullingEnabled () const { return _isCullingEnabled; } - + +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) + void runInNextUpdate(std::function method); +#endif protected: void reset(); @@ -636,7 +639,11 @@ class CC_DLL Director : public Ref friend class GLView; bool _isCullingEnabled; - + +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) + // 为实现拍照功能而添加,JAVA层的拍照功能在子线程中完成,而V8不允许跨线程调用Isolate,无法直接执行回调函数,因此添加此缓存,待下一帧执行。 + std::list> _cachedFunctionsNextUpdate; +#endif private: /** Use std::vector to implement a quick matrix stack. diff --git a/frameworks/cocos2d-x/cocos/platform/android/libcocos2dx/android-libcocos2dx.iml b/frameworks/cocos2d-x/cocos/platform/android/libcocos2dx/android-libcocos2dx.iml new file mode 100644 index 0000000..283ada7 --- /dev/null +++ b/frameworks/cocos2d-x/cocos/platform/android/libcocos2dx/android-libcocos2dx.iml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/runtime-src/Classes/sdks/Android.mk b/frameworks/runtime-src/Classes/sdks/Android.mk index 4c48486..e728d48 100644 --- a/frameworks/runtime-src/Classes/sdks/Android.mk +++ b/frameworks/runtime-src/Classes/sdks/Android.mk @@ -19,6 +19,7 @@ LOCAL_SRC_FILES := SdkManager.cpp \ Bugly/BuglySdk.cpp \ wechat/WechatSdk.mm \ hash/HashSdk.cpp \ + utils/photos/PhotoPicker.cpp \ LOCAL_CPP_EXTENSION := .mm .cpp .cc diff --git a/frameworks/runtime-src/Classes/sdks/utils/UtilsSdk.h b/frameworks/runtime-src/Classes/sdks/utils/UtilsSdk.h index 097eec2..4f32543 100644 --- a/frameworks/runtime-src/Classes/sdks/utils/UtilsSdk.h +++ b/frameworks/runtime-src/Classes/sdks/utils/UtilsSdk.h @@ -10,7 +10,10 @@ #include "Sdk.h" -class UtilsSdk : public Sdk { +class UtilsSdk : public Sdk +{ +private: + std::map _callbacks; public: UtilsSdk() : Sdk("UtilsSdk") {} @@ -22,6 +25,7 @@ class UtilsSdk : public Sdk { // memory unsigned long long getTotalMemorySize(); unsigned long long getAvailableMemorySize(); + unsigned long long getIOSProcessMemoryUsage(); // file system storage unsigned long long getFileSystemTotalSize(); @@ -30,17 +34,26 @@ class UtilsSdk : public Sdk { static std::string getUUID(); static int getSignatureCode(); - enum NetworkType { - NO_NETWORK = 0, - WIFI, - MOBILE, + enum NetworkType + { + NO_NETWORK = 0, + WIFI, + MOBILE, }; static NetworkType getNetworkType(); -#ifdef SDK_BUGLY - static void setBuglyUserID(const std::string& id); - static void setBuglyUserData(const std::string& params); -#endif + #ifdef SDK_BUGLY + static void setBuglyUserID(const std::string &id); + static void setBuglyUserData(const std::string ¶ms); + #endif + + std::string getTimestamp(); + + void takeOrPickPhoto(const std::string& method, const std::string& path, const SdkCallback &callback); + + // 将结果同步至主线程中进行处理 + void invokeCallbackOnMainThread(const std::string key, const std::string argument); + void invokeCallback(const std::string &key, const std::string &argument); }; #endif /* UtilsSdk_hpp */ diff --git a/frameworks/runtime-src/Classes/sdks/utils/UtilsSdk.mm b/frameworks/runtime-src/Classes/sdks/utils/UtilsSdk.mm index 7936c71..8315b1e 100644 --- a/frameworks/runtime-src/Classes/sdks/utils/UtilsSdk.mm +++ b/frameworks/runtime-src/Classes/sdks/utils/UtilsSdk.mm @@ -12,11 +12,15 @@ #import #import #import "Reachability.h" +#import +#import +#import #endif #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #include #include "platform/android/jni/JniHelper.h" +#include #define CLASS_NAME "org/cocos2dx/javascript/UtilsSdk" #endif @@ -24,6 +28,8 @@ #include "Bugly/CocosPlugin/bugly/CrashReport.h" #endif +#include "utils/photos/PhotoPicker.h" + USING_NS_CC; static std::string size2string(size_t size){ @@ -81,6 +87,10 @@ NetworkType type = getNetworkType(); callback(int32tostring(type)); } + else if(method == "getIOSProcessMemoryUsage") { + unsigned long long size = getIOSProcessMemoryUsage(); + callback(uint64tostring(size)); + } #ifdef SDK_BUGLY else if(method == "setBuglyUserID") { setBuglyUserID(params); @@ -91,6 +101,9 @@ callback(""); } #endif + else if ("takePhoto" == method || "pickPhoto" == method || "takeOrPickPhoto" == method){ + this->takeOrPickPhoto(method, params, callback); + } else { callback(""); } @@ -145,6 +158,24 @@ return 0; } +unsigned long long UtilsSdk::getIOSProcessMemoryUsage() { +#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) + task_vm_info info; + mach_msg_type_number_t count = TASK_VM_INFO_COUNT; + int r = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&info, &count); + if (r == KERN_SUCCESS) + { + NSLog(@"Memory: %ldd", (unsigned long) info.phys_footprint); + return info.phys_footprint; + } + else + { + return -1; + } +#endif + return 0; +} + unsigned long long UtilsSdk::getFileSystemTotalSize() { #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) @@ -364,3 +395,88 @@ } } #endif + +std::string UtilsSdk::getTimestamp() +{ + char buffer[128] = {}; +#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) + sprintf(buffer, "%lld", (long long)([[NSDate date] timeIntervalSince1970] * 1000)); +#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) + time_t current; + time(¤t); + sprintf(buffer, "%lld", (long long)current); +#endif + return buffer; +} + +void UtilsSdk::takeOrPickPhoto(const std::string& method, const std::string& path, const SdkCallback &callback) +{ + std::string timestamp = this->getTimestamp(); + std::string key = std::string(timestamp) + ":" + method; + auto finder = _callbacks.find(key); + if (_callbacks.end() != finder) + { + callback(""); + return; + } + _callbacks[key] = callback; + + FileUtils::getInstance()->createDirectory(path); + +#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) + PhotoPicker* picker = [[PhotoPicker alloc] initWithKey:[NSString stringWithUTF8String:key.c_str()] :this]; + if ("takeOrPickPhoto" == method) + { + [picker takeOrPickPhoto:[NSString stringWithUTF8String:(path + "/" + timestamp + ".png").c_str()]]; + } + else if ("takePhoto" == method) + { + [picker takePhoto:[NSString stringWithUTF8String:(path + "/" + timestamp + ".png").c_str()]]; + } + else if ("pickPhoto" == method) + { + [picker pickPhoto:[NSString stringWithUTF8String:(path + "/" + timestamp + ".png").c_str()]]; + } +#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) + PhotoPicker* picker = new PhotoPicker(key, this); + if ("takeOrPickPhoto" == method) + { + picker->takeOrPickPhoto(path + "/" + timestamp + ".png"); + } + else if ("takePhoto" == method) + { + picker->takePhoto(path + "/" + timestamp + ".png"); + } + else if ("pickPhoto" == method) + { + picker->pickPhoto(path + "/" + timestamp + ".png"); + } +#endif +} + +void UtilsSdk::invokeCallbackOnMainThread(const std::string key, const std::string argument) +{ +#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) + dispatch_async(dispatch_get_main_queue(), ^{ + this->invoke(key, argument); + }); +#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) + // 安卓系统中似乎没有原生的方式进行任务的线程间同步,资料显示可以通过libuv中uv_async_send实现,因需要添加第三方库,故未尝试。 + Director::getInstance()->runInNextUpdate( + [=] + { + this->invokeCallback(key, argument); + }); +#endif +} + +void UtilsSdk::invokeCallback(const std::string &key, const std::string &argument) +{ + auto finder = _callbacks.find(key); + if (_callbacks.end() != finder) + { + SdkCallback& callback = finder->second; + callback(argument); + _callbacks.erase(finder); + } +} diff --git a/frameworks/runtime-src/Classes/sdks/utils/photos/PhotoPicker.cpp b/frameworks/runtime-src/Classes/sdks/utils/photos/PhotoPicker.cpp new file mode 100644 index 0000000..66ee658 --- /dev/null +++ b/frameworks/runtime-src/Classes/sdks/utils/photos/PhotoPicker.cpp @@ -0,0 +1,61 @@ +// +// PhotoPicker.m +// Smart_Pi-mobile +// +// Created by 朱嘉灵 on 2020/1/14. +// + +#include "PhotoPicker.h" +#include "../UtilsSdk.h" +#include +#include "platform/android/jni/JniHelper.h" +using namespace cocos2d; + +extern "C" +{ + void Java_com_congmingpai_mobile_PhotoPicker_response(JNIEnv *env, jobject thiz, long long handle, jstring jFilename) + { + cocos2d::log("Java_org_cocos2dx_javascript_PhotoPicker_response"); + PhotoPicker* picker = reinterpret_cast(handle); + const char* filename = env->GetStringUTFChars(jFilename, JNI_FALSE); + picker->response(reinterpret_cast(filename)); + } +} + +void PhotoPicker::takeOrPickPhoto(const std::string &filename) +{ + _filename = filename; + this->callActivity("takeOrPickPhoto"); +} + +void PhotoPicker::takePhoto(const std::string &filename) +{ + _filename = filename; + this->callActivity("takePhoto"); +} + +void PhotoPicker::pickPhoto(const std::string &filename) +{ + _filename = filename; + this->callActivity("pickPhoto"); +} + +void PhotoPicker::callActivity(const std::string &method) +{ + JniMethodInfo minfo; + if (JniHelper::getStaticMethodInfo(minfo, "com/congmingpai/mobile/PhotoPicker", "takeOrPickPhoto", "(JLjava/lang/String;Ljava/lang/String;)V")) { + jlong instance = reinterpret_cast(this); + JNIEnv* env = JniHelper::getEnv(); + jstring jMethod = env->NewStringUTF(method.c_str()); + jstring jFilename = env->NewStringUTF(_filename.c_str()); + minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID, instance, jMethod, jFilename); + env->DeleteLocalRef(jFilename); + env->DeleteLocalRef(jMethod); + } +} + +void PhotoPicker::response(const std::string &filename) +{ + UtilsSdk* utils = reinterpret_cast(_owner); + utils->invokeCallbackOnMainThread(_key, filename); +} diff --git a/frameworks/runtime-src/Classes/sdks/utils/photos/PhotoPicker.h b/frameworks/runtime-src/Classes/sdks/utils/photos/PhotoPicker.h new file mode 100644 index 0000000..84bdc88 --- /dev/null +++ b/frameworks/runtime-src/Classes/sdks/utils/photos/PhotoPicker.h @@ -0,0 +1,59 @@ +// +// PhotoPicker.h +// Smart_Pi +// +// 暂时不支持编辑功能。 +// +// Created by 朱嘉灵 on 2020/1/14. +// + +#ifndef PhotoPicker_h +#define PhotoPicker_h + +#include "cocos2d.h" + +#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) + +#import +#import + +@interface PhotoPicker : NSObject +{ + UIViewController* _viewController; + + NSString* _key; + NSString* _filename; + void* _owner; +} + +-(id) initWithKey:(NSString*)key :(void*)owner; +-(void) takeOrPickPhoto:(NSString*)filename; +-(void) takePhoto:(NSString*)filename; +-(void) pickPhoto:(NSString*)filename; + +@end + +#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) + +#include + +class PhotoPicker +{ +private: + std::string _key; + std::string _filename; + void* _owner; +public: + PhotoPicker(const std::string& key, void* owner) : _key(key), _owner(owner) { } + ~PhotoPicker() { } + + void takeOrPickPhoto(const std::string& filename); + void takePhoto(const std::string& filename); + void pickPhoto(const std::string& filename); + + void callActivity(const std::string& method); + void response(const std::string& filename); +}; +#endif + +#endif /* PhotoPicker_h */ diff --git a/frameworks/runtime-src/Classes/sdks/utils/photos/PhotoPicker.mm b/frameworks/runtime-src/Classes/sdks/utils/photos/PhotoPicker.mm new file mode 100644 index 0000000..63730d1 --- /dev/null +++ b/frameworks/runtime-src/Classes/sdks/utils/photos/PhotoPicker.mm @@ -0,0 +1,205 @@ +// +// PhotoPicker.mm +// Smart_Pi-mobile +// +// Created by 朱嘉灵 on 2020/1/14. +// + +#import "PhotoPicker.h" +#include "../UtilsSdk.h" +#pragma mark - 01.使用相机相册要导入头文件的 +#import +#import +#import + +@interface PhotoPicker () +#pragma mark - 02.拖线一个imageView控件用来展示选中的图片 & 创建一个弹框; +@property (nonatomic, strong) UIImagePickerController *imagePickerController; +@end + +@implementation PhotoPicker +#pragma mark - 03.懒加载初始化弹框; +- (UIImagePickerController *)imagePickerController { + if (_imagePickerController == nil) { + _imagePickerController = [[UIImagePickerController alloc] init]; + _imagePickerController.delegate = self; //delegate遵循了两个代理 + _imagePickerController.allowsEditing = NO; + } + return _imagePickerController; +} + +- (id)initWithKey:(NSString*)key :(void*)owner { + self = [super init]; + + _key = [NSString stringWithString:key]; + [_key retain]; + _owner = owner; + + UIWindow *window = [[UIApplication sharedApplication].delegate window]; + _viewController = [window rootViewController]; + + return self; +} + +-(void) dealloc { + if (_key){ + [_key release]; + } + if (_filename){ + [_filename release]; + } + [super dealloc]; +} + +#pragma mark - 05.在租来的触发方法里添加事件; +- (void)takeOrPickPhoto:(NSString*)filename { + _filename = [NSString stringWithString:filename]; + [_filename retain]; + //MARK: - 06.点击图片调起弹窗并检查权限; + UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + + UIAlertAction *camera = [UIAlertAction actionWithTitle:@"使用相机拍摄" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self checkCameraPermission];//调用检查相机权限方法 + }]; + UIAlertAction *album = [UIAlertAction actionWithTitle:@"从相册中选择" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self checkAlbumPermission];//调起检查相册权限方法 + }]; + UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { + [_viewController dismissViewControllerAnimated:YES completion:nil]; + [self response:@""]; + }]; + + [alert addAction:camera]; + [alert addAction:album]; + [alert addAction:cancel]; + + [_viewController presentViewController:alert animated:YES completion:nil]; +} + +- (void)takePhoto:(NSString *)filename +{ + _filename = [NSString stringWithString:filename]; + [_filename retain]; + + [self checkCameraPermission]; +} + +#pragma mark - Camera(检查相机权限方法) +- (void)checkCameraPermission { + AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; + if (status == AVAuthorizationStatusNotDetermined) { + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { + if (granted) { + [self callCamera]; + } + }]; + } else if (status == AVAuthorizationStatusDenied || status == AVAuthorizationStatusRestricted) { +// [self alertAlbum];//如果没有权限给出提示 + [self response:@""]; + } else { + [self callCamera];//有权限进入调起相机方法 + } +} + +- (void)callCamera { +#pragma mark - 07.判断相机是否可用,如果可用调起 + if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { + self.imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; + [_viewController presentViewController:self.imagePickerController animated:YES completion:^{}]; + } else {//不可用只能GG了 + NSLog(@"木有相机"); + [self response:@""]; + } +} + +- (void)pickPhoto:(NSString *)filename +{ + _filename = [NSString stringWithString:filename]; + [_filename retain]; + + [self checkAlbumPermission]; +} + +#pragma mark - Album(相册流程与相机流程相同,相册是不存在硬件问题的,只要有权限就可以直接调用) +- (void)checkAlbumPermission { + PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; + if (status == PHAuthorizationStatusNotDetermined) { + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (status == PHAuthorizationStatusAuthorized) { + [self selectAlbum]; + } + }); + }]; + } else if (status == PHAuthorizationStatusDenied || status == PHAuthorizationStatusRestricted) { + [self alertAlbum]; + } else { + [self selectAlbum]; + } +} + +- (void)selectAlbum { + //判断相册是否可用 + if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) { + self.imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; + [_viewController presentViewController:self.imagePickerController animated:YES completion:^{}]; + } +} + +- (void)alertAlbum { + UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:@"请在设置中打开相册" preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { + [_viewController dismissViewControllerAnimated:YES completion:nil]; + [self response:@""]; + }]; + [alert addAction:cancel]; + [_viewController presentViewController:alert animated:YES completion:nil]; +} + +//- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_NA,__MAC_NA,__IPHONE_2_0,__IPHONE_3_0) +//{ +// [picker dismissViewControllerAnimated:YES completion:nil]; +// image = [editingInfo valueForKey:UIImagePickerControllerEditedImage]; +// BOOL result = [UIImageJPEGRepresentation(image, 1) writeToFile:_filename atomically:YES]; +// if (result) +// { +// [self response:_filename]; +// } +// else +// { +// [self response:@""]; +// } +// [self autorelease]; +//} + +- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { + [picker dismissViewControllerAnimated:YES completion:nil]; + UIImage *image = [info valueForKey:UIImagePickerControllerOriginalImage]; + BOOL result = [UIImageJPEGRepresentation(image, 1) writeToFile:_filename atomically:YES]; + if (result) + { + [self response:_filename]; + } + else + { + [self response:@""]; + } + [self autorelease]; +} + +- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker +{ + [picker dismissViewControllerAnimated:YES completion:nil]; + [self response:@""]; +} + +- (void)response:(NSString*)filename +{ + if (_owner) + { + UtilsSdk* utils = reinterpret_cast(_owner); + utils->callbackToMainThread(_key.UTF8String, filename.UTF8String); + } +} + +@end diff --git a/frameworks/runtime-src/proj.android-studio/app/AndroidManifest.xml b/frameworks/runtime-src/proj.android-studio/app/AndroidManifest.xml index 3c8e134..8b81db8 100755 --- a/frameworks/runtime-src/proj.android-studio/app/AndroidManifest.xml +++ b/frameworks/runtime-src/proj.android-studio/app/AndroidManifest.xml @@ -2,6 +2,7 @@ @@ -16,6 +17,7 @@ + @@ -35,7 +37,18 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/runtime-src/proj.android-studio/app/build.gradle b/frameworks/runtime-src/proj.android-studio/app/build.gradle index 8254143..3397987 100755 --- a/frameworks/runtime-src/proj.android-studio/app/build.gradle +++ b/frameworks/runtime-src/proj.android-studio/app/build.gradle @@ -3,12 +3,12 @@ import org.apache.tools.ant.taskdefs.condition.Os apply plugin: 'com.android.application' android { - compileSdkVersion 22 + compileSdkVersion 23 buildToolsVersion '26.0.2' defaultConfig { // applicationId "com.congmingpai.mobile" - minSdkVersion 22 + minSdkVersion 23 targetSdkVersion PROP_TARGET_SDK_VERSION // versionCode 1 // versionName "1.0" @@ -151,4 +151,6 @@ dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':libcocos2dx') implementation 'com.tencent.bugly:nativecrashreport:latest.release' + implementation 'com.android.support:support-compat:26.0.0' // 版本与buildToolsVersion一致,为了通过ActivityCompat解决动态申请权限问题,否则无法调用相机 + implementation 'com.android.support:support-v4:26.0.0' // 版本与buildToolsVersion一致,添加对FileProvider的支持,7.0以上的系统将file://限制为应用内部uri,需要通过FileProvider传递给外部应用 } diff --git a/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_background.png b/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_background.png new file mode 100644 index 0000000..3798d09 Binary files /dev/null and b/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_background.png differ diff --git a/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_button_cancel.png b/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_button_cancel.png new file mode 100644 index 0000000..b68c28d Binary files /dev/null and b/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_button_cancel.png differ diff --git a/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_button_capture.png b/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_button_capture.png new file mode 100644 index 0000000..1e5ff50 Binary files /dev/null and b/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_button_capture.png differ diff --git a/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_button_check.png b/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_button_check.png new file mode 100644 index 0000000..aa7ecfb Binary files /dev/null and b/frameworks/runtime-src/proj.android-studio/app/res/drawable/photo_button_check.png differ diff --git a/frameworks/runtime-src/proj.android-studio/app/res/values-hdpi/dimens.xml b/frameworks/runtime-src/proj.android-studio/app/res/values-hdpi/dimens.xml new file mode 100644 index 0000000..c849db0 --- /dev/null +++ b/frameworks/runtime-src/proj.android-studio/app/res/values-hdpi/dimens.xml @@ -0,0 +1,11 @@ + + + + 50dp + 25dp + 27.25dp + 36.5dp + 38dp + 320dp + 180dp + diff --git a/frameworks/runtime-src/proj.android-studio/app/res/values-mdpi/dimens.xml b/frameworks/runtime-src/proj.android-studio/app/res/values-mdpi/dimens.xml new file mode 100644 index 0000000..22703c6 --- /dev/null +++ b/frameworks/runtime-src/proj.android-studio/app/res/values-mdpi/dimens.xml @@ -0,0 +1,11 @@ + + + + 33.33dp + 16.67dp + 18.17dp + 24.33dp + 25.33dp + 213.33dp + 120dp + diff --git a/frameworks/runtime-src/proj.android-studio/app/res/values-xhdpi/dimens.xml b/frameworks/runtime-src/proj.android-studio/app/res/values-xhdpi/dimens.xml new file mode 100644 index 0000000..fddf9f9 --- /dev/null +++ b/frameworks/runtime-src/proj.android-studio/app/res/values-xhdpi/dimens.xml @@ -0,0 +1,11 @@ + + + + 66.67dp + 33.33dp + 36.33dp + 48.67dp + 50.67dp + 426.67dp + 240dp + diff --git a/frameworks/runtime-src/proj.android-studio/app/res/values-xxhdpi/dimens.xml b/frameworks/runtime-src/proj.android-studio/app/res/values-xxhdpi/dimens.xml new file mode 100644 index 0000000..5c04244 --- /dev/null +++ b/frameworks/runtime-src/proj.android-studio/app/res/values-xxhdpi/dimens.xml @@ -0,0 +1,11 @@ + + + + 100dp + 50dp + 54.5dp + 73dp + 76dp + 640dp + 360dp + diff --git a/frameworks/runtime-src/proj.android-studio/app/res/values/strings.xml b/frameworks/runtime-src/proj.android-studio/app/res/values/strings.xml index 222ab42..63015e9 100755 --- a/frameworks/runtime-src/proj.android-studio/app/res/values/strings.xml +++ b/frameworks/runtime-src/proj.android-studio/app/res/values/strings.xml @@ -1,3 +1,4 @@ - 聪明派 + STABLE + com.congmingpai.mobile.fileprovider diff --git a/frameworks/runtime-src/proj.android-studio/app/res/xml/file_paths.xml b/frameworks/runtime-src/proj.android-studio/app/res/xml/file_paths.xml new file mode 100644 index 0000000..f3ca085 --- /dev/null +++ b/frameworks/runtime-src/proj.android-studio/app/res/xml/file_paths.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/frameworks/runtime-src/proj.android-studio/app/src/main/java/com/congmingpai/mobile/Camera.java b/frameworks/runtime-src/proj.android-studio/app/src/main/java/com/congmingpai/mobile/Camera.java new file mode 100644 index 0000000..28ef860 --- /dev/null +++ b/frameworks/runtime-src/proj.android-studio/app/src/main/java/com/congmingpai/mobile/Camera.java @@ -0,0 +1,221 @@ +package com.congmingpai.mobile; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.ImageFormat; +import android.graphics.Rect; +import android.hardware.camera2.*; +import android.media.Image; +import android.media.ImageReader; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +public class Camera extends CameraDevice.StateCallback { + static public abstract class Callback { + public abstract void response(@Nullable Bitmap bitmap); + } + + private CameraManager mCameraManager = null; + private CameraDevice mCameraDevice = null; + + private SurfaceHolder mTarget = null; + private Context mOwner = null; + + private Callback mCallback = null; + + private CameraCaptureSession mCaptureSession = null; + private ImageReader mImageReader = null; + + private Bitmap mResult = null; + public Bitmap getResult() { return mResult; } + +// private boolean mCaptured = false; + + private Camera mSelf = null; + + public Camera(@NonNull Context owner, @NonNull SurfaceHolder target, @NonNull Callback callback){ + mSelf = this; + mOwner = owner; + mTarget = target; + mCallback = callback; + } + + @SuppressLint("MissingPermission") + public boolean open(){ + Log.d("Camera", "try to open camera"); + if (null != mCameraDevice){ + this.captureToRender(); + return true; + } + +// CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(camera.getId()); +// StreamConfigurationMap configurationMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); +// Size[] supportedSizes = configurationMap.getOutputSizes(ImageFormat.JPEG); +// mTarget.setFixedSize(supportedSizes[0].getWidth(), supportedSizes[0].getHeight()); + mTarget.setFixedSize(1280, 720); + + CameraManager manager = mCameraManager = mOwner.getSystemService(CameraManager.class); + try { + // 使用前置摄像头 + String targetId = ""; + String[] deviceIds = manager.getCameraIdList(); + for (int i = 0; i < deviceIds.length; ++i) { + CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(deviceIds[i]); + if (CameraCharacteristics.LENS_FACING_FRONT == characteristics.get(CameraCharacteristics.LENS_FACING)) { + targetId = deviceIds[i]; + break; + } + } + manager.openCamera("" == targetId ? deviceIds[0] : targetId, this, null); + return true; + } catch (CameraAccessException e) { + e.printStackTrace(); + mSelf.response(null); + } + return false; + } + + public boolean capture(){ + if (null == mCaptureSession){ + return false; + } + try { + mCaptureSession.stopRepeating(); + + CaptureRequest.Builder requestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + requestBuilder.addTarget(mImageReader.getSurface()); + mCaptureSession.capture(requestBuilder.build(), mCaptureCallback, null); + } catch (CameraAccessException e) { + e.printStackTrace(); + mSelf.response(null); + return false; + } + return true; + } + + private CameraCaptureSession.StateCallback mStateCallback = new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(@NonNull CameraCaptureSession session) { + try { + CaptureRequest.Builder requestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + requestBuilder.addTarget(mTarget.getSurface()); + session.setRepeatingRequest(requestBuilder.build(), mCaptureCallback, null); + mCaptureSession = session; + } catch (CameraAccessException e) { + e.printStackTrace(); + mSelf.response(null); + } + } + + @Override + public void onConfigureFailed(@NonNull CameraCaptureSession session) { + mSelf.response(null); + } + }; + + private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { + @Override + public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { + super.onCaptureCompleted(session, request, result); +// mCaptured = true; + } + + @Override + public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) { + super.onCaptureFailed(session, request, failure); + mSelf.response(null); + } + }; + + private ImageReader.OnImageAvailableListener mImageAvailableListener = new ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(ImageReader reader) { + Bitmap bitmap = null; + try { + Image image = reader.acquireNextImage(); + + Image.Plane[] planes = image.getPlanes(); + ByteBuffer byteBuffer = planes[0].getBuffer(); + byte[] buffer = new byte[byteBuffer.limit()]; + byteBuffer.get(buffer); + bitmap = Bitmap.createBitmap(BitmapFactory.decodeByteArray(buffer, 0, buffer.length)); + bitmap = Bitmap.createScaledBitmap(bitmap, -bitmap.getWidth(), bitmap.getHeight(), false); + + image.close(); + }catch (Exception e){ + e.printStackTrace(); + } + mResult = bitmap; + mSelf.response(bitmap); + } + }; + + private void captureToRender() { + if (null == mCameraDevice){ + return; + } + + mResult = null; + + try { + Log.d("Camera", "try to create capture session"); + List surfaces = new ArrayList(); + Surface surface = mTarget.getSurface(); + surfaces.add(surface); + + Rect rect = mTarget.getSurfaceFrame(); + ImageReader imageReader = mImageReader = ImageReader.newInstance(rect.width(), rect.height(), ImageFormat.JPEG, 1); + imageReader.setOnImageAvailableListener(mImageAvailableListener, null); + surfaces.add(imageReader.getSurface()); + + mCameraDevice.createCaptureSession(surfaces, mStateCallback, null); + } catch (CameraAccessException e) { + e.printStackTrace(); + mSelf.response(null); + } + } + + @Override + public void onOpened(@NonNull CameraDevice camera) { + Log.d("Camera", "camera open"); + mCameraDevice = camera; + this.captureToRender(); + } + + @Override + public void onDisconnected(@NonNull CameraDevice camera) { + // 以下三种情况会触发此事件: + // 1. 启动相机之后,其他应用占用相机 + // 2. 启动相机之后,再次启动相机 + // 3. 主动释放相机 +// if (!mCaptured) { +// mSelf.response(null); +// } + } + + @Override + public void onError(@NonNull CameraDevice camera, int error) { + Log.e("General custom camera", String.format("CameraDevice error : %d", error)); + mSelf.response(null); + } + + private void response(@Nullable Bitmap bitmap) { + mCallback.response(bitmap); + } + + public void Release() { + if (null != mCameraDevice) { + mCameraDevice.close(); + mCameraDevice = null; + } + } +} diff --git a/frameworks/runtime-src/proj.android-studio/app/src/main/java/com/congmingpai/mobile/PathUtils.java b/frameworks/runtime-src/proj.android-studio/app/src/main/java/com/congmingpai/mobile/PathUtils.java new file mode 100644 index 0000000..9ec2f38 --- /dev/null +++ b/frameworks/runtime-src/proj.android-studio/app/src/main/java/com/congmingpai/mobile/PathUtils.java @@ -0,0 +1,113 @@ +package com.congmingpai.mobile; + + +import android.annotation.SuppressLint; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.provider.DocumentsContract; +import android.provider.MediaStore; + +public class PathUtils { + /** + * 根据Uri获取图片的绝对路径 + * + * @param context 上下文对象 + * @param uri 图片的Uri + * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null + */ + public static String getRealPathFromUri(Context context, Uri uri) { + int sdkVersion = Build.VERSION.SDK_INT; + if (sdkVersion >= 19) { + return getRealPathFromUriAboveApi19(context, uri); + } else { + return getRealPathFromUriBelowAPI19(context, uri); + } + } + + /** + * 适配api19以下(不包括api19),根据uri获取图片的绝对路径 + * + * @param context 上下文对象 + * @param uri 图片的Uri + * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null + */ + private static String getRealPathFromUriBelowAPI19(Context context, Uri uri) { + return getDataColumn(context, uri, null, null); + } + + /** + * 适配api19及以上,根据uri获取图片的绝对路径 + * + * @param context 上下文对象 + * @param uri 图片的Uri + * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null + */ + @SuppressLint("NewApi") + private static String getRealPathFromUriAboveApi19(Context context, Uri uri) { + String filePath = null; + if (DocumentsContract.isDocumentUri(context, uri)) { + // 如果是document类型的 uri, 则通过document id来进行处理 + String documentId = DocumentsContract.getDocumentId(uri); + if (isMediaDocument(uri)) { + // 使用':'分割 + String id = documentId.split(":")[1]; + + String selection = MediaStore.Images.Media._ID + "=?"; + String[] selectionArgs = {id}; + filePath = getDataColumn(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs); + } else if (isDownloadsDocument(uri)) { + Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(documentId)); + filePath = getDataColumn(context, contentUri, null, null); + } + } else if ("content".equalsIgnoreCase(uri.getScheme())) { + // 如果是 content 类型的 Uri + filePath = getDataColumn(context, uri, null, null); + } else if ("file".equals(uri.getScheme())) { + // 如果是 file 类型的 Uri,直接获取图片对应的路径 + filePath = uri.getPath(); + } + return filePath; + } + + /** + * 获取数据库表中的 _data 列,即返回Uri对应的文件路径 + * + */ + private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { + String path = null; + + String[] projection = new String[]{MediaStore.Images.Media.DATA}; + Cursor cursor = null; + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); + if (cursor != null && cursor.moveToFirst()) { + int columnIndex = cursor.getColumnIndexOrThrow(projection[0]); + path = cursor.getString(columnIndex); + } + } catch (Exception e) { + if (cursor != null) { + cursor.close(); + } + } + return path; + } + + /** + * @param uri the Uri to check + * @return Whether the Uri authority is MediaProvider + */ + private static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + /** + * @param uri the Uri to check + * @return Whether the Uri authority is DownloadsProvider + */ + private static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } +} diff --git a/frameworks/runtime-src/proj.android-studio/app/src/main/java/com/congmingpai/mobile/PhotoPicker.java b/frameworks/runtime-src/proj.android-studio/app/src/main/java/com/congmingpai/mobile/PhotoPicker.java new file mode 100644 index 0000000..846394f --- /dev/null +++ b/frameworks/runtime-src/proj.android-studio/app/src/main/java/com/congmingpai/mobile/PhotoPicker.java @@ -0,0 +1,295 @@ +package com.congmingpai.mobile; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.net.Uri; +import android.os.Bundle; +import android.os.StrictMode; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.WindowManager; +import android.widget.Button; + +import com.congmingpai.mobile.stable.R; + +import org.cocos2dx.lib.Cocos2dxHelper; + +import java.io.FileInputStream; +import java.io.FileOutputStream; + +public class PhotoPicker extends Activity implements View.OnClickListener { + static final String KEY_METHOD = "method"; + static final String KEY_INSTANCE = "instance"; + static final String KEY_FILENAME = "filename"; + + static final String METHOD_TAKE = "takePhoto"; + static final String METHOD_PICK = "pickPhoto"; + + static final int PERMISSION_TAKE = 1; + static final int PERMISSION_PICK = 2; + + static final int REQUEST_TAKE_PHOTO = 1; + static final int REQUEST_PICK_PHOTO = 2; + + private long mInstance = 0; + private String mFilename = ""; + + private Button mTakeButton = null; + private Button mCheckButton = null; + private Button mCancelButton = null; + private SurfaceView mSurfaceView = null; + + private Camera mCamera = null; + private PhotoPicker mSelf = null; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mSelf = this; + + Intent intent = this.getIntent(); + mInstance = intent.getLongExtra(KEY_INSTANCE, 0); + mFilename = intent.getStringExtra(KEY_FILENAME); + String method = intent.getStringExtra(KEY_METHOD); + switch (method) { + case "takeOrPickPhoto": + break; + case METHOD_TAKE: + if (this.checkCameraPermission()) { + this.takePhoto(); + } + break; + case METHOD_PICK: + // 解决7.0 "exposed beyond app through ClipData.Item.getUri"的错误 + StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); + StrictMode.setVmPolicy(builder.build()); + builder.detectFileUriExposure(); + + if (this.checkAlbumPermission()){ + this.pickPhoto(); + } + break; + } + } + + private void initializeContent() { + Log.d("PhotoPicker", "initializeContent"); + Point screenSize = new Point(); + this.getWindowManager().getDefaultDisplay().getSize(screenSize); + + WindowManager.LayoutParams windowParams = this.getWindow().getAttributes(); + // 锁定宽高比,与游戏内一致 + windowParams.height = screenSize.y; + windowParams.width = (int)(windowParams.height * 1280.0 / 720); + // 隐藏下方导航栏,全屏显示 + windowParams.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE; // SYSTEM_UI_FLAG_IMMERSIVE为沉浸式体验,点击也不显示导航栏 + + this.setContentView(R.layout.activity_photopicker); + + mTakeButton = (Button)this.findViewById(R.id.photo_button_take_photo); + mTakeButton.setOnClickListener(this); + mTakeButton.setVisibility(View.INVISIBLE); + mCheckButton = (Button)this.findViewById(R.id.photo_button_check); + mCheckButton.setVisibility(View.INVISIBLE); + mCheckButton.setOnClickListener(this); + mCancelButton = (Button)this.findViewById(R.id.photo_button_cancel); + mCancelButton.setVisibility(View.INVISIBLE); + mCancelButton.setOnClickListener(this); + + Log.d("PhotoPicker", "initialize surface view"); + mSurfaceView = (SurfaceView)this.findViewById(R.id.photo_surface_view); + SurfaceHolder holder = mSurfaceView.getHolder(); + holder.setKeepScreenOn(true); + holder.addCallback(mSurfaceHolderCallback); +// holder.lockCanvas(); + } + + private Camera.Callback mCameraCallback = new Camera.Callback() { + @Override + public void response(Bitmap bitmap) { + if (null == bitmap) { + // 理论上的拍照失败 + mTakeButton.setVisibility(View.VISIBLE); + } + else { + mCheckButton.setVisibility(View.VISIBLE); + mCancelButton.setVisibility(View.VISIBLE); + } + } + }; + + private SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() { + @Override + public void surfaceCreated(SurfaceHolder holder) { + Log.d("PhotoPicker", "surface view created"); + mTakeButton.setVisibility(View.VISIBLE); + + mCamera = new Camera(mSelf, holder, mCameraCallback); + mCamera.open(); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + // surface刚刚创建时,会改变大小 + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + // APP进入后台时,会触发此事件 + // mSelf.response(""); + } + }; + + private void initializeCamera() { + + } + + @Override + public void onClick(final View v) { + if (v == mTakeButton && null != mCamera) { + if (mCamera.capture()) { + mTakeButton.setVisibility(View.INVISIBLE); + } + else { + this.response(""); + } + } + if (v == mCancelButton && null != mCamera) { + mCamera.open(); + mTakeButton.setVisibility(View.VISIBLE); + mCheckButton.setVisibility(View.INVISIBLE); + mCancelButton.setVisibility(View.INVISIBLE); + } + if (v == mCheckButton && null != mCamera) { + try { + Bitmap bitmap = mCamera.getResult(); + FileOutputStream outStream = new FileOutputStream(mFilename, false); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream); + outStream.close(); + this.response(mFilename); + } catch (Exception e) { + e.printStackTrace(); + this.response(""); + } + } + } + + private boolean checkCameraPermission() { + // TODO: 也许可以使用this.getApplicationContext()替代? + int oldStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA); + if (PackageManager.PERMISSION_GRANTED == oldStatus) { + return true; + } + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_TAKE); + return false; + } + + private void takePhoto() { + this.initializeContent(); + this.initializeCamera(); + } + + private boolean checkAlbumPermission() { + int oldStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE); + if (PackageManager.PERMISSION_GRANTED == oldStatus) { + return true; + } + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSION_PICK); + return false; + } + + private void pickPhoto(){ + Intent intent = new Intent(Intent.ACTION_PICK); + intent.setType("image/*"); + this.startActivityForResult(intent, REQUEST_PICK_PHOTO); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + for (int i = 0; i < permissions.length; ++i) { + if (PackageManager.PERMISSION_GRANTED == grantResults[i]) { + switch (requestCode) { + case PERMISSION_TAKE: + this.takePhoto(); + break; + case PERMISSION_PICK: + this.pickPhoto(); + break; + } + } + else { + this.response(""); + } + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case REQUEST_TAKE_PHOTO: + this.response(RESULT_OK == resultCode ? mFilename : ""); + break; + case REQUEST_PICK_PHOTO: + if (RESULT_OK == resultCode) { + Uri uri = data.getData(); + try { + String filename = PathUtils.getRealPathFromUri(this, uri); + FileInputStream inStream = new FileInputStream(filename); + FileOutputStream outStream = new FileOutputStream(mFilename, false); + byte[] buffer = new byte[inStream.available()]; + inStream.read(buffer); + outStream.write(buffer); + outStream.close(); + inStream.close(); + + this.response(mFilename); + return; + } + catch(Exception e){ + e.printStackTrace(); + } + } + this.response(""); + break; + } + } + + private void response(String filename) { + PhotoPicker.response(mInstance, filename); + this.finish(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (null != mCamera) { + mCamera.Release(); + } + // 若之前已经response过正确的相片路径,此处会再次触发,但是native层的回调函数已被上一次response注销,因此此次response会被忽略。 + this.response(""); + } + + static public void takeOrPickPhoto(long instance, String method, String filename) { + Activity current = Cocos2dxHelper.getActivity(); + Intent intent = new Intent(current, PhotoPicker.class); + intent.putExtra(KEY_INSTANCE, instance); + intent.putExtra(KEY_METHOD, method); + intent.putExtra(KEY_FILENAME, filename); + current.startActivity(intent); + } + + static native void response(long instance, String filename); +} \ No newline at end of file diff --git a/frameworks/runtime-src/proj.android-studio/app/src/main/res/layout/activity_photopicker.xml b/frameworks/runtime-src/proj.android-studio/app/src/main/res/layout/activity_photopicker.xml new file mode 100644 index 0000000..f368abb --- /dev/null +++ b/frameworks/runtime-src/proj.android-studio/app/src/main/res/layout/activity_photopicker.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + +