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..3eaa71a 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,24 @@ 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 + + void takeOrPickPhoto(const std::string& method, const std::string& path, const SdkCallback &callback); + + // 将结果同步至主线程中进行处理 + void callbackToMainThread(const std::string key, const std::string argument); + void invoke(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..7917983 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){ @@ -91,6 +97,9 @@ callback(""); } #endif + else if ("takePhoto" == method || "pickPhoto" == method || "takeOrPickPhoto" == method){ + this->takeOrPickPhoto(method, params, callback); + } else { callback(""); } @@ -364,3 +373,86 @@ } } #endif + +void UtilsSdk::takeOrPickPhoto(const std::string& method, const std::string& path, const SdkCallback &callback) +{ + 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 + std::string key = std::string(buffer) + ":takePhoto"; + auto finder = _callbacks.find(key); + if (_callbacks.end() != finder) + { +#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +// SE_REPORT_ERROR("key [%s] for callback already exists!", key.c_str()); +#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) +#endif + 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]; +#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) + PhotoPicker* picker = new PhotoPicker(key, this); +#endif + if ("takeOrPickPhoto" == method) + { +#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) + [picker takeOrPickPhoto:[NSString stringWithUTF8String:(path + "/" + buffer + ".png").c_str()]]; +#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) + picker->takeOrPickPhoto(path + "/" + buffer + ".png"); +#endif + } + else if ("takePhoto" == method) + { +#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) + [picker takePhoto:[NSString stringWithUTF8String:(path + "/" + buffer + ".png").c_str()]]; +#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) + picker->takePhoto(path + "/" + buffer + ".png"); +#endif + } + else if ("pickPhoto" == method) + { +#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) + [picker pickPhoto:[NSString stringWithUTF8String:(path + "/" + buffer + ".png").c_str()]]; +#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) + picker->pickPhoto(path + "/" + buffer + ".png"); +#endif + } +} + +void UtilsSdk::callbackToMainThread(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->invoke(key, argument); + }); +#endif +} + +void UtilsSdk::invoke(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..82950d7 --- /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_org_cocos2dx_javascript_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, "org/cocos2dx/javascript/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->callbackToMainThread(_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..beaf553 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/values/strings.xml b/frameworks/runtime-src/proj.android-studio/app/res/values/strings.xml index 222ab42..fd29c9c 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,3 @@ - 聪明派 + STABLE 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/org/cocos2dx/javascript/AppActivity.java b/frameworks/runtime-src/proj.android-studio/app/src/org/cocos2dx/javascript/AppActivity.java index 2650317..aa23381 100755 --- a/frameworks/runtime-src/proj.android-studio/app/src/org/cocos2dx/javascript/AppActivity.java +++ b/frameworks/runtime-src/proj.android-studio/app/src/org/cocos2dx/javascript/AppActivity.java @@ -148,4 +148,9 @@ public void uncaughtException(Thread thread, Throwable ex) { mDefaultExceptionHandler.uncaughtException(thread, ex); } } + + static public void takeOrPickPhoto(String method) + { + + } } diff --git a/frameworks/runtime-src/proj.android-studio/app/src/org/cocos2dx/javascript/PathUtils.java b/frameworks/runtime-src/proj.android-studio/app/src/org/cocos2dx/javascript/PathUtils.java new file mode 100644 index 0000000..71ee496 --- /dev/null +++ b/frameworks/runtime-src/proj.android-studio/app/src/org/cocos2dx/javascript/PathUtils.java @@ -0,0 +1,113 @@ +package org.cocos2dx.javascript; + + +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/org/cocos2dx/javascript/PhotoPicker.java b/frameworks/runtime-src/proj.android-studio/app/src/org/cocos2dx/javascript/PhotoPicker.java new file mode 100644 index 0000000..e587fec --- /dev/null +++ b/frameworks/runtime-src/proj.android-studio/app/src/org/cocos2dx/javascript/PhotoPicker.java @@ -0,0 +1,173 @@ +package org.cocos2dx.javascript; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.StrictMode; +import android.provider.MediaStore; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.support.v4.content.FileProvider; + +import org.cocos2dx.lib.Cocos2dxHelper; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; + +public class PhotoPicker extends Activity { + static final String KEY_METHOD = "method"; + static final String KEY_INSTANCE = "instance"; + static final String KEY_FILENAME = "filename"; + + 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 = ""; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // 解决7.0 "exposed beyond app through ClipData.Item.getUri"的错误 + StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); + StrictMode.setVmPolicy(builder.build()); + builder.detectFileUriExposure(); + + Intent intent = this.getIntent(); + boolean needCheckPermission = Build.VERSION.SDK_INT >= 23; + mInstance = intent.getLongExtra(KEY_INSTANCE, 0); + mFilename = intent.getStringExtra(KEY_FILENAME); + String method = intent.getStringExtra(KEY_METHOD); + switch (method) { + case "takeOrPickPhoto": + break; + case "takePhoto": + if (!needCheckPermission || this.checkCameraPermission()) { + this.takePhoto(); + } + break; + case "pickPhoto": + if (!needCheckPermission || this.checkAlbumPermission()){ + this.pickPhoto(); + } + break; + } + } + + private boolean checkCameraPermission() { + 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() { + File file = new File(mFilename); + if (file.exists()) { + file.delete(); + } + try { + if (file.createNewFile()) { + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(this, "com.congmingpai.mobild.fileprovider", file)); + this.startActivityForResult(intent, REQUEST_TAKE_PHOTO); + return; + } + } catch (Exception e) { + e.printStackTrace(); + } + this.response(""); + } + + 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) { + switch (requestCode) { + case PERMISSION_TAKE: + this.takePhoto(); + break; + case PERMISSION_PICK: + this.pickPhoto(); + break; + } + } + } + + @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, mFilename); + this.finish(); + } + + 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.ios_mac/Smart_Pi.xcodeproj/project.pbxproj b/frameworks/runtime-src/proj.ios_mac/Smart_Pi.xcodeproj/project.pbxproj index faa99d4..7e8a49d 100755 --- a/frameworks/runtime-src/proj.ios_mac/Smart_Pi.xcodeproj/project.pbxproj +++ b/frameworks/runtime-src/proj.ios_mac/Smart_Pi.xcodeproj/project.pbxproj @@ -120,6 +120,8 @@ DEFCD3C121D1E13E00A872C6 /* hl_sha384wrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DEFCD39121D1E13E00A872C6 /* hl_sha384wrapper.cpp */; }; DEFCD3C421D1E18700A872C6 /* HashSdk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DEFCD3C221D1E18700A872C6 /* HashSdk.cpp */; }; DEFCD3C521D1E18700A872C6 /* HashSdk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DEFCD3C221D1E18700A872C6 /* HashSdk.cpp */; }; + E20825B523D171DC00D373E2 /* PhotoPicker.mm in Sources */ = {isa = PBXBuildFile; fileRef = E20825B323D171DB00D373E2 /* PhotoPicker.mm */; }; + E20D6CC723CD972B00DBFC39 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E20D6CC623CD972B00DBFC39 /* Photos.framework */; }; E2735178215492D700BCBE60 /* courses in Resources */ = {isa = PBXBuildFile; fileRef = E2735177215492D700BCBE60 /* courses */; }; E285AE8B2154E017008821D8 /* project.manifest in Resources */ = {isa = PBXBuildFile; fileRef = E285AE8A2154E017008821D8 /* project.manifest */; }; E2E8A2512361C32A0085A21B /* FirstViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = E2E8A2502361C32A0085A21B /* FirstViewController.mm */; }; @@ -320,6 +322,9 @@ DEFCD39121D1E13E00A872C6 /* hl_sha384wrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hl_sha384wrapper.cpp; sourceTree = ""; }; DEFCD3C221D1E18700A872C6 /* HashSdk.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HashSdk.cpp; sourceTree = ""; }; DEFCD3C321D1E18700A872C6 /* HashSdk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HashSdk.h; sourceTree = ""; }; + E20825B323D171DB00D373E2 /* PhotoPicker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PhotoPicker.mm; sourceTree = ""; }; + E20825B423D171DB00D373E2 /* PhotoPicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhotoPicker.h; sourceTree = ""; }; + E20D6CC623CD972B00DBFC39 /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk/System/Library/Frameworks/Photos.framework; sourceTree = DEVELOPER_DIR; }; E2735177215492D700BCBE60 /* courses */ = {isa = PBXFileReference; lastKnownFileType = folder; name = courses; path = ../../../courses; sourceTree = ""; }; E285AE8A2154E017008821D8 /* project.manifest */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = project.manifest; path = ../../../project.manifest; sourceTree = ""; }; E2E8A24F2361C32A0085A21B /* FirstViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FirstViewController.h; path = ios/FirstViewController.h; sourceTree = ""; }; @@ -360,6 +365,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + E20D6CC723CD972B00DBFC39 /* Photos.framework in Frameworks */, DE36599820FF29DB00D046A0 /* Bugly.framework in Frameworks */, DE10A5C820CFE80800B856E3 /* CoreTelephony.framework in Frameworks */, DE10A5C620CFE7FC00B856E3 /* libc++.tbd in Frameworks */, @@ -456,6 +462,7 @@ A92275401517C094001B78AA /* Frameworks */ = { isa = PBXGroup; children = ( + E20D6CC623CD972B00DBFC39 /* Photos.framework */, DE10A5C720CFE80800B856E3 /* CoreTelephony.framework */, DE10A5C520CFE7FC00B856E3 /* libc++.tbd */, 1AC62C121F4D1E7F00F43CB1 /* JavaScriptCore.framework */, @@ -682,6 +689,7 @@ DE4883DD20FDB31F006717A2 /* utils */ = { isa = PBXGroup; children = ( + E20D6CC223CD90B500DBFC39 /* photos */, DE4883DE20FDB31F006717A2 /* UtilsSdk.h */, DE4883DF20FDB31F006717A2 /* UtilsSdk.mm */, ); @@ -759,6 +767,15 @@ path = src; sourceTree = ""; }; + E20D6CC223CD90B500DBFC39 /* photos */ = { + isa = PBXGroup; + children = ( + E20825B423D171DB00D373E2 /* PhotoPicker.h */, + E20825B323D171DB00D373E2 /* PhotoPicker.mm */, + ); + path = photos; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -966,6 +983,7 @@ DECED05A20F8B5F0000DC6B7 /* Reachability.m in Sources */, DE10A5A520CFC37400B856E3 /* SdkManager.cpp in Sources */, 4D3EB2581F8F4A6C007DA644 /* jsb_module_register.cpp in Sources */, + E20825B523D171DC00D373E2 /* PhotoPicker.mm in Sources */, DE10A5DA20CFFB4A00B856E3 /* SendMessageToWXReq+requestWithTextOrMediaMessage.m in Sources */, DEFCD3B821D1E13E00A872C6 /* hl_sha256.cpp in Sources */, DECED06120F8BC08000DC6B7 /* ReachabilitySdk.mm in Sources */, @@ -1128,6 +1146,7 @@ "$(SRCROOT)/../Classes/sdks/wechat/ios/", "$(SRCROOT)/../Classes/sdks/Bugly/CocosPlugin/agent/iOS/BuglyAgent", ); + MARKETING_VERSION = 1.55.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = com.congmingpai.mobile.stable; PRODUCT_NAME = Stable; @@ -1170,6 +1189,7 @@ "$(SRCROOT)/../Classes/sdks/wechat/ios/", "$(SRCROOT)/../Classes/sdks/Bugly/CocosPlugin/agent/iOS/BuglyAgent", ); + MARKETING_VERSION = 1.55.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = com.congmingpai.mobile.stable; PRODUCT_NAME = Stable; diff --git a/frameworks/runtime-src/proj.ios_mac/ios/Info.plist b/frameworks/runtime-src/proj.ios_mac/ios/Info.plist index c0b3722..3b08314 100755 --- a/frameworks/runtime-src/proj.ios_mac/ios/Info.plist +++ b/frameworks/runtime-src/proj.ios_mac/ios/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.42.0 + $(MARKETING_VERSION) CFBundleSignature ???? CFBundleURLTypes @@ -48,6 +48,10 @@ NSAllowsArbitraryLoads + NSCameraUsageDescription + App需获取调用系统相机的权限 + NSPhotoLibraryUsageDescription + App需获取调用系统相册的权限 UILaunchStoryboardName LaunchScreen UIPrerenderedIcon