Compare commits
14 Commits
master
...
phiz_2.41.
Author | SHA1 | Date |
---|---|---|
stewen | f9ee987c5a | |
stewen | 2b3bd6a226 | |
stewen | 957f14dde5 | |
stewen | 6749f2fc82 | |
stewen | a6bd02e789 | |
stewen | 366f8ab9ac | |
stewen | 58a8e5b804 | |
stewen | 9951bebc56 | |
stewen | 8ef8d78b2a | |
jayce | b800cb3786 | |
jayce | f097d86cd1 | |
jayce | 195b2d11a9 | |
simpleman1984@126.com | 4df164b35f | |
simpleman1984@126.com | 4cfdbafcbf |
|
@ -91,6 +91,6 @@ kapt {
|
|||
}
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation 'com.finogeeks.lib:finapplet:2.41.11'
|
||||
implementation 'com.finogeeks.mop:plugins:2.41.11'
|
||||
implementation 'com.finogeeks.lib:finapplet:2.41.5'
|
||||
implementation 'com.finogeeks.mop:plugins:2.41.5'
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// The code is already well written and doesn't need any improvement.
|
||||
// However, I will add some comments to make it more readable.
|
||||
|
||||
package com.finogeeks.mop.api;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.Nullable;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public abstract class SyncApi extends BaseApi {
|
||||
public SyncApi(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
// This method is used to invoke the API.
|
||||
@Nullable
|
||||
public abstract String invoke(String url, JSONObject jsonObject);
|
||||
|
||||
// This method is used to get the success response.
|
||||
public JSONObject getSuccessRes(String message) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String key = "errMsg";
|
||||
try {
|
||||
jsonObject.put(key, message + ":ok");
|
||||
return jsonObject;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
||||
|
||||
// This method is used to get the failure response.
|
||||
public String getFailureRes(String message, String error) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String key = "errMsg";
|
||||
try {
|
||||
return jsonObject.put(key, message + ":fail " + error).toString();
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return "{\"errMsg\":" + message + "\":fail \"" + error + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -26,8 +26,6 @@ import com.finogeeks.mop.utils.AppletUtils;
|
|||
import com.finogeeks.mop.utils.GsonUtil;
|
||||
import com.finogeeks.mop.impls.MyUserProfileHandler;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.finogeeks.lib.applet.sdk.api.IAppletLifecycleCallback;
|
||||
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -71,66 +69,6 @@ public class AppletHandlerModule extends BaseApi {
|
|||
// getUserProfile的内置实现
|
||||
FinAppClient.INSTANCE.getFinAppConfig().setGetUserProfileHandlerClass(MyUserProfileHandler.class.getName());
|
||||
|
||||
FinAppClient.INSTANCE.getAppletApiManager().setAppletLifecycleCallback(new IAppletLifecycleCallback() {
|
||||
|
||||
@Override
|
||||
public void onCreate(@NotNull String appId) {
|
||||
Log.d(TAG, "IAppletLifecycleCallback onCreate : " + appId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitComplete(@NotNull String appId) {
|
||||
Log.d(TAG, "IAppletLifecycleCallback onInitComplete : " + appId);
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("appId", appId);
|
||||
handler.post(() -> {
|
||||
channel.invokeMethod("extensionApi:appletDidOpen", params, new MethodChannel.Result() {
|
||||
@Override
|
||||
public void success(Object result) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String errorCode, String errorMessage, Object errorDetails) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notImplemented() {
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(@NotNull String appId) {
|
||||
Log.d(TAG, "IAppletLifecycleCallback onStart : " + appId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume(@NotNull String appId) {
|
||||
Log.d(TAG, "IAppletLifecycleCallback onResume : " + appId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause(@NotNull String appId) {
|
||||
Log.d(TAG, "IAppletLifecycleCallback onPause : " + appId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(@NotNull String appId) {
|
||||
Log.d(TAG, "IAppletLifecycleCallback onStop : " + appId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy(@NotNull String appId) {
|
||||
Log.d(TAG, "IAppletLifecycleCallback onDestroy : " + appId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull String appId, String errMsg) {
|
||||
Log.d(TAG, "IAppletLifecycleCallback onFailure : " + appId);
|
||||
}
|
||||
});
|
||||
|
||||
FinAppClient.INSTANCE.getAppletApiManager().setAppletHandler(mIAppletHandler = new IAppletHandler() {
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -33,12 +33,14 @@ public class ExtensionApiModule extends BaseApi {
|
|||
|
||||
@Override
|
||||
public String[] apis() {
|
||||
return new String[]{"registerExtensionApi","addWebExtentionApi"};
|
||||
return new String[]{"registerExtensionApi", "registerSyncExtensionApi", "addWebExtentionApi"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(String s, Map param, ICallback iCallback) {
|
||||
if(s.equals("registerExtensionApi")) {
|
||||
boolean isFinAppProcess = FinAppClient.INSTANCE.isFinAppProcess(getContext());
|
||||
Log.d(TAG, "ExtensionApiModule invoke register api s:" + s + " param:" + param +" isFinAppProcess:"+isFinAppProcess);
|
||||
if (s.equals("registerExtensionApi")) {
|
||||
MethodChannel channel = MopPluginService.getInstance().getMethodChannel();
|
||||
String name = (String) param.get("name");
|
||||
Log.d(TAG, "registerExtensionApi:" + name);
|
||||
|
@ -50,7 +52,7 @@ public class ExtensionApiModule extends BaseApi {
|
|||
|
||||
@Override
|
||||
public void invoke(String s, JSONObject jsonObject, com.finogeeks.lib.applet.interfaces.ICallback iCallback) {
|
||||
Log.d("MopPlugin", "invoke extensionApi:" + s + ",params:" + jsonObject);
|
||||
Log.d("MopPlugin", "invoke extensionApi:" + s + ",params:" + jsonObject+" isFinAppProcess:"+isFinAppProcess);
|
||||
Map params = GsonUtil.gson.fromJson(jsonObject.toString(), HashMap.class);
|
||||
handler.post(() -> {
|
||||
channel.invokeMethod("extensionApi:" + name, params, new MethodChannel.Result() {
|
||||
|
@ -95,7 +97,64 @@ public class ExtensionApiModule extends BaseApi {
|
|||
});
|
||||
}
|
||||
});
|
||||
}else if(s.equals("addWebExtentionApi")){
|
||||
} else if (s.equals("registerSyncExtensionApi")) {
|
||||
MethodChannel channel = MopPluginService.getInstance().getMethodChannel();
|
||||
String name = (String) param.get("name");
|
||||
Log.d(TAG, "registerSyncExtensionApi:" + name);
|
||||
FinAppClient.INSTANCE.getExtensionApiManager().registerApi(new com.finogeeks.lib.applet.api.BaseApi(getContext()) {
|
||||
@Override
|
||||
public String[] apis() {
|
||||
return new String[]{name};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(String s, JSONObject jsonObject, com.finogeeks.lib.applet.interfaces.ICallback iCallback) {
|
||||
Log.d("MopPlugin", "sync invoke extensionApi:" + s + ",params:" + jsonObject);
|
||||
Map params = GsonUtil.gson.fromJson(jsonObject.toString(), HashMap.class);
|
||||
handler.post(() -> {
|
||||
channel.invokeMethod("syncextensionapi:" + name, params, new MethodChannel.Result() {
|
||||
@Override
|
||||
public void success(Object result) {
|
||||
String json = GsonUtil.gson.toJson(result);
|
||||
Log.d(ExtensionApiModule.TAG, "channel invokeMethod:" + name
|
||||
+ " success, result=" + result + ", json=" + json);
|
||||
JSONObject ret = null;
|
||||
if (json != null && !json.equals("null")) {
|
||||
try {
|
||||
ret = new JSONObject(json);
|
||||
if (ret.has("errMsg")) {
|
||||
String errMsg = ret.getString("errMsg");
|
||||
if (errMsg.startsWith(name + ":fail")) {
|
||||
iCallback.onFail(ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
iCallback.onSuccess(ret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String errorCode, String errorMessage, Object errorDetails) {
|
||||
FLog.e(ExtensionApiModule.TAG, "channel invokeMethod:" + name
|
||||
+ " error, errorCode=" + errorCode
|
||||
+ ", errorMessage=" + errorMessage
|
||||
+ ", errorDetails=" + errorDetails);
|
||||
iCallback.onFail();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notImplemented() {
|
||||
iCallback.onFail();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (s.equals("addWebExtentionApi")) {
|
||||
MethodChannel channel = MopPluginService.getInstance().getMethodChannel();
|
||||
String name = (String) param.get("name");
|
||||
Log.d(TAG, "addWebExtentionApi:" + name);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Uncomment this line to define a global platform for your project
|
||||
platform :ios, '9.0'
|
||||
platform :ios, '11.0'
|
||||
|
||||
source 'ssh://gitlab.finogeeks.club/finclip-ios/DevPods'
|
||||
source 'ssh://gitlab.finogeeks.club/finclip-ios/FinPods'
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 51;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
@ -231,10 +231,12 @@
|
|||
};
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
|
||||
);
|
||||
name = "Thin Binary";
|
||||
outputPaths = (
|
||||
|
@ -262,6 +264,7 @@
|
|||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
|
@ -351,7 +354,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
|
@ -430,7 +433,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
|
@ -479,7 +482,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
|
|
|
@ -43,5 +43,7 @@
|
|||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -216,7 +216,7 @@ class MyAppletHandler extends AppletHandler {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<List<CustomMenu>> getCustomMenus(String appId) {
|
||||
List<CustomMenu> getCustomMenus(String appId) {
|
||||
CustomMenu menu1 = CustomMenu('WXTest', 'https://img1.baidu.com/it/u=2878938773,1765835171&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500', '百度图标', 'common');
|
||||
menu1.darkImage = 'https://img95.699pic.com/xsj/14/46/mh.jpg%21/fw/700/watermark/url/L3hzai93YXRlcl9kZXRhaWwyLnBuZw/align/southeast';
|
||||
|
||||
|
@ -233,7 +233,7 @@ class MyAppletHandler extends AppletHandler {
|
|||
// CustomMenu('WXShareAPPFriends', 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSpvugSNLs9R7iopz_noeotAelvgzYj-74iCg&usqp=CAU', '微信好朋友', 'common'),
|
||||
// CustomMenu('WXShareAPPMoments', 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR7cO4KB4e5-Ugdcq4pIyWunliH7LZRZzguKQ&usqp=CAU', '微信朋友圈', 'common'),
|
||||
];
|
||||
return Future.value(customMenus);
|
||||
return customMenus;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -5,27 +5,31 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.9.0"
|
||||
version: "2.11.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.1.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
version: "1.3.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
|
@ -33,13 +37,15 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.17.1"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
|
@ -47,6 +53,7 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
|
@ -59,6 +66,7 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
|
@ -66,6 +74,7 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: c224ac897bed083dabf11f238dd11a239809b446740be0c2044608c50029ffdf
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.9"
|
||||
|
@ -74,10 +83,19 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
|
@ -85,37 +103,41 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.12.12"
|
||||
version: "0.12.15"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.1.5"
|
||||
version: "0.2.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.8.0"
|
||||
version: "1.9.1"
|
||||
mop:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "2.41.1"
|
||||
version: "2.41.5"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.8.2"
|
||||
version: "1.8.3"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -125,34 +147,39 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
version: "1.9.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
version: "1.11.0"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.1.1"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
|
@ -160,16 +187,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.4.12"
|
||||
version: "0.5.1"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.4"
|
||||
sdks:
|
||||
dart: ">=2.17.0-0 <3.0.0"
|
||||
dart: ">=3.0.0-0 <4.0.0"
|
||||
flutter: ">=3.0.0"
|
||||
|
|
|
@ -57,9 +57,9 @@
|
|||
}
|
||||
|
||||
- (BOOL)appletInfo:(FATAppletInfo *)appletInfo didClickMoreBtnAtPath:(NSString *)path {
|
||||
NSLog(@"appletInfo:didClickMoreBtnAtPath");
|
||||
__block BOOL flag;
|
||||
FlutterMethodChannel *channel = [[MopPlugin instance] methodChannel];
|
||||
NSLog(@"appletInfo:didClickMoreBtnAtPath,appId=%@,path=%@,channel=%@",appletInfo.appId,path,channel);
|
||||
[channel invokeMethod:@"extensionApi:customCapsuleMoreButtonClick" arguments:@{@"appId": appletInfo.appId} result:^(id _Nullable result) {
|
||||
CFRunLoopStop(CFRunLoopGetMain());
|
||||
if ([result isKindOfClass:[NSNumber class]]) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#import "Mop_initSDK.h"
|
||||
#import "MOPTools.h"
|
||||
#import "FinAppletExt.h"
|
||||
|
||||
@implementation MOP_initSDK
|
||||
|
||||
|
@ -35,6 +36,9 @@
|
|||
storeConfig.encryptServerData = [dict[@"encryptServerData"] boolValue];
|
||||
storeConfig.enablePreloadFramework = [storeConfig.apiServer isEqualToString:@"https://api.finclip.com"];
|
||||
[storeArrayM addObject:storeConfig];
|
||||
|
||||
//google map key
|
||||
[[FATExtClient sharedClient] registerGoogleMapService:dict[@"googleMapApiKey"] placesKey:dict[@"googleMapApiKey"]];
|
||||
}
|
||||
config = [FATConfig configWithStoreConfigs:storeArrayM];
|
||||
} else {
|
||||
|
@ -70,7 +74,6 @@
|
|||
config.language = FATPreferredLanguageSimplifiedChinese;
|
||||
}
|
||||
|
||||
|
||||
NSError* error = nil;
|
||||
FATUIConfig *uiconfig = [[FATUIConfig alloc]init];
|
||||
if (_uiConfig) {
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
|
||||
#import "MOP_initialize.h"
|
||||
#import <FinApplet/FinApplet.h>
|
||||
#import <FinAppletExt/FinAppletExt.h>
|
||||
//#import <FinAppletExt/FinAppletExt.h>
|
||||
#import "FinAppletExt.h"
|
||||
#import <FinAppletClipBoard/FinAppletClipBoard.h>
|
||||
#import "MOPTools.h"
|
||||
|
||||
@implementation MOP_initialize
|
||||
|
@ -126,8 +128,9 @@
|
|||
// [[FATExtClient sharedClient] fat_prepareExtensionApis];
|
||||
[[FATClient sharedClient].logManager initLogWithLogDir:nil logLevel:FATLogLevelVerbose consoleLog:YES];
|
||||
|
||||
|
||||
[[FATClient sharedClient] setEnableLog:YES];
|
||||
// ClipBoard初始化
|
||||
[FATClipBoardComponent registerComponent];
|
||||
|
||||
success(@{});
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// MOP_registerSyncExtensionApi.h
|
||||
// mop
|
||||
//
|
||||
// Created by Stewen on 2023/6/30.
|
||||
//
|
||||
|
||||
#import "MOPBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MOP_registerSyncExtensionApi : MOPBaseApi
|
||||
|
||||
@property(nonatomic, copy) NSString* name;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// MOP_registerSyncExtensionApi.m
|
||||
// mop
|
||||
//
|
||||
// Created by Stewen on 2023/6/30.
|
||||
//
|
||||
|
||||
#import "MOP_registerSyncExtensionApi.h"
|
||||
#import "MopPlugin.h"
|
||||
#import <FinApplet/FinApplet.h>
|
||||
#import "PhizLanguageData.h"
|
||||
|
||||
@implementation MOP_registerSyncExtensionApi
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *,id> * _Nonnull))success failure:(void (^)(id _Nullable))failure cancel:(void (^)(void))cancel
|
||||
{
|
||||
NSLog(@"MOP_registerSyncExtensionApi,name=%@",self.name);
|
||||
FlutterMethodChannel *channel = [[MopPlugin instance] methodChannel];
|
||||
[[FATClient sharedClient] registerSyncExtensionApi:self.name handler:^NSDictionary *(FATAppletInfo *appletInfo, id param) {
|
||||
if([self.name isEqualToString:@"getLanguageCodeSync"]){
|
||||
NSDictionary *resultDict = [NSDictionary dictionary];
|
||||
NSString* shortCode = [PhizLanguageData sharedInstance].languageCode;
|
||||
NSString* countryCode = [PhizLanguageData sharedInstance].countryCode;
|
||||
resultDict = @{@"languageCode":shortCode,@"countryCode":countryCode};
|
||||
return resultDict;
|
||||
}
|
||||
return @{};
|
||||
}];
|
||||
success(@{});
|
||||
}
|
||||
|
||||
|
||||
@end
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// PhizLanguageData.h
|
||||
// FinDemo
|
||||
//
|
||||
// Created by stewen on 2023/8/4.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PhizLanguageData : NSObject
|
||||
|
||||
@property (nonatomic, copy) NSString *languageCode;
|
||||
@property (nonatomic, copy) NSString *countryCode;
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// PhizLanguageData.m
|
||||
// FinDemo
|
||||
//
|
||||
// Created by stewen on 2023/8/4.
|
||||
//
|
||||
|
||||
#import "PhizLanguageData.h"
|
||||
|
||||
@implementation PhizLanguageData
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static PhizLanguageData *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
sharedInstance.languageCode = @"en"; // Set default language code
|
||||
sharedInstance.countryCode = @"US";
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// FATClient+ext.h
|
||||
// Pods
|
||||
//
|
||||
// Created by 王滔 on 2021/11/15.
|
||||
//
|
||||
|
||||
#ifndef FATClient_ext_h
|
||||
#define FATClient_ext_h
|
||||
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
@interface FATClient (FATAppletExt)
|
||||
|
||||
/// 获取小程序的权限
|
||||
/// @param authType 权限类型,0:相册 1:相机 2:麦克风 3:位置
|
||||
/// @param appletId 小程序id
|
||||
/// @param complete 结果回调 status: 0 允许 1:用户拒绝 2: sdk拒绝
|
||||
- (void)fat_requestAppletAuthorize:(FATAuthorizationType)authType appletId:(NSString *)appletId complete:(void (^)(NSInteger status))complete;
|
||||
|
||||
/// 内部sdk注入API方法,包括扩展sdk和其他地图等sdk 注入的API会加到内部白名单列表,保证小程序在设置了api白名单的情况下,也能正常响应
|
||||
/// @param extApiName API名称
|
||||
/// @param handler 回调
|
||||
- (BOOL)registerInnerExtensionApi:(NSString *)extApiName handler:(void (^)(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback))handler;
|
||||
|
||||
/**
|
||||
内部sdk注入API方法,包括扩展sdk和其他地图等sdk 注入的API会加到内部白名单列表,保证小程序在设置了api白名单的情况下,也能正常响应
|
||||
@param syncExtApiName 扩展的api名称
|
||||
@param handler 回调
|
||||
@return 返回注册结果
|
||||
*/
|
||||
- (BOOL)registerInnerSyncExtensionApi:(NSString *)syncExtApiName handler:(NSDictionary *(^)(FATAppletInfo *appletInfo, id param))handler;
|
||||
|
||||
|
||||
/**
|
||||
为HTML 注册要调用的原生 api(内部sdk注入的api)
|
||||
@param webApiName 原生api名字
|
||||
@param handler 回调
|
||||
*/
|
||||
- (BOOL)fat_registerInnerWebApi:(NSString *)webApiName handler:(void (^)(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback))handler;
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
#endif /* FATClient_ext_h */
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// FATExtClient.h
|
||||
// FinAppletExtension
|
||||
//
|
||||
// Created by Haley on 2020/8/11.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface FATExtClient : NSObject
|
||||
|
||||
+ (instancetype)sharedClient;
|
||||
|
||||
/// 版本号
|
||||
+ (NSString *)SDKVersion;
|
||||
|
||||
- (void)fat_prepareExtensionApis;
|
||||
|
||||
- (void)registerGoogleMapService:(NSString*)apiKey placesKey:(NSString*)placeKey;
|
||||
|
||||
/// 获取webView
|
||||
/// @param frame frame
|
||||
/// @param URL 网页的URL
|
||||
/// @param appletId 小程序ID
|
||||
- (UIView *)webViewWithFrame:(CGRect)frame URL:(NSURL *)URL appletId:(NSString *)appletId;
|
||||
|
||||
@end
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// FATExtClient.m
|
||||
// FinAppletExtension
|
||||
//
|
||||
// Created by Haley on 2020/8/11.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtClient.h"
|
||||
#import "FATExtBaseApi.h"
|
||||
#import "FATWebView.h"
|
||||
#import <FinApplet/FinApplet.h>
|
||||
#import "FATExtPrivateConstant.h"
|
||||
#import "FATExtMapManager.h"
|
||||
#import "FATMapViewDelegate.h"
|
||||
#import "FATClient+ext.h"
|
||||
|
||||
static FATExtClient *instance = nil;
|
||||
|
||||
@implementation FATExtClient
|
||||
|
||||
+ (instancetype)sharedClient {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[FATExtClient alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [super allocWithZone:zone];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (NSString *)SDKVersion
|
||||
{
|
||||
return FATExtVersionString;
|
||||
}
|
||||
|
||||
/// 注册地图展示类
|
||||
/// @param mapClass 地图类,需要是UIView的子类,实现FATMapViewDelegate协议
|
||||
- (BOOL)fat_registerMapClass:(Class)mapClass {
|
||||
if (![mapClass isSubclassOfClass:UIView.class]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (![mapClass conformsToProtocol:@protocol(FATMapViewDelegate)]) {
|
||||
return NO;
|
||||
}
|
||||
[FATExtMapManager shareInstance].mapClass = mapClass;
|
||||
return YES;
|
||||
}
|
||||
- (void)registerGoogleMapService:(NSString*)apiKey placesKey:(NSString*)placeKey{
|
||||
//[GMSServices provideAPIKey: apiKey];
|
||||
[FATExtMapManager shareInstance].googleMapApiKey = apiKey;
|
||||
if(placeKey == nil || [placeKey length] == 0){
|
||||
[FATExtMapManager shareInstance].placesApiKey = apiKey;
|
||||
}else{
|
||||
[FATExtMapManager shareInstance].placesApiKey = placeKey;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)fat_prepareExtensionApis {
|
||||
|
||||
}
|
||||
|
||||
- (void)registerExtensionBLEApi {
|
||||
//该空方法不能移除。如果集成了蓝牙拓展SDK,分类会覆盖此方法
|
||||
}
|
||||
|
||||
- (UIView *)webViewWithFrame:(CGRect)frame URL:(NSURL *)URL appletId:(NSString *)appletId {
|
||||
if (![FATClient sharedClient].inited) {
|
||||
NSLog(@"appKey invalid");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!URL || ![URL isKindOfClass:[NSURL class]]) {
|
||||
NSLog(@"URL invalid");
|
||||
return nil;
|
||||
}
|
||||
|
||||
FATWebView *webView = [[FATWebView alloc] initWithFrame:frame URL:URL appletId:appletId];
|
||||
return webView;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@end
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// UIView+FATExtFrame.h
|
||||
// FinAppletBDMap
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/12.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface UIView (FATExtFrame)
|
||||
|
||||
@property (nonatomic, assign) CGFloat x;
|
||||
@property (nonatomic, assign) CGFloat y;
|
||||
@property (nonatomic, assign) CGFloat centerX;
|
||||
@property (nonatomic, assign) CGFloat centerY;
|
||||
@property (nonatomic, assign) CGFloat width;
|
||||
@property (nonatomic, assign) CGFloat height;
|
||||
@property (nonatomic, assign) CGSize size;
|
||||
|
||||
@property (nonatomic, assign) CGPoint origin;
|
||||
|
||||
@property (nonatomic, assign) CGFloat top;
|
||||
@property (nonatomic, assign) CGFloat bottom;
|
||||
@property (nonatomic, assign) CGFloat left;
|
||||
@property (nonatomic, assign) CGFloat right;
|
||||
|
||||
//判断self和View是否重叠
|
||||
- (BOOL)intersectWithView:(UIView *)view;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,131 @@
|
|||
//
|
||||
// UIView+FATExtFrame.m
|
||||
// FinAppletBDMap
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/12.
|
||||
//
|
||||
|
||||
#import "UIView+FATExtFrame.h"
|
||||
|
||||
@implementation UIView (FATExtFrame)
|
||||
|
||||
- (void)setX:(CGFloat)x {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.x = x;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGFloat)x {
|
||||
return self.frame.origin.x;
|
||||
}
|
||||
|
||||
- (void)setY:(CGFloat)y {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.y = y;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGFloat)y {
|
||||
return self.frame.origin.y;
|
||||
}
|
||||
|
||||
- (void)setCenterX:(CGFloat)centerX {
|
||||
CGPoint center = self.center;
|
||||
center.x = centerX;
|
||||
self.center = center;
|
||||
}
|
||||
|
||||
- (CGFloat)centerX {
|
||||
return self.center.x;
|
||||
}
|
||||
|
||||
- (void)setCenterY:(CGFloat)centerY {
|
||||
CGPoint center = self.center;
|
||||
center.y = centerY;
|
||||
self.center = center;
|
||||
}
|
||||
|
||||
- (CGFloat)centerY {
|
||||
return self.center.y;
|
||||
}
|
||||
|
||||
- (void)setWidth:(CGFloat)width {
|
||||
CGRect frame = self.frame;
|
||||
frame.size.width = width;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGFloat)width {
|
||||
return self.frame.size.width;
|
||||
}
|
||||
|
||||
- (void)setHeight:(CGFloat)height {
|
||||
CGRect frame = self.frame;
|
||||
frame.size.height = height;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGFloat)height {
|
||||
return self.frame.size.height;
|
||||
}
|
||||
|
||||
- (void)setSize:(CGSize)size {
|
||||
CGRect frame = self.frame;
|
||||
frame.size = size;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGSize)size {
|
||||
return self.frame.size;
|
||||
}
|
||||
|
||||
- (CGPoint)origin {
|
||||
return self.frame.origin;
|
||||
}
|
||||
|
||||
- (void)setOrigin:(CGPoint)origin {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin = origin;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (void)setTop:(CGFloat)t {
|
||||
self.frame = CGRectMake(self.left, t, self.width, self.height);
|
||||
}
|
||||
- (CGFloat)top {
|
||||
return self.frame.origin.y;
|
||||
}
|
||||
|
||||
- (void)setBottom:(CGFloat)b {
|
||||
self.frame = CGRectMake(self.left, b - self.height, self.width, self.height);
|
||||
}
|
||||
|
||||
- (CGFloat)bottom {
|
||||
return self.frame.origin.y + self.frame.size.height;
|
||||
}
|
||||
|
||||
- (void)setLeft:(CGFloat)l {
|
||||
self.frame = CGRectMake(l, self.top, self.width, self.height);
|
||||
}
|
||||
|
||||
- (CGFloat)left {
|
||||
return self.frame.origin.x;
|
||||
}
|
||||
|
||||
- (void)setRight:(CGFloat)r {
|
||||
self.frame = CGRectMake(r - self.width, self.top, self.width, self.height);
|
||||
}
|
||||
|
||||
- (CGFloat)right {
|
||||
return self.frame.origin.x + self.frame.size.width;
|
||||
}
|
||||
|
||||
//判断self和View是否重叠 nil表示为window
|
||||
- (BOOL)intersectWithView:(UIView *)view {
|
||||
if (view == nil) view = [UIApplication sharedApplication].keyWindow;
|
||||
CGRect rect1 = [self convertRect:self.bounds toView:nil];
|
||||
CGRect rect2 = [view convertRect:view.bounds toView:nil];
|
||||
return CGRectIntersectsRect(rect1, rect2);
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// FATExtConstant.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2021/9/6.
|
||||
//
|
||||
|
||||
#ifndef FATExtPrivateConstant_h
|
||||
#define FATExtPrivateConstant_h
|
||||
|
||||
static NSString *kExtSendToCoreEventNotification = @"kExtSendToCoreEventNotification";
|
||||
|
||||
static NSString *FATExtVersionString = @"2.41.3";
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FATExtEventType) {
|
||||
FATExtEventTypeService, // 发送给service的事件
|
||||
FATExtEventTypePage, // 发送给page层的事件
|
||||
FATExtEventTypeView, // view 操作事件
|
||||
};
|
||||
|
||||
#endif /* FATExtPrivateConstant_h */
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// FATExtAVManager.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2020/8/14.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef void (^FATExtAVSuccess)(NSString *filePath);
|
||||
typedef void (^FATExtAVFail)(NSString *failMsg);
|
||||
|
||||
@interface FATExtAVManager : NSObject
|
||||
|
||||
+ (instancetype)sharedManager;
|
||||
|
||||
/**
|
||||
开始录音
|
||||
|
||||
@param success 成功回调
|
||||
@param fail 失败回调
|
||||
*/
|
||||
- (void)startRecordWithSuccess:(FATExtAVSuccess)success fail:(FATExtAVFail)fail;
|
||||
|
||||
/**
|
||||
停止录音
|
||||
*/
|
||||
- (void)stopRecord;
|
||||
|
||||
/**
|
||||
检查录音状态
|
||||
*/
|
||||
- (void)checkRecordState;
|
||||
|
||||
@end
|
|
@ -0,0 +1,195 @@
|
|||
//
|
||||
// FATExtAVManager.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2020/8/14.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtAVManager.h"
|
||||
#import "FATExtFileManager.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
@interface FATExtAVManager () <AVAudioRecorderDelegate>
|
||||
|
||||
@property (nonatomic, strong) AVAudioRecorder *recorder;
|
||||
|
||||
@property (nonatomic, copy) FATExtAVSuccess recordSuccess;
|
||||
@property (nonatomic, copy) FATExtAVFail recordFail;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FATExtAVManager
|
||||
|
||||
+ (instancetype)sharedManager {
|
||||
static id _sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_sharedInstance = [[FATExtAVManager alloc] init];
|
||||
[_sharedInstance add_notifications];
|
||||
});
|
||||
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
- (void)add_notifications {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appletPageDisappear:) name:kFATPageDidDisappearNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)appletPageDisappear:(NSNotification *)notification
|
||||
{
|
||||
if (self.recorder) {
|
||||
[self.recorder pause];
|
||||
if (self.recordFail) {
|
||||
self.recordFail(@"fail");
|
||||
}
|
||||
[self.recorder deleteRecording];
|
||||
self.recorder = nil;
|
||||
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:YES state:FATCapsuleButtonStateMicroPhone animate:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
开始录音
|
||||
|
||||
@param success 成功回调
|
||||
@param fail 失败回调
|
||||
*/
|
||||
- (void)startRecordWithSuccess:(FATExtAVSuccess)success fail:(FATExtAVFail)fail {
|
||||
if ([self.recorder isRecording]) {
|
||||
fail(@"正在录音中...");
|
||||
return;
|
||||
}
|
||||
|
||||
self.recordSuccess = success;
|
||||
self.recordFail = fail;
|
||||
|
||||
self.recorder = [self createAudioRecord];
|
||||
|
||||
[self.recorder prepareToRecord];
|
||||
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
||||
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:NULL];
|
||||
[audioSession setActive:YES error:NULL];
|
||||
[self.recorder recordForDuration:60];
|
||||
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:NO state:FATCapsuleButtonStateMicroPhone animate:YES];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
停止录音
|
||||
*/
|
||||
- (void)stopRecord {
|
||||
if (self.recorder) {
|
||||
[self.recorder stop];
|
||||
self.recorder = nil;
|
||||
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:YES state:FATCapsuleButtonStateMicroPhone animate:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)checkRecordState {
|
||||
if ([self.recorder isRecording]) {
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:NO state:FATCapsuleButtonStateMicroPhone animate:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - private method
|
||||
|
||||
- (AVAudioRecorder *)createAudioRecord {
|
||||
// 使用此配置 录制1分钟大小200KB左右
|
||||
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithInt:kAudioFormatMPEG4AAC], AVFormatIDKey,
|
||||
[NSNumber numberWithFloat:16000.0], AVSampleRateKey,
|
||||
[NSNumber numberWithInt:1], AVNumberOfChannelsKey,
|
||||
nil];
|
||||
|
||||
// 使用当前时间戳的md5作为文件名
|
||||
NSString *currentDt = [NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]];
|
||||
NSData *data = [currentDt dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *nameMD5 = [self fat_md5WithBytes:(char *)[data bytes] length:data.length];
|
||||
NSString *fileName = [NSString stringWithFormat:@"tmp_%@.m4a", nameMD5];
|
||||
NSString *filePath = [[self tmpDir] stringByAppendingPathComponent:fileName];
|
||||
AVAudioRecorder *recorder = [[AVAudioRecorder alloc] initWithURL:[NSURL fileURLWithPath:filePath] settings:settings error:nil];
|
||||
recorder.delegate = self;
|
||||
|
||||
return recorder;
|
||||
}
|
||||
|
||||
- (NSString *)tmpDir {
|
||||
FATAppletInfo *appInfo = [[FATClient sharedClient] currentApplet];
|
||||
NSString *cacheDir = [FATExtFileManager appTempDirPath:appInfo.appId];
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
BOOL flag = YES;
|
||||
if (![fileManager fileExistsAtPath:cacheDir isDirectory:&flag]) {
|
||||
[fileManager createDirectoryAtPath:cacheDir withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
return cacheDir;
|
||||
}
|
||||
|
||||
- (NSString *)fat_md5WithBytes:(char *)bytes length:(NSUInteger)length {
|
||||
unsigned char result[16];
|
||||
CC_MD5(bytes, (CC_LONG)length, result);
|
||||
return [NSString stringWithFormat:
|
||||
@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
result[0], result[1], result[2], result[3],
|
||||
result[4], result[5], result[6], result[7],
|
||||
result[8], result[9], result[10], result[11],
|
||||
result[12], result[13], result[14], result[15]];
|
||||
}
|
||||
|
||||
#pragma mark - AVAudioRecord Delegate
|
||||
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag {
|
||||
NSString *filePath = recorder.url.lastPathComponent;
|
||||
if (flag) {
|
||||
if (self.recordSuccess) {
|
||||
self.recordSuccess([@"finfile://" stringByAppendingString:filePath]);
|
||||
}
|
||||
} else {
|
||||
[recorder deleteRecording];
|
||||
if (self.recordFail) {
|
||||
self.recordFail(@"fail");
|
||||
}
|
||||
}
|
||||
|
||||
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
||||
[audioSession setCategory:AVAudioSessionCategoryPlayback error:NULL];
|
||||
[audioSession setActive:YES error:NULL];
|
||||
|
||||
self.recorder = nil;
|
||||
self.recordSuccess = nil;
|
||||
self.recordFail = nil;
|
||||
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:YES state:FATCapsuleButtonStateMicroPhone animate:NO];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// FATExtFileManager.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2020/8/17.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FATExtFileManager : NSObject
|
||||
|
||||
/**
|
||||
工程根目录路径
|
||||
|
||||
@return 工程根目录路径
|
||||
*/
|
||||
+ (NSString *)projectRootAppsDirPath;
|
||||
|
||||
/**
|
||||
获取当前小程序根目录
|
||||
*/
|
||||
+ (NSString *)appRootDirPath:(NSString *)appId;
|
||||
|
||||
/**
|
||||
获取当前小程序临时存储目录
|
||||
*/
|
||||
+ (NSString *)appTempDirPath:(NSString *)appId;
|
||||
|
||||
@end
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// FATExtFileManager.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2020/8/17.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtFileManager.h"
|
||||
#import "FATExtUtil.h"
|
||||
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
static NSString *FATEXT_PROJECT_ROOT = @"FinChatRoot";
|
||||
static NSString *FATEXT_PROJECT_ROOT_App = @"app";
|
||||
static NSString *FATEXT_PROJECT_ROOT_Framework = @"framework";
|
||||
|
||||
@implementation FATExtFileManager
|
||||
|
||||
+ (NSString *)projectRootDirPath {
|
||||
NSString *rootPath;
|
||||
if ([FATExtUtil currentProductIdentificationIsEmpty]) {
|
||||
rootPath = [NSTemporaryDirectory() stringByAppendingPathComponent:FATEXT_PROJECT_ROOT];
|
||||
} else {
|
||||
rootPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[FATExtUtil currentProductIdentification]];
|
||||
}
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
+ (NSString *)projectRootAppsDirPath {
|
||||
NSString *rootPath = [[FATExtFileManager projectRootDirPath] stringByAppendingFormat:@"/%@", FATEXT_PROJECT_ROOT_App];
|
||||
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
/**
|
||||
获取当前小程序根路径
|
||||
|
||||
@return 获取当前小程序根路径
|
||||
*/
|
||||
+ (NSString *)appRootDirPath:(NSString *)appId {
|
||||
NSString *rootPath = [FATExtFileManager projectRootAppsDirPath];
|
||||
NSString *appDirPath = [rootPath stringByAppendingPathComponent:appId];
|
||||
return appDirPath;
|
||||
}
|
||||
|
||||
/**
|
||||
小程序临时存储目录
|
||||
|
||||
@return NSString *
|
||||
*/
|
||||
+ (NSString *)appTempDirPath:(NSString *)appId {
|
||||
NSString *currtUserId = [FATExtUtil currentUserId];
|
||||
NSString *tempFileCachePath = [[FATExtFileManager appRootDirPath:appId] stringByAppendingPathComponent:[currtUserId fat_md5String]];
|
||||
tempFileCachePath = [tempFileCachePath stringByAppendingPathComponent:@"Temp"];
|
||||
|
||||
return tempFileCachePath;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// FATExtLocationManager.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by beetle_92 on 2022/8/19.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExtLocationManager : CLLocationManager
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// FATExtLocationManager.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by beetle_92 on 2022/8/19.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtLocationManager.h"
|
||||
#import "FATExtAVManager.h"
|
||||
#import "FATExtRecordManager.h"
|
||||
#import "FATExt_LocationUpdateManager.h"
|
||||
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
@implementation FATExtLocationManager
|
||||
|
||||
- (void)startUpdatingLocation {
|
||||
[super startUpdatingLocation];
|
||||
if ([self.delegate isKindOfClass:NSClassFromString(@"FATMapView")]) {
|
||||
return;
|
||||
}
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:NO state:FATCapsuleButtonStateLocation animate:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopUpdatingLocation {
|
||||
[super stopUpdatingLocation];
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[nav controlCapsuleStateButton:YES state:FATCapsuleButtonStateLocation animate:NO];
|
||||
[[FATExtAVManager sharedManager] checkRecordState];
|
||||
[[FATExtRecordManager shareManager] checkRecordState];
|
||||
[[FATExt_LocationUpdateManager sharedManager] checkLocationState];
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// FATExtMapManager.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2021/11/18.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "FATExtPrivateConstant.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExtMapManager : NSObject
|
||||
@property (nonatomic, copy) NSString *pageId;
|
||||
@property (nonatomic, strong) Class mapClass;
|
||||
@property (nonatomic, strong) NSString *googleMapApiKey;
|
||||
@property (nonatomic, strong) NSString *placesApiKey;
|
||||
+ (instancetype)shareInstance;
|
||||
|
||||
@property (nonatomic, strong) NSMutableDictionary *dataDic;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// FATExtMapManager.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2021/11/18.
|
||||
//
|
||||
|
||||
#import "FATExtMapManager.h"
|
||||
#import <FinApplet/FinApplet.h>
|
||||
#import "FATMapViewDelegate.h"
|
||||
#import "FATMapView.h"
|
||||
|
||||
static FATExtMapManager *instance = nil;
|
||||
|
||||
@implementation FATExtMapManager
|
||||
|
||||
+ (instancetype)shareInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[FATExtMapManager alloc] init];
|
||||
});
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [super allocWithZone:zone];
|
||||
});
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_dataDic = [[NSMutableDictionary alloc] init];
|
||||
self.mapClass = FATMapView.class;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// FATRecordManager.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2021/1/21.
|
||||
// Copyright © 2021 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FATExtRecordManager : NSObject
|
||||
|
||||
+ (instancetype)shareManager;
|
||||
|
||||
#pragma mark - new api
|
||||
- (BOOL)startRecordWithData:(NSDictionary *)data appletId:(NSString *)appletId eventBlock:(void (^)(NSInteger eventType,NSString *eventName, NSDictionary *paramDic,NSDictionary *extDic))eventBlock;
|
||||
|
||||
- (BOOL)pauseRecordWithData:(NSDictionary *)data appletId:(NSString *)appletId;
|
||||
|
||||
- (BOOL)resumeRecordWithData:(NSDictionary *)data appletId:(NSString *)appletId;
|
||||
|
||||
- (BOOL)stopRecordWithData:(NSDictionary *)data appletId:(NSString *)appletId;
|
||||
|
||||
- (BOOL)checkRecordWithMethod:(NSString *)method data:(NSDictionary *)data appletId:(NSString *)appletId;
|
||||
|
||||
- (void)sendRecordFrameBufferWithData:(NSDictionary *)data appletId:(NSString *)appletId;
|
||||
|
||||
- (void)checkRecordState;
|
||||
|
||||
@end
|
|
@ -0,0 +1,362 @@
|
|||
//
|
||||
// FATRecordManager.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2021/1/21.
|
||||
// Copyright © 2021 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtRecordManager.h"
|
||||
#import "FATExtRecorder.h"
|
||||
#import "FATExtUtil.h"
|
||||
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
static FATExtRecordManager *instance = nil;
|
||||
|
||||
@interface FATExtRecordManager () <FATExtRecorderDelegate>
|
||||
|
||||
@property (nonatomic, strong) NSMutableDictionary *recordDictionary;
|
||||
|
||||
@property (nonatomic, strong) NSMutableDictionary *recorderDict;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FATExtRecordManager
|
||||
|
||||
+ (instancetype)shareManager {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[FATExtRecordManager alloc] init];
|
||||
instance.recordDictionary = [[NSMutableDictionary alloc] init];
|
||||
instance.recorderDict = [[NSMutableDictionary alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [super allocWithZone:zone];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self p_addNotifications];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - private methods
|
||||
- (void)p_addNotifications {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)_clearAudioSession {
|
||||
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
||||
[audioSession setCategory:AVAudioSessionCategoryPlayback error:NULL];
|
||||
[audioSession setActive:YES error:NULL];
|
||||
}
|
||||
|
||||
#pragma mark - notification handler
|
||||
- (void)applicationDidEnterBackground:(NSNotification *)notice {
|
||||
[self.recorderDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, FATExtRecorder *recorder, BOOL * _Nonnull stop) {
|
||||
if (!recorder.isPausing) {
|
||||
[self pauseRecordWithData:nil appletId:key];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - new api
|
||||
- (BOOL)startRecordWithData:(NSDictionary *)data appletId:(NSString *)appletId eventBlock:(void (^)(NSInteger eventType,NSString *eventName, NSDictionary *paramDic,NSDictionary *extDic))eventBlock {
|
||||
FATExtRecorder *record = [[FATExtRecorder alloc] init];
|
||||
record.delegate = self;
|
||||
record.eventCallBack = eventBlock;
|
||||
BOOL started = [record startRecordWithDict:data appId:appletId];
|
||||
if (started) {
|
||||
[self.recorderDict setObject:record forKey:appletId];
|
||||
NSDictionary *params = @{@"method" : @"onStart"};
|
||||
|
||||
if (eventBlock) {
|
||||
eventBlock(0,@"onRecorderManager",params,nil);
|
||||
}
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:NO state:FATCapsuleButtonStateMicroPhone animate:YES];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)pauseRecordWithData:(NSDictionary *)data appletId:(NSString *)appletId {
|
||||
FATExtRecorder *record = [self.recorderDict objectForKey:appletId];
|
||||
if (!record) {
|
||||
return NO;
|
||||
}
|
||||
BOOL result = [record pauseRecord];
|
||||
if (result) {
|
||||
if (record.eventCallBack) {
|
||||
NSDictionary *params = @{@"method" : @"onPause"};
|
||||
record.eventCallBack(0, @"onRecorderManager", params,nil);
|
||||
}
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:YES state:FATCapsuleButtonStateMicroPhone animate:NO];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)resumeRecordWithData:(NSDictionary *)data appletId:(NSString *)appletId {
|
||||
FATExtRecorder *record = [self.recorderDict objectForKey:appletId];
|
||||
BOOL result = [record resumeRecord];
|
||||
if (result) {
|
||||
if (record.eventCallBack) {
|
||||
NSDictionary *params = @{@"method" : @"onResume"};
|
||||
record.eventCallBack(0, @"onRecorderManager", params,nil);
|
||||
}
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:NO state:FATCapsuleButtonStateMicroPhone animate:YES];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)stopRecordWithData:(NSDictionary *)data appletId:(NSString *)appletId {
|
||||
FATExtRecorder *record = [self.recorderDict objectForKey:appletId];
|
||||
[record stopRecord];
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:YES state:FATCapsuleButtonStateMicroPhone animate:NO];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)checkRecordWithMethod:(NSString *)method data:(NSDictionary *)data appletId:(NSString *)appletId {
|
||||
FATExtRecorder *recorder = [self.recorderDict objectForKey:appletId];
|
||||
if ([method isEqualToString:@"start"]) { // 录制中 或 暂停 时,不能开始
|
||||
if (recorder.isRecording || recorder.isPausing) {
|
||||
NSDictionary *params = @{
|
||||
@"method" : @"onError",
|
||||
@"data" : @{@"errMsg" : @"is recording or paused"},
|
||||
};
|
||||
if (recorder.eventCallBack) {
|
||||
recorder.eventCallBack(0, @"onRecorderManager", params,nil);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
} else if ([method isEqualToString:@"pause"]) { // 非录制中状态,不能暂停
|
||||
if (![recorder isRecording]) {
|
||||
NSDictionary *params = @{
|
||||
@"method" : @"onError",
|
||||
@"data" : @{@"errMsg" : @"not recording"},
|
||||
};
|
||||
if (recorder.eventCallBack) {
|
||||
recorder.eventCallBack(0, @"onRecorderManager", params,nil);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
} else if ([method isEqualToString:@"resume"]) { // 非暂停状态,不能继续录制
|
||||
if (!recorder.isPausing) {
|
||||
NSDictionary *params = @{
|
||||
@"method" : @"onError",
|
||||
@"data" : @{@"errMsg" : @"not paused"},
|
||||
};
|
||||
if (recorder.eventCallBack) {
|
||||
recorder.eventCallBack(0, @"onRecorderManager", params,nil);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
} else if ([method isEqualToString:@"stop"]) { // 非开始状态,不能停止
|
||||
if (!recorder.isStarted) {
|
||||
NSDictionary *params = @{
|
||||
@"method" : @"onError",
|
||||
@"data" : @{@"errMsg" : @"recorder not start"},
|
||||
};
|
||||
if (recorder.eventCallBack) {
|
||||
recorder.eventCallBack(0, @"onRecorderManager", params,nil);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)sendRecordFrameBufferWithData:(NSDictionary *)data appletId:(NSString *)appletId {
|
||||
FATExtRecorder *recorder = [self.recorderDict objectForKey:appletId];
|
||||
recorder.frameState = FATFrameStatePrepareToSend;
|
||||
if (recorder.frameInfoArray.count == 0) {
|
||||
recorder.waitToSendBuffer = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
[self sendFrameDataWithRecorder:recorder withFrameBufferData:nil];
|
||||
}
|
||||
|
||||
- (void)sendFrameDataWithRecorder:(FATExtRecorder *)recorder withFrameBufferData:(NSData *)data {
|
||||
// 0.判断是否为分包小程序
|
||||
|
||||
recorder.waitToSendBuffer = NO;
|
||||
recorder.frameState = FATFrameStateAlreadyWillSend;
|
||||
// 1.从文件中取出buffer挂载至jscore上
|
||||
NSDictionary *dict = [recorder.frameInfoArray firstObject];
|
||||
if (!dict) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NSNumber *frameIndex = dict[@"frameIndex"];
|
||||
NSString *frameBufferKey = dict[@"frameBufferKey"];
|
||||
NSString *frameBufferPath = dict[@"frameBufferPath"];
|
||||
NSNumber *isLastFrame = dict[@"isLastFrame"];
|
||||
|
||||
NSData *frameData;
|
||||
if (data) {
|
||||
frameData = data;
|
||||
} else {
|
||||
frameData = [NSData dataWithContentsOfFile:frameBufferPath options:0 error:nil];
|
||||
}
|
||||
Byte *bytes = (Byte *)frameData.bytes;
|
||||
NSMutableArray *arrayM = [NSMutableArray array];
|
||||
for (int i = 0; i < frameData.length; i++) {
|
||||
int number = (int)bytes[i];
|
||||
[arrayM addObject:@(number)];
|
||||
}
|
||||
|
||||
NSDictionary *params = @{
|
||||
@"method" : @"onFrameRecorded",
|
||||
@"data" : @{
|
||||
@"isLastFrame": isLastFrame,
|
||||
@"buffer_id": frameBufferKey
|
||||
}
|
||||
};
|
||||
NSDictionary *extDic = @{@"jsContextKey":frameBufferKey,@"jsContextValue":arrayM};
|
||||
[recorder.frameInfoArray removeObject:dict];
|
||||
if (recorder.eventCallBack) {
|
||||
recorder.eventCallBack(0, @"onRecorderManager", params,extDic);
|
||||
}
|
||||
// [[FATExtCoreEventManager shareInstance] sendToServiceWithAppId:[[FATClient sharedClient] currentApplet].appId eventName:@"onRecorderManager" paramDict:params];
|
||||
// 2.将帧信息从数组中删除
|
||||
recorder.frameState = FATFrameStateAlreadySent;
|
||||
}
|
||||
|
||||
- (void)checkRecordState {
|
||||
FATAppletInfo *appInfo = [[FATClient sharedClient] currentApplet];
|
||||
if (!appInfo.appId) {
|
||||
return;
|
||||
}
|
||||
FATExtRecorder *recorder = [self.recorderDict objectForKey:appInfo.appId];
|
||||
if ([recorder isRecording]) {
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:NO state:FATCapsuleButtonStateMicroPhone animate:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - FATExtRecorderDelegate
|
||||
- (void)extRecorder:(FATExtRecorder *)recorder
|
||||
onFrameData:(NSData *)frameData
|
||||
frameIndex:(NSInteger)frameIndex
|
||||
isLastFrame:(BOOL)isLastFrame {
|
||||
NSString *frameBufferKey = [recorder.recordFilePath lastPathComponent];
|
||||
frameBufferKey = [frameBufferKey stringByDeletingPathExtension];
|
||||
frameBufferKey = [frameBufferKey stringByAppendingFormat:@"_%ld", (long)frameIndex];
|
||||
|
||||
NSString *frameFileName = [frameBufferKey stringByAppendingPathExtension:recorder.recordFilePath.pathExtension];
|
||||
NSString *frameBufferPath = [[recorder.recordFilePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:frameFileName];
|
||||
|
||||
NSDictionary *infoDict = @{@"frameIndex": @(frameIndex),
|
||||
@"isLastFrame": @(isLastFrame),
|
||||
@"frameBufferKey": frameBufferKey,
|
||||
@"frameBufferPath": frameBufferPath
|
||||
};
|
||||
|
||||
if ([NSThread isMainThread]) {
|
||||
[recorder.frameInfoArray addObject:infoDict];
|
||||
if (recorder.frameState == FATFrameStatePrepareToSend || recorder.waitToSendBuffer) {
|
||||
// 取出录音数据发送
|
||||
[self sendFrameDataWithRecorder:recorder withFrameBufferData:frameData];
|
||||
}
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[recorder.frameInfoArray addObject:infoDict];
|
||||
if (recorder.frameState == FATFrameStatePrepareToSend || recorder.waitToSendBuffer) {
|
||||
// 取出录音数据发送
|
||||
[self sendFrameDataWithRecorder:recorder withFrameBufferData:frameData];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)extRecorder:(FATExtRecorder *)recorder onError:(NSError *)error {
|
||||
NSString *msg = error.localizedDescription ? : @"fail";
|
||||
NSDictionary *params = @{
|
||||
@"method" : @"onError",
|
||||
@"data" : @{@"errMsg" : msg},
|
||||
};
|
||||
if (recorder.eventCallBack) {
|
||||
recorder.eventCallBack(0, @"onRecorderManager", params,nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)extRecorderDidCompletion:(FATExtRecorder *)recorder {
|
||||
NSString *filePath = recorder.recordFilePath;
|
||||
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
|
||||
float duration = [FATExtUtil durtaionWithFileURL:fileURL];
|
||||
long long fileSize = [FATExtUtil fileSizeWithFileURL:fileURL];
|
||||
NSString *tempFilePath = [@"finfile://" stringByAppendingString:filePath.lastPathComponent];
|
||||
|
||||
NSMutableDictionary *result = [NSMutableDictionary dictionary];
|
||||
[result setValue:tempFilePath forKey:@"tempFilePath"];
|
||||
[result setValue:@(duration) forKey:@"duration"];
|
||||
[result setValue:@(fileSize) forKey:@"fileSize"];
|
||||
|
||||
NSDictionary *params = @{
|
||||
@"method" : @"onStop",
|
||||
@"data" : result,
|
||||
};
|
||||
[self.recorderDict removeObjectForKey:recorder.recorderId];
|
||||
[self _clearAudioSession];
|
||||
if (recorder.eventCallBack) {
|
||||
recorder.eventCallBack(0, @"onRecorderManager", params,nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)extRecorderBeginInterruption:(FATExtRecorder *)recorder {
|
||||
NSDictionary *params = @{@"method" : @"onInterruptionBegin"};
|
||||
if (recorder.eventCallBack) {
|
||||
recorder.eventCallBack(0, @"onRecorderManager", params,nil);
|
||||
}
|
||||
// 暂停录制
|
||||
[self pauseRecordWithData:nil appletId:recorder.recorderId];
|
||||
}
|
||||
|
||||
- (void)extRecorderEndInterruption:(FATExtRecorder *)recorder withOptions:(NSUInteger)flags {
|
||||
NSDictionary *params = @{@"method" : @"onInterruptionEnd"};
|
||||
if (recorder.eventCallBack) {
|
||||
recorder.eventCallBack(0, @"onRecorderManager", params,nil);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// FATMapViewDelegate.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王滔 on 2021/11/24.
|
||||
// Copyright © 2021 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef FATMapViewDelegate_h
|
||||
#define FATMapViewDelegate_h
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol FATMapViewDelegate <NSObject>
|
||||
|
||||
@required
|
||||
|
||||
/// 发送Subscribe事件给page层
|
||||
/// eventName 事件名
|
||||
/// resultDic 事件的参数
|
||||
@property (nonatomic, copy) void(^eventCallBack)(NSString *eventName, NSDictionary *paramDic);
|
||||
|
||||
- (instancetype)initWithParam:(NSDictionary *)param mapPageId:(NSString *)pageId;
|
||||
|
||||
- (void)updateWithParam:(NSDictionary *)param;
|
||||
|
||||
/// 获取中心点的经纬度
|
||||
- (NSDictionary *)fat_getCenter;
|
||||
|
||||
/// 计算缩放级别
|
||||
- (double)fat_getScale;
|
||||
|
||||
/// 移动到指定位置
|
||||
/// @param data 要移动的经纬度
|
||||
- (NSString *)fat_moveToLocation:(NSDictionary *)data;
|
||||
|
||||
/// 缩放地图,展示所有的经纬度
|
||||
/// @param data 对应的数据
|
||||
- (void)fat_includePoints:(NSDictionary *)data;
|
||||
|
||||
/// 获取左下,右上角的经纬度信息
|
||||
- (NSDictionary *)fat_mapgetRegion;
|
||||
|
||||
/// 获取屏幕上的点对应的经纬度,坐标原点为地图左上角。
|
||||
- (NSDictionary *)fat_fromScreenLocation;
|
||||
|
||||
/// 获取经纬度对应的屏幕坐标,坐标原点为地图左上角
|
||||
/// @param data 包含经纬度信息
|
||||
- (CGPoint)fat_toScreenLocation:(NSDictionary *)data;
|
||||
|
||||
///// 打开地图app进行导航
|
||||
- (void)fat_openMapApp:(NSDictionary *)data;
|
||||
|
||||
/// 在地图上添加大头针
|
||||
- (void)fat_addMarkers:(NSDictionary *)data;
|
||||
|
||||
/// 在地图上移除大头针
|
||||
- (void)fat_removeMarkers:(NSDictionary *)data;
|
||||
|
||||
/// 在地图上平移大头针
|
||||
- (BOOL)fat_translateMarker:(NSDictionary *)data;
|
||||
|
||||
/// 沿指定路径移动 marker
|
||||
- (BOOL)fat_moveAlong:(NSDictionary *)data;
|
||||
|
||||
@optional
|
||||
|
||||
/// 设置地图中心点偏移,向后向下为增长,屏幕比例范围(0.25~0.75),默认偏移为[0.5, 0.5]
|
||||
- (void)mapSetCenterOffset:(NSDictionary *)data;
|
||||
|
||||
- (void)fat_setLocMarkerIcon:(NSDictionary *)data;
|
||||
|
||||
/// 获取当前地图的旋转角,地图sdk未实现返回nil
|
||||
- (NSDictionary *)fat_getRotate;
|
||||
|
||||
/// 获取当前地图的倾斜角
|
||||
- (NSDictionary *)fat_getskew;
|
||||
|
||||
/// 用于customCallout的数据更新
|
||||
- (void)updateNativeMapMarkers:(NSDictionary *)param;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* FATMapViewDelegate_h */
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// FATExtHelper.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2020/8/19.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface FATExtHelper : NSObject
|
||||
|
||||
+ (UIImage *)fat_ext_imageFromBundleWithName:(NSString *)imageName;
|
||||
|
||||
@end
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// FATExtHelper.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2020/8/19.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtHelper.h"
|
||||
|
||||
@implementation FATExtHelper
|
||||
|
||||
+ (UIImage *)fat_ext_imageFromBundleWithName:(NSString *)imageName {
|
||||
NSString *bundleResourcePath = [NSBundle bundleForClass:[FATExtHelper class]].resourcePath;
|
||||
NSString *assetPath = [bundleResourcePath stringByAppendingPathComponent:@"FinAppletExt.bundle"];
|
||||
NSBundle *assetBundle = [NSBundle bundleWithPath:assetPath];
|
||||
NSString *path = [[assetBundle bundlePath] stringByAppendingPathComponent:[NSString stringWithFormat:@"/%@", imageName]];
|
||||
return [UIImage imageWithContentsOfFile:path];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// FATExtUtil.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2021/1/25.
|
||||
// Copyright © 2021 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
@interface FATExtUtil : NSObject
|
||||
|
||||
+ (NSString *)tmpDirWithAppletId:(NSString *)appletId;
|
||||
|
||||
+ (NSString *)fat_md5WithBytes:(char *)bytes length:(NSUInteger)length;
|
||||
|
||||
+ (NSString *)jsonStringFromDict:(NSDictionary *)dict;
|
||||
|
||||
+ (NSString *)jsonStringFromArray:(NSArray *)array;
|
||||
|
||||
+ (NSString *)signFromDict:(NSDictionary *)dict;
|
||||
|
||||
+ (NSString *)realPathForFINFile:(NSString *)finfile appId:(NSString *)appId;
|
||||
|
||||
/// 获取音频文件时长
|
||||
/// @param fileURL 文件url(必须为AVURLAsset可解码的文件格式,如 .caf .aac .wav .mp3 等)
|
||||
+ (float)durtaionWithFileURL:(NSURL *)fileURL;
|
||||
|
||||
/// 获取音频文件大小
|
||||
/// @param fileURL 文件url
|
||||
+ (long long)fileSizeWithFileURL:(NSURL *)fileURL;
|
||||
|
||||
/// 获取userId
|
||||
+ (NSString *)currentUserId;
|
||||
|
||||
/**
|
||||
返回是否设置了产品标识。
|
||||
*/
|
||||
+ (BOOL)currentProductIdentificationIsEmpty;
|
||||
|
||||
/**
|
||||
返回设置了的产品标识。
|
||||
*/
|
||||
+ (NSString *)currentProductIdentification;
|
||||
|
||||
+ (NSString *)getAppName;
|
||||
|
||||
+ (void)getNearbyPlacesByCategory:(NSString *)category coordinates:(CLLocationCoordinate2D)coordinates radius:(NSInteger)radius token:(NSString *)token completion:(void (^)(NSDictionary *))completion;
|
||||
|
||||
+ (NSArray *)convertPlaceDictToArray:(NSDictionary*)dict;
|
||||
@end
|
|
@ -0,0 +1,263 @@
|
|||
//
|
||||
// FATExtUtil.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2021/1/25.
|
||||
// Copyright © 2021 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtUtil.h"
|
||||
#import "FATExtFileManager.h"
|
||||
#import "FATExtMapManager.h"
|
||||
#import "fincore.h"
|
||||
#import "FATMapPlace.h"
|
||||
|
||||
#import <FinApplet/FinApplet.h>
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
#define FAT_EXT_FILE_SCHEMA @"finfile://"
|
||||
|
||||
@implementation FATExtUtil
|
||||
|
||||
+ (NSString *)tmpDirWithAppletId:(NSString *)appletId {
|
||||
if (!appletId) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *cacheDir = [FATExtFileManager appTempDirPath:appletId];
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
BOOL flag = YES;
|
||||
if (![fileManager fileExistsAtPath:cacheDir isDirectory:&flag]) {
|
||||
[fileManager createDirectoryAtPath:cacheDir withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
return cacheDir;
|
||||
}
|
||||
|
||||
+ (NSString *)fat_md5WithBytes:(char *)bytes length:(NSUInteger)length {
|
||||
unsigned char result[16];
|
||||
CC_MD5(bytes, (CC_LONG)length, result);
|
||||
return [NSString stringWithFormat:
|
||||
@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
result[0], result[1], result[2], result[3],
|
||||
result[4], result[5], result[6], result[7],
|
||||
result[8], result[9], result[10], result[11],
|
||||
result[12], result[13], result[14], result[15]];
|
||||
}
|
||||
|
||||
+ (NSString *)jsonStringFromDict:(NSDictionary *)dict {
|
||||
if (!dict || ![dict isKindOfClass:[NSDictionary class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
+ (NSString *)jsonStringFromArray:(NSArray *)array {
|
||||
if (!array || ![array isKindOfClass:[NSArray class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:array options:NSJSONWritingPrettyPrinted error:nil];
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
+ (NSString *)signFromDict:(NSDictionary *)dict {
|
||||
if (!dict || ![dict isKindOfClass:[NSDictionary class]]) {
|
||||
return nil;
|
||||
}
|
||||
NSArray *keys = [dict allKeys];
|
||||
NSArray *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
|
||||
return [obj1 compare:obj2 options:NSNumericSearch]; //正序
|
||||
}];
|
||||
|
||||
NSString *plainText = @"";
|
||||
for (NSString *key in sortedKeys) {
|
||||
NSString *and = [plainText isEqualToString:@""] ? @"" : @"&";
|
||||
NSString *value = [dict valueForKey:key];
|
||||
// if ([value isKindOfClass:[NSDictionary class]]) {
|
||||
// NSDictionary *dictValue = (NSDictionary *)value;
|
||||
// value = [FATExtUtil jsonStringFromDict:dictValue];
|
||||
// } else if ([key isKindOfClass:[NSArray class]]) {
|
||||
// NSArray *arrayValue = (NSArray *)value;
|
||||
// value = [FATExtUtil jsonStringFromArray:arrayValue];
|
||||
// }
|
||||
if ([key isEqualToString:@"sign"]) {
|
||||
continue;
|
||||
}
|
||||
if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]]) {
|
||||
NSString *append = [NSString stringWithFormat:@"%@%@=%@", and, key, value];
|
||||
plainText = [plainText stringByAppendingString:append];
|
||||
}
|
||||
}
|
||||
// NSLog(@"扩展:%@", plainText);
|
||||
NSString *digest = [[[SDKCoreClient sharedInstance].finoLicenseService fin_messageDigest:plainText] uppercaseString];
|
||||
NSData *data = [digest dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *encodeData = [[SDKCoreClient sharedInstance].finoLicenseService fin_encodeSMContent:data];
|
||||
NSString *sign = [[NSString alloc] initWithData:encodeData encoding:NSUTF8StringEncoding];
|
||||
// NSLog(@"扩展sign:%@", sign);
|
||||
|
||||
return sign;
|
||||
}
|
||||
|
||||
/// 获取音频文件时长
|
||||
/// @param fileURL 文件url(必须为AVURLAsset可解码的文件格式,如 .caf .aac .wav .mp3 等)
|
||||
+ (float)durtaionWithFileURL:(NSURL *)fileURL {
|
||||
NSDictionary *options = @{AVURLAssetPreferPreciseDurationAndTimingKey : @(YES)};
|
||||
AVURLAsset *recordAsset = [AVURLAsset URLAssetWithURL:fileURL options:options];
|
||||
// 录音的时长,单位ms
|
||||
CMTime durationTime = recordAsset.duration;
|
||||
durationTime.value = durationTime.value;
|
||||
float seconds = CMTimeGetSeconds(durationTime);
|
||||
float duration = seconds * 1000;
|
||||
return duration;
|
||||
}
|
||||
|
||||
/// 获取音频文件大小
|
||||
/// @param fileURL 文件url
|
||||
+ (long long)fileSizeWithFileURL:(NSURL *)fileURL {
|
||||
// 录音文件的大小,单位Byte
|
||||
NSFileManager *manager = [NSFileManager defaultManager];
|
||||
long long fileSize = [[manager attributesOfItemAtPath:fileURL.path error:nil] fileSize];
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
+ (NSString *)currentUserId {
|
||||
NSString *currentUserId = [FATClient sharedClient].config.currentUserId;
|
||||
NSString *productIdentification = [FATClient sharedClient].config.productIdentification;
|
||||
if (!currentUserId || currentUserId.length == 0) {
|
||||
if ([NSString fat_isEmptyWithString:productIdentification]) {
|
||||
currentUserId = @"finclip_default";
|
||||
} else {
|
||||
currentUserId = productIdentification;
|
||||
}
|
||||
}
|
||||
return currentUserId;
|
||||
}
|
||||
|
||||
+ (BOOL)currentProductIdentificationIsEmpty {
|
||||
NSString *productIdentification = [FATClient sharedClient].config.productIdentification;
|
||||
return [NSString fat_isEmptyWithString:productIdentification];
|
||||
}
|
||||
|
||||
+ (NSString *)currentProductIdentification {
|
||||
NSString *productIdentification = [FATClient sharedClient].config.productIdentification;
|
||||
return productIdentification;
|
||||
}
|
||||
|
||||
+ (NSString *)getAppName {
|
||||
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
|
||||
NSString *appName = [infoDict valueForKey:@"CFBundleDisplayName"];
|
||||
if (!appName) appName = [infoDict valueForKey:@"CFBundleName"];
|
||||
if (!appName) appName = [infoDict valueForKey:@"CFBundleExecutable"];
|
||||
return appName;
|
||||
}
|
||||
|
||||
+ (void)getNearbyPlacesByCategory:(NSString *)category coordinates:(CLLocationCoordinate2D)coordinates radius:(NSInteger)radius token:(NSString *)token
|
||||
completion:(void (^)(NSDictionary *))completion {
|
||||
NSURL *url = [NSURL URLWithString:[self searchApiHost]];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
request.HTTPMethod = @"GET";
|
||||
|
||||
if (token && [token length] > 0) {
|
||||
NSDictionary *parameters = @{
|
||||
@"key" : [FATExtMapManager shareInstance].googleMapApiKey,
|
||||
@"pagetoken" : token
|
||||
};
|
||||
NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
||||
NSMutableArray *queryItems = [NSMutableArray array];
|
||||
for (NSString *key in parameters) {
|
||||
NSString *value = [NSString stringWithFormat:@"%@", parameters[key]];
|
||||
[queryItems addObject:[NSURLQueryItem queryItemWithName:key value:value]];
|
||||
}
|
||||
urlComponents.queryItems = queryItems;
|
||||
request.URL = urlComponents.URL;
|
||||
} else {
|
||||
NSDictionary *parameters = @{
|
||||
@"key" : [FATExtMapManager shareInstance].placesApiKey,
|
||||
@"radius" : @(radius),
|
||||
@"location" : [NSString stringWithFormat:@"%f,%f", coordinates.latitude, coordinates.longitude],
|
||||
@"type" : [category lowercaseString]
|
||||
};
|
||||
NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
||||
NSMutableArray *queryItems = [NSMutableArray array];
|
||||
for (NSString *key in parameters) {
|
||||
NSString *value = [NSString stringWithFormat:@"%@", parameters[key]];
|
||||
[queryItems addObject:[NSURLQueryItem queryItemWithName:key value:value]];
|
||||
}
|
||||
urlComponents.queryItems = queryItems;
|
||||
request.URL = urlComponents.URL;
|
||||
}
|
||||
|
||||
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (error) {
|
||||
NSLog(@"Error: %@", error);
|
||||
completion(nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
NSError *jsonError;
|
||||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
|
||||
if (jsonError) {
|
||||
NSLog(@"Error: %@", jsonError);
|
||||
completion(nil);
|
||||
} else {
|
||||
completion(json);
|
||||
}
|
||||
} else {
|
||||
completion(nil);
|
||||
}
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
}
|
||||
|
||||
+ (NSArray *)convertPlaceDictToArray:(NSDictionary*)dict{
|
||||
NSMutableArray *placeArrayM = [NSMutableArray array];
|
||||
for (NSDictionary *dictItem in [dict objectForKey:@"results"]) {
|
||||
FATMapPlace *place = [[FATMapPlace alloc] init];
|
||||
place.name = dictItem[@"name"];
|
||||
place.address = dictItem[@"vicinity"];
|
||||
FATMapPlace *mark = [[FATMapPlace alloc] init];
|
||||
NSDictionary *dict = dictItem[@"geometry"];
|
||||
mark.name = dictItem[@"name"];
|
||||
mark.address = dictItem[@"vicinity"];
|
||||
double lat = [dict[@"location"][@"lat"] doubleValue];
|
||||
double lng = [dict[@"location"][@"lng"] doubleValue];
|
||||
place.location = [[CLLocation alloc] initWithLatitude:lat longitude:lng] ;
|
||||
[placeArrayM addObject:place];
|
||||
}
|
||||
return placeArrayM;
|
||||
}
|
||||
|
||||
+ (NSArray *)getCategories {
|
||||
NSArray *list = @[@"Places",@"Bakery", @"Doctor", @"School", @"Taxi_stand", @"Hair_care", @"Restaurant", @"Pharmacy", @"Atm", @"Gym", @"Store", @"Spa"];
|
||||
return list;
|
||||
}
|
||||
|
||||
+ (NSString *)searchApiHost {
|
||||
return @"https://maps.googleapis.com/maps/api/place/nearbysearch/json";
|
||||
}
|
||||
|
||||
+ (NSString *)googlePhotosHost {
|
||||
return @"https://maps.googleapis.com/maps/api/place/photo";
|
||||
}
|
||||
|
||||
+ (NSString *)googlePlaceDetailsHost {
|
||||
return @"https://maps.googleapis.com/maps/api/place/details/json";
|
||||
}
|
||||
|
||||
|
||||
@end
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// FATLocationManager.h
|
||||
// FinApplet
|
||||
//
|
||||
// Created by Haley on 2020/4/7.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
@interface FATLocationManager : NSObject
|
||||
|
||||
@property (nonatomic, strong) CLLocation *location;
|
||||
|
||||
@property (nonatomic, strong) CLPlacemark *placemark;
|
||||
|
||||
+ (instancetype)manager;
|
||||
|
||||
- (void)updateLocation;
|
||||
|
||||
@end
|
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// FATLocationManager.m
|
||||
// FinApplet
|
||||
//
|
||||
// Created by Haley on 2020/4/7.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATLocationManager.h"
|
||||
#import "FATWGS84ConvertToGCJ02.h"
|
||||
#import "FATExtLocationManager.h"
|
||||
|
||||
static FATLocationManager *instance = nil;
|
||||
|
||||
@interface FATLocationManager () <CLLocationManagerDelegate>
|
||||
|
||||
@property (nonatomic, strong) FATExtLocationManager *locationManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FATLocationManager
|
||||
|
||||
+ (instancetype)manager {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[FATLocationManager alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [super allocWithZone:zone];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (void)updateLocation {
|
||||
if (![FATExtLocationManager locationServicesEnabled]) {
|
||||
return;
|
||||
}
|
||||
|
||||
CLAuthorizationStatus status = [FATExtLocationManager authorizationStatus];
|
||||
if (status == kCLAuthorizationStatusAuthorizedWhenInUse ||
|
||||
status == kCLAuthorizationStatusAuthorizedAlways ||
|
||||
status == kCLAuthorizationStatusNotDetermined) {
|
||||
//定位功能可用
|
||||
FATExtLocationManager *locationManager = [[FATExtLocationManager alloc] init];
|
||||
self.locationManager = locationManager;
|
||||
locationManager.delegate = self;
|
||||
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
|
||||
[locationManager requestWhenInUseAuthorization];
|
||||
[locationManager startUpdatingLocation];
|
||||
|
||||
} else if (status == kCLAuthorizationStatusDenied) {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
|
||||
CLLocation *newLocation = [locations firstObject];
|
||||
//判断是不是属于国内范围
|
||||
if (![FATWGS84ConvertToGCJ02ForAMapView isLocationOutOfChina:[newLocation coordinate]]) {
|
||||
//转换后的coord
|
||||
CLLocationCoordinate2D coord = [FATWGS84ConvertToGCJ02ForAMapView transformFromWGSToGCJ:[newLocation coordinate]];
|
||||
newLocation = [[CLLocation alloc] initWithLatitude:coord.latitude longitude:coord.longitude];
|
||||
}
|
||||
self.location = newLocation;
|
||||
|
||||
[self.locationManager stopUpdatingLocation];
|
||||
|
||||
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
|
||||
[geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray<CLPlacemark *> *_Nullable placemarks, NSError *_Nullable error) {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (placemarks.count > 0) {
|
||||
CLPlacemark *placemark = [placemarks objectAtIndex:0];
|
||||
self.placemark = placemark;
|
||||
// //获取省份
|
||||
// NSString *province = placemark.administrativeArea;
|
||||
// // 位置名
|
||||
// NSLog(@"name,%@", placemark.name);
|
||||
// // 街道
|
||||
// NSLog(@"thoroughfare,%@", placemark.thoroughfare);
|
||||
// // 子街道
|
||||
// NSLog(@"subThoroughfare,%@", placemark.subThoroughfare);
|
||||
// // 市
|
||||
// NSLog(@"locality,%@", placemark.locality);
|
||||
// // 区
|
||||
// NSLog(@"subLocality,%@", placemark.subLocality);
|
||||
// // 国家
|
||||
// NSLog(@"country,%@", placemark.country);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// FATWGS84ConvertToGCJ02.h
|
||||
// FinApplet
|
||||
//
|
||||
// Created by 杨涛 on 2018/8/9.
|
||||
// Copyright © 2018年 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <MapKit/MapKit.h>
|
||||
|
||||
@interface FATWGS84ConvertToGCJ02ForAMapView : NSObject
|
||||
//判断是否已经超出中国范围
|
||||
+ (BOOL)isLocationOutOfChina:(CLLocationCoordinate2D)location;
|
||||
//转GCJ-02
|
||||
+ (CLLocationCoordinate2D)transformFromWGSToGCJ:(CLLocationCoordinate2D)wgsLoc;
|
||||
// gcj02转wgs82
|
||||
+ (CLLocationCoordinate2D)transformFromGCJToWGS:(CLLocationCoordinate2D)wgsLoc;
|
||||
@end
|
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// FATWGS84ConvertToGCJ02ForAMapView.m
|
||||
// FinApplet
|
||||
//
|
||||
// Created by 杨涛 on 2018/8/9.
|
||||
// Copyright © 2018年 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATWGS84ConvertToGCJ02.h"
|
||||
#define RANGE_LON_MAX 137.8347
|
||||
#define RANGE_LON_MIN 72.004
|
||||
#define RANGE_LAT_MAX 55.8271
|
||||
#define RANGE_LAT_MIN 0.8293
|
||||
#define LAT_OFFSET_0(x,y) -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(fabs(x))
|
||||
#define LAT_OFFSET_1 (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0
|
||||
#define LAT_OFFSET_2 (20.0 * sin(y * M_PI) + 40.0 * sin(y / 3.0 * M_PI)) * 2.0 / 3.0
|
||||
#define LAT_OFFSET_3 (160.0 * sin(y / 12.0 * M_PI) + 320 * sin(y * M_PI / 30.0)) * 2.0 / 3.0
|
||||
|
||||
#define LON_OFFSET_0(x,y) 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(fabs(x))
|
||||
#define LON_OFFSET_1 (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0
|
||||
#define LON_OFFSET_2 (20.0 * sin(x * M_PI) + 40.0 * sin(x / 3.0 * M_PI)) * 2.0 / 3.0
|
||||
#define LON_OFFSET_3 (150.0 * sin(x / 12.0 * M_PI) + 300.0 * sin(x / 30.0 * M_PI)) * 2.0 / 3.0
|
||||
#define jzA 6378245.0
|
||||
#define jzEE 0.00669342162296594323
|
||||
|
||||
static const double fat_a = 6378245.0;
|
||||
static const double fat_ee = 0.00669342162296594323;
|
||||
|
||||
@implementation FATWGS84ConvertToGCJ02ForAMapView
|
||||
+ (CLLocationCoordinate2D)transformFromWGSToGCJ:(CLLocationCoordinate2D)wgsLoc {
|
||||
CLLocationCoordinate2D adjustLoc;
|
||||
if ([self isLocationOutOfChina:wgsLoc]) {
|
||||
adjustLoc = wgsLoc;
|
||||
} else {
|
||||
double adjustLat = [self transformLatWithX:wgsLoc.longitude - 105.0 withY:wgsLoc.latitude - 35.0];
|
||||
double adjustLon = [self transformLonWithX:wgsLoc.longitude - 105.0 withY:wgsLoc.latitude - 35.0];
|
||||
double radLat = wgsLoc.latitude / 180.0 * M_PI;
|
||||
double magic = sin(radLat);
|
||||
magic = 1 - fat_ee * magic * magic;
|
||||
double sqrtMagic = sqrt(magic);
|
||||
adjustLat = (adjustLat * 180.0) / ((fat_a * (1 - fat_ee)) / (magic * sqrtMagic) * M_PI);
|
||||
adjustLon = (adjustLon * 180.0) / (fat_a / sqrtMagic * cos(radLat) * M_PI);
|
||||
adjustLoc.latitude = wgsLoc.latitude + adjustLat - 0.00039900; // 减去这个数字 完全是凑数,准确性有待验证
|
||||
adjustLoc.longitude = wgsLoc.longitude + adjustLon;
|
||||
}
|
||||
return adjustLoc;
|
||||
}
|
||||
|
||||
+ (CLLocationCoordinate2D)transformFromGCJToWGS:(CLLocationCoordinate2D)wgsLoc {
|
||||
return [self gcj02Decrypt:wgsLoc.latitude gjLon:wgsLoc.longitude];
|
||||
}
|
||||
|
||||
+ (CLLocationCoordinate2D)gcj02Decrypt:(double)gjLat gjLon:(double)gjLon {
|
||||
CLLocationCoordinate2D gPt = [self gcj02Encrypt:gjLat bdLon:gjLon];
|
||||
double dLon = gPt.longitude - gjLon;
|
||||
double dLat = gPt.latitude - gjLat;
|
||||
CLLocationCoordinate2D pt;
|
||||
pt.latitude = gjLat - dLat;
|
||||
pt.longitude = gjLon - dLon;
|
||||
return pt;
|
||||
}
|
||||
|
||||
+ (CLLocationCoordinate2D)gcj02Encrypt:(double)ggLat bdLon:(double)ggLon {
|
||||
CLLocationCoordinate2D resPoint;
|
||||
double mgLat;
|
||||
double mgLon;
|
||||
if ([self outOfChina:ggLat bdLon:ggLon]) {
|
||||
resPoint.latitude = ggLat;
|
||||
resPoint.longitude = ggLon;
|
||||
return resPoint;
|
||||
}
|
||||
double dLat = [self transformLat:(ggLon - 105.0)bdLon:(ggLat - 35.0)];
|
||||
double dLon = [self transformLon:(ggLon - 105.0) bdLon:(ggLat - 35.0)];
|
||||
double radLat = ggLat / 180.0 * M_PI;
|
||||
double magic = sin(radLat);
|
||||
magic = 1 - jzEE * magic * magic;
|
||||
double sqrtMagic = sqrt(magic);
|
||||
dLat = (dLat * 180.0) / ((jzA * (1 - jzEE)) / (magic * sqrtMagic) * M_PI);
|
||||
dLon = (dLon * 180.0) / (jzA / sqrtMagic * cos(radLat) * M_PI);
|
||||
mgLat = ggLat + dLat;
|
||||
mgLon = ggLon + dLon;
|
||||
|
||||
resPoint.latitude = mgLat;
|
||||
resPoint.longitude = mgLon;
|
||||
return resPoint;
|
||||
}
|
||||
|
||||
+ (BOOL)outOfChina:(double)lat bdLon:(double)lon {
|
||||
if (lon < RANGE_LON_MIN || lon > RANGE_LON_MAX)
|
||||
return true;
|
||||
if (lat < RANGE_LAT_MIN || lat > RANGE_LAT_MAX)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
+ (double)transformLat:(double)x bdLon:(double)y {
|
||||
double ret = LAT_OFFSET_0(x, y);
|
||||
ret += LAT_OFFSET_1;
|
||||
ret += LAT_OFFSET_2;
|
||||
ret += LAT_OFFSET_3;
|
||||
return ret;
|
||||
}
|
||||
|
||||
+ (double)transformLon:(double)x bdLon:(double)y {
|
||||
double ret = LON_OFFSET_0(x, y);
|
||||
ret += LON_OFFSET_1;
|
||||
ret += LON_OFFSET_2;
|
||||
ret += LON_OFFSET_3;
|
||||
return ret;
|
||||
}
|
||||
|
||||
//判断是不是在中国
|
||||
+ (BOOL)isLocationOutOfChina:(CLLocationCoordinate2D)location {
|
||||
if (location.longitude < 72.004 || location.longitude > 137.8347 || location.latitude < 0.8293 || location.latitude > 55.8271)
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (double)transformLatWithX:(double)x withY:(double)y {
|
||||
double lat = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(fabs(x));
|
||||
lat += (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0;
|
||||
lat += (20.0 * sin(y * M_PI) + 40.0 * sin(y / 3.0 * M_PI)) * 2.0 / 3.0;
|
||||
lat += (160.0 * sin(y / 12.0 * M_PI) + 320 * sin(y * M_PI / 30.0)) * 2.0 / 3.0;
|
||||
return lat;
|
||||
}
|
||||
|
||||
+ (double)transformLonWithX:(double)x withY:(double)y {
|
||||
double lon = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(fabs(x));
|
||||
lon += (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0;
|
||||
lon += (20.0 * sin(x * M_PI) + 40.0 * sin(x / 3.0 * M_PI)) * 2.0 / 3.0;
|
||||
lon += (150.0 * sin(x / 12.0 * M_PI) + 300.0 * sin(x / 30.0 * M_PI)) * 2.0 / 3.0;
|
||||
return lon;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// UIView+FATExtSafaFrame.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2023/5/25.
|
||||
// Copyright © 2023 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface UIView (FATExtSafaFrame)
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// UIView+FATExtSafaFrame.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2023/5/25.
|
||||
// Copyright © 2023 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "UIView+FATExtSafaFrame.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@implementation UIView (FATExtSafaFrame)
|
||||
|
||||
+ (void)load{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
Method method1 = class_getInstanceMethod(self.class, @selector(setFrame:));
|
||||
Method method2 = class_getInstanceMethod(self.class, @selector(fatSafe_setFrame:));
|
||||
method_exchangeImplementations(method1, method2);
|
||||
Method method3 = class_getInstanceMethod(self.class, @selector(setCenter:));
|
||||
Method method4 = class_getInstanceMethod(self.class, @selector(fatSafe_setCenter:));
|
||||
method_exchangeImplementations(method3, method4);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)fatSafe_setFrame:(CGRect)frame{
|
||||
|
||||
CGRect unitFrame = frame;
|
||||
|
||||
if(isnan(unitFrame.origin.x)){
|
||||
return;
|
||||
}
|
||||
if(isnan(unitFrame.origin.y)){
|
||||
return;
|
||||
}
|
||||
if(isnan(unitFrame.size.width)){
|
||||
return;
|
||||
}
|
||||
if(isnan(unitFrame.size.height)){
|
||||
return;
|
||||
}
|
||||
|
||||
@try {
|
||||
[self fatSafe_setFrame:unitFrame];
|
||||
} @catch (NSException *exception) {
|
||||
|
||||
} @finally {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)fatSafe_setCenter:(CGPoint)center{
|
||||
|
||||
CGPoint unitCenter = center;
|
||||
|
||||
if(isnan(unitCenter.x)){
|
||||
return;
|
||||
}
|
||||
if(isnan(unitCenter.y)){
|
||||
return;
|
||||
}
|
||||
|
||||
@try {
|
||||
[self fatSafe_setCenter:unitCenter];
|
||||
} @catch (NSException *exception) {
|
||||
|
||||
} @finally {
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// FATAnnotation.h
|
||||
// AppletDemo
|
||||
//
|
||||
// Created by Haley on 2020/4/17.
|
||||
// Copyright © 2020 weidian. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <MapKit/MapKit.h>
|
||||
|
||||
@interface FATAnnotation : NSObject <MKAnnotation>
|
||||
|
||||
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
|
||||
|
||||
@property (nonatomic, copy) NSString *title;
|
||||
@property (nonatomic, copy) NSString *address;
|
||||
|
||||
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate;
|
||||
|
||||
@end
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// FATAnnotation.m
|
||||
// AppletDemo
|
||||
//
|
||||
// Created by Haley on 2020/4/17.
|
||||
// Copyright © 2020 weidian. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATAnnotation.h"
|
||||
|
||||
@implementation FATAnnotation
|
||||
|
||||
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate {
|
||||
if (self = [super init]) {
|
||||
self.coordinate = coordinate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// FATExtChoosePoiViewController.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/8.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^SureBlock)(NSDictionary *locationInfo);
|
||||
|
||||
@interface FATExtChoosePoiViewController : FATUIViewController
|
||||
|
||||
@property (nonatomic, copy) dispatch_block_t cancelBlock;
|
||||
@property (nonatomic, copy) SureBlock sureBlock;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,389 @@
|
|||
//
|
||||
// FATExtChoosePoiViewController.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/8.
|
||||
//
|
||||
|
||||
#import "FATExtChoosePoiViewController.h"
|
||||
#import "FATLocationResultViewController.h"
|
||||
#import "FATAnnotation.h"
|
||||
#import "FATExtHelper.h"
|
||||
#import "FATExtLocationManager.h"
|
||||
#import "FATExtMapManager.h"
|
||||
#import "FATExtUtil.h"
|
||||
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
static NSString *kAnnotationId = @"FATAnnotationViewId";
|
||||
static NSString *kUserAnnotationId = @"FATUserAnnotationViewId";
|
||||
|
||||
@interface FATExtChoosePoiViewController () <UITableViewDelegate, UITableViewDataSource, FATLocationResultDelegate, CLLocationManagerDelegate>
|
||||
|
||||
@property (nonatomic, strong) UIImageView *centerLocationView;
|
||||
@property (nonatomic, strong) MKUserLocation *userLocation;
|
||||
|
||||
@property (nonatomic, strong) UITableView *tableView;
|
||||
|
||||
@property (nonatomic, strong) UIActivityIndicatorView *indicatorView;
|
||||
|
||||
@property (nonatomic, strong) UISearchController *searchController;
|
||||
@property (nonatomic, strong) FATLocationResultViewController *locationResultVC;
|
||||
@property (nonatomic, strong) FATExtLocationManager *locationManager;
|
||||
@property (nonatomic, strong) NSMutableArray *dataArray;
|
||||
@property (nonatomic, copy) NSString *cityName;
|
||||
@end
|
||||
|
||||
@implementation FATExtChoosePoiViewController
|
||||
|
||||
- (NSMutableArray *)dataArray {
|
||||
if (!_dataArray) {
|
||||
_dataArray = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _dataArray;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
|
||||
[self p_initNavigationBar];
|
||||
|
||||
[self p_initSubViews];
|
||||
|
||||
[self startLocation];
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews {
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
CGFloat width = self.view.bounds.size.width;
|
||||
CGFloat height = self.view.bounds.size.height;
|
||||
CGFloat searchBarH = self.searchController.searchBar.bounds.size.height;
|
||||
|
||||
CGFloat totalH = height - searchBarH;
|
||||
|
||||
self.centerLocationView.center = CGPointMake(width * 0.5, totalH * 0.5 * 0.5);
|
||||
|
||||
self.tableView.frame = CGRectMake(0, 0, width, height);
|
||||
|
||||
self.indicatorView.center = CGPointMake(width * 0.5, height * 0.5);
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
_searchController = nil;
|
||||
_locationResultVC = nil;
|
||||
}
|
||||
|
||||
#pragma mark - private method
|
||||
- (void)p_initNavigationBar {
|
||||
self.title = [[FATClient sharedClient] fat_localizedStringForKey:@"Location"];
|
||||
|
||||
NSString *cancel = [[FATClient sharedClient] fat_localizedStringForKey:@"Cancel"];
|
||||
NSString *ok = [[FATClient sharedClient] fat_localizedStringForKey:@"OK"];
|
||||
UIButton *cancelButton = [[UIButton alloc] init];
|
||||
[cancelButton setTitle:cancel forState:UIControlStateNormal];
|
||||
[cancelButton setTitleColor:[UIColor systemBlueColor] forState:UIControlStateNormal];
|
||||
[cancelButton addTarget:self action:@selector(cancelItemClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
UIButton *okButton = [[UIButton alloc] init];
|
||||
[okButton setTitle:ok forState:UIControlStateNormal];
|
||||
[okButton setTitleColor:[UIColor systemBlueColor] forState:UIControlStateNormal];
|
||||
[okButton addTarget:self action:@selector(sureItemClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:cancelButton];
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:okButton];
|
||||
}
|
||||
|
||||
- (void)p_initSubViews {
|
||||
[self p_initSearchBar];
|
||||
|
||||
self.centerLocationView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
|
||||
self.centerLocationView.image = [FATExtHelper fat_ext_imageFromBundleWithName:@"fav_fileicon_loc90"];
|
||||
|
||||
[self.view addSubview:self.tableView];
|
||||
|
||||
[self.view addSubview:self.indicatorView];
|
||||
[self.indicatorView startAnimating];
|
||||
}
|
||||
|
||||
//开始定位
|
||||
- (void)startLocation {
|
||||
// CLog(@"--------开始定位");
|
||||
self.locationManager = [[FATExtLocationManager alloc] init];
|
||||
self.locationManager.delegate = self;
|
||||
// 控制定位精度,越高耗电量越
|
||||
self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
|
||||
|
||||
[self.locationManager requestWhenInUseAuthorization];
|
||||
self.locationManager.distanceFilter = 10.0f;
|
||||
[self.locationManager startUpdatingLocation];
|
||||
}
|
||||
|
||||
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
|
||||
if ([error code] == kCLErrorDenied) {
|
||||
// NSLog(@"访问被拒绝");
|
||||
}
|
||||
if ([error code] == kCLErrorLocationUnknown) {
|
||||
// NSLog(@"无法获取位置信息");
|
||||
}
|
||||
}
|
||||
//定位代理经纬度回调
|
||||
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
|
||||
CLLocation *newLocation = locations[0];
|
||||
|
||||
CLLocationCoordinate2D centerCoordinate = newLocation.coordinate;
|
||||
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
|
||||
CLLocation *location = [[CLLocation alloc] initWithLatitude:centerCoordinate.latitude longitude:centerCoordinate.longitude];
|
||||
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *array, NSError *error) {
|
||||
CLPlacemark *placemark = nil;
|
||||
if (!error) {
|
||||
placemark = [array firstObject];
|
||||
}
|
||||
// 获取当前城市名
|
||||
NSString *city = placemark.locality;
|
||||
if (!city) {
|
||||
city = placemark.administrativeArea;
|
||||
}
|
||||
self.cityName = city;
|
||||
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
|
||||
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(centerCoordinate, 1000, 1000);
|
||||
request.region = region;
|
||||
request.naturalLanguageQuery = [[FATClient sharedClient] fat_localizedStringForKey:@"Office Building"];
|
||||
|
||||
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
|
||||
if([FATExtMapManager shareInstance].googleMapApiKey.length > 1){
|
||||
[FATExtUtil getNearbyPlacesByCategory:request.naturalLanguageQuery coordinates: region.center radius:1000 token:@""
|
||||
completion:^(NSDictionary * _Nonnull dict) {
|
||||
|
||||
self.dataArray = [[NSMutableArray alloc] initWithArray: [FATExtUtil convertPlaceDictToArray:dict]];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.indicatorView stopAnimating];
|
||||
[self addSpecialCity];
|
||||
[self.tableView reloadData];
|
||||
});
|
||||
}];
|
||||
}else {
|
||||
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *_Nullable response, NSError *_Nullable error) {
|
||||
//NSMutableArray *placeArrayM = [NSMutableArray array];
|
||||
if (placemark) {
|
||||
FATMapPlace *place = [[FATMapPlace alloc] init];
|
||||
place.name = placemark.name;
|
||||
place.address = placemark.thoroughfare;
|
||||
place.location = placemark.location;
|
||||
place.selected = YES;
|
||||
[self.dataArray addObject:place];
|
||||
}
|
||||
|
||||
for (MKMapItem *item in response.mapItems) {
|
||||
if (!item.isCurrentLocation) {
|
||||
FATMapPlace *place = [[FATMapPlace alloc] init];
|
||||
place.name = item.placemark.name;
|
||||
place.address = item.placemark.thoroughfare;
|
||||
place.location = item.placemark.location;
|
||||
[self.dataArray addObject:place];
|
||||
}
|
||||
}
|
||||
[self.indicatorView stopAnimating];
|
||||
[self addSpecialCity];
|
||||
[self.tableView reloadData];
|
||||
}];
|
||||
}
|
||||
}];
|
||||
[manager stopUpdatingLocation];
|
||||
}
|
||||
|
||||
- (void)p_initSearchBar {
|
||||
_locationResultVC = [FATLocationResultViewController new];
|
||||
_locationResultVC.delegate = self;
|
||||
_locationResultVC.searchBarHeight = self.searchController.searchBar.bounds.size.height;
|
||||
_searchController = [[UISearchController alloc] initWithSearchResultsController:_locationResultVC];
|
||||
_searchController.searchResultsUpdater = _locationResultVC;
|
||||
|
||||
NSString *placeholder = [[FATClient sharedClient] fat_localizedStringForKey:@"Search nearby"];
|
||||
_searchController.searchBar.placeholder = placeholder;
|
||||
_searchController.searchBar.backgroundColor = [UIColor colorWithRed:239 / 255.0 green:239 / 255.0 blue:244 / 255.0 alpha:1];
|
||||
UITextField *searchField;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
searchField = _searchController.searchBar.searchTextField;
|
||||
} else {
|
||||
searchField = [_searchController.searchBar valueForKey:@"searchField"];
|
||||
}
|
||||
searchField.layer.borderColor = [UIColor colorWithRed:218 / 255.0 green:219 / 255.0 blue:233 / 255.0 alpha:1].CGColor;
|
||||
self.tableView.tableHeaderView = _searchController.searchBar;
|
||||
}
|
||||
|
||||
#pragma mark - click events
|
||||
- (void)cancelItemClick {
|
||||
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
||||
if (self.cancelBlock) {
|
||||
self.cancelBlock();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sureItemClick {
|
||||
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
||||
FATMapPlace *selectPlace = nil;
|
||||
for (FATMapPlace *place in self.dataArray) {
|
||||
if (place.selected) {
|
||||
selectPlace = place;
|
||||
break;
|
||||
}
|
||||
}
|
||||
NSString *type;
|
||||
CLLocationCoordinate2D coordinate = selectPlace.location.coordinate;
|
||||
NSMutableDictionary *locationInfo = [[NSMutableDictionary alloc] initWithDictionary:@{@"latitude" : [NSString stringWithFormat:@"%@", @(coordinate.latitude)],
|
||||
@"longitude" : [NSString stringWithFormat:@"%@", @(coordinate.longitude)]}];
|
||||
if ([selectPlace.name isEqualToString:[[FATClient sharedClient] fat_localizedStringForKey:@"Don't show location"]]) {
|
||||
type = @"0";
|
||||
} else if (!selectPlace.address) {
|
||||
type = @"1";
|
||||
[locationInfo setValue: selectPlace.name ?: @"" forKey:@"city"];
|
||||
} else {
|
||||
type = @"2";
|
||||
[locationInfo setValue: selectPlace.address ?: @"" forKey:@"name"];
|
||||
[locationInfo setValue: selectPlace.name ?: @"" forKey:@"address"];
|
||||
}
|
||||
[locationInfo setValue:type forKey:@"type"];
|
||||
if (self.sureBlock) {
|
||||
self.sureBlock(locationInfo);
|
||||
}
|
||||
}
|
||||
|
||||
- (UITableView *)tableView {
|
||||
if (!_tableView) {
|
||||
_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||||
_tableView.dataSource = self;
|
||||
_tableView.delegate = self;
|
||||
_tableView.backgroundColor = [UIColor whiteColor];
|
||||
_tableView.tableFooterView = [UIView new];
|
||||
}
|
||||
return _tableView;
|
||||
}
|
||||
|
||||
- (UIActivityIndicatorView *)indicatorView {
|
||||
if (!_indicatorView) {
|
||||
_indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
|
||||
_indicatorView.hidesWhenStopped = YES;
|
||||
}
|
||||
|
||||
return _indicatorView;
|
||||
}
|
||||
|
||||
#pragma mark - FATLocationResultDelegate
|
||||
- (void)selectedLocationWithLocation:(FATMapPlace *)place {
|
||||
[self dismissViewControllerAnimated:NO completion:nil];
|
||||
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
||||
NSString *type;
|
||||
CLLocationCoordinate2D coordinate = place.location.coordinate;
|
||||
NSMutableDictionary *locationInfo = [[NSMutableDictionary alloc] initWithDictionary:@{@"latitude" : [NSString stringWithFormat:@"%@", @(coordinate.latitude)],
|
||||
@"longitude" : [NSString stringWithFormat:@"%@", @(coordinate.longitude)]}];
|
||||
if ([place.name isEqualToString:[[FATClient sharedClient] fat_localizedStringForKey:@"Don't show location"]]) {
|
||||
type = @"0";
|
||||
} else if (!place.address) {
|
||||
type = @"1";
|
||||
[locationInfo setValue: place.name ?: @"" forKey:@"city"];
|
||||
} else {
|
||||
type = @"2";
|
||||
[locationInfo setValue: place.address ?: @"" forKey:@"name"];
|
||||
[locationInfo setValue: place.name ?: @"" forKey:@"address"];
|
||||
}
|
||||
[locationInfo setValue:type forKey:@"type"];
|
||||
if (self.sureBlock) {
|
||||
self.sureBlock(locationInfo);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - MKMapViewDelegate
|
||||
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
|
||||
if (!self.userLocation) {
|
||||
_userLocation = userLocation;
|
||||
CLLocationCoordinate2D center = userLocation.location.coordinate;
|
||||
if (center.latitude > 0 && center.longitude > 0) {
|
||||
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center, 1000, 1000);
|
||||
mapView.centerCoordinate = center;
|
||||
mapView.region = region;
|
||||
|
||||
self.locationResultVC.region = region;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 单独加上所在的城市和不显示位置两条数据
|
||||
- (void)addSpecialCity {
|
||||
// 避免重复添加。
|
||||
NSMutableArray *deleteArray = [[NSMutableArray alloc] init];
|
||||
for (FATMapPlace *place in self.dataArray) {
|
||||
if ([place.name isEqualToString:[[FATClient sharedClient] fat_localizedStringForKey:@"Don't show location"]]) {
|
||||
[deleteArray insertObject:place atIndex:0];
|
||||
}
|
||||
if (place.name && !place.address) {
|
||||
[deleteArray insertObject:place atIndex:0];
|
||||
}
|
||||
}
|
||||
if (deleteArray.count != 0) {
|
||||
[self.dataArray removeObjectsInArray:deleteArray];
|
||||
}
|
||||
|
||||
FATMapPlace *place = [[FATMapPlace alloc] init];
|
||||
place.name = self.cityName;
|
||||
place.location = place.location;
|
||||
place.selected = NO;
|
||||
[self.dataArray insertObject:place atIndex:0];
|
||||
|
||||
FATMapPlace *NoPlace = [[FATMapPlace alloc] init];
|
||||
NoPlace.name = [[FATClient sharedClient] fat_localizedStringForKey:@"Don't show location"];
|
||||
NoPlace.selected = NO;
|
||||
[self.dataArray insertObject:NoPlace atIndex:0];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.dataArray.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
static NSString *identifer = @"identifer";
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifer];
|
||||
if (cell == nil) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifer];
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
cell.detailTextLabel.textColor = [UIColor grayColor];
|
||||
}
|
||||
|
||||
FATMapPlace *place = self.dataArray[indexPath.row];
|
||||
if ([place.name isEqualToString:[[FATClient sharedClient] fat_localizedStringForKey:@"Don't show location"]]) {
|
||||
cell.textLabel.textColor = [UIColor fat_colorWithRGBHexString:@"0066ff" alpha:1.0];
|
||||
} else {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
cell.textLabel.textColor = UIColor.labelColor;
|
||||
} else {
|
||||
cell.textLabel.textColor = UIColor.blackColor;
|
||||
}
|
||||
}
|
||||
cell.textLabel.text = place.name;
|
||||
cell.detailTextLabel.text = place.address;
|
||||
if (place.selected) {
|
||||
cell.accessoryType = UITableViewCellAccessoryCheckmark;
|
||||
} else {
|
||||
cell.accessoryType = UITableViewCellAccessoryNone;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
for (FATMapPlace *place in self.dataArray) {
|
||||
place.selected = NO;
|
||||
}
|
||||
FATMapPlace *place = self.dataArray[indexPath.row];
|
||||
place.selected = YES;
|
||||
[self.tableView reloadData];
|
||||
|
||||
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(place.location.coordinate, 1000, 1000);
|
||||
self.locationResultVC.region = region;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// FATExtSliderView.h
|
||||
// FinAppletGDMap
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/13.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <MapKit/MapKit.h>
|
||||
#import "FATMapPlace.h"
|
||||
|
||||
#define fatKScreenWidth ([UIScreen mainScreen].bounds.size.width)
|
||||
#define fatKScreenHeight ([UIScreen mainScreen].bounds.size.height)
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^SelectItemBlock)(FATMapPlace *locationInfo);
|
||||
typedef void (^TopDistance)(float height, BOOL isTopOrBottom);
|
||||
|
||||
@interface FATExtSliderView : UIView
|
||||
|
||||
@property (nonatomic, copy) SelectItemBlock selectItemBlock;
|
||||
@property (nonatomic, assign) float topH; //上滑后距离顶部的距离
|
||||
@property (nonatomic, copy) TopDistance topDistance;
|
||||
@property (nonatomic, strong) UITableView *tableView;
|
||||
@property (nonatomic, strong) NSMutableArray<FATMapPlace *> *poiInfoListArray;
|
||||
|
||||
- (void)updateSearchFrameWithColcationCoordinate:(CLLocationCoordinate2D)coord;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,365 @@
|
|||
//
|
||||
// FATExtSliderView.m
|
||||
// FinAppletGDMap
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/13.
|
||||
//
|
||||
|
||||
#import "FATExtSliderView.h"
|
||||
#import "UIView+FATExtFrame.h"
|
||||
#import <FinApplet/FinApplet.h>
|
||||
#import "UIView+FATExtSafaFrame.h"
|
||||
#import "FATExtUtil.h"
|
||||
#import "FATExtMapManager.h"
|
||||
|
||||
@interface FATExtSliderView () <UITableViewDelegate, UITableViewDataSource, UIGestureRecognizerDelegate, UISearchControllerDelegate, UISearchBarDelegate>
|
||||
|
||||
@property (nonatomic, assign) float bottomH; //下滑后距离顶部的距离
|
||||
@property (nonatomic, assign) float stop_y; //tableView滑动停止的位置
|
||||
@property (nonatomic, strong) UISearchController *searchController;
|
||||
@property (nonatomic, strong) NSMutableArray<FATMapPlace *> *tempPoiInfoListArray;
|
||||
@property (nonatomic, assign) NSInteger selectNumber;
|
||||
@property (nonatomic, strong) UISearchController *search;
|
||||
@property (nonatomic, assign) CLLocationCoordinate2D locationCoordinate;
|
||||
@property (nonatomic, assign) NSInteger pageIndex;
|
||||
@property (nonatomic, strong) NSString *cityString;
|
||||
@property (nonatomic, assign) MKCoordinateRegion region;
|
||||
@end
|
||||
|
||||
@implementation FATExtSliderView
|
||||
|
||||
- (NSMutableArray<FATMapPlace *> *)poiInfoListArray {
|
||||
if (!_poiInfoListArray) {
|
||||
_poiInfoListArray = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _poiInfoListArray;
|
||||
}
|
||||
|
||||
- (NSMutableArray<FATMapPlace *> *)tempPoiInfoListArray {
|
||||
if (!_tempPoiInfoListArray) {
|
||||
_tempPoiInfoListArray = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _tempPoiInfoListArray;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self setUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setUI {
|
||||
self.backgroundColor = [UIColor systemGrayColor];
|
||||
self.bottomH = self.top;
|
||||
self.pageIndex = 0;
|
||||
self.selectNumber = 0;
|
||||
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
|
||||
panGestureRecognizer.delegate = self;
|
||||
[self addGestureRecognizer:panGestureRecognizer];
|
||||
[self creatUI];
|
||||
}
|
||||
|
||||
- (void)creatUI {
|
||||
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, fatKScreenWidth, self.frame.size.height) style:UITableViewStylePlain];
|
||||
self.tableView.backgroundColor = [UIColor whiteColor];
|
||||
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
self.tableView.showsVerticalScrollIndicator = NO;
|
||||
self.tableView.delegate = self;
|
||||
self.tableView.dataSource = self;
|
||||
self.tableView.bounces = NO;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
[self addSubview:self.tableView];
|
||||
[self.tableView registerNib:[UINib nibWithNibName:@"SlideTableViewCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"SlideTableViewCell"];
|
||||
self.tableView.tableHeaderView = self.searchController.searchBar;
|
||||
self.search.delegate = self;
|
||||
}
|
||||
|
||||
- (void)getData {
|
||||
CLLocationCoordinate2D centerCoordinate = self.locationCoordinate;
|
||||
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
|
||||
CLLocation *location = [[CLLocation alloc] initWithLatitude:centerCoordinate.latitude longitude:centerCoordinate.longitude];
|
||||
|
||||
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *array, NSError *error) {
|
||||
CLPlacemark *placemark = nil;
|
||||
if (!error) {
|
||||
placemark = [array firstObject];
|
||||
}
|
||||
|
||||
MKCoordinateSpan span = MKCoordinateSpanMake(centerCoordinate.latitude, centerCoordinate.longitude);
|
||||
MKCoordinateRegion newRegion = MKCoordinateRegionMake(centerCoordinate, span);
|
||||
|
||||
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
|
||||
request.region = self.region;
|
||||
request.naturalLanguageQuery = @"Place";
|
||||
CLLocationCoordinate2D destCenter = self.region.center;
|
||||
if(destCenter.latitude == 0){
|
||||
destCenter = centerCoordinate;
|
||||
}
|
||||
|
||||
if([FATExtMapManager shareInstance].googleMapApiKey.length > 1){
|
||||
[FATExtUtil getNearbyPlacesByCategory:@"All" coordinates:destCenter radius:1000 token:@""
|
||||
completion:^(NSDictionary * _Nonnull dict) {
|
||||
NSMutableArray *placeArrayM = [NSMutableArray array];
|
||||
if (placemark) {
|
||||
FATMapPlace *place = [[FATMapPlace alloc] init];
|
||||
place.name = placemark.name;
|
||||
place.address = placemark.thoroughfare;
|
||||
place.location = placemark.location;
|
||||
place.selected = YES;
|
||||
[placeArrayM addObject:place];
|
||||
}
|
||||
[placeArrayM addObjectsFromArray:[FATExtUtil convertPlaceDictToArray:dict]];
|
||||
self.poiInfoListArray = [[NSMutableArray alloc] initWithArray: placeArrayM];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.tableView reloadData];
|
||||
});
|
||||
}];
|
||||
}else{
|
||||
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
|
||||
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *_Nullable response, NSError *_Nullable error) {
|
||||
NSMutableArray *placeArrayM = [NSMutableArray array];
|
||||
for (MKMapItem *item in response.mapItems) {
|
||||
if (!item.isCurrentLocation) {
|
||||
FATMapPlace *place = [[FATMapPlace alloc] init];
|
||||
place.name = item.placemark.name;
|
||||
place.address = item.placemark.thoroughfare;
|
||||
place.location = item.placemark.location;
|
||||
[placeArrayM addObject:place];
|
||||
}
|
||||
}
|
||||
self.poiInfoListArray = [[NSMutableArray alloc] initWithArray:placeArrayM];
|
||||
[self.tableView reloadData];
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateSearchFrameWithColcationCoordinate:(CLLocationCoordinate2D)coord {
|
||||
// struct CLLocationCoordinate2D my2D = {40.0, 115.0};//self.locationCoordinate
|
||||
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(self.locationCoordinate, 1000, 1000);
|
||||
self.region = region;
|
||||
self.locationCoordinate = coord;
|
||||
[self.poiInfoListArray removeAllObjects];
|
||||
[self.tableView reloadData];
|
||||
[self getData];
|
||||
}
|
||||
|
||||
- (void)p_searchLocationsWithSearchText:(NSString *)searchText {
|
||||
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
|
||||
request.region = self.region;
|
||||
request.naturalLanguageQuery = searchText;
|
||||
CLLocationCoordinate2D destCenter = self.region.center;
|
||||
if(destCenter.latitude == 0){
|
||||
destCenter = self.locationCoordinate;
|
||||
}
|
||||
|
||||
if([FATExtMapManager shareInstance].googleMapApiKey.length > 1){
|
||||
[FATExtUtil getNearbyPlacesByCategory:searchText coordinates:destCenter radius:1000 token:@""
|
||||
completion:^(NSDictionary * _Nonnull dict) {
|
||||
|
||||
self.poiInfoListArray = [[NSMutableArray alloc] initWithArray: [FATExtUtil convertPlaceDictToArray:dict]];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.tableView reloadData];
|
||||
});
|
||||
}];
|
||||
}else { MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
|
||||
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *_Nullable response, NSError *_Nullable error) {
|
||||
NSMutableArray *placeArrayM = [NSMutableArray array];
|
||||
for (MKMapItem *item in response.mapItems) {
|
||||
if (!item.isCurrentLocation) {
|
||||
FATMapPlace *place = [[FATMapPlace alloc] init];
|
||||
place.name = item.placemark.name;
|
||||
place.address = item.placemark.thoroughfare;
|
||||
place.location = item.placemark.location;
|
||||
[placeArrayM addObject:place];
|
||||
}
|
||||
}
|
||||
self.poiInfoListArray = [[NSMutableArray alloc] initWithArray:placeArrayM];
|
||||
[self.tableView reloadData];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 滑动
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
|
||||
CGFloat currentPostion = scrollView.contentOffset.y;
|
||||
self.stop_y = currentPostion;
|
||||
|
||||
if (self.top > self.topH) {
|
||||
[scrollView setContentOffset:CGPointMake(0, 0)];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)panAction:(UIPanGestureRecognizer *)pan {
|
||||
// 获取视图偏移量
|
||||
CGPoint point = [pan translationInView:self];
|
||||
// stop_y是tableview的偏移量,当tableview的偏移量大于0时则不去处理视图滑动的事件
|
||||
if (self.stop_y > 0) {
|
||||
// 将视频偏移量重置为0
|
||||
[pan setTranslation:CGPointMake(0, 0) inView:self];
|
||||
return;
|
||||
}
|
||||
|
||||
// self.top是视图距离顶部的距离
|
||||
self.top += point.y;
|
||||
if (self.top < self.topH) {
|
||||
self.top = self.topH;
|
||||
}
|
||||
|
||||
// self.bottomH是视图在底部时距离顶部的距离
|
||||
if (self.top > self.bottomH) {
|
||||
self.top = self.bottomH;
|
||||
}
|
||||
|
||||
// 在滑动手势结束时判断滑动视图距离顶部的距离是否超过了屏幕的一半,如果超过了一半就往下滑到底部
|
||||
// 如果小于一半就往上滑到顶部
|
||||
if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) {
|
||||
// 滑动速度
|
||||
CGPoint velocity = [pan velocityInView:self];
|
||||
CGFloat speed = 350;
|
||||
if (velocity.y < -speed) {
|
||||
[self goTop];
|
||||
[pan setTranslation:CGPointMake(0, 0) inView:self];
|
||||
return;
|
||||
} else if (velocity.y > speed) {
|
||||
[self goBack];
|
||||
[pan setTranslation:CGPointMake(0, 0) inView:self];
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.top > fatKScreenHeight / 2) {
|
||||
[self goBack];
|
||||
} else {
|
||||
[self goTop];
|
||||
}
|
||||
}
|
||||
|
||||
[pan setTranslation:CGPointMake(0, 0) inView:self];
|
||||
if (self.top != self.bottomH) {
|
||||
if (!isnan(point.y)) {
|
||||
if (self.topDistance) {
|
||||
self.topDistance(point.y, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)goTop {
|
||||
if (self.top != self.bottomH) {
|
||||
if (self.topDistance) {
|
||||
if (!isnan(self.topH)) {
|
||||
self.topDistance(self.topH, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
[UIView animateWithDuration:0.5 animations:^{
|
||||
self.top = self.topH;
|
||||
} completion:^(BOOL finished){
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)goBack {
|
||||
if (self.topDistance) {
|
||||
if (!isnan(self.bottomH)) {
|
||||
self.topDistance(self.bottomH, true);
|
||||
}
|
||||
}
|
||||
[UIView animateWithDuration:0.5 animations:^{
|
||||
self.top = self.bottomH;
|
||||
} completion:^(BOOL finished){
|
||||
// self.tableView.userInteractionEnabled = NO;
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.poiInfoListArray.count;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return 50;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
static NSString *identifer = @"identifer";
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifer];
|
||||
if (cell == nil) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifer];
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
cell.detailTextLabel.textColor = [UIColor grayColor];
|
||||
}
|
||||
|
||||
cell.textLabel.text = self.poiInfoListArray[indexPath.row].name;
|
||||
cell.detailTextLabel.text = self.poiInfoListArray[indexPath.row].address;
|
||||
if (indexPath.row == self.selectNumber) {
|
||||
cell.accessoryType = UITableViewCellAccessoryCheckmark;
|
||||
} else {
|
||||
cell.accessoryType = UITableViewCellAccessoryNone;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:NO];
|
||||
self.selectNumber = indexPath.row;
|
||||
[tableView reloadData];
|
||||
FATMapPlace *poiInfo = self.poiInfoListArray[indexPath.row];
|
||||
if (self.selectItemBlock) {
|
||||
self.selectItemBlock(poiInfo);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UISearchResultsUpdating
|
||||
|
||||
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
|
||||
[self goTop];
|
||||
self.tempPoiInfoListArray = [[NSMutableArray alloc] initWithArray:self.poiInfoListArray];
|
||||
[self.poiInfoListArray removeAllObjects];
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
|
||||
self.selectNumber = -1;
|
||||
NSString *searchString = self.searchController.searchBar.text;
|
||||
[self p_searchLocationsWithSearchText:searchString];
|
||||
self.searchController.active = NO;
|
||||
[self goBack];
|
||||
self.searchController.searchBar.text = searchString;
|
||||
}
|
||||
|
||||
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
|
||||
[self goBack];
|
||||
self.poiInfoListArray = [[NSMutableArray alloc] initWithArray:self.tempPoiInfoListArray];
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (UISearchController *)searchController {
|
||||
if (!_searchController) {
|
||||
_searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
|
||||
_searchController.searchBar.delegate = self;
|
||||
_searchController.dimsBackgroundDuringPresentation = NO;
|
||||
_searchController.hidesNavigationBarDuringPresentation = NO;
|
||||
_searchController.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
_searchController.searchBar.frame = CGRectMake(0, 0, fatKScreenWidth, 44);
|
||||
NSString *placeholder = [[FATClient sharedClient] fat_localizedStringForKey:@"Search for location"];
|
||||
_searchController.searchBar.placeholder = placeholder;
|
||||
}
|
||||
return _searchController;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// FATLocationResultViewController.h
|
||||
// AppletDemo
|
||||
//
|
||||
// Created by Haley on 2020/4/17.
|
||||
// Copyright © 2020 weidian. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <MapKit/MapKit.h>
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
#import "FATMapPlace.h"
|
||||
|
||||
@protocol FATLocationResultDelegate <NSObject>
|
||||
|
||||
- (void)selectedLocationWithLocation:(FATMapPlace *)place;
|
||||
|
||||
@end
|
||||
|
||||
@interface FATLocationResultViewController : FATUIViewController <UISearchResultsUpdating>
|
||||
|
||||
@property (nonatomic, assign) MKCoordinateRegion region;
|
||||
@property (nonatomic, weak) id<FATLocationResultDelegate> delegate;
|
||||
@property (nonatomic, assign) NSInteger searchBarHeight;
|
||||
|
||||
@end
|
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// FATLocationResultViewController.m
|
||||
// AppletDemo
|
||||
//
|
||||
// Created by Haley on 2020/4/17.
|
||||
// Copyright © 2020 weidian. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATLocationResultViewController.h"
|
||||
#import "FATExtMapManager.h"
|
||||
#import "FATExtUtil.h"
|
||||
|
||||
|
||||
@interface FATLocationResultViewController () <UITableViewDataSource, UITableViewDelegate>
|
||||
|
||||
@property (nonatomic, strong) UITableView *tableView;
|
||||
|
||||
@property (nonatomic, copy) NSArray<FATMapPlace *> *places;
|
||||
|
||||
@property (nonatomic, strong) FATMapPlace *selectedPlace;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FATLocationResultViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
[self p_initSubViews];
|
||||
}
|
||||
|
||||
#pragma mark - private method
|
||||
- (void)p_initSubViews {
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
CGFloat width = self.view.bounds.size.width;
|
||||
CGFloat height = self.view.bounds.size.height;
|
||||
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, self.searchBarHeight, width, height - self.searchBarHeight - 60) style:UITableViewStylePlain];
|
||||
self.tableView.dataSource = self;
|
||||
self.tableView.delegate = self;
|
||||
self.tableView.rowHeight = 60;
|
||||
[self.view addSubview:self.tableView];
|
||||
}
|
||||
|
||||
- (void)p_searchLocationsWithSearchText:(NSString *)searchText {
|
||||
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
|
||||
request.region = self.region;
|
||||
request.naturalLanguageQuery = searchText;
|
||||
|
||||
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
|
||||
if([FATExtMapManager shareInstance].googleMapApiKey.length > 1){
|
||||
[FATExtUtil getNearbyPlacesByCategory:searchText coordinates:self.region.center radius:1000 token:@""
|
||||
completion:^(NSDictionary * _Nonnull dict) {
|
||||
self.places = [[FATExtUtil convertPlaceDictToArray:dict] copy];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.tableView reloadData];
|
||||
});
|
||||
}];
|
||||
}else{[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *_Nullable response, NSError *_Nullable error) {
|
||||
NSMutableArray *placeArrayM = [NSMutableArray array];
|
||||
for (MKMapItem *item in response.mapItems) {
|
||||
if (!item.isCurrentLocation) {
|
||||
FATMapPlace *place = [[FATMapPlace alloc] init];
|
||||
place.name = item.placemark.name;
|
||||
place.address = item.placemark.thoroughfare;
|
||||
place.location = item.placemark.location;
|
||||
[placeArrayM addObject:place];
|
||||
}
|
||||
}
|
||||
self.places = [placeArrayM copy];
|
||||
[self.tableView reloadData];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UISearchResultsUpdating
|
||||
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
|
||||
NSString *searchString = searchController.searchBar.text;
|
||||
[self p_searchLocationsWithSearchText:searchString];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.places.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
static NSString *identifer = @"placeCell";
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifer];
|
||||
if (cell == nil) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifer];
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
cell.detailTextLabel.textColor = [UIColor grayColor];
|
||||
}
|
||||
|
||||
FATMapPlace *place = self.places[indexPath.row];
|
||||
cell.textLabel.text = place.name;
|
||||
cell.detailTextLabel.text = place.address;
|
||||
if (place.selected) {
|
||||
cell.accessoryType = UITableViewCellAccessoryCheckmark;
|
||||
} else {
|
||||
cell.accessoryType = UITableViewCellAccessoryNone;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
for (FATMapPlace *place in self.places) {
|
||||
place.selected = NO;
|
||||
}
|
||||
FATMapPlace *place = self.places[indexPath.row];
|
||||
place.selected = YES;
|
||||
[self.tableView reloadData];
|
||||
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(selectedLocationWithLocation:)]) {
|
||||
[self.delegate selectedLocationWithLocation:place];
|
||||
}
|
||||
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// FATMapPlace.h
|
||||
// AppletDemo
|
||||
//
|
||||
// Created by Haley on 2020/4/17.
|
||||
// Copyright © 2020 weidian. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreLocation/CLPlacemark.h>
|
||||
|
||||
@interface FATMapPlace : NSObject
|
||||
|
||||
@property (nonatomic, copy) NSString *name;
|
||||
|
||||
@property (nonatomic, copy) NSString *address;
|
||||
|
||||
//@property (nonatomic, strong) CLPlacemark *placemark;
|
||||
@property (nonatomic, strong) CLLocation *location;
|
||||
|
||||
@property (nonatomic, assign) BOOL selected;
|
||||
|
||||
@end
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// FATMapPlace.m
|
||||
// AppletDemo
|
||||
//
|
||||
// Created by Haley on 2020/4/17.
|
||||
// Copyright © 2020 weidian. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATMapPlace.h"
|
||||
|
||||
@implementation FATMapPlace
|
||||
|
||||
@end
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// FATMapViewController.h
|
||||
// AppletDemo
|
||||
//
|
||||
// Created by Haley on 2020/4/16.
|
||||
// Copyright © 2020 weidian. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
typedef void (^SureBlock)(NSDictionary *locationInfo);
|
||||
|
||||
@interface FATMapViewController : FATUIViewController
|
||||
|
||||
@property (nonatomic, copy) dispatch_block_t cancelBlock;
|
||||
@property (nonatomic, copy) SureBlock sureBlock;
|
||||
|
||||
/// 目标地纬度
|
||||
@property (nonatomic, strong) NSString *latitude;
|
||||
/// 目标地经度
|
||||
@property (nonatomic, strong) NSString *longitude;
|
||||
|
||||
@end
|
|
@ -0,0 +1,350 @@
|
|||
//
|
||||
// FATMapViewController.m
|
||||
// AppletDemo
|
||||
//
|
||||
// Created by Haley on 2020/4/16.
|
||||
// Copyright © 2020 weidian. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATMapViewController.h"
|
||||
#import "FATLocationResultViewController.h"
|
||||
#import "FATAnnotation.h"
|
||||
#import "FATExtHelper.h"
|
||||
#import "FATExtSliderView.h"
|
||||
#import "FATWGS84ConvertToGCJ02.h"
|
||||
#import "FATExtLocationManager.h"
|
||||
#import <MapKit/MapKit.h>
|
||||
#import "UIView+FATExtSafaFrame.h"
|
||||
|
||||
#define fatKScreenWidth ([UIScreen mainScreen].bounds.size.width)
|
||||
#define fatKScreenHeight ([UIScreen mainScreen].bounds.size.height)
|
||||
|
||||
static NSString *kAnnotationId = @"FATAnnotationViewId";
|
||||
static NSString *kUserAnnotationId = @"FATUserAnnotationViewId";
|
||||
|
||||
|
||||
@interface FATMapViewController () <MKMapViewDelegate, CLLocationManagerDelegate, FATLocationResultDelegate>
|
||||
|
||||
@property (nonatomic, strong) MKMapView *mapView;
|
||||
@property (nonatomic, strong) MKUserLocation *userLocation;
|
||||
@property (nonatomic, strong) UIButton *returnButton;
|
||||
@property (nonatomic, strong) UIButton *determineButton;
|
||||
@property (nonatomic, strong) UIButton *positionButton;
|
||||
@property (nonatomic, strong) FATExtSliderView *slideView;
|
||||
@property (nonatomic, strong) FATMapPlace *poiInfo;
|
||||
@property (nonatomic, strong) MKPointAnnotation *centerAnnotation;
|
||||
@property (nonatomic, strong) FATExtLocationManager *locationManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FATMapViewController
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
[self.navigationController setNavigationBarHidden:YES animated:animated];
|
||||
}
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
// [self.navigationController setNavigationBarHidden:NO animated:animated];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated {
|
||||
[super viewDidDisappear: animated];
|
||||
_mapView.showsUserLocation = NO;
|
||||
_mapView.userTrackingMode = MKUserTrackingModeNone;
|
||||
[_mapView.layer removeAllAnimations];
|
||||
[_mapView removeAnnotations:self.mapView.annotations];
|
||||
[_mapView removeOverlays:self.mapView.overlays];
|
||||
[_mapView removeFromSuperview];
|
||||
_mapView.delegate = nil;
|
||||
_mapView = nil;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
|
||||
[self p_initSubViews];
|
||||
|
||||
[self settingMapCenter];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
_mapView.showsUserLocation = NO;
|
||||
_mapView.userTrackingMode = MKUserTrackingModeNone;
|
||||
[_mapView.layer removeAllAnimations];
|
||||
[_mapView removeAnnotations:self.mapView.annotations];
|
||||
[_mapView removeOverlays:self.mapView.overlays];
|
||||
[_mapView removeFromSuperview];
|
||||
_mapView.delegate = nil;
|
||||
_mapView = nil;
|
||||
}
|
||||
|
||||
- (void)p_initSubViews {
|
||||
// 判断是否是暗黑模式
|
||||
BOOL isDark = false;
|
||||
if (@available(iOS 12.0, *)) {
|
||||
isDark = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark);
|
||||
}
|
||||
self.view.backgroundColor = isDark ? UIColor.blackColor : UIColor.whiteColor;
|
||||
[self.view addSubview:self.mapView];
|
||||
self.mapView.frame = CGRectMake(0, 0, fatKScreenWidth, fatKScreenHeight - 200);
|
||||
|
||||
self.centerAnnotation = [[MKPointAnnotation alloc] init];
|
||||
self.centerAnnotation.coordinate = self.mapView.centerCoordinate;
|
||||
[self.mapView addAnnotation:self.centerAnnotation];
|
||||
|
||||
CGFloat top = [UIView fat_statusHeight];
|
||||
self.returnButton = [[UIButton alloc] initWithFrame:CGRectMake(16, top, 57, 32)];
|
||||
[self.returnButton setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_back_n"] forState:UIControlStateNormal];
|
||||
[self.returnButton addTarget:self action:@selector(cancelItemClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.mapView addSubview:self.returnButton];
|
||||
|
||||
self.determineButton = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width - 16 - 57, top, 57, 32)];
|
||||
NSString *ok = [[FATClient sharedClient] fat_localizedStringForKey:@"OK"];
|
||||
[self.determineButton setTitle:ok forState:UIControlStateNormal];
|
||||
[self.determineButton setTitleColor:[UIColor colorWithRed:255 / 255.0 green:255 / 255.0 blue:255 / 255.0 alpha:1 / 1.0] forState:UIControlStateNormal];
|
||||
[self.determineButton setBackgroundColor:[UIColor colorWithRed:64 / 255.0 green:158 / 255.0 blue:255 / 255.0 alpha:1 / 1.0]];
|
||||
[self.determineButton addTarget:self action:@selector(sureItemClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
self.determineButton.titleLabel.font = [UIFont systemFontOfSize:17];
|
||||
[self.mapView addSubview:self.determineButton];
|
||||
|
||||
self.positionButton = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width - 73, fatKScreenHeight - 260, 45, 45)];
|
||||
|
||||
if (isDark) {
|
||||
[self.positionButton setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_location_dn"] forState:UIControlStateNormal];
|
||||
[self.positionButton setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_location_dp"] forState:UIControlStateHighlighted];
|
||||
self.mapView.mapType = MKMapTypeStandard;
|
||||
} else {
|
||||
[self.positionButton setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_location_ln"] forState:UIControlStateNormal];
|
||||
[self.positionButton setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_location_lp"] forState:UIControlStateHighlighted];
|
||||
self.mapView.mapType = MKMapTypeStandard;
|
||||
}
|
||||
[self.positionButton addTarget:self action:@selector(locationOnClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.mapView addSubview:self.positionButton];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.slideView = [[FATExtSliderView alloc] initWithFrame:CGRectMake(0, fatKScreenHeight - 200, fatKScreenWidth, fatKScreenHeight - 250)];
|
||||
self.slideView.backgroundColor = isDark ? UIColor.blackColor : UIColor.whiteColor;
|
||||
self.slideView.tableView.backgroundColor = isDark ? UIColor.blackColor : UIColor.whiteColor;
|
||||
self.slideView.topH = 300;
|
||||
self.slideView.selectItemBlock = ^(FATMapPlace *locationInfo) {
|
||||
CLLocationCoordinate2D centerCoord = {locationInfo.location.coordinate.latitude, locationInfo.location.coordinate.longitude};
|
||||
weakSelf.poiInfo = locationInfo;
|
||||
[UIView animateWithDuration:1 animations:^{
|
||||
weakSelf.centerAnnotation.coordinate = centerCoord;
|
||||
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(centerCoord, 1000, 1000);
|
||||
weakSelf.mapView.centerCoordinate = centerCoord;
|
||||
weakSelf.mapView.region = region;
|
||||
}];
|
||||
};
|
||||
|
||||
if ([self.latitude isEqualToString:@"nil"] || [self.longitude isEqualToString:@"nil"] || (!self.latitude && !self.longitude)) {
|
||||
[self startStandardUpdates];
|
||||
} else {
|
||||
CLLocationCoordinate2D centerCoord = {[self.latitude doubleValue], [self.longitude doubleValue]};
|
||||
self.mapView.centerCoordinate = centerCoord;
|
||||
[self.slideView updateSearchFrameWithColcationCoordinate:self.mapView.centerCoordinate];
|
||||
}
|
||||
__block float heights = fatKScreenHeight - 200;
|
||||
__block float positionButtonY = fatKScreenHeight - 260;
|
||||
self.slideView.topDistance = ^(float height, BOOL isTopOrBottom) {
|
||||
if (!isTopOrBottom) {
|
||||
heights += height;
|
||||
positionButtonY += height;
|
||||
} else {
|
||||
heights = height;
|
||||
positionButtonY = height;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
weakSelf.mapView.frame = CGRectMake(0, 0, fatKScreenWidth, heights);
|
||||
weakSelf.positionButton.frame = CGRectMake(self.view.frame.size.width - 73, positionButtonY > fatKScreenHeight - 260 ? fatKScreenHeight - 260 : positionButtonY, 45, 45);
|
||||
if (heights + weakSelf.slideView.frame.size.height < fatKScreenHeight) {
|
||||
weakSelf.mapView.frame = CGRectMake(0, 0, fatKScreenWidth, fatKScreenHeight - weakSelf.slideView.frame.size.height);
|
||||
weakSelf.positionButton.frame = CGRectMake(self.view.frame.size.width - 73, fatKScreenHeight - 260, 45, 45);
|
||||
}
|
||||
});
|
||||
};
|
||||
[self.view addSubview:self.slideView];
|
||||
}
|
||||
|
||||
- (void)locationOnClick {
|
||||
[self startStandardUpdates];
|
||||
}
|
||||
|
||||
- (void)startStandardUpdates {
|
||||
// if (![FATExtLocationManager locationServicesEnabled]) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
CLAuthorizationStatus status = [FATExtLocationManager authorizationStatus];
|
||||
if (status == kCLAuthorizationStatusAuthorizedWhenInUse ||
|
||||
status == kCLAuthorizationStatusAuthorizedAlways ||
|
||||
status == kCLAuthorizationStatusNotDetermined) {
|
||||
self.locationManager.delegate = self;
|
||||
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
|
||||
[self.locationManager requestWhenInUseAuthorization];
|
||||
[self.locationManager startUpdatingLocation];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)settingMapCenter {
|
||||
if ([NSString fat_isEmptyWithString:self.latitude] || [NSString fat_isEmptyWithString:self.longitude]) {
|
||||
return;
|
||||
}
|
||||
|
||||
double LongitudeDelta = [self fat_getLongitudeDelta:14.00];
|
||||
// double LatitudeDelta = LongitudeDelta * 2;
|
||||
CLLocationCoordinate2D centerCoord = {[self judgeLatition:[self.latitude doubleValue]], [self judgeLongitude:[self.longitude doubleValue]]};
|
||||
MKCoordinateRegion region = MKCoordinateRegionMake(centerCoord, MKCoordinateSpanMake(LongitudeDelta, LongitudeDelta));
|
||||
|
||||
[self.mapView setRegion:region animated:YES];
|
||||
}
|
||||
|
||||
#pragma mark - click events
|
||||
- (void)cancelItemClick {
|
||||
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
||||
if (self.cancelBlock) {
|
||||
self.cancelBlock();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sureItemClick {
|
||||
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
||||
|
||||
if (self.poiInfo == nil && self.slideView.poiInfoListArray.count > 0) {
|
||||
self.poiInfo = self.slideView.poiInfoListArray.firstObject;
|
||||
}
|
||||
|
||||
CLLocationCoordinate2D coordinate = self.poiInfo.location.coordinate;
|
||||
NSDictionary *locationInfo = @{@"name" : self.poiInfo.name ?: @"",
|
||||
@"address" : self.poiInfo.address ?: @"",
|
||||
@"latitude" : @(coordinate.latitude),
|
||||
@"longitude" : @(coordinate.longitude)};
|
||||
if (self.sureBlock) {
|
||||
self.sureBlock(locationInfo);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - FATLocationResultDelegate
|
||||
- (void)selectedLocationWithLocation:(FATMapPlace *)place {
|
||||
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(place.location.coordinate, 1000, 1000);
|
||||
self.mapView.centerCoordinate = place.location.coordinate;
|
||||
self.mapView.region = region;
|
||||
}
|
||||
|
||||
#pragma mark - MKMapViewDelegate
|
||||
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
|
||||
if (!self.userLocation) {
|
||||
_userLocation = userLocation;
|
||||
CLLocationCoordinate2D center = [FATWGS84ConvertToGCJ02ForAMapView transformFromWGSToGCJ:userLocation.location.coordinate];
|
||||
if (center.latitude > 0 && center.longitude > 0) {
|
||||
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center, 1000, 1000);
|
||||
if ([NSString fat_isEmptyWithString:self.latitude] || [NSString fat_isEmptyWithString:self.longitude]) {
|
||||
mapView.centerCoordinate = center;
|
||||
mapView.region = region;
|
||||
[self.mapView setRegion:region animated:YES];
|
||||
self.centerAnnotation.coordinate = center;
|
||||
}
|
||||
}
|
||||
[self.slideView updateSearchFrameWithColcationCoordinate:center];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新到位置之后调用
|
||||
*
|
||||
* @param manager 位置管理者
|
||||
* @param locations 位置数组
|
||||
*/
|
||||
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
|
||||
CLLocation *location = [locations firstObject];
|
||||
//位置更新后的经纬度
|
||||
CLLocationCoordinate2D theCoordinate = location.coordinate;
|
||||
//设置地图显示的中心及范围
|
||||
MKCoordinateRegion theRegion;
|
||||
theRegion.center = theCoordinate;
|
||||
CLLocationCoordinate2D coord = [FATWGS84ConvertToGCJ02ForAMapView transformFromWGSToGCJ:theCoordinate];
|
||||
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coord, 1000, 1000);
|
||||
[self.mapView setRegion:region animated:YES];
|
||||
self.centerAnnotation.coordinate = coord;
|
||||
[self.locationManager stopUpdatingLocation];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||
CLLocationCoordinate2D centerCoordinate = self.mapView.centerCoordinate;
|
||||
// CLLocationCoordinate2D centerCoordinate = self.centerAnnotation.coordinate;
|
||||
[self.slideView updateSearchFrameWithColcationCoordinate:centerCoordinate];
|
||||
}
|
||||
|
||||
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
|
||||
CLLocationCoordinate2D centerCoordinate = mapView.centerCoordinate;
|
||||
[UIView animateWithDuration:1 animations:^{
|
||||
self.centerAnnotation.coordinate = centerCoordinate;
|
||||
}];
|
||||
}
|
||||
|
||||
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
|
||||
if ([annotation isKindOfClass:[MKUserLocation class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSString *ID = @"anno";
|
||||
MKPinAnnotationView *annoView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:ID];
|
||||
if (annoView == nil) {
|
||||
annoView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID];
|
||||
// 显示气泡
|
||||
annoView.canShowCallout = YES;
|
||||
// 设置绿色
|
||||
}
|
||||
|
||||
return annoView;
|
||||
}
|
||||
|
||||
- (double)fat_getLongitudeDelta:(double)scale {
|
||||
double longitudeDelta = (360 * self.mapView.frame.size.width / 256.0 / pow(2, scale));
|
||||
return longitudeDelta;
|
||||
}
|
||||
|
||||
// 校验经度是否合规
|
||||
- (double)judgeLatition:(double)latitude {
|
||||
if (latitude >= 90) {
|
||||
latitude = 85.00;
|
||||
}
|
||||
if (latitude <= -90) {
|
||||
latitude = -85.00;
|
||||
}
|
||||
return latitude;
|
||||
}
|
||||
// 校验纬度是否合规
|
||||
- (double)judgeLongitude:(double)longitude {
|
||||
if (longitude >= 180) {
|
||||
longitude = 180.00;
|
||||
}
|
||||
if (longitude <= -180) {
|
||||
longitude = -180.00;
|
||||
}
|
||||
return longitude;
|
||||
}
|
||||
|
||||
- (FATExtLocationManager *)locationManager {
|
||||
if (_locationManager == nil) {
|
||||
_locationManager = [[FATExtLocationManager alloc] init];
|
||||
_locationManager.delegate = self;
|
||||
}
|
||||
return _locationManager;
|
||||
}
|
||||
|
||||
- (MKMapView *)mapView {
|
||||
if (!_mapView) {
|
||||
_mapView = [[MKMapView alloc] init];
|
||||
_mapView.delegate = self;
|
||||
_mapView.mapType = MKMapTypeStandard;
|
||||
_mapView.showsUserLocation = YES;
|
||||
_mapView.userTrackingMode = MKUserTrackingModeNone;
|
||||
}
|
||||
return _mapView;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// FATOpenLocationViewController.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/9.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^SureBlock)(NSDictionary *locationInfo);
|
||||
|
||||
@interface FATOpenLocationViewController : FATUIViewController
|
||||
|
||||
@property (nonatomic, copy) dispatch_block_t cancelBlock;
|
||||
@property (nonatomic, copy) SureBlock sureBlock;
|
||||
|
||||
/// 目标地纬度
|
||||
@property (nonatomic, strong) NSString *latitude;
|
||||
/// 目标地经度
|
||||
@property (nonatomic, strong) NSString *longitude;
|
||||
|
||||
@property (nonatomic, strong) NSString *scale;
|
||||
|
||||
@property (nonatomic, strong) NSString *name;
|
||||
|
||||
@property (nonatomic, strong) NSString *address;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,339 @@
|
|||
//
|
||||
// FATOpenLocationViewController.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/9.
|
||||
//
|
||||
|
||||
#import "FATOpenLocationViewController.h"
|
||||
#import "FATExtHelper.h"
|
||||
#import "MKMarkerView.h"
|
||||
#import "FATExtLocationManager.h"
|
||||
#import "FATWGS84ConvertToGCJ02.h"
|
||||
#import "FATExtUtil.h"
|
||||
|
||||
#import <MapKit/MapKit.h>
|
||||
|
||||
@interface FATOpenLocationViewController () <MKMapViewDelegate, CLLocationManagerDelegate>
|
||||
|
||||
@property (nonatomic, strong) MKMapView *mapView;
|
||||
|
||||
@property (nonatomic, strong) UIButton *locationButton;
|
||||
|
||||
@property (nonatomic, strong) UIButton *returnButton;
|
||||
|
||||
@property (nonatomic, strong) FATExtLocationManager *locationManager;
|
||||
|
||||
@property (nonatomic, assign) double Delta;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FATOpenLocationViewController
|
||||
|
||||
- (MKMapView *)mapView {
|
||||
if (!_mapView) {
|
||||
_mapView = [[MKMapView alloc] init];
|
||||
_mapView.delegate = self;
|
||||
_mapView.mapType = MKMapTypeStandard;
|
||||
_mapView.showsUserLocation = YES;
|
||||
_mapView.userTrackingMode = MKUserTrackingModeNone;
|
||||
}
|
||||
return _mapView;
|
||||
}
|
||||
|
||||
- (FATExtLocationManager *)locationManager {
|
||||
if (_locationManager == nil) {
|
||||
_locationManager = [[FATExtLocationManager alloc] init];
|
||||
_locationManager.delegate = self;
|
||||
}
|
||||
return _locationManager;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
[self.navigationController setNavigationBarHidden:YES animated:animated];
|
||||
}
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
[self fatCreatUI];
|
||||
|
||||
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, self.view.frame.size.height)];
|
||||
view.backgroundColor = UIColor.clearColor;
|
||||
[self.view addSubview:view];
|
||||
|
||||
UIScreenEdgePanGestureRecognizer *edgeGes = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgePan:)];
|
||||
edgeGes.edges = UIRectEdgeLeft;
|
||||
[view addGestureRecognizer:edgeGes];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
// _mapView.showsUserLocation = NO;
|
||||
// _mapView.userTrackingMode = MKUserTrackingModeNone;
|
||||
[_mapView.layer removeAllAnimations];
|
||||
[_mapView removeAnnotations:self.mapView.annotations];
|
||||
[_mapView removeOverlays:self.mapView.overlays];
|
||||
[_mapView removeFromSuperview];
|
||||
_mapView.delegate = nil;
|
||||
_mapView = nil;
|
||||
}
|
||||
|
||||
- (void)edgePan:(UIPanGestureRecognizer *)recognizer {
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fatCreatUI {
|
||||
CGFloat width = self.view.bounds.size.width;
|
||||
CGFloat height = self.view.bounds.size.height;
|
||||
CGFloat bottomViewHeight = 100;
|
||||
|
||||
self.mapView.frame = CGRectMake(0, 0, width, height);
|
||||
[self.view addSubview:self.mapView];
|
||||
|
||||
CGFloat top = [UIView fat_statusHeight];
|
||||
self.returnButton = [[UIButton alloc] initWithFrame:CGRectMake(16, top, 50, 50)];
|
||||
[self.returnButton setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_back_n"] forState:UIControlStateNormal];
|
||||
[self.returnButton addTarget:self action:@selector(returnOnClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.mapView addSubview:self.returnButton];
|
||||
|
||||
self.locationButton = [[UIButton alloc] initWithFrame:CGRectMake(width - 48 - 16.5, height - bottomViewHeight - 24.5 - 48, 48, 48)];
|
||||
// 判断是否是暗黑模式
|
||||
BOOL isDark = false;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
isDark = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark);
|
||||
}
|
||||
if (isDark) {
|
||||
[self.locationButton setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_location_dn"] forState:UIControlStateNormal];
|
||||
[self.locationButton setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_location_dp"] forState:UIControlStateHighlighted];
|
||||
} else {
|
||||
[self.locationButton setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_location_ln"] forState:UIControlStateNormal];
|
||||
[self.locationButton setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_location_lp"] forState:UIControlStateHighlighted];
|
||||
}
|
||||
[self.locationButton addTarget:self action:@selector(locationOnClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.mapView addSubview:self.locationButton];
|
||||
|
||||
UIView *bgView = [[UIView alloc] initWithFrame:CGRectMake(0, height - bottomViewHeight, width, bottomViewHeight)];
|
||||
if (@available(iOS 13.0, *)) {
|
||||
bgView.backgroundColor = UIColor.systemGray6Color;
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
[self.mapView addSubview:bgView];
|
||||
UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 25.5, width - 70, 31)];
|
||||
nameLabel.font = [UIFont systemFontOfSize:22];
|
||||
nameLabel.text = self.name;
|
||||
nameLabel.textColor = isDark ? [UIColor colorWithRed:208 / 255.0 green:208 / 255.0 blue:208 / 255.0 alpha:1 / 1.0] : [UIColor colorWithRed:34 / 255.0 green:34 / 255.0 blue:34 / 255.0 alpha:1 / 1.0];
|
||||
[bgView addSubview:nameLabel];
|
||||
UILabel *addressLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 65.5, width - 70, 16.5)];
|
||||
addressLabel.font = [UIFont systemFontOfSize:12];
|
||||
addressLabel.text = self.address;
|
||||
addressLabel.textColor = UIColor.lightGrayColor;
|
||||
[bgView addSubview:addressLabel];
|
||||
|
||||
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(bgView.frame.size.width - 70, bottomViewHeight / 2 - 25, 50, 50)];
|
||||
[button setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_navigation_n"] forState:UIControlStateNormal];
|
||||
[button setImage:[FATExtHelper fat_ext_imageFromBundleWithName:@"map_navigation_p"] forState:UIControlStateHighlighted];
|
||||
button.layer.cornerRadius = 25;
|
||||
[button addTarget:self action:@selector(navigationOnClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[bgView addSubview:button];
|
||||
|
||||
double delta = [self.scale doubleValue];
|
||||
if (delta < 5) {
|
||||
delta = 5.00;
|
||||
}
|
||||
if (delta > 18) {
|
||||
delta = 18.00;
|
||||
}
|
||||
double LongitudeDelta = [self fat_getLongitudeDelta:delta];
|
||||
self.Delta = LongitudeDelta;
|
||||
CLLocationCoordinate2D centerCoord = {[self judgeLatition:[self.latitude doubleValue]], [self judgeLongitude:[self.longitude doubleValue]]};
|
||||
[self.mapView setRegion:MKCoordinateRegionMake(centerCoord, MKCoordinateSpanMake(LongitudeDelta, LongitudeDelta)) animated:YES];
|
||||
// 添加一个大头针
|
||||
MKMarker *marker = [[MKMarker alloc] init];
|
||||
marker.coordinate = centerCoord;
|
||||
[self.mapView addAnnotation:marker];
|
||||
}
|
||||
|
||||
- (void)locationOnClick {
|
||||
[self startStandardUpdates];
|
||||
}
|
||||
|
||||
- (void)returnOnClick {
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)startStandardUpdates {
|
||||
if (![FATExtLocationManager locationServicesEnabled]) {
|
||||
return;
|
||||
}
|
||||
|
||||
CLAuthorizationStatus status = [FATExtLocationManager authorizationStatus];
|
||||
if (status == kCLAuthorizationStatusAuthorizedWhenInUse ||
|
||||
status == kCLAuthorizationStatusAuthorizedAlways ||
|
||||
status == kCLAuthorizationStatusNotDetermined) {
|
||||
//定位功能可用
|
||||
|
||||
self.locationManager.delegate = self;
|
||||
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
|
||||
[self.locationManager requestWhenInUseAuthorization];
|
||||
[self.locationManager startUpdatingLocation];
|
||||
|
||||
} else if (status == kCLAuthorizationStatusDenied) {
|
||||
}
|
||||
}
|
||||
|
||||
- (double)fat_getLongitudeDelta:(double)scale {
|
||||
double longitudeDelta = (360 * self.mapView.frame.size.width / 256.0 / pow(2, scale));
|
||||
return longitudeDelta;
|
||||
}
|
||||
|
||||
- (void)navigationOnClick {
|
||||
[self fat_openMapApp:@{@"latitude" : self.latitude, @"longitude" : self.longitude, @"destination" : self.name}];
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新到位置之后调用
|
||||
*
|
||||
* @param manager 位置管理者
|
||||
* @param locations 位置数组
|
||||
*/
|
||||
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
|
||||
CLLocation *location = [locations firstObject];
|
||||
//位置更新后的经纬度
|
||||
CLLocationCoordinate2D coord = [FATWGS84ConvertToGCJ02ForAMapView transformFromWGSToGCJ:location.coordinate];
|
||||
CLLocation *newLocations = [[CLLocation alloc] initWithLatitude:coord.latitude longitude:coord.longitude];
|
||||
|
||||
[self.mapView setRegion:MKCoordinateRegionMake(newLocations.coordinate, MKCoordinateSpanMake(self.Delta, self.Delta)) animated:YES];
|
||||
[self.locationManager stopUpdatingLocation];
|
||||
}
|
||||
|
||||
- (nullable MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
|
||||
// If the annotation is the user location, just return nil.(如果是显示用户位置的Annotation,则使用默认的蓝色圆点)
|
||||
if ([annotation isKindOfClass:[MKUserLocation class]]) {
|
||||
return nil;
|
||||
}
|
||||
if ([annotation isKindOfClass:MKMarker.class]) {
|
||||
MKMarker *marker = (MKMarker *)annotation;
|
||||
MKAnnotationView *markerView = [mapView dequeueReusableAnnotationViewWithIdentifier:@"markerView"];
|
||||
markerView.canShowCallout = YES;
|
||||
markerView.annotation = marker;
|
||||
markerView.image = marker.image;
|
||||
return markerView;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)fat_openMapApp:(NSDictionary *)data {
|
||||
/*
|
||||
常见app的url Scheme
|
||||
1.苹果自带地图(不需要检测,所以不需要URL Scheme)
|
||||
2.百度地图 :baidumap
|
||||
3.高德地图 :iosamap
|
||||
4.谷歌地图 :comgooglemaps
|
||||
IOS9之后,苹果进一步完善了安全机制,必须在plist里面设置url scheme白名单,不然无法打开对应的应用.
|
||||
添加一个字段:LSApplicationQueriesSchemes,类型为数组,然后在这个数组里面再添加我们所需要的地图 URL Scheme :
|
||||
*/
|
||||
|
||||
// 1.先检测有没有对应的app,有的话再加入
|
||||
NSString *appName = [FATExtUtil getAppName];
|
||||
NSString *title = [NSString stringWithFormat:@"%@%@", [[FATClient sharedClient] fat_localizedStringForKey:@"Navigate to"], data[@"destination"]];
|
||||
CLLocationCoordinate2D coordinate = {[data[@"latitude"] doubleValue], [data[@"longitude"] doubleValue]};
|
||||
// 打开地图进行导航
|
||||
// 1.创建UIAlertController
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title
|
||||
message:nil
|
||||
preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
UIAlertAction *appleAction = [UIAlertAction actionWithTitle:[[FATClient sharedClient] fat_localizedStringForKey:@"Apple Maps"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) {
|
||||
CLLocationCoordinate2D loc = CLLocationCoordinate2DMake(coordinate.latitude, coordinate.longitude);
|
||||
MKMapItem *currentLocation = [MKMapItem mapItemForCurrentLocation];
|
||||
MKMapItem *toLocation = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:loc addressDictionary:nil]];
|
||||
toLocation.name = data[@"destination"] ? data[@"destination"] : @"未知地点";
|
||||
[MKMapItem openMapsWithItems:@[ currentLocation, toLocation ]
|
||||
launchOptions:@{MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving,
|
||||
MKLaunchOptionsShowsTrafficKey : [NSNumber numberWithBool:YES]}];
|
||||
}];
|
||||
[appleAction setValue:[self labelColor] forKey:@"titleTextColor"];
|
||||
UIAlertAction *bdAction = [UIAlertAction actionWithTitle:[[FATClient sharedClient] fat_localizedStringForKey:@"Baidu Maps"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) {
|
||||
NSString *urlString = [[NSString stringWithFormat:@"baidumap://map/direction?origin={{我的位置}}&destination=latlng:%f,%f|name=目的地&mode=driving&coord_type=gcj02", coordinate.latitude, coordinate.longitude] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
|
||||
}];
|
||||
[bdAction setValue:[self labelColor] forKey:@"titleTextColor"];
|
||||
UIAlertAction *gdAction = [UIAlertAction actionWithTitle:[[FATClient sharedClient] fat_localizedStringForKey:@"Amap"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) {
|
||||
NSString *urlString = [[NSString stringWithFormat:@"iosamap://path?sourceApplication=%@&backScheme=%@&dlat=%f&dlon=%f&dev=0&t=0&dname=%@", appName, @"iosamap://", coordinate.latitude, coordinate.longitude, data[@"destination"]] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
|
||||
}];
|
||||
[gdAction setValue:[self labelColor] forKey:@"titleTextColor"];
|
||||
UIAlertAction *googleAction = [UIAlertAction actionWithTitle:[[FATClient sharedClient] fat_localizedStringForKey:@"Google Maps"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) {
|
||||
NSString *urlString = [[NSString stringWithFormat:@"comgooglemaps://?x-source=%@&x-success=%@&saddr=&daddr=%f,%f&directionsmode=driving", appName, @"comgooglemaps://", coordinate.latitude, coordinate.longitude] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
|
||||
}];
|
||||
[googleAction setValue:[self labelColor] forKey:@"titleTextColor"];
|
||||
UIAlertAction *tencentAction = [UIAlertAction actionWithTitle:[[FATClient sharedClient] fat_localizedStringForKey:@"Tencent Maps"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) {
|
||||
NSString *urlString = [[NSString stringWithFormat:@"qqmap://map/routeplan?from=我的位置&type=drive&to=%@&tocoord=%f,%f&coord_type=1&referer={ios.blackfish.XHY}",data[@"destination"],coordinate.latitude,coordinate.longitude] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
|
||||
}];
|
||||
[tencentAction setValue:[self labelColor] forKey:@"titleTextColor"];
|
||||
|
||||
NSString *cancel = [[FATClient sharedClient] fat_localizedStringForKey:@"Cancel"];
|
||||
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancel style:UIAlertActionStyleCancel handler:^(UIAlertAction *_Nonnull action){
|
||||
// [alertController ]
|
||||
}];
|
||||
[cancelAction setValue:[self labelColor] forKey:@"titleTextColor"];
|
||||
// 1.先检测有没有对应的app,有的话再加入
|
||||
[alertController addAction:appleAction];
|
||||
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"baidumap://"]]) {
|
||||
[alertController addAction:bdAction];
|
||||
}
|
||||
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"iosamap://"]]) {
|
||||
[alertController addAction:gdAction];
|
||||
}
|
||||
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"comgooglemaps://"]]) {
|
||||
[alertController addAction:googleAction];
|
||||
}
|
||||
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"qqmap://"]]){
|
||||
[alertController addAction:tencentAction];
|
||||
}
|
||||
[alertController addAction:cancelAction];
|
||||
UIViewController *topVC = [[UIApplication sharedApplication] fat_topViewController];
|
||||
[topVC presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (UIColor *)labelColor {
|
||||
if (@available(iOS 12.0, *)) {
|
||||
BOOL isDark = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark);
|
||||
return isDark ? [UIColor colorWithRed:208/255.0 green:208/255.0 blue:208/255.0 alpha:1/1.0] : [UIColor colorWithRed:34/255.0 green:34/255.0 blue:34/255.0 alpha:1/1.0];
|
||||
} else {
|
||||
return [UIColor colorWithRed:34/255.0 green:34/255.0 blue:34/255.0 alpha:1/1.0];
|
||||
}
|
||||
}
|
||||
|
||||
// 校验经度是否合规
|
||||
- (double)judgeLatition:(double)latitude {
|
||||
if (latitude >= 90) {
|
||||
latitude = 85.00;
|
||||
}
|
||||
if (latitude <= -90) {
|
||||
latitude = -85.00;
|
||||
}
|
||||
return latitude;
|
||||
}
|
||||
// 校验纬度是否合规
|
||||
- (double)judgeLongitude:(double)longitude {
|
||||
if (longitude >= 180) {
|
||||
longitude = 180.00;
|
||||
}
|
||||
if (longitude <= -180) {
|
||||
longitude = -180.00;
|
||||
}
|
||||
return longitude;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// FATExtRecorder.h
|
||||
// HLProject
|
||||
//
|
||||
// Created by Haley on 2021/12/28.
|
||||
// Copyright © 2021 Haley. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FATFrameState) {
|
||||
FATFrameStatePrepareToSend,
|
||||
FATFrameStateAlreadyWillSend,
|
||||
FATFrameStateAlreadySent
|
||||
};
|
||||
|
||||
@class FATExtRecorder;
|
||||
@protocol FATExtRecorderDelegate <NSObject>
|
||||
|
||||
- (void)extRecorder:(FATExtRecorder *)recorder
|
||||
onFrameData:(NSData *)frameData
|
||||
frameIndex:(NSInteger)frameIndex
|
||||
isLastFrame:(BOOL)isLastFrame;
|
||||
|
||||
- (void)extRecorderDidCompletion:(FATExtRecorder *)recorder;
|
||||
|
||||
- (void)extRecorderBeginInterruption:(FATExtRecorder *)recorder;
|
||||
|
||||
- (void)extRecorderEndInterruption:(FATExtRecorder *)recorder withOptions:(NSUInteger)flags;
|
||||
|
||||
- (void)extRecorder:(FATExtRecorder *)recorder onError:(NSError *)error;
|
||||
|
||||
@end
|
||||
|
||||
@interface FATExtRecorder : NSObject
|
||||
|
||||
@property (nonatomic, assign) BOOL isStarted;
|
||||
|
||||
@property (nonatomic, assign) BOOL isRecording;
|
||||
|
||||
@property (nonatomic, assign) BOOL isPausing;
|
||||
|
||||
@property (nonatomic, strong) NSString *recordFilePath;
|
||||
|
||||
@property (nonatomic, weak) id<FATExtRecorderDelegate> delegate;
|
||||
|
||||
@property (nonatomic, readonly, copy) NSString *recorderId;
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *frameInfoArray;
|
||||
|
||||
@property (nonatomic, assign) FATFrameState frameState;
|
||||
|
||||
@property (nonatomic, assign) BOOL waitToSendBuffer;
|
||||
|
||||
@property (nonatomic, copy) void(^eventCallBack)(NSInteger eventType,NSString *eventName, NSDictionary *paramDic,NSDictionary *extDic);
|
||||
|
||||
- (BOOL)startRecordWithDict:(NSDictionary *)dict appId:(NSString *)appId;
|
||||
|
||||
- (BOOL)pauseRecord;
|
||||
|
||||
- (BOOL)resumeRecord;
|
||||
|
||||
- (BOOL)stopRecord;
|
||||
|
||||
@end
|
|
@ -0,0 +1,799 @@
|
|||
//
|
||||
// FATExtRecorder.m
|
||||
// HLProject
|
||||
//
|
||||
// Created by Haley on 2021/12/28.
|
||||
// Copyright © 2021 Haley. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtRecorder.h"
|
||||
#import "FATExtUtil.h"
|
||||
#import "lame.h"
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
#define FATDefaultBufferSize 4096 //设置录音的缓冲区默认大小为4096,实际会小于或等于4096,需要处理小于4096的情况
|
||||
#define QUEUE_BUFFER_SIZE 3 //输出音频队列缓冲个数
|
||||
|
||||
|
||||
@interface FATExtRecorder ()
|
||||
{
|
||||
AudioQueueRef audioQRef; //音频队列对象指针
|
||||
AudioStreamBasicDescription inputDescription; //音频流配置
|
||||
AudioStreamBasicDescription outputDescription; //音频流配置
|
||||
AudioQueueBufferRef audioBuffers[QUEUE_BUFFER_SIZE]; //音频流缓冲区对象
|
||||
|
||||
AudioConverterRef _encodeConvertRef;
|
||||
|
||||
lame_t lame;
|
||||
FILE *mp3;
|
||||
|
||||
int pcm_buffer_size;
|
||||
uint8_t pcm_buffer[FATDefaultBufferSize*2];
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) AudioFileID recordFileID; //音频文件标识 用于关联音频文件
|
||||
@property (nonatomic, assign) SInt64 recordPacketNum;
|
||||
|
||||
@property (nonatomic, copy) NSString *recorderId;
|
||||
|
||||
// 录音的声道数
|
||||
@property (nonatomic, assign) UInt32 mChannelsPerFrame;
|
||||
// 录音采样率
|
||||
@property (nonatomic, assign) UInt32 mSampleRate;
|
||||
// 编码码率
|
||||
@property (nonatomic, assign) UInt32 encodeBitRate;
|
||||
// 录制的最大时长
|
||||
@property (nonatomic, assign) int duration;
|
||||
// 录音文件格式
|
||||
@property (nonatomic, copy) NSString *format;
|
||||
@property (nonatomic, assign) long long frameSize;
|
||||
@property (nonatomic, assign) BOOL shouldFrameCallback;
|
||||
|
||||
@property (nonatomic, assign) NSInteger currentBufferIndex;
|
||||
// 当前这一帧的buffer数据
|
||||
@property (nonatomic, strong) NSMutableData *currentFrameData;
|
||||
@property (nonatomic, assign) long long currentFrameOffset;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FATExtRecorder
|
||||
|
||||
void FATAudioInputCallbackHandler(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime, UInt32 inNumPackets, const AudioStreamPacketDescription *inPacketDesc) {
|
||||
|
||||
FATExtRecorder *recorder = (__bridge FATExtRecorder *)(inUserData);
|
||||
[recorder processAudioBuffer:inBuffer inStartTime:inStartTime inNumPackets:inNumPackets inPacketDesc:inPacketDesc audioQueue:inAQ];
|
||||
}
|
||||
|
||||
OSStatus FATAudioConverterComplexInputDataProc(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData) {
|
||||
|
||||
FATExtRecorder *recorder = (__bridge FATExtRecorder *)(inUserData);
|
||||
|
||||
BOOL result = [recorder handleConverterDataProcWithBufferList:ioData];
|
||||
if (result) {
|
||||
return noErr;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#pragma mark - override
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self p_addNotifications];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
// NSLog(@"FATExtRecorder---dealloc");
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
AudioQueueDispose(audioQRef, true);
|
||||
}
|
||||
|
||||
- (void)handleInterruption:(NSNotification *)notification {
|
||||
NSDictionary *info = notification.userInfo;
|
||||
AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
|
||||
if (type == AVAudioSessionInterruptionTypeBegan) {
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorderBeginInterruption:)]) {
|
||||
[self.delegate extRecorderBeginInterruption:self];
|
||||
}
|
||||
} else {
|
||||
AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
|
||||
if (options == AVAudioSessionInterruptionOptionShouldResume) {
|
||||
//Handle Resume
|
||||
}
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorderEndInterruption:withOptions:)]) {
|
||||
[self.delegate extRecorderEndInterruption:self withOptions:options];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)startRecordWithDict:(NSDictionary *)dict appId:(NSString *)appId {
|
||||
self.currentBufferIndex = 0;
|
||||
self.currentFrameOffset = 0;
|
||||
self.currentFrameData = [[NSMutableData alloc] init];
|
||||
self.frameState = FATFrameStatePrepareToSend;
|
||||
self.frameInfoArray = [NSMutableArray array];
|
||||
|
||||
memset(pcm_buffer, 0, pcm_buffer_size);
|
||||
pcm_buffer_size = 0;
|
||||
|
||||
self.recorderId = appId;
|
||||
// 1.配置录音的参数
|
||||
// 录音的音频输入源
|
||||
NSString *audioSource;
|
||||
if (!audioSource || ![audioSource isKindOfClass:[NSString class]]) {
|
||||
audioSource = @"auto";
|
||||
} else {
|
||||
audioSource = dict[@"audioSource"];
|
||||
}
|
||||
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
||||
if ([audioSource isEqualToString:@"buildInMic"]) {
|
||||
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:NULL];
|
||||
} else if ([audioSource isEqualToString:@"headsetMic"]) {
|
||||
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:NULL];
|
||||
} else {
|
||||
if (@available(iOS 10.0, *)) {
|
||||
// 这里通过计算,找不到与运算后为45的结果(45为微信的值)
|
||||
// NSUInteger options = AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionAllowAirPlay;
|
||||
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord mode:AVAudioSessionModeDefault options:45 error:NULL];
|
||||
} else {
|
||||
NSUInteger options = AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionAllowBluetooth;
|
||||
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:options error:NULL];
|
||||
}
|
||||
}
|
||||
[audioSession setActive:YES error:NULL];
|
||||
|
||||
// 1.1 格式
|
||||
NSString *format = [self _format:dict];
|
||||
|
||||
self.format = format;
|
||||
AudioFormatID formatID = [self _formatIDWithFormat:format];
|
||||
inputDescription.mFormatID = formatID;
|
||||
|
||||
// 1.2.录音通道数
|
||||
NSString *numberOfChannelsString = dict[@"numberOfChannels"];
|
||||
if (![self _isValidWithNumberOfChannelsString:numberOfChannelsString]) {
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorder:onError:)]) {
|
||||
NSError *error = [NSError errorWithDomain:@"FATExtRecorder" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"operateRecorder:fail channel error"}];
|
||||
[self.delegate extRecorder:self onError:error];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSNumber *numberOfChannelsNub = [NSNumber numberWithInt:[numberOfChannelsString intValue]];
|
||||
if (numberOfChannelsString == nil) {
|
||||
numberOfChannelsNub = @(2);
|
||||
}
|
||||
if ([format isEqualToString:@"aac"]) {
|
||||
// aac 格式双声道有杂音,暂时只支持单声道
|
||||
numberOfChannelsNub = @(1);
|
||||
}
|
||||
|
||||
inputDescription.mChannelsPerFrame = [numberOfChannelsNub intValue];
|
||||
self.mChannelsPerFrame = [numberOfChannelsNub intValue];
|
||||
|
||||
// 1.3.采样率
|
||||
NSString *sampleRateString = dict[@"sampleRate"];
|
||||
NSNumber *sampleRateNub;
|
||||
if (sampleRateString == nil || ![sampleRateString isKindOfClass:[NSString class]]) {
|
||||
sampleRateNub = @(8000);
|
||||
} else {
|
||||
sampleRateNub = [NSNumber numberWithInt:[sampleRateString intValue]];
|
||||
}
|
||||
inputDescription.mSampleRate = [sampleRateNub floatValue];
|
||||
self.mSampleRate = inputDescription.mSampleRate;
|
||||
|
||||
// 1.4.编码比特率
|
||||
NSString *encodeBitRateString = dict[@"encodeBitRate"];
|
||||
NSNumber *encodeBitRateNub;
|
||||
if (encodeBitRateString == nil || ![encodeBitRateString isKindOfClass:[NSString class]]) {
|
||||
encodeBitRateNub = @(48000);
|
||||
} else {
|
||||
encodeBitRateNub = [NSNumber numberWithInt:[encodeBitRateString intValue]];
|
||||
}
|
||||
self.encodeBitRate = [encodeBitRateNub floatValue];
|
||||
|
||||
NSString *durationString = dict[@"duration"];
|
||||
NSNumber *durationNub;
|
||||
if (durationString == nil || ![durationString isKindOfClass:[NSString class]]) {
|
||||
durationNub = @(60000);
|
||||
} else {
|
||||
durationNub = [NSNumber numberWithInt:[durationString intValue]];
|
||||
}
|
||||
|
||||
if ([durationNub intValue] <= 0) {
|
||||
durationNub = @(60000);
|
||||
}
|
||||
|
||||
if ([durationNub intValue] > 600000) {
|
||||
durationNub = @(600000);
|
||||
}
|
||||
|
||||
self.duration = [durationNub intValue];
|
||||
|
||||
// 编码格式
|
||||
inputDescription.mFormatID = kAudioFormatLinearPCM;
|
||||
inputDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
||||
|
||||
inputDescription.mBitsPerChannel = 16;
|
||||
// 每帧的字节数
|
||||
inputDescription.mBytesPerFrame = (inputDescription.mBitsPerChannel / 8) * inputDescription.mChannelsPerFrame;
|
||||
// 每个包的字节数
|
||||
inputDescription.mBytesPerPacket = inputDescription.mBytesPerFrame;
|
||||
// 每个包的帧数
|
||||
inputDescription.mFramesPerPacket = 1;
|
||||
|
||||
// 2. 生成文件名
|
||||
NSString *fileName = [self _fileNameWithFormat:format];
|
||||
NSString *filePath = [[FATExtUtil tmpDirWithAppletId:appId] stringByAppendingPathComponent:fileName];
|
||||
self.recordFilePath = filePath;
|
||||
|
||||
if ([format isEqualToString:@"mp3"]) {
|
||||
mp3 = fopen([self.recordFilePath cStringUsingEncoding:1], "wb");
|
||||
lame = lame_init();
|
||||
//采样率跟原音频参数设置一致
|
||||
lame_set_in_samplerate(lame, inputDescription.mSampleRate);
|
||||
// 通道数跟原音频参数设置一致,不设置默认为双通道
|
||||
lame_set_num_channels(lame, inputDescription.mChannelsPerFrame);
|
||||
lame_set_VBR(lame, vbr_default);
|
||||
lame_init_params(lame);
|
||||
} else if ([format isEqualToString:@"aac"]) {
|
||||
NSError *error;
|
||||
if (![self _createConverter:&error]) {
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorder:onError:)]) {
|
||||
[self.delegate extRecorder:self onError:error];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
[self copyEncoderCookieToFile];
|
||||
}
|
||||
|
||||
OSStatus status = AudioQueueNewInput(&inputDescription, FATAudioInputCallbackHandler, (__bridge void *)(self), NULL, NULL, 0, &audioQRef);
|
||||
if ( status != kAudioSessionNoError ) {
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorder:onError:)]) {
|
||||
NSError *error = [NSError errorWithDomain:@"FATExtRecorder" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"AudioQueueNewInput fail"}];
|
||||
[self.delegate extRecorder:self onError:error];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 1.5 frameSize
|
||||
NSString *frameSizeString = dict[@"frameSize"];
|
||||
if (![frameSizeString isKindOfClass:[NSString class]]) {
|
||||
frameSizeString = nil;
|
||||
}
|
||||
//设置的缓冲区有多大,那么在回调函数的时候得到的inbuffer的大小就是多大。callBack不足的时候,需要拼接,等待满足frameSize
|
||||
long long bufferByteSize = FATDefaultBufferSize;
|
||||
if ([frameSizeString floatValue]> 0.00 && [self _canCallbackFormat:format]) {
|
||||
self.shouldFrameCallback = YES;
|
||||
self.frameSize = (long long)[frameSizeString floatValue] * 1024;
|
||||
}
|
||||
|
||||
if ([format isEqualToString:@"aac"]) {
|
||||
// aac格式需要1024帧,每帧2字节
|
||||
bufferByteSize = 1024 * 2 * self.mChannelsPerFrame;
|
||||
}
|
||||
|
||||
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++){
|
||||
AudioQueueAllocateBuffer(audioQRef, bufferByteSize, &audioBuffers[i]);
|
||||
AudioQueueEnqueueBuffer(audioQRef, audioBuffers[i], 0, NULL);
|
||||
}
|
||||
|
||||
CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, (CFStringRef)self.recordFilePath, NULL);
|
||||
// 创建音频文件
|
||||
if ([format isEqualToString:@"aac"]) {
|
||||
AudioFileCreateWithURL(url, kAudioFileCAFType, &outputDescription, kAudioFileFlags_EraseFile, &_recordFileID);
|
||||
} else {
|
||||
AudioFileCreateWithURL(url, kAudioFileCAFType, &inputDescription, kAudioFileFlags_EraseFile, &_recordFileID);
|
||||
}
|
||||
|
||||
CFRelease(url);
|
||||
|
||||
self.recordPacketNum = 0;
|
||||
status = AudioQueueStart(audioQRef, NULL);
|
||||
if (status != kAudioSessionNoError) {
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorder:onError:)]) {
|
||||
NSError *error = [NSError errorWithDomain:@"FATExtRecorder" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"AudioQueueStart fail"}];
|
||||
[self.delegate extRecorder:self onError:error];
|
||||
}
|
||||
AudioQueueStop(audioQRef, NULL);
|
||||
return NO;
|
||||
}
|
||||
self.isStarted = YES;
|
||||
self.isRecording = YES;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)pauseRecord {
|
||||
OSStatus status = AudioQueuePause(audioQRef);
|
||||
if (status != kAudioSessionNoError) {
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorder:onError:)]) {
|
||||
NSError *error = [NSError errorWithDomain:@"FATExtRecorder" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"pause fail"}];
|
||||
[self.delegate extRecorder:self onError:error];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
self.isRecording = NO;
|
||||
self.isPausing = YES;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)resumeRecord {
|
||||
OSStatus status = AudioQueueStart(audioQRef, NULL);
|
||||
if (status != kAudioSessionNoError) {
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorder:onError:)]) {
|
||||
NSError *error = [NSError errorWithDomain:@"FATExtRecorder" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"resume fail"}];
|
||||
[self.delegate extRecorder:self onError:error];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
self.isRecording = YES;
|
||||
self.isPausing = NO;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)stopRecord {
|
||||
if (self.isRecording) {
|
||||
self.isRecording = NO;
|
||||
}
|
||||
self.isStarted = NO;
|
||||
// 停止录音队列
|
||||
AudioQueueStop(audioQRef, true);
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
AudioFileClose(_recordFileID);
|
||||
[self didEndRecord];
|
||||
});
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - private
|
||||
- (void)processAudioBuffer:(AudioQueueBufferRef)inBuffer inStartTime:(AudioTimeStamp *)inStartTime inNumPackets:(UInt32)inNumPackets inPacketDesc:(AudioStreamPacketDescription *)inPacketDesc audioQueue:(AudioQueueRef)audioQueueRef
|
||||
{
|
||||
const int audioSize = inBuffer->mAudioDataByteSize;
|
||||
// NSLog(@"processAudioBuffer---inNumPackets:%d, size:%d", inNumPackets, audioSize);
|
||||
if (inNumPackets > 0) {
|
||||
if ([self.format isEqualToString:@"mp3"]) {
|
||||
unsigned char mp3_buffer[audioSize];
|
||||
// 说法1:因为录音数据是char *类型的,一个char占一个字节。而这里要传的数据是short *类型的,一个short占2个字节
|
||||
// 说法2:每packet 有 1 帧; 单声道: 每 帧2 Bytes; 双声道: 每帧 4 Bytes。
|
||||
// 所以 单声道 nsamples = inBuffer->mAudioDataByteSize / 2;
|
||||
// 双声道 nsamples = inBuffer->mAudioDataByteSize / 4;
|
||||
// 结果 nsamples 正好等于 inNumPackets;
|
||||
int nsamples = inNumPackets;
|
||||
/**
|
||||
双声道必须要使用lame_encode_buffer_interleaved这个函数
|
||||
lame_encode_buffer //录音数据单声道16位整形用这个方法
|
||||
lame_encode_buffer_interleaved //录音数据双声道交错用这个方法
|
||||
lame_encode_buffer_float //录音数据采样深度32位浮点型用这个方法
|
||||
*/
|
||||
int recvLen;
|
||||
if (self.mChannelsPerFrame == 1) {
|
||||
// 单声道音频转码
|
||||
recvLen = lame_encode_buffer(lame, inBuffer->mAudioData, NULL, nsamples, mp3_buffer, audioSize);
|
||||
} else {
|
||||
recvLen = lame_encode_buffer_interleaved(lame, inBuffer->mAudioData, nsamples, mp3_buffer, audioSize);
|
||||
}
|
||||
// 写入文件
|
||||
fwrite(mp3_buffer, recvLen, 1, mp3);
|
||||
if (self.shouldFrameCallback) {
|
||||
[self.currentFrameData appendBytes:mp3_buffer length:recvLen];
|
||||
}
|
||||
// NSLog(@"%d", recvLen);
|
||||
} else if ([self.format isEqualToString:@"pcm"]) {
|
||||
// 非MP3格式直接写入文件
|
||||
void *bufferData = inBuffer->mAudioData;
|
||||
UInt32 buffersize = inBuffer->mAudioDataByteSize;
|
||||
if (self.shouldFrameCallback) {
|
||||
[self.currentFrameData appendBytes:bufferData length:buffersize];
|
||||
}
|
||||
AudioFileWritePackets(self.recordFileID, FALSE, audioSize, inPacketDesc, self.recordPacketNum, &inNumPackets, inBuffer->mAudioData);
|
||||
self.recordPacketNum += inNumPackets;
|
||||
} else if ([self.format isEqualToString:@"aac"]) {
|
||||
memcpy(pcm_buffer + pcm_buffer_size, inBuffer->mAudioData, inBuffer->mAudioDataByteSize);
|
||||
pcm_buffer_size = pcm_buffer_size + inBuffer->mAudioDataByteSize;
|
||||
|
||||
long long willEncodePCMBufferSize = 1024 * 2 * self.mChannelsPerFrame;
|
||||
if (pcm_buffer_size >= willEncodePCMBufferSize) {
|
||||
AudioBufferList *bufferList = [self convertedAACBufferListWith:inBuffer];
|
||||
|
||||
memcpy(pcm_buffer, pcm_buffer + willEncodePCMBufferSize, pcm_buffer_size - willEncodePCMBufferSize);
|
||||
pcm_buffer_size = pcm_buffer_size - willEncodePCMBufferSize;
|
||||
|
||||
void *bufferData = inBuffer->mAudioData;
|
||||
UInt32 buffersize = inBuffer->mAudioDataByteSize;
|
||||
|
||||
if (self.shouldFrameCallback) {
|
||||
[self.currentFrameData appendBytes:bufferData length:buffersize];
|
||||
}
|
||||
// free memory
|
||||
if (bufferList) {
|
||||
free(bufferList->mBuffers[0].mData);
|
||||
free(bufferList);
|
||||
}
|
||||
}
|
||||
} else if ([self.format isEqualToString:@"wav"]) {
|
||||
// 新增wav格式的音频。
|
||||
void *bufferData = inBuffer->mAudioData;
|
||||
UInt32 buffersize = inBuffer->mAudioDataByteSize;
|
||||
AudioFileWritePackets(self.recordFileID, FALSE, audioSize, inPacketDesc, self.recordPacketNum, &inNumPackets, inBuffer->mAudioData);
|
||||
self.recordPacketNum += inNumPackets;
|
||||
}
|
||||
|
||||
if (self.shouldFrameCallback) {
|
||||
if ([self.format isEqualToString:@"pcm"]) {
|
||||
long long fileSize = [self _getFileSize:self.recordFilePath];
|
||||
long long currentFrameSize;
|
||||
if (self.currentBufferIndex == 0) {
|
||||
currentFrameSize = fileSize - FATDefaultBufferSize;
|
||||
self.currentFrameOffset = FATDefaultBufferSize;
|
||||
} else {
|
||||
currentFrameSize = fileSize - self.currentFrameOffset;
|
||||
}
|
||||
|
||||
if (fileSize > self.frameSize) {
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorder:onFrameData:frameIndex:isLastFrame:)]) {
|
||||
[self.delegate extRecorder:self onFrameData:self.currentFrameData frameIndex:self.currentBufferIndex isLastFrame:NO];
|
||||
}
|
||||
self.currentFrameData = [[NSMutableData alloc] init];
|
||||
self.currentBufferIndex++;
|
||||
}
|
||||
|
||||
} else if ([self.format isEqualToString:@"mp3"]) {
|
||||
long long fileSize = [self _getFileSize:self.recordFilePath];
|
||||
long long currentFrameSize;
|
||||
if (self.currentBufferIndex == 0) {
|
||||
currentFrameSize = fileSize - FATDefaultBufferSize;
|
||||
self.currentFrameOffset = FATDefaultBufferSize;
|
||||
} else {
|
||||
currentFrameSize = fileSize - self.currentFrameOffset;
|
||||
}
|
||||
|
||||
if (currentFrameSize > self.frameSize) {
|
||||
// 满足一帧
|
||||
NSString *frameFilePath = [self.recordFilePath stringByDeletingPathExtension];
|
||||
frameFilePath = [frameFilePath stringByAppendingFormat:@"_%d", self.currentBufferIndex];
|
||||
frameFilePath = [frameFilePath stringByAppendingPathExtension:self.recordFilePath.pathExtension];
|
||||
BOOL result = [self.currentFrameData writeToFile:frameFilePath atomically:YES];
|
||||
// NSLog(@"写入文件:%@, 结果:%d",frameFilePath.lastPathComponent, result);
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorder:onFrameData:frameIndex:isLastFrame:)]) {
|
||||
[self.delegate extRecorder:self onFrameData:self.currentFrameData frameIndex:self.currentBufferIndex isLastFrame:NO];
|
||||
}
|
||||
|
||||
self.currentFrameData = [[NSMutableData alloc] init];
|
||||
self.currentFrameOffset += currentFrameSize;
|
||||
self.currentBufferIndex++;
|
||||
}
|
||||
} else if ([self.format isEqualToString:@"aac"]) {
|
||||
long long fileSize = [self _getFileSize:self.recordFilePath];
|
||||
// NSLog(@"fileSize:%lld", fileSize);
|
||||
|
||||
long long currentFrameSize;
|
||||
if (self.currentBufferIndex == 0) {
|
||||
currentFrameSize = fileSize - FATDefaultBufferSize;
|
||||
self.currentFrameOffset = FATDefaultBufferSize;
|
||||
} else {
|
||||
currentFrameSize = fileSize - self.currentFrameOffset;
|
||||
}
|
||||
|
||||
if (currentFrameSize > self.frameSize) {
|
||||
// 满足一帧
|
||||
// NSLog(@"currentFrameSize:%d", currentFrameSize);
|
||||
NSString *frameFilePath = [self.recordFilePath stringByDeletingPathExtension];
|
||||
frameFilePath = [frameFilePath stringByAppendingFormat:@"_%d", self.currentBufferIndex];
|
||||
frameFilePath = [frameFilePath stringByAppendingPathExtension:self.recordFilePath.pathExtension];
|
||||
BOOL result = [self.currentFrameData writeToFile:frameFilePath atomically:YES];
|
||||
// NSLog(@"写入文件:%@, 结果:%d",frameFilePath.lastPathComponent, result);
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorder:onFrameData:frameIndex:isLastFrame:)]) {
|
||||
[self.delegate extRecorder:self onFrameData:self.currentFrameData frameIndex:self.currentBufferIndex isLastFrame:NO];
|
||||
}
|
||||
|
||||
self.currentFrameData = [[NSMutableData alloc] init];
|
||||
self.currentFrameOffset += currentFrameSize;
|
||||
self.currentBufferIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSTimeInterval recordDuration = inStartTime->mSampleTime / self.mSampleRate * 1000.0;
|
||||
if (recordDuration >= self.duration) {
|
||||
if (self.isRecording) {
|
||||
[self stopRecord];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.isRecording || self.isPausing) {
|
||||
// 将缓冲器重新放入缓冲队列,以便重复使用该缓冲器
|
||||
AudioQueueEnqueueBuffer(audioQueueRef, inBuffer, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)handleConverterDataProcWithBufferList:(AudioBufferList *)bufferList
|
||||
{
|
||||
if ([self.format isEqualToString:@"aac"]) {
|
||||
bufferList->mBuffers[0].mData = pcm_buffer;
|
||||
bufferList->mBuffers[0].mNumberChannels = self.mChannelsPerFrame;
|
||||
bufferList->mBuffers[0].mDataByteSize = 1024 * 2 * self.mChannelsPerFrame;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)didEndRecord {
|
||||
if ([self.format isEqualToString:@"mp3"]) {
|
||||
// 写入VBR 头文件, 否则录音的时长不准
|
||||
lame_mp3_tags_fid(lame, mp3);
|
||||
lame_close(lame);
|
||||
fclose(mp3);
|
||||
}
|
||||
|
||||
if (self.currentFrameData.length > 0) {
|
||||
NSString *frameFilePath = [self.recordFilePath stringByDeletingPathExtension];
|
||||
frameFilePath = [frameFilePath stringByAppendingFormat:@"_%d", self.currentBufferIndex];
|
||||
frameFilePath = [frameFilePath stringByAppendingPathExtension:self.recordFilePath.pathExtension];
|
||||
BOOL result = [self.currentFrameData writeToFile:frameFilePath atomically:YES];
|
||||
// NSLog(@"Last写入文件:%@, 结果:%d",frameFilePath.lastPathComponent, result);
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorder:onFrameData:frameIndex:isLastFrame:)]) {
|
||||
[self.delegate extRecorder:self onFrameData:self.currentFrameData frameIndex:self.currentBufferIndex isLastFrame:YES];
|
||||
}
|
||||
|
||||
self.currentFrameData = nil;
|
||||
self.currentFrameOffset = 0;
|
||||
self.currentBufferIndex = 0;
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(extRecorderDidCompletion:)]) {
|
||||
[self.delegate extRecorderDidCompletion:self];
|
||||
}
|
||||
|
||||
// 胶囊按钮(不管什么原因导致的结束录音,都在这里面恢复胶囊按钮的状态)
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:YES state:FATCapsuleButtonStateMicroPhone animate:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)p_addNotifications {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:nil];
|
||||
}
|
||||
|
||||
#pragma mark - tool method
|
||||
- (NSString *)_format:(NSDictionary *)data {
|
||||
NSString *format = data[@"format"];
|
||||
if (!format || ![format isKindOfClass:[NSString class]]) {
|
||||
format = @"aac";
|
||||
}
|
||||
format = [format lowercaseString];
|
||||
NSArray *validFormats = @[@"mp3", @"aac", @"wav", @"pcm"];
|
||||
if (![validFormats containsObject:format]) {
|
||||
return @"aac";
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
- (AudioFormatID)_formatIDWithFormat:(NSString *)format {
|
||||
return kAudioFormatLinearPCM;
|
||||
}
|
||||
|
||||
- (BOOL)_isValidWithNumberOfChannelsString:(NSString *)numberOfChannelsString {
|
||||
if (!numberOfChannelsString) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (![numberOfChannelsString isKindOfClass:[NSString class]]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ([numberOfChannelsString isEqualToString:@"1"] || [numberOfChannelsString isEqualToString:@"2"]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *)_fileNameWithFormat:(NSString *)format {
|
||||
NSString *currentDt = [NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]];
|
||||
NSData *data = [currentDt dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *nameMD5 = [[FATExtUtil fat_md5WithBytes:(char *)[data bytes] length:data.length] lowercaseString];
|
||||
NSString *extensionName = [self _extensionNameWithFormat:format];
|
||||
NSString *fileName = [NSString stringWithFormat:@"tmp_%@%@", nameMD5, extensionName];
|
||||
return fileName;
|
||||
}
|
||||
|
||||
- (NSString *)_extensionNameWithFormat:(NSString *)format {
|
||||
// if ([format isEqualToString:@"aac"]) {
|
||||
// return @".m4a";
|
||||
// }
|
||||
if ([format isEqualToString:@"pcm"]) {
|
||||
return @".caf";
|
||||
}
|
||||
|
||||
NSString *ext = [NSString stringWithFormat:@".%@", format];
|
||||
return ext;
|
||||
}
|
||||
|
||||
- (BOOL)_canCallbackFormat:(NSString *)format
|
||||
{
|
||||
if ([format isEqualToString:@"mp3"]) {
|
||||
return YES;
|
||||
}
|
||||
if ([format isEqualToString:@"pcm"]) {
|
||||
return YES;
|
||||
}
|
||||
if ([format isEqualToString:@"aac"]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (long long)_getFileSize:(NSString *)path
|
||||
{
|
||||
NSFileManager *filemanager = [NSFileManager defaultManager];
|
||||
if (![filemanager fileExistsAtPath:path]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NSDictionary *attributes = [filemanager attributesOfItemAtPath:path error:nil];
|
||||
NSNumber *theFileSize = [attributes objectForKey:NSFileSize];
|
||||
return [theFileSize longLongValue];
|
||||
}
|
||||
|
||||
- (BOOL)_createConverter:(NSError **)aError {
|
||||
// 此处目标格式其他参数均为默认,系统会自动计算,否则无法进入encodeConverterComplexInputDataProc回调
|
||||
AudioStreamBasicDescription sourceDes = inputDescription; // 原始格式
|
||||
AudioStreamBasicDescription targetDes; // 转码后格式
|
||||
|
||||
// 设置目标格式及基本信息
|
||||
memset(&targetDes, 0, sizeof(targetDes));
|
||||
targetDes.mFormatID = kAudioFormatMPEG4AAC;
|
||||
targetDes.mSampleRate = inputDescription.mSampleRate ;
|
||||
targetDes.mChannelsPerFrame = inputDescription.mChannelsPerFrame;
|
||||
targetDes.mFramesPerPacket = 1024; // 采集的为AAC需要将targetDes.mFramesPerPacket设置为1024,AAC软编码需要喂给转换器1024个样点才开始编码,这与回调函数中inNumPackets有关,不可随意更改
|
||||
|
||||
OSStatus status = 0;
|
||||
UInt32 targetSize = sizeof(targetDes);
|
||||
status = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &targetSize, &targetDes);
|
||||
|
||||
memset(&outputDescription, 0, sizeof(outputDescription));
|
||||
memcpy(&outputDescription, &targetDes, targetSize);
|
||||
|
||||
// 选择软件编码
|
||||
AudioClassDescription audioClassDes;
|
||||
status = AudioFormatGetPropertyInfo(kAudioFormatProperty_Encoders,
|
||||
sizeof(targetDes.mFormatID),
|
||||
&targetDes.mFormatID,
|
||||
&targetSize);
|
||||
|
||||
UInt32 numEncoders = targetSize/sizeof(AudioClassDescription);
|
||||
AudioClassDescription audioClassArr[numEncoders];
|
||||
AudioFormatGetProperty(kAudioFormatProperty_Encoders,
|
||||
sizeof(targetDes.mFormatID),
|
||||
&targetDes.mFormatID,
|
||||
&targetSize,
|
||||
audioClassArr);
|
||||
|
||||
for (int i = 0; i < numEncoders; i++) {
|
||||
if (audioClassArr[i].mSubType == kAudioFormatMPEG4AAC && audioClassArr[i].mManufacturer == kAppleSoftwareAudioCodecManufacturer) {
|
||||
memcpy(&audioClassDes, &audioClassArr[i], sizeof(AudioClassDescription));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
status = AudioConverterNewSpecific(&sourceDes, &targetDes, 1, &audioClassDes, &_encodeConvertRef);
|
||||
|
||||
if (status != noErr) {
|
||||
*aError = [NSError errorWithDomain:@"FATExtRecorder" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"create converter fail"}];
|
||||
// NSLog(@"Error : New convertRef failed");
|
||||
return NO;
|
||||
}
|
||||
|
||||
targetSize = sizeof(sourceDes);
|
||||
status = AudioConverterGetProperty(_encodeConvertRef, kAudioConverterCurrentInputStreamDescription, &targetSize, &sourceDes);
|
||||
|
||||
targetSize = sizeof(targetDes);
|
||||
status = AudioConverterGetProperty(_encodeConvertRef, kAudioConverterCurrentOutputStreamDescription, &targetSize, &targetDes);
|
||||
|
||||
// 设置码率,需要和采样率对应
|
||||
UInt32 bitRate = self.encodeBitRate;
|
||||
targetSize = sizeof(bitRate);
|
||||
status = AudioConverterSetProperty(_encodeConvertRef,
|
||||
kAudioConverterEncodeBitRate,
|
||||
targetSize, &bitRate);
|
||||
if (status != noErr) {
|
||||
*aError = [NSError errorWithDomain:@"FATExtRecorder" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"encodeBitRate not applicable"}];
|
||||
// NSLog(@"Error : set encodeBitRate failed");
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)copyEncoderCookieToFile {
|
||||
UInt32 cookieSize = 0;
|
||||
OSStatus status = AudioConverterGetPropertyInfo(_encodeConvertRef, kAudioConverterCompressionMagicCookie, &cookieSize, NULL);
|
||||
|
||||
if (status != noErr || cookieSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *cookie = (char *)malloc(cookieSize * sizeof(char));
|
||||
status = AudioConverterGetProperty(_encodeConvertRef, kAudioConverterCompressionMagicCookie, &cookieSize, cookie);
|
||||
|
||||
if (status == noErr) {
|
||||
status = AudioFileSetProperty(_recordFileID, kAudioFilePropertyMagicCookieData, cookieSize, cookie);
|
||||
if (status == noErr) {
|
||||
UInt32 willEatTheCookie = false;
|
||||
status = AudioFileGetPropertyInfo(_recordFileID, kAudioFilePropertyMagicCookieData, NULL, &willEatTheCookie);
|
||||
printf("Writing magic cookie to destination file: %u\n cookie:%d \n", (unsigned int)cookieSize, willEatTheCookie);
|
||||
} else {
|
||||
printf("Even though some formats have cookies, some files don't take them and that's OK\n");
|
||||
}
|
||||
} else {
|
||||
printf("Could not Get kAudioConverterCompressionMagicCookie from Audio Converter!\n");
|
||||
}
|
||||
|
||||
free(cookie);
|
||||
}
|
||||
|
||||
- (AudioBufferList *)convertedAACBufferListWith:(AudioQueueBufferRef)inBuffer
|
||||
{
|
||||
UInt32 maxPacketSize = 0;
|
||||
UInt32 size = sizeof(maxPacketSize);
|
||||
OSStatus status;
|
||||
|
||||
status = AudioConverterGetProperty(_encodeConvertRef,
|
||||
kAudioConverterPropertyMaximumOutputPacketSize,
|
||||
&size,
|
||||
&maxPacketSize);
|
||||
|
||||
AudioBufferList *bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList));
|
||||
bufferList->mNumberBuffers = 1;
|
||||
bufferList->mBuffers[0].mNumberChannels = self.mChannelsPerFrame;
|
||||
bufferList->mBuffers[0].mData = malloc(maxPacketSize);
|
||||
bufferList->mBuffers[0].mDataByteSize = 1024 * 2 * self.mChannelsPerFrame;
|
||||
|
||||
UInt32 inNumPackets = 1;
|
||||
AudioStreamPacketDescription outputPacketDescriptions;
|
||||
status = AudioConverterFillComplexBuffer(_encodeConvertRef,
|
||||
FATAudioConverterComplexInputDataProc,
|
||||
(__bridge void * _Nullable)(self),
|
||||
&inNumPackets,
|
||||
bufferList,
|
||||
&outputPacketDescriptions);
|
||||
|
||||
if (status != noErr) {
|
||||
free(bufferList->mBuffers[0].mData);
|
||||
free(bufferList);
|
||||
return nil;
|
||||
}
|
||||
|
||||
status = AudioFileWritePackets(self.recordFileID,
|
||||
FALSE,
|
||||
bufferList->mBuffers[0].mDataByteSize,
|
||||
&outputPacketDescriptions,
|
||||
self.recordPacketNum,
|
||||
&inNumPackets,
|
||||
bufferList->mBuffers[0].mData);
|
||||
if (status == noErr) {
|
||||
self.recordPacketNum += inNumPackets; // 用于记录起始位置
|
||||
} else {
|
||||
// NSLog(@"数据写入失败");
|
||||
}
|
||||
|
||||
return bufferList;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// FATExtBaseApi.h
|
||||
// FinAppletExtension
|
||||
//
|
||||
// Created by Haley on 2020/8/11.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
@class FATAppletInfo;
|
||||
|
||||
@protocol FATApiHanderContextDelegate <NSObject>
|
||||
//获取当前控制器
|
||||
- (UIViewController *)getCurrentViewController;
|
||||
|
||||
//获取当前的页面id
|
||||
- (NSString *)getCurrentPageId;
|
||||
|
||||
/// API发送回调事件给service层或者page层
|
||||
/// eventName 事件名
|
||||
/// eventType 0: service层订阅事件(callSubscribeHandlerWithEvent) 1:page层订阅事件(callSubscribeHandlerWithEvent) int类型方便以后有需要再添加事件类型
|
||||
/// paramDic 回调事件的参数
|
||||
/// extDic 扩展参数,预留字段,方便以后扩展 可以包含webId,表示发送事件给某个指定页面 service事件可以包含jsContextKey和jsContextValue,可以给分包service的JSContext设置值,用在数据帧
|
||||
- (void)sendResultEvent:(NSInteger)eventType eventName:(NSString *)eventName eventParams:(NSDictionary *)param extParams:(NSDictionary *)extDic;
|
||||
|
||||
@optional
|
||||
|
||||
- (NSString *)insertChildView:(UIView *)childView viewId:(NSString *)viewId parentViewId:(NSString *)parentViewId isFixed:(BOOL)isFixed isHidden:(BOOL)isHidden;
|
||||
|
||||
//更新hide属性为NO时使用
|
||||
/// @param childView 原生视图
|
||||
/// @param viewId 原生视图id
|
||||
/// @param parentViewId 原生视图父视图id,即WkScrollView的id,对应事件的cid
|
||||
/// @param isFixed 同层渲染这个字段不起作用,传NO或YES都可以,非同层渲染生效,传NO则组件会跟着页面滚动,传YES则组件固定在页面上,不会随着滚动
|
||||
- (void)updateChildViewHideProperty:(UIView *)childView viewId:(NSString *)viewId parentViewId:(NSString *)parentViewId isFixed:(BOOL)isFixed isHidden:(BOOL)isHidden complete:(void(^)(BOOL result))complete;
|
||||
|
||||
|
||||
- (UIView *)getChildViewById:(NSString *)viewId;
|
||||
|
||||
- (BOOL)removeChildView:(NSString *)viewId;
|
||||
|
||||
@end
|
||||
|
||||
@protocol FATApiProtocol <NSObject>
|
||||
|
||||
@required
|
||||
|
||||
@property (nonatomic, strong) FATAppletInfo *appletInfo;
|
||||
|
||||
/**
|
||||
api名称
|
||||
*/
|
||||
@property (nonatomic, readonly, copy) NSString *command;
|
||||
|
||||
/**
|
||||
原始参数
|
||||
*/
|
||||
@property (nonatomic, readonly, copy) NSDictionary<NSString *, id> *param;
|
||||
|
||||
@property (nonatomic, weak) id<FATApiHanderContextDelegate> context;
|
||||
|
||||
|
||||
/**
|
||||
设置API, 子类重写
|
||||
|
||||
@param success 成功回调
|
||||
@param failure 失败回调
|
||||
@param cancel 取消回调
|
||||
*/
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel;
|
||||
|
||||
@optional
|
||||
/**
|
||||
同步api,子类重写
|
||||
*/
|
||||
- (NSString *)setupSyncApi;
|
||||
|
||||
@end
|
||||
|
||||
@interface FATExtBaseApi : NSObject<FATApiProtocol>
|
||||
|
||||
@property (nonatomic, copy, readonly) NSDictionary *param;
|
||||
|
||||
@property (nonatomic, strong) FATAppletInfo *appletInfo;
|
||||
|
||||
/**
|
||||
api名称
|
||||
*/
|
||||
@property (nonatomic, readonly, copy) NSString *command;
|
||||
|
||||
@property (nonatomic, weak) id<FATApiHanderContextDelegate> context;
|
||||
|
||||
|
||||
//+ (FATExtBaseApi *)apiWithCommand:(NSString *)command param:(NSDictionary *)param;
|
||||
|
||||
//- (void)setupApiWithCallback:(FATExtensionApiCallback)callback;
|
||||
|
||||
//创建API对象,用于地图sdk的API,地图的API有点特殊,如果用户使用了高德或者百度地图,需要创建对应sdk的API对象
|
||||
+ (id<FATApiProtocol>)fat_apiWithApiClass:(NSString *)apiClassName params:(NSDictionary *)params;
|
||||
|
||||
@end
|
|
@ -0,0 +1,180 @@
|
|||
//
|
||||
// FATExtBaseApi.m
|
||||
// FinAppletExtension
|
||||
//
|
||||
// Created by Haley on 2020/8/11.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@implementation FATExtBaseApi
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
//默认实现,子类重写!!!
|
||||
if (cancel) {
|
||||
cancel(nil);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
|
||||
if (failure) {
|
||||
failure(nil);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
同步api,子类重写
|
||||
*/
|
||||
- (NSString *)setupSyncApi {
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (id<FATApiProtocol>)fat_apiWithApiClass:(NSString *)apiClassName params:(NSDictionary *)params {
|
||||
if (!apiClassName) {
|
||||
return nil;
|
||||
}
|
||||
Class apiClass = NSClassFromString(apiClassName);
|
||||
if (!apiClass) {
|
||||
return nil;
|
||||
}
|
||||
id apiObj = [[apiClass alloc]init];
|
||||
if (![apiObj conformsToProtocol:@protocol(FATApiProtocol)]) {
|
||||
return nil;
|
||||
}
|
||||
id<FATApiProtocol> api = (id<FATApiProtocol>)apiObj;
|
||||
NSString *apiName = @"";
|
||||
//分离出事件名
|
||||
NSArray *apiNameArray = [apiClassName componentsSeparatedByString:@"_"];
|
||||
if (apiNameArray && apiNameArray.count > 1) {
|
||||
apiName = apiNameArray[1];
|
||||
}
|
||||
[self setAPiObjectProperty:api command:apiName params:params];
|
||||
return api;
|
||||
}
|
||||
|
||||
+ (void)setAPiObjectProperty:(id<FATApiProtocol>) api command:(NSString *)command params:(NSDictionary *)param {
|
||||
if (![api isKindOfClass:NSObject.class]) {
|
||||
return;
|
||||
}
|
||||
NSObject *apiObj = (NSObject *)api;
|
||||
[apiObj setValue:command forKey:@"command"];
|
||||
[apiObj setValue:param forKey:@"param"];
|
||||
//postMessage事件传过来的params是NSString
|
||||
if ([param isKindOfClass:NSDictionary.class]) {
|
||||
for (NSString *datakey in param.allKeys) {
|
||||
NSString *propertyKey = datakey;
|
||||
@autoreleasepool {
|
||||
objc_property_t property = class_getProperty([apiObj class], [propertyKey UTF8String]);
|
||||
if (!property) {
|
||||
continue;
|
||||
}
|
||||
|
||||
id value = [param objectForKey:datakey];
|
||||
id safetyValue = [self parseFromKeyValue:value];
|
||||
|
||||
if (!safetyValue) continue;
|
||||
|
||||
NSString *propertyType = [NSString stringWithUTF8String:property_copyAttributeValue(property, "T")];
|
||||
propertyType = [propertyType stringByReplacingOccurrencesOfString:@"@" withString:@""];
|
||||
propertyType = [propertyType stringByReplacingOccurrencesOfString:@"\\" withString:@""];
|
||||
propertyType = [propertyType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
|
||||
//NSLog(@"propertyType:%@,value是:%@", propertyType,value);
|
||||
|
||||
//只检校以下几种类型,可变类型我们一般用不着,故不检校
|
||||
if (
|
||||
[propertyType isEqualToString:@"NSString"] ||
|
||||
[propertyType isEqualToString:@"NSArray"] ||
|
||||
[propertyType isEqualToString:@"NSDictionary"]) {
|
||||
if (![safetyValue isKindOfClass:NSClassFromString(propertyType)]) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//NSNumber类型和基本类型统一处理为string,也不需要检校了
|
||||
//其他类型不检校
|
||||
[apiObj setValue:safetyValue forKey:propertyKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+ (id)parseFromKeyValue:(id)value {
|
||||
// //值无效
|
||||
// if ([value isKindOfClass:[NSNull class]]) {
|
||||
// return nil;
|
||||
// }
|
||||
//
|
||||
// if ([value isKindOfClass:[NSNumber class]]) { //统一处理为字符串
|
||||
// value = [NSString stringWithFormat:@"%@", value];
|
||||
// }
|
||||
//
|
||||
// return value;
|
||||
//}
|
||||
|
||||
//// 作空值过滤处理-任意对象
|
||||
+ (id)parseFromKeyValue:(id)value {
|
||||
//值无效
|
||||
if ([value isKindOfClass:[NSNull class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ([value isKindOfClass:[NSNumber class]]) { //统一处理为字符串
|
||||
value = [NSString stringWithFormat:@"%@", value];
|
||||
} else if ([value isKindOfClass:[NSArray class]]) { //数组
|
||||
value = [self parseFromArray:value];
|
||||
} else if ([value isKindOfClass:[NSDictionary class]]) { //字典
|
||||
value = [self parseFromDictionary:value];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// 作空值过滤处理-字典对象
|
||||
+ (NSDictionary *)parseFromDictionary:(NSDictionary *)container {
|
||||
if ([container isKindOfClass:[NSDictionary class]]) {
|
||||
NSMutableDictionary *result = [NSMutableDictionary new];
|
||||
for (id key in container.allKeys) {
|
||||
@autoreleasepool {
|
||||
id value = container[key];
|
||||
|
||||
id safetyValue = [self parseFromKeyValue:value];
|
||||
if (!safetyValue) {
|
||||
safetyValue = @"";
|
||||
}
|
||||
[result setObject:safetyValue forKey:key];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
// 作空值过滤处理-数组对象
|
||||
+ (NSArray *)parseFromArray:(NSArray *)container {
|
||||
if ([container isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *result = [NSMutableArray new];
|
||||
for (int i = 0; i < container.count; i++) {
|
||||
@autoreleasepool {
|
||||
id value = container[i];
|
||||
|
||||
id safetyValue = [self parseFromKeyValue:value];
|
||||
if (!safetyValue) {
|
||||
safetyValue = @"";
|
||||
}
|
||||
|
||||
[result addObject:safetyValue];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// FATExt_recorderManager.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2021/1/21.
|
||||
// Copyright © 2021 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_recorderManager : FATExtBaseApi
|
||||
|
||||
/**
|
||||
真实事件名
|
||||
*/
|
||||
@property (nonatomic, copy) NSString *method;
|
||||
|
||||
/**
|
||||
参数
|
||||
*/
|
||||
@property (nonatomic, copy) NSDictionary *data;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// FATExt_recorderManager.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2021/1/21.
|
||||
// Copyright © 2021 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExt_recorderManager.h"
|
||||
#import "FATExtRecordManager.h"
|
||||
#import "FATClient+ext.h"
|
||||
|
||||
#import "FATExtUtil.h"
|
||||
|
||||
@implementation FATExt_recorderManager
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
NSArray *validMethods = @[@"start",@"pause",@"resume",@"stop",@"onFrameRecordedRemove"];
|
||||
NSString *method = self.method;
|
||||
if (![validMethods containsObject:method]) {
|
||||
return;
|
||||
}
|
||||
__block NSDictionary *dataDict = self.data;
|
||||
FATAppletInfo *appInfo = [[FATClient sharedClient] currentApplet];
|
||||
BOOL result = [[FATExtRecordManager shareManager] checkRecordWithMethod:method data:dataDict appletId:appInfo.appId];
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
if ([method isEqualToString:@"start"]) {
|
||||
[[FATClient sharedClient] fat_requestAppletAuthorize:FATAuthorizationTypeMicrophone appletId:appInfo.appId complete:^(NSInteger status) {
|
||||
if (status == 1) { //拒绝
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized,用户未授予麦克风权限"});
|
||||
}
|
||||
NSDictionary *params = @{
|
||||
@"method" : @"onError",
|
||||
@"data" : @{@"errMsg" : @"operateRecorder:fail fail_system permissionn denied"},
|
||||
};
|
||||
if (self.context) {
|
||||
[self.context sendResultEvent:0 eventName:@"onRecorderManager" eventParams:params extParams:nil];
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (status == 2) { //sdk拒绝
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized disableauthorized,SDK被禁止申请麦克风权限"});
|
||||
}
|
||||
return;
|
||||
}
|
||||
dataDict = [self checkAACAuioParams:dataDict];
|
||||
[[FATExtRecordManager shareManager] startRecordWithData:dataDict appletId:appInfo.appId eventBlock:^(NSInteger eventType, NSString *eventName, NSDictionary *paramDic, NSDictionary *extDic) {
|
||||
if (self.context) {
|
||||
[self.context sendResultEvent:eventType eventName:eventName eventParams:paramDic extParams:extDic];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
} else if ([method isEqualToString:@"pause"]) {
|
||||
[[FATExtRecordManager shareManager] pauseRecordWithData:dataDict appletId:appInfo.appId];
|
||||
} else if ([method isEqualToString:@"resume"]) {
|
||||
[[FATExtRecordManager shareManager] resumeRecordWithData:dataDict appletId:appInfo.appId];
|
||||
} else if ([method isEqualToString:@"stop"]) {
|
||||
[[FATExtRecordManager shareManager] stopRecordWithData:dataDict appletId:appInfo.appId];
|
||||
} else if ([method isEqualToString:@"onFrameRecordedRemove"]) {
|
||||
[[FATExtRecordManager shareManager] sendRecordFrameBufferWithData:dataDict appletId:appInfo.appId];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 检测录音参数,如果是aac格式的音频,并且参数都是默认值,需要把sampleRate由8000改为16000。否则会录制失败。
|
||||
/// - Parameter dic: 录音参数。
|
||||
- (NSDictionary *)checkAACAuioParams:(NSDictionary *)dic {
|
||||
NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithDictionary:dic];
|
||||
if ([dic[@"format"] isEqualToString:@"aac"]) {
|
||||
NSString *encodeBitRate = dic[@"encodeBitRate"];
|
||||
NSString *sampleRate = dic[@"sampleRate"];
|
||||
NSString *numberOfChannels = dic[@"numberOfChannels"];
|
||||
if ([encodeBitRate isEqualToString:@"48000"] && [numberOfChannels isEqualToString:@"2"] && [sampleRate isEqualToString:@"8000"]) {
|
||||
[data setValue:@"16000" forKey:@"sampleRate"];
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Copyright (c) 2017, finogeeks.com
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
/**
|
||||
开始录音。当主动调用wx.stopRecord,或者录音超过1分钟时自动结束录音,返回录音文件的临时文件路径。
|
||||
如果前一次录音还在录音 本次录音则不会开始
|
||||
*/
|
||||
@interface FATExt_startRecord : FATExtBaseApi
|
||||
|
||||
@end
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// Copyright (c) 2017, finogeeks.com
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
#import "FATExt_startRecord.h"
|
||||
#import "FATExtAVManager.h"
|
||||
#import "FATClient+ext.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@implementation FATExt_startRecord
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
FATAppletInfo *appInfo = [[FATClient sharedClient] currentApplet];
|
||||
[[FATClient sharedClient] fat_requestAppletAuthorize:FATAuthorizationTypeMicrophone appletId:appInfo.appId complete:^(NSInteger status) {
|
||||
if (status == 1) {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized,用户未授予麦克风权限"});
|
||||
}
|
||||
return;
|
||||
} else if (status == 2) {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized disableauthorized,SDK被禁止申请麦克风权限"});
|
||||
}
|
||||
return;
|
||||
}
|
||||
[[FATExtAVManager sharedManager] startRecordWithSuccess:^(NSString *tempFilePath) {
|
||||
if (success) {
|
||||
success(@{@"tempFilePath" : tempFilePath});
|
||||
}
|
||||
} fail:^(NSString *msg) {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : msg});
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// Copyright (c) 2017, finogeeks.com
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
/**
|
||||
主动调用停止录音。
|
||||
*/
|
||||
@interface FATExt_stopRecord : FATExtBaseApi
|
||||
|
||||
@end
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// Copyright (c) 2017, finogeeks.com
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
#import "FATExt_stopRecord.h"
|
||||
#import "FATExtAVManager.h"
|
||||
|
||||
@implementation FATExt_stopRecord
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
[[FATExtAVManager sharedManager] stopRecord];
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// FATExtNavigationController.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/10/28.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExtNavigationController : UINavigationController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// FATExtNavigationController.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/10/28.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtNavigationController.h"
|
||||
|
||||
@interface FATExtNavigationController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation FATExtNavigationController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
//支持旋转
|
||||
- (BOOL)shouldAutorotate {
|
||||
return NO;
|
||||
}
|
||||
|
||||
//支持的方向
|
||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
|
||||
return UIInterfaceOrientationMaskPortrait;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// FATExt_LocationUpdateManager.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/11/6.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "FATExtBaseApi.h"
|
||||
#import "FATExtLocationManager.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_LocationUpdateManager : NSObject
|
||||
|
||||
+ (instancetype)sharedManager;
|
||||
|
||||
@property (nonatomic, strong) FATExtLocationManager *locationManager;
|
||||
|
||||
@property (nonatomic, assign) BOOL locationIsInit;
|
||||
|
||||
@property (nonatomic, copy) NSString *appletId;
|
||||
|
||||
@property (nonatomic, weak) id<FATApiHanderContextDelegate> context;
|
||||
|
||||
- (void)startLocationUpdateType:(NSString *)type isAllowsBackgroundLocationUpdates:(BOOL)result withAppId:(NSString *)appId Success:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel;
|
||||
|
||||
- (void)onLocationUpdate;
|
||||
|
||||
- (void)stopLocationUpdate;
|
||||
|
||||
- (void)checkLocationState;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,200 @@
|
|||
//
|
||||
// FATExt_LocationUpdateManager.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/11/6.
|
||||
//
|
||||
|
||||
#import "FATExt_LocationUpdateManager.h"
|
||||
#import <FinApplet/FinApplet.h>
|
||||
#import "FATWGS84ConvertToGCJ02.h"
|
||||
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
static FATExt_LocationUpdateManager *instance = nil;
|
||||
|
||||
|
||||
NSString *const FATExtAppletUpdateBackgroudPermissions = @"FATAppletUpdateBackgroudPermissions";
|
||||
|
||||
@interface FATExt_LocationUpdateManager ()<CLLocationManagerDelegate>
|
||||
|
||||
|
||||
@property (nonatomic, copy) NSString *type;
|
||||
|
||||
@property (nonatomic, copy) void (^success)(NSDictionary<NSString *, id> *_Nonnull);
|
||||
@property (nonatomic, copy) void (^failure)(NSDictionary *_Nullable);
|
||||
@property (nonatomic, copy) void (^cancel)(NSDictionary *_Nullable);
|
||||
|
||||
@end
|
||||
|
||||
@implementation FATExt_LocationUpdateManager
|
||||
|
||||
+ (instancetype)sharedManager {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[[self class] alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [super allocWithZone:zone];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
//小程序关闭通知
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appletClose:) name:FATAppletDestroyNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(permissionsUpdate:) name:FATExtAppletUpdateBackgroudPermissions object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appletEnterBackground:) name:FATAppletEnterBackgroundNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidForeground:) name:FATAppletForegroundNotification object:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma 通知的相关处理
|
||||
- (void)appletClose:(NSNotification *)notification {
|
||||
NSDictionary *dic = notification.userInfo;
|
||||
NSString *appletId = [dic objectForKey:@"appletId"];
|
||||
// 关闭小程序时,需要停止获取定位。
|
||||
if (appletId && [appletId isEqualToString:self.appletId] && self.locationIsInit) {
|
||||
[self stopLocationUpdate];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)permissionsUpdate:(NSNotification *)notification {
|
||||
NSDictionary *dic = notification.userInfo;
|
||||
NSInteger type = [[dic objectForKey:@"type"] integerValue];
|
||||
if (type == 0 ) {
|
||||
[self stopLocationUpdate];
|
||||
}
|
||||
if (_locationManager.allowsBackgroundLocationUpdates && self.locationIsInit) {
|
||||
if (type == 1) {
|
||||
[self stopLocationUpdate];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appletEnterBackground:(NSNotification *)notification {
|
||||
if (!_locationManager.allowsBackgroundLocationUpdates && self.locationIsInit) {
|
||||
[_locationManager stopUpdatingLocation];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appDidForeground:(NSNotification *)notification {
|
||||
if (!_locationManager.allowsBackgroundLocationUpdates && self.locationIsInit) {
|
||||
if ([CLLocationManager locationServicesEnabled] && ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedAlways)) {
|
||||
[_locationManager startUpdatingLocation];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)checkLocationState {
|
||||
if (self.locationIsInit) {
|
||||
// 胶囊按钮
|
||||
UIViewController *vc = [[UIApplication sharedApplication] fat_topViewController];
|
||||
UINavigationController<FATCapsuleViewProtocol> *nav = (UINavigationController<FATCapsuleViewProtocol> *)vc.navigationController;
|
||||
if ([nav respondsToSelector:@selector(controlCapsuleStateButton:state:animate:)]) {
|
||||
[nav controlCapsuleStateButton:NO state:FATCapsuleButtonStateLocation animate:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)startLocationUpdateType:(NSString *)type isAllowsBackgroundLocationUpdates:(BOOL)result withAppId:(NSString *)appId Success:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
// 如果已经初始化了,并且参数一致,就终止
|
||||
if (self.locationIsInit && _locationManager.allowsBackgroundLocationUpdates == result) {
|
||||
return;
|
||||
}
|
||||
//定位功能可用
|
||||
_locationManager = [[FATExtLocationManager alloc] init];
|
||||
_locationManager.delegate = self;
|
||||
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
|
||||
_locationManager.allowsBackgroundLocationUpdates = result;
|
||||
_locationManager.pausesLocationUpdatesAutomatically = YES;
|
||||
[_locationManager requestWhenInUseAuthorization];
|
||||
self.type = type;
|
||||
self.locationIsInit = YES;
|
||||
if ([CLLocationManager locationServicesEnabled] && ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedAlways)) {
|
||||
[self onLocationUpdate];
|
||||
}
|
||||
self.appletId = appId;
|
||||
self.success = success;
|
||||
self.failure = failure;
|
||||
}
|
||||
|
||||
- (void)onLocationUpdate {
|
||||
[_locationManager startUpdatingLocation];
|
||||
}
|
||||
|
||||
- (void)stopLocationUpdate {
|
||||
[_locationManager stopUpdatingLocation];
|
||||
if (self.context) {
|
||||
[self.context sendResultEvent:0 eventName:@"offLocationChange" eventParams:@{} extParams:nil];
|
||||
}
|
||||
self.appletId = @"";
|
||||
self.locationIsInit = NO;
|
||||
}
|
||||
|
||||
#pragma mark - CLLocationManagerDelegate
|
||||
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
|
||||
CLLocation *newLocation = [locations objectAtIndex:0];
|
||||
// 把默认值改为gcj02
|
||||
NSString *typeString = @"gcj02";
|
||||
CLLocationCoordinate2D coord = [FATWGS84ConvertToGCJ02ForAMapView transformFromWGSToGCJ:[newLocation coordinate]];
|
||||
newLocation = [[CLLocation alloc] initWithLatitude:coord.latitude longitude:coord.longitude];
|
||||
|
||||
if ([self.type isEqualToString:@"wgs84"]) {
|
||||
CLLocationCoordinate2D coord = [FATWGS84ConvertToGCJ02ForAMapView transformFromGCJToWGS:[newLocation coordinate]];
|
||||
newLocation = [[CLLocation alloc] initWithLatitude:coord.latitude longitude:coord.longitude];
|
||||
typeString = @"wgs84";
|
||||
}
|
||||
|
||||
CLLocationCoordinate2D coordinate = newLocation.coordinate;
|
||||
NSDictionary *params = @{@"altitude" : @(newLocation.altitude),
|
||||
@"latitude" : @(coordinate.latitude),
|
||||
@"longitude" : @(coordinate.longitude),
|
||||
@"speed" : @(newLocation.speed),
|
||||
@"accuracy" : @(newLocation.horizontalAccuracy),
|
||||
@"type" : typeString,
|
||||
@"verticalAccuracy" : @(newLocation.verticalAccuracy),
|
||||
@"horizontalAccuracy" : @(newLocation.horizontalAccuracy)
|
||||
};
|
||||
if (self.context) {
|
||||
[self.context sendResultEvent:0 eventName:@"onLocationChange" eventParams:params extParams:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
|
||||
if (self.context) {
|
||||
[self.context sendResultEvent:0 eventName:@"onLocationChangeError" eventParams:@{} extParams:nil];
|
||||
}
|
||||
[self stopLocationUpdate];
|
||||
if (self.failure) {
|
||||
self.failure(@{});
|
||||
}
|
||||
}
|
||||
|
||||
//- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
|
||||
// if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) {
|
||||
// [self onLocationUpdate];
|
||||
// if (self.success) {
|
||||
// self.success(@{});
|
||||
// }
|
||||
// } else {
|
||||
// if (self.failure) {
|
||||
// self.failure(@{@"errMsg" : @"system permission denied"});
|
||||
// }
|
||||
// [self stopLocationUpdate];
|
||||
// }
|
||||
//}
|
||||
|
||||
@end
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// FATExt_chooseLocation.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2020/8/19.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
@interface FATExt_chooseLocation : FATExtBaseApi
|
||||
/// 目标地纬度
|
||||
@property (nonatomic, strong) NSString *latitude;
|
||||
/// 目标地经度
|
||||
@property (nonatomic, strong) NSString *longitude;
|
||||
|
||||
@end
|
|
@ -0,0 +1,97 @@
|
|||
//
|
||||
// FATExt_chooseLocation.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2020/8/19.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExt_chooseLocation.h"
|
||||
#import "FATMapViewController.h"
|
||||
#import "FATClient+ext.h"
|
||||
#import "FATExtMapManager.h"
|
||||
#import "FATExtNavigationController.h"
|
||||
#import "FATExt_locationAuthManager.h"
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
@implementation FATExt_chooseLocation
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
// if (![CLLocationManager locationServicesEnabled]) {
|
||||
// if (callback) {
|
||||
// callback(FATExtensionCodeFailure, @{@"errMsg" : @"location service not open"});
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
NSString *mapClassStr = NSStringFromClass([FATExtMapManager shareInstance].mapClass);
|
||||
NSString *apiName = nil;
|
||||
if ([mapClassStr isEqualToString:@"FATBDMapView"]) {
|
||||
apiName = @"FATBDExt_chooseLocation";
|
||||
} else if ([mapClassStr isEqualToString:@"FATTXMapView"]) {
|
||||
apiName = @"FATTXExt_chooseLocation";
|
||||
} else if ([mapClassStr isEqualToString:@"FATGDMapView"]) {
|
||||
apiName = @"FATGDExt_chooseLocation";
|
||||
}
|
||||
if (apiName) {
|
||||
id<FATApiProtocol> api = [self.class fat_apiWithApiClass:apiName params:self.param];
|
||||
if (api) {
|
||||
api.appletInfo = self.appletInfo;
|
||||
api.context = self.context;
|
||||
[api setupApiWithSuccess:success failure:failure cancel:cancel];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[[FATClient sharedClient] fat_requestAppletAuthorize:FATAuthorizationTypeLocation appletId:self.appletInfo.appId complete:^(NSInteger status) {
|
||||
if (status == 0) {
|
||||
[[FATExt_locationAuthManager shareInstance] fat_requestAppletLocationAuthorize:self.appletInfo isBackground:NO withComplete:^(BOOL status) {
|
||||
if (status) {
|
||||
[self callChooseLocationWithSuccess:success failure:failure cancel:cancel];
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"system permission denied"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
} else if (status == 1) {
|
||||
//定位不能用
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized,用户未授予位置权限"});
|
||||
}
|
||||
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized disableauthorized,SDK被禁止申请位置权限"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)callChooseLocationWithSuccess:(void (^)(NSDictionary<NSString *, id> *_Nonnull))success failure:(void (^)(NSDictionary *_Nullable))failure cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
// 弹出定位选择界面
|
||||
UIViewController *topVC = [[UIApplication sharedApplication] fat_topViewController];
|
||||
|
||||
FATMapViewController *mapVC = [[FATMapViewController alloc] init];
|
||||
mapVC.latitude = self.latitude;
|
||||
mapVC.longitude = self.longitude;
|
||||
mapVC.cancelBlock = ^{
|
||||
cancel(nil);
|
||||
};
|
||||
mapVC.sureBlock = ^(NSDictionary *locationInfo) {
|
||||
success(locationInfo);
|
||||
};
|
||||
FATExtNavigationController *nav = [[FATExtNavigationController alloc] initWithRootViewController:mapVC];
|
||||
if (@available(iOS 15, *)) {
|
||||
UINavigationBarAppearance *barAppearance = [[UINavigationBarAppearance alloc] init];
|
||||
[barAppearance configureWithOpaqueBackground];
|
||||
barAppearance.backgroundColor = [UIColor whiteColor];
|
||||
nav.navigationBar.standardAppearance = barAppearance;
|
||||
nav.navigationBar.scrollEdgeAppearance = barAppearance;
|
||||
}
|
||||
|
||||
[topVC presentViewController:nav animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// FATExt_choosePoi.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/7.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_choosePoi : FATExtBaseApi
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// FATExt_choosePoi.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/7.
|
||||
//
|
||||
|
||||
#import "FATExt_choosePoi.h"
|
||||
#import "FATLocationManager.h"
|
||||
#import "FATWGS84ConvertToGCJ02.h"
|
||||
#import "FATClient+ext.h"
|
||||
#import "FATExtChoosePoiViewController.h"
|
||||
#import "FATExtMapManager.h"
|
||||
#import "FATExtNavigationController.h"
|
||||
#import "FATExt_locationAuthManager.h"
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
@implementation FATExt_choosePoi
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
// FATAppletInfo *appInfo = [[FATClient sharedClient] currentApplet];
|
||||
//如果使用的是百度地图sdk,需要调用百度地图的API方法
|
||||
NSString *mapClassStr = NSStringFromClass([FATExtMapManager shareInstance].mapClass);
|
||||
NSString *apiName = nil;
|
||||
if ([mapClassStr isEqualToString:@"FATBDMapView"]) {
|
||||
apiName = @"FATBDExt_choosePoi";
|
||||
} else if ([mapClassStr isEqualToString:@"FATTXMapView"]) {
|
||||
apiName = @"FATTXExt_choosePoi";
|
||||
}
|
||||
if (apiName) {
|
||||
id<FATApiProtocol> api = [self.class fat_apiWithApiClass:apiName params:self.param];
|
||||
if (api) {
|
||||
api.appletInfo = self.appletInfo;
|
||||
api.context = self.context;
|
||||
[api setupApiWithSuccess:success failure:failure cancel:cancel];
|
||||
return;
|
||||
}
|
||||
}
|
||||
[[FATClient sharedClient] fat_requestAppletAuthorize:FATAuthorizationTypeLocation appletId:self.appletInfo.appId complete:^(NSInteger status) {
|
||||
if (status == 0) {
|
||||
//定位功能可用
|
||||
[[FATExt_locationAuthManager shareInstance] fat_requestAppletLocationAuthorize:self.appletInfo isBackground:NO withComplete:^(BOOL status) {
|
||||
if (status) {
|
||||
[self callChooseLocationWithWithSuccess:success failure:failure cancel:cancel];
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"system permission denied"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
} else if (status == 1) {
|
||||
//定位不能用
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized,用户未授予位置权限"});
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized disableauthorized,SDK被禁止申请位置权限"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (void)callChooseLocationWithWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
// 弹出定位选择界面
|
||||
UIViewController *topVC = [[UIApplication sharedApplication] fat_topViewController];
|
||||
|
||||
FATExtChoosePoiViewController *mapVC = [[FATExtChoosePoiViewController alloc] init];
|
||||
mapVC.cancelBlock = ^{
|
||||
cancel(nil);
|
||||
};
|
||||
mapVC.sureBlock = ^(NSDictionary *locationInfo) {
|
||||
success(locationInfo);
|
||||
};
|
||||
FATExtNavigationController *nav = [[FATExtNavigationController alloc] initWithRootViewController:mapVC];
|
||||
if (@available(iOS 15, *)) {
|
||||
UINavigationBarAppearance *barAppearance = [[UINavigationBarAppearance alloc] init];
|
||||
[barAppearance configureWithOpaqueBackground];
|
||||
barAppearance.backgroundColor = [UIColor whiteColor];
|
||||
nav.navigationBar.standardAppearance = barAppearance;
|
||||
nav.navigationBar.scrollEdgeAppearance = barAppearance;
|
||||
}
|
||||
|
||||
[topVC presentViewController:nav animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// FATExt_getLocation.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2020/12/10.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
@interface FATExt_getLocation : FATExtBaseApi
|
||||
|
||||
@property (nonatomic, copy) NSString *type;
|
||||
|
||||
@property (nonatomic, assign) BOOL altitude;
|
||||
|
||||
@property (nonatomic, assign) BOOL isHighAccuracy;
|
||||
|
||||
@property (nonatomic, assign) NSInteger highAccuracyExpireTime;
|
||||
|
||||
|
||||
@end
|
|
@ -0,0 +1,142 @@
|
|||
//
|
||||
// FATExt_getLocation.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by Haley on 2020/12/10.
|
||||
// Copyright © 2020 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExt_getLocation.h"
|
||||
#import "FATLocationManager.h"
|
||||
#import "FATWGS84ConvertToGCJ02.h"
|
||||
#import "FATClient+ext.h"
|
||||
#import "FATExtLocationManager.h"
|
||||
#import "FATExtMapManager.h"
|
||||
#import "FATExt_locationAuthManager.h"
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
@interface FATExt_getLocation () <CLLocationManagerDelegate>
|
||||
|
||||
@property (nonatomic, strong) FATExtLocationManager *locationManager;
|
||||
|
||||
@property (nonatomic, strong) FATExtBaseApi *strongSelf;
|
||||
@property (nonatomic, copy) void (^success)(NSDictionary<NSString *, id> *_Nonnull);
|
||||
@property (nonatomic, copy) void (^failure)(NSDictionary *_Nullable);
|
||||
@property (nonatomic, strong) NSTimer *locationUpdateTimer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FATExt_getLocation
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
|
||||
NSString *mapClassStr = NSStringFromClass([FATExtMapManager shareInstance].mapClass);
|
||||
NSString *apiName = nil;
|
||||
if ([mapClassStr isEqualToString:@"FATBDMapView"]) {
|
||||
apiName = @"FATBDExt_getLocation";
|
||||
} else if ([mapClassStr isEqualToString:@"FATTXMapView"]) {
|
||||
apiName = @"FATTXExt_getLocation";
|
||||
} else if ([mapClassStr isEqualToString:@"FATGDMapView"]) {
|
||||
apiName = @"FATGDExt_getLocation";
|
||||
}
|
||||
if (apiName) {
|
||||
id<FATApiProtocol> api = [self.class fat_apiWithApiClass:apiName params:self.param];
|
||||
if (api) {
|
||||
api.appletInfo = self.appletInfo;
|
||||
api.context = self.context;
|
||||
[api setupApiWithSuccess:success failure:failure cancel:cancel];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.success = success;
|
||||
self.failure = failure;
|
||||
// FATAppletInfo *appInfo = [[FATClient sharedClient] currentApplet];
|
||||
[[FATClient sharedClient] fat_requestAppletAuthorize:FATAuthorizationTypeLocation appletId:self.appletInfo.appId complete:^(NSInteger status) {
|
||||
if (status == 0) {
|
||||
//定位功能可用
|
||||
[[FATExt_locationAuthManager shareInstance] fat_requestAppletLocationAuthorize:self.appletInfo isBackground:NO withComplete:^(BOOL status) {
|
||||
if (status) {
|
||||
[self startUpdateLocation];
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"system permission denied"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
} else if (status == 1) {
|
||||
//定位不能用
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized,用户未授予位置权限"});
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized disableauthorized,SDK被禁止申请位置权限"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)startUpdateLocation {
|
||||
_locationManager = [[FATExtLocationManager alloc] init];
|
||||
_locationManager.delegate = self;
|
||||
if (self.isHighAccuracy) {
|
||||
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
|
||||
} else {
|
||||
_locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
|
||||
}
|
||||
[_locationManager requestWhenInUseAuthorization];
|
||||
[_locationManager startUpdatingLocation];
|
||||
|
||||
_strongSelf = self;
|
||||
__weak typeof(self) weak_self = self;
|
||||
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 10);
|
||||
dispatch_after(time, dispatch_get_global_queue(0, 0), ^{
|
||||
weak_self.strongSelf = nil;
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - CLLocationManagerDelegate
|
||||
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
|
||||
CLLocation *newLocation = [locations objectAtIndex:0];
|
||||
NSString *typeString = @"wgs84";
|
||||
//判断是不是属于国内范围
|
||||
if ([self.type isEqualToString:@"gcj02"]) {
|
||||
//转换后的coord
|
||||
CLLocationCoordinate2D coord = [FATWGS84ConvertToGCJ02ForAMapView transformFromWGSToGCJ:[newLocation coordinate]];
|
||||
newLocation = [[CLLocation alloc] initWithLatitude:coord.latitude longitude:coord.longitude];
|
||||
typeString = @"gcj02";
|
||||
}
|
||||
|
||||
[FATLocationManager manager].location = newLocation;
|
||||
CLLocationCoordinate2D coordinate = newLocation.coordinate;
|
||||
|
||||
if (self.success) {
|
||||
NSDictionary *params = @{@"altitude" : @(newLocation.altitude),
|
||||
@"latitude" : @(coordinate.latitude),
|
||||
@"longitude" : @(coordinate.longitude),
|
||||
@"speed" : @(newLocation.speed),
|
||||
@"accuracy" : @(newLocation.horizontalAccuracy),
|
||||
@"type" : typeString,
|
||||
@"verticalAccuracy" : @(newLocation.verticalAccuracy),
|
||||
@"horizontalAccuracy" : @(newLocation.horizontalAccuracy)
|
||||
};
|
||||
NSMutableDictionary *dataDic = [[NSMutableDictionary alloc] initWithDictionary:params];
|
||||
if (!self.altitude) {
|
||||
[dataDic removeObjectForKey:@"altitude"];
|
||||
}
|
||||
self.success(dataDic);
|
||||
}
|
||||
|
||||
[_locationManager stopUpdatingLocation];
|
||||
}
|
||||
|
||||
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
|
||||
if (self.failure) {
|
||||
self.failure(nil);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// FATExt_locationAuthManager.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/12/24.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_locationAuthManager : NSObject
|
||||
|
||||
+ (instancetype)shareInstance;
|
||||
|
||||
- (void)fat_requestAppletLocationAuthorize:(FATAppletInfo *)appletInfo isBackground:(BOOL)isBackground withComplete:(void (^)(BOOL status))complete;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,136 @@
|
|||
//
|
||||
// FATExt_locationAuthManager.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/12/24.
|
||||
//
|
||||
|
||||
#import "FATExt_locationAuthManager.h"
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
|
||||
@interface FATExt_locationAuthManager () <CLLocationManagerDelegate>
|
||||
|
||||
@property (nonatomic, strong) CLLocationManager *locationManager;
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *locationAuthCompleteArray;
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray<FATAppletInfo*> *appletInfoArray;
|
||||
@property (nonatomic, strong) NSMutableDictionary *authTypeDic; //key是小程序Id value是权限类型,用来区分后台定位和正常位置权限
|
||||
@end
|
||||
|
||||
static FATExt_locationAuthManager *instance = nil;
|
||||
|
||||
@implementation FATExt_locationAuthManager
|
||||
|
||||
+ (instancetype)shareInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[FATExt_locationAuthManager alloc] init];
|
||||
});
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [super allocWithZone:zone];
|
||||
});
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self p_init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)p_init {
|
||||
_locationAuthCompleteArray = [NSMutableArray array];
|
||||
_appletInfoArray = [[NSMutableArray alloc]init];
|
||||
_authTypeDic = [[NSMutableDictionary alloc]init];
|
||||
}
|
||||
|
||||
- (void)fat_requestAppletLocationAuthorize:(FATAppletInfo *)appletInfo isBackground:(BOOL)isBackground withComplete:(void (^)(BOOL status))complete {
|
||||
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
|
||||
if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways) {
|
||||
[self notifyApp:appletInfo authType:isBackground ? FATAuthorizationTypeLocationBackground : FATAuthorizationTypeLocation authResult:FATAuthorizationStatusAuthorized];
|
||||
if (complete) {
|
||||
complete(YES);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (status != kCLAuthorizationStatusNotDetermined) {
|
||||
[self notifyApp:appletInfo authType:isBackground ? FATAuthorizationTypeLocationBackground : FATAuthorizationTypeLocation authResult:FATAuthorizationStatusDenied];
|
||||
if (complete) {
|
||||
complete(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.locationAuthCompleteArray addObject:complete];
|
||||
if (appletInfo) {
|
||||
[self.appletInfoArray addObject:appletInfo];
|
||||
if (appletInfo.appId) {
|
||||
[self.authTypeDic setValue:isBackground ? @(FATAuthorizationTypeLocationBackground) : @(FATAuthorizationTypeLocation) forKey:appletInfo.appId];
|
||||
}
|
||||
}
|
||||
self.locationManager = [[CLLocationManager alloc] init];
|
||||
self.locationManager.delegate = self;
|
||||
[self.locationManager requestWhenInUseAuthorization];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
- (void)notifyApp:(FATAppletInfo *)appletInfo authType:(FATAuthorizationType)type authResult:(FATAuthorizationStatus)result {
|
||||
id<FATAppletAuthDelegate> delegate = [FATClient sharedClient].authDelegate;
|
||||
if (delegate && [delegate respondsToSelector:@selector(applet:didRequestAuth:withResult:)]) {
|
||||
[delegate applet:appletInfo didRequestAuth:type withResult:result];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
|
||||
|
||||
if (status == kCLAuthorizationStatusNotDetermined) {
|
||||
return;
|
||||
}
|
||||
for (FATAppletInfo *appInfo in self.appletInfoArray) {
|
||||
FATAuthorizationStatus authStatus = FATAuthorizationStatusDenied;
|
||||
if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways) {
|
||||
authStatus = FATAuthorizationStatusAuthorized;
|
||||
}
|
||||
NSNumber *authtype = [self.authTypeDic objectForKey:appInfo.appId];
|
||||
FATAuthorizationType type = FATAuthorizationTypeLocation;
|
||||
if (authtype) {
|
||||
type = [authtype integerValue];
|
||||
}
|
||||
[self notifyApp:appInfo authType:type authResult:authStatus];
|
||||
}
|
||||
[self.appletInfoArray removeAllObjects];
|
||||
[self.authTypeDic removeAllObjects];
|
||||
if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways) {
|
||||
for (int i = 0; i < self.locationAuthCompleteArray.count; i++) {
|
||||
void(^locationComplete)(BOOL status) = self.locationAuthCompleteArray[i];
|
||||
locationComplete(YES);
|
||||
}
|
||||
[self.locationAuthCompleteArray removeAllObjects];
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < self.locationAuthCompleteArray.count; i++) {
|
||||
void(^locationComplete)(BOOL status) = self.locationAuthCompleteArray[i];
|
||||
locationComplete(NO);
|
||||
}
|
||||
|
||||
[self.locationAuthCompleteArray removeAllObjects];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// FATExt_locationChange.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/11/6.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_locationChange : FATExtBaseApi
|
||||
|
||||
@property (nonatomic, assign) BOOL enable;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// FATExt_locationChange.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/11/6.
|
||||
//
|
||||
|
||||
#import "FATExt_locationChange.h"
|
||||
#import "FATClient+ext.h"
|
||||
#import "FATExt_LocationUpdateManager.h"
|
||||
|
||||
@implementation FATExt_locationChange
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
// if (self.enable) {
|
||||
// [FATExt_LocationUpdateManager sharedManager].context = self.context;
|
||||
// [[FATExt_LocationUpdateManager sharedManager] onLocationUpdate];
|
||||
// } else {
|
||||
// [FATExt_LocationUpdateManager sharedManager].context = self.context;
|
||||
// [[FATExt_LocationUpdateManager sharedManager] stopLocationUpdate];
|
||||
// }
|
||||
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// FATExt_openLocation.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/7.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_openLocation : FATExtBaseApi
|
||||
|
||||
/// 目标地纬度
|
||||
@property (nonatomic, strong) NSString *latitude;
|
||||
/// 目标地经度
|
||||
@property (nonatomic, strong) NSString *longitude;
|
||||
|
||||
@property (nonatomic, strong) NSString *scale;
|
||||
|
||||
@property (nonatomic, strong) NSString *name;
|
||||
|
||||
@property (nonatomic, strong) NSString *address;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// FATExt_openLocation.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2021/12/7.
|
||||
//
|
||||
|
||||
#import "FATExt_openLocation.h"
|
||||
#import "FATWGS84ConvertToGCJ02.h"
|
||||
#import "FATClient+ext.h"
|
||||
#import "FATOpenLocationViewController.h"
|
||||
#import "FATExtMapManager.h"
|
||||
#import "FATExtNavigationController.h"
|
||||
#import "FATExt_locationAuthManager.h"
|
||||
#import <FinApplet/FinApplet.h>
|
||||
|
||||
@implementation FATExt_openLocation
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
// if (![CLLocationManager locationServicesEnabled]) {
|
||||
// if (callback) {
|
||||
// callback(FATExtensionCodeFailure, @{@"errMsg" : @"location service not open"});
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
|
||||
NSString *mapClassStr = NSStringFromClass([FATExtMapManager shareInstance].mapClass);
|
||||
NSString *apiName = nil;
|
||||
if ([mapClassStr isEqualToString:@"FATBDMapView"]) {
|
||||
apiName = @"FATBDExt_openLocation";
|
||||
} else if ([mapClassStr isEqualToString:@"FATGDMapView"]) {
|
||||
apiName = @"FATGDExt_openLocation";
|
||||
} else if ([mapClassStr isEqualToString:@"FATTXMapView"]) {
|
||||
apiName = @"FATTXExt_openLocation";
|
||||
}
|
||||
if (apiName) {
|
||||
id<FATApiProtocol> api = [self.class fat_apiWithApiClass:apiName params:self.param];
|
||||
if (api) {
|
||||
api.appletInfo = self.appletInfo;
|
||||
api.context = self.context;
|
||||
[api setupApiWithSuccess:success failure:failure cancel:cancel];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[[FATClient sharedClient] fat_requestAppletAuthorize:FATAuthorizationTypeLocation appletId:self.appletInfo.appId complete:^(NSInteger status) {
|
||||
if (status == 0) {
|
||||
//定位功能可用
|
||||
[[FATExt_locationAuthManager shareInstance] fat_requestAppletLocationAuthorize:self.appletInfo isBackground:NO withComplete:^(BOOL status) {
|
||||
if (status) {
|
||||
[self callChooseLocationWithCallback];
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"system permission denied"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
} else if (status == 1) {
|
||||
//定位不能用
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized,用户未授予位置权限"});
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized disableauthorized,SDK被禁止申请位置权限"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)callChooseLocationWithCallback {
|
||||
// 弹出定位选择界面
|
||||
UIViewController *topVC = [[UIApplication sharedApplication] fat_topViewController];
|
||||
|
||||
FATOpenLocationViewController *mapVC = [[FATOpenLocationViewController alloc] init];
|
||||
mapVC.latitude = self.latitude;
|
||||
mapVC.longitude = self.longitude;
|
||||
mapVC.scale = self.scale;
|
||||
mapVC.name = self.name ? self.name : @"[位置]";
|
||||
mapVC.address = self.address;
|
||||
FATExtNavigationController *nav = [[FATExtNavigationController alloc] initWithRootViewController:mapVC];
|
||||
if (@available(iOS 15, *)) {
|
||||
UINavigationBarAppearance *barAppearance = [[UINavigationBarAppearance alloc] init];
|
||||
[barAppearance configureWithOpaqueBackground];
|
||||
barAppearance.backgroundColor = [UIColor whiteColor];
|
||||
nav.navigationBar.standardAppearance = barAppearance;
|
||||
nav.navigationBar.scrollEdgeAppearance = barAppearance;
|
||||
}
|
||||
|
||||
[topVC presentViewController:nav animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// FATExt_startLocationUpdate.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/11/3.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_startLocationUpdate : FATExtBaseApi
|
||||
|
||||
@property (nonatomic, copy) NSString *type;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// FATExt_startLocationUpdate.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/11/3.
|
||||
//
|
||||
|
||||
#import "FATExt_startLocationUpdate.h"
|
||||
#import "FATClient+ext.h"
|
||||
#import "FATExt_LocationUpdateManager.h"
|
||||
#import "FATExt_locationAuthManager.h"
|
||||
|
||||
@implementation FATExt_startLocationUpdate
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
|
||||
[[FATClient sharedClient] fat_requestAppletAuthorize:FATAuthorizationTypeLocation appletId:self.appletInfo.appId complete:^(NSInteger status) {
|
||||
if (status == 0) {
|
||||
[[FATExt_locationAuthManager shareInstance] fat_requestAppletLocationAuthorize:self.appletInfo isBackground:NO withComplete:^(BOOL status) {
|
||||
if (status) {
|
||||
[FATExt_LocationUpdateManager sharedManager].context = self.context;
|
||||
[[FATExt_LocationUpdateManager sharedManager] startLocationUpdateType:self.type isAllowsBackgroundLocationUpdates:NO withAppId:self.appletInfo.appId Success:success failure:failure cancel:cancel];
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"system permission denied"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
} else if (status == 1) {
|
||||
//定位不能用
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized,用户未授予位置权限"});
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized disableauthorized,SDK被禁止申请位置权限"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// FATExt_startLocationUpdateBackground.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/11/3.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_startLocationUpdateBackground : FATExtBaseApi
|
||||
|
||||
@property (nonatomic, copy) NSString *type;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// FATExt_startLocationUpdateBackground.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/11/3.
|
||||
//
|
||||
|
||||
#import "FATExt_startLocationUpdateBackground.h"
|
||||
#import "FATClient+ext.h"
|
||||
#import "FATExt_LocationUpdateManager.h"
|
||||
#import "FATExt_locationAuthManager.h"
|
||||
|
||||
@implementation FATExt_startLocationUpdateBackground
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
if ([FATExt_LocationUpdateManager sharedManager].locationIsInit && [FATExt_LocationUpdateManager sharedManager].locationManager.allowsBackgroundLocationUpdates && ![self.appletInfo.appId isEqualToString:[FATExt_LocationUpdateManager sharedManager].appletId]) {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"reach max concurrent background count"});
|
||||
}
|
||||
return;
|
||||
}
|
||||
[[FATClient sharedClient] fat_requestAppletAuthorize:FATAuthorizationTypeLocationBackground appletId:self.appletInfo.appId complete:^(NSInteger status) {
|
||||
NSInteger SDKSatus = status;
|
||||
if (status == 3) {
|
||||
//定位功能可用
|
||||
[[FATExt_locationAuthManager shareInstance] fat_requestAppletLocationAuthorize:self.appletInfo isBackground:YES withComplete:^(BOOL status) {
|
||||
if (status) {
|
||||
[FATExt_LocationUpdateManager sharedManager].context = self.context;
|
||||
[[FATExt_LocationUpdateManager sharedManager] startLocationUpdateType:self.type isAllowsBackgroundLocationUpdates:SDKSatus == 3 ? YES : NO withAppId:self.appletInfo.appId Success:success failure:failure cancel:cancel];
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"system permission denied"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
} else if (status == 2) {
|
||||
// [[FATExt_LocationUpdateManager sharedManager] stopLocationUpdate];
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized,用户未授予位置权限"});
|
||||
}
|
||||
} else if (status == 1) {
|
||||
// [[FATExt_LocationUpdateManager sharedManager] stopLocationUpdate];
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized,用户未授予后台定位权限"});
|
||||
}
|
||||
} else {
|
||||
// [[FATExt_LocationUpdateManager sharedManager] stopLocationUpdate];
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"unauthorized disableauthorized,SDK被禁止申请位置权限"});
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@end
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// FATExt_stopLocationUpdate.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/11/18.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_stopLocationUpdate : FATExtBaseApi
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// FATExt_stopLocationUpdate.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 王兆耀 on 2022/11/18.
|
||||
//
|
||||
|
||||
#import "FATExt_stopLocationUpdate.h"
|
||||
#import "FATClient+ext.h"
|
||||
#import "FATExt_LocationUpdateManager.h"
|
||||
|
||||
@implementation FATExt_stopLocationUpdate
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
if (![self.appletInfo.appId isEqualToString:[FATExt_LocationUpdateManager sharedManager].appletId]) {
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
return;
|
||||
}
|
||||
[FATExt_LocationUpdateManager sharedManager].context = self.context;
|
||||
[[FATExt_LocationUpdateManager sharedManager] stopLocationUpdate];
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// FATExt_insertNativeMap.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 滔 on 2022/9/18.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_insertNativeMap : FATExtBaseApi
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// FATExt_insertNativeMap.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 滔 on 2022/9/18.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExt_insertNativeMap.h"
|
||||
#import "FATMapViewDelegate.h"
|
||||
#import "FATMapView.h"
|
||||
#import "FATExtMapManager.h"
|
||||
|
||||
@implementation FATExt_insertNativeMap
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
UIView<FATMapViewDelegate> *mapObject = [[FATExtMapManager shareInstance].mapClass alloc];
|
||||
UIView<FATMapViewDelegate> *mapView = [mapObject initWithParam:self.param mapPageId:self.appletInfo.appId];
|
||||
mapView.eventCallBack = ^(NSString *eventName, NSDictionary *paramDic) {
|
||||
if (self.context) {
|
||||
[self.context sendResultEvent:1 eventName:eventName eventParams:paramDic extParams:nil];
|
||||
}
|
||||
};
|
||||
if (self.context && [self.context respondsToSelector:@selector(insertChildView:viewId:parentViewId:isFixed:isHidden:)]) {
|
||||
NSString *parentId = self.param[@"cid"];
|
||||
if (parentId && parentId.length > 0) { //同层渲染
|
||||
CGRect frame = mapView.frame;
|
||||
frame.origin = CGPointZero;
|
||||
mapView.frame = frame;
|
||||
}
|
||||
BOOL isHidden = [self.param[@"hide"] boolValue];
|
||||
NSString *viewId = [self.context insertChildView:mapView viewId:self.param[@"mapId"] parentViewId:self.param[@"cid"] isFixed:NO isHidden:isHidden];
|
||||
if (viewId && viewId.length > 0) {
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{});
|
||||
}
|
||||
}
|
||||
}
|
||||
@end
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// FATExt_invokeMapTask.h
|
||||
// FBRetainCycleDetector
|
||||
//
|
||||
// Created by 王兆耀 on 2021/9/2.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_invokeMapTask : FATExtBaseApi
|
||||
|
||||
@property (nonatomic, copy) NSString *eventName;
|
||||
|
||||
@property (nonatomic, copy) NSString *mapId;
|
||||
|
||||
@property (nonatomic, copy) NSString *key;
|
||||
|
||||
@property (nonatomic, copy) NSDictionary *data;
|
||||
|
||||
@property (nonatomic, assign) NSInteger nativeViewId;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,169 @@
|
|||
//
|
||||
// FATExt_invokeMapTask.m
|
||||
// FBRetainCycleDetector
|
||||
//
|
||||
// Created by 王兆耀 on 2021/9/2.
|
||||
//
|
||||
|
||||
#import "FATExt_invokeMapTask.h"
|
||||
#import "FATMapView.h"
|
||||
#import "FATExtMapManager.h"
|
||||
#import "FATExtUtil.h"
|
||||
|
||||
@implementation FATExt_invokeMapTask
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
UIView<FATMapViewDelegate> *map = nil;
|
||||
if (self.context && [self.context respondsToSelector:@selector(getChildViewById:)]) {
|
||||
UIView *targetView = [self.context getChildViewById:self.param[@"mapId"]];
|
||||
if (targetView && [targetView conformsToProtocol:@protocol(FATMapViewDelegate)]) {
|
||||
map = (UIView<FATMapViewDelegate> *)targetView;
|
||||
}
|
||||
}
|
||||
if (!map) {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"map view not exist"});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ([self.eventName isEqualToString:@"getCenterLocation"]) {
|
||||
NSMutableDictionary *dic = [[NSMutableDictionary alloc] initWithDictionary:[map fat_getCenter]];
|
||||
if (success) {
|
||||
success(dic);
|
||||
}
|
||||
} else if ([self.eventName isEqualToString:@"getRegion"]) {
|
||||
NSDictionary *dic = [map fat_mapgetRegion];
|
||||
if (success) {
|
||||
success(dic);
|
||||
}
|
||||
} else if ([self.eventName isEqualToString:@"getScale"]) {
|
||||
double scale = [map fat_getScale];
|
||||
if (success) {
|
||||
success(@{@"scale" : [NSNumber numberWithDouble:scale]});
|
||||
}
|
||||
} else if ([self.eventName isEqualToString:@"includePoints"]) {
|
||||
[map fat_includePoints:self.data];
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
} else if ([self.eventName isEqualToString:@"moveToLocation"]) {
|
||||
NSString *status = [map fat_moveToLocation:self.data];
|
||||
if ([status isEqualToString:@"fail"]) {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"not show user location"});
|
||||
}
|
||||
} else {
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
}
|
||||
} else if ([self.eventName isEqualToString:@"fromScreenLocation"]) {
|
||||
NSDictionary *dic = [map fat_fromScreenLocation];
|
||||
if (success) {
|
||||
success(dic);
|
||||
}
|
||||
} else if ([self.eventName isEqualToString:@"toScreenLocation"]) {
|
||||
// 暂时有bug,微信端有问题。
|
||||
CGPoint point = [map fat_toScreenLocation:self.data];
|
||||
if (success) {
|
||||
success(@{@"x" : @(point.x),
|
||||
@"y" : @(point.y)});
|
||||
};
|
||||
} else if ([self.eventName isEqualToString:@"openMapApp"]) {
|
||||
[map fat_openMapApp:self.data];
|
||||
if (success) {
|
||||
success(@{});
|
||||
};
|
||||
} else if ([self.eventName isEqualToString:@"addMarkers"]) {
|
||||
[map fat_addMarkers:self.data];
|
||||
if (success) {
|
||||
success(@{});
|
||||
};
|
||||
} else if ([self.eventName isEqualToString:@"removeMarkers"]) {
|
||||
[map fat_removeMarkers:self.data];
|
||||
if (success) {
|
||||
success(@{});
|
||||
};
|
||||
} else if ([self.eventName isEqualToString:@"translateMarker"]) {
|
||||
BOOL isExit = [map fat_translateMarker:self.data];
|
||||
if (isExit) {
|
||||
if (success) {
|
||||
success(@{});
|
||||
};
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"error markerid"});
|
||||
}
|
||||
}
|
||||
} else if ([self.eventName isEqualToString:@"moveAlong"]) {
|
||||
BOOL isExit = [map fat_moveAlong:self.data];
|
||||
if (isExit) {
|
||||
if (success) {
|
||||
success(@{});
|
||||
};
|
||||
} else {
|
||||
NSArray *pathArray = [[NSArray alloc] initWithArray:self.data[@"path"]];
|
||||
if (pathArray.count == 0 || ![pathArray isKindOfClass:[NSArray class]]) {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"parameter error: parameter.duration should be Number instead of Undefined;"});
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"error markerid"});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ([self.eventName isEqualToString:@"setCenterOffset"]) {
|
||||
if ([map respondsToSelector:@selector(mapSetCenterOffset:)]) {
|
||||
[map mapSetCenterOffset:self.data];
|
||||
if (success) {
|
||||
success(@{});
|
||||
};
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"not support"});
|
||||
};
|
||||
}
|
||||
} else if ([self.eventName isEqualToString:@"getRotate"]) {
|
||||
if ([map respondsToSelector:@selector(fat_getRotate)]) {
|
||||
NSDictionary *dic = [map fat_getRotate];
|
||||
if (success) {
|
||||
success(dic);
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"not support"});
|
||||
};
|
||||
}
|
||||
} else if ([self.eventName isEqualToString:@"getSkew"]) {
|
||||
if ([map respondsToSelector:@selector(fat_getskew)]) {
|
||||
NSDictionary *dic = [map fat_getskew];
|
||||
if (success) {
|
||||
success(dic);
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"not support"});
|
||||
};
|
||||
}
|
||||
} else if ([self.eventName isEqualToString:@"initMarkerCluster"]) {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"not support"});
|
||||
};
|
||||
} else if ([self.eventName isEqualToString:@"setLocMarkerIcon"]) {
|
||||
if ([map respondsToSelector:@selector(fat_setLocMarkerIcon:)]) {
|
||||
[map fat_setLocMarkerIcon:self.data];
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{@"errMsg" : @"not support"});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// FATExt_removeNativeMap.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 滔 on 2022/9/18.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_removeNativeMap : FATExtBaseApi
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// FATExt_removeNativeMap.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 滔 on 2022/9/18.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExt_removeNativeMap.h"
|
||||
|
||||
@implementation FATExt_removeNativeMap
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
if (self.context && [self.context respondsToSelector:@selector(removeChildView:)]) {
|
||||
BOOL result = [self.context removeChildView:self.param[@"mapId"]];
|
||||
if (result) {
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
failure(@{});
|
||||
}
|
||||
}
|
||||
}
|
||||
@end
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// FATExt_updateNativeMap.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 滔 on 2022/9/18.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_updateNativeMap : FATExtBaseApi
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// FATExt_updateNativeMap.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 滔 on 2022/9/18.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExt_updateNativeMap.h"
|
||||
#import "FATMapViewDelegate.h"
|
||||
|
||||
@implementation FATExt_updateNativeMap
|
||||
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
if (self.context && [self.context respondsToSelector:@selector(getChildViewById:)]) {
|
||||
UIView *targetView = [self.context getChildViewById:self.param[@"mapId"]];
|
||||
if (targetView && [targetView conformsToProtocol:@protocol(FATMapViewDelegate)]) {
|
||||
UIView<FATMapViewDelegate> *mapView = (UIView<FATMapViewDelegate> *)targetView;
|
||||
if (mapView && [mapView respondsToSelector:@selector(updateWithParam:)]) {
|
||||
//是否是更新hide属性
|
||||
if ([self.param objectForKey:@"hide"]) {
|
||||
BOOL hide = [[self.param objectForKey:@"hide"] boolValue];
|
||||
if (hide) {
|
||||
[targetView removeFromSuperview];
|
||||
} else {
|
||||
if (self.context && [self.context respondsToSelector:@selector(updateChildViewHideProperty:viewId:parentViewId:isFixed:isHidden:complete:)]) {
|
||||
[self.context updateChildViewHideProperty:targetView viewId:self.param[@"mapId"] parentViewId:self.param[@"cid"] isFixed:NO isHidden:hide complete:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
[mapView updateWithParam:self.param];
|
||||
if (success) {
|
||||
success(@{});
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
NSDictionary *dictParam = @{};
|
||||
failure(dictParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
NSDictionary *dictParam = @{};
|
||||
failure(dictParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
@end
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// FATExt_updateNativeMapMarkers.h
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 滔 on 2022/9/18.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExtBaseApi.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FATExt_updateNativeMapMarkers : FATExtBaseApi
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// FATExt_updateNativeMapMarkers.m
|
||||
// FinAppletExt
|
||||
//
|
||||
// Created by 滔 on 2022/9/18.
|
||||
// Copyright © 2022 finogeeks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FATExt_updateNativeMapMarkers.h"
|
||||
#import "FATMapViewDelegate.h"
|
||||
|
||||
@implementation FATExt_updateNativeMapMarkers
|
||||
- (void)setupApiWithSuccess:(void (^)(NSDictionary<NSString *, id> *successResult))success
|
||||
failure:(void (^)(NSDictionary *failResult))failure
|
||||
cancel:(void (^)(NSDictionary *cancelResult))cancel {
|
||||
|
||||
if (self.context && [self.context respondsToSelector:@selector(getChildViewById:)]) {
|
||||
UIView *targetView = [self.context getChildViewById:self.param[@"mapId"]];
|
||||
if (targetView && [targetView conformsToProtocol:@protocol(FATMapViewDelegate)]) {
|
||||
UIView<FATMapViewDelegate> *mapView = (UIView<FATMapViewDelegate> *)targetView;
|
||||
if (mapView && [mapView respondsToSelector:@selector(updateNativeMapMarkers:)]) {
|
||||
[mapView updateNativeMapMarkers:self.param];
|
||||
} else {
|
||||
if (failure) {
|
||||
NSDictionary *dictParam = @{};
|
||||
failure(dictParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (failure) {
|
||||
NSDictionary *dictParam = @{};
|
||||
failure(dictParam);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue