function adapt
@@ -1,71 +1,48 @@
|
||||
# iOS App (Xcode host)
|
||||
|
||||
Kotlin Multiplatform Compose UI runs inside a native iOS shell. Sources here are **Xcode-ready scaffolding**; building requires **macOS + Xcode**.
|
||||
Kotlin Multiplatform Compose UI runs inside a native iOS shell.
|
||||
|
||||
## App Group migration
|
||||
|
||||
| Old (Flutter) | New (KMP) |
|
||||
|---|---|
|
||||
| `group.com.zhushenwudi.lovelivemusicplayer` | **`group.top.zhushenwudi.llmp`** |
|
||||
|
||||
Kotlin writes widget / PiP / CarPlay keys via `WidgetDataWriter` (`shared/.../IosPlatformHooks.kt`). Swift Widget + CarPlay read the same suite.
|
||||
|
||||
## Build steps (macOS)
|
||||
## Open in Xcode
|
||||
|
||||
```bash
|
||||
cd LoveLiveMusicPlayerKMP
|
||||
./gradlew :composeApp:linkDebugFrameworkIosSimulatorArm64
|
||||
# or iosArm64 for device
|
||||
cd iosApp
|
||||
./setup.sh # needs xcodegen (`brew install xcodegen`)
|
||||
open iosApp.xcodeproj
|
||||
```
|
||||
|
||||
1. Open Xcode → **File → New → Project → App** (or add targets to an existing workspace).
|
||||
2. Set bundle ID **`top.zhushenwudi.llmp`** (main app).
|
||||
3. Copy `iosApp/iosApp/*` into the app target; add `WidgetExtension/*` as a Widget Extension target (`top.zhushenwudi.llmp.widget`).
|
||||
4. Link the Gradle-produced **`ComposeApp.framework`** (and embedded **`Shared.framework`** if split).
|
||||
5. Signing: enable App Groups `group.top.zhushenwudi.llmp` on **both** app + widget targets (`iosApp.entitlements`, `WidgetExtension.entitlements`).
|
||||
6. CarPlay: entitlements include `com.apple.developer.carplay-audio` and `com.apple.developer.playable-content` — **requires Apple Developer approval** before TestFlight/App Store CarPlay works.
|
||||
7. Add **`black.mp4`** (minimal black video) to the app bundle for PiP — copy from Flutter `ios/Runner` if present, or generate a 1-frame H.264 clip.
|
||||
8. Copy widget image assets from Flutter `ios/HomeWidgetExample/Assets.xcassets` (`logo`, `cd_white`, `cd_black`, favorites).
|
||||
1. 选择模拟器或真机
|
||||
2. 签名已与 Flutter Runner **Manual** 对齐(见下表);模拟器会走本地签名
|
||||
3. Run(⌘R)——预编译脚本会自动跑 `:composeApp:embedAndSignAppleFrameworkForXcode`
|
||||
|
||||
Entry point:
|
||||
## Bundle ID / App Group / 签名(与 Flutter 一致)
|
||||
|
||||
```swift
|
||||
MainViewControllerKt.MainViewController() // ComposeApp framework
|
||||
```
|
||||
| | Value |
|
||||
|---|---|
|
||||
| App | `com.zhushenwudi.lovelivemusicplayer` |
|
||||
| Widget | `com.zhushenwudi.lovelivemusicplayer.HomeWidgetExample` |
|
||||
| App Group | `group.com.zhushenwudi.lovelivemusicplayer` |
|
||||
| Team | `V73L9LHPCA` |
|
||||
| Version | `1.7.4` (build `74`) |
|
||||
| Debug profile | `LLMP-M-DEVELOP` / `LLMP-M-DEVELOP-WIDGET` |
|
||||
| Release profile | `LLMP-M-RELEASE` / `LLMP-M-RELEASE-WIDGET` |
|
||||
| Profile (AdHoc) | `LLMP-M-AD-HOC` / `LLMP-M-AD-HOC-WIDGET` |
|
||||
|
||||
`MainViewController.kt` calls `installIosPlatformServices()` on startup.
|
||||
最低系统:App **iOS 18.0**,Widget **iOS 17.0**。
|
||||
|
||||
## Targets & layout
|
||||
## Layout
|
||||
|
||||
```
|
||||
iosApp/
|
||||
iosApp/
|
||||
AppDelegate.swift — lifecycle, widget shutdown, notification wiring
|
||||
SceneDelegate.swift — UIWindow + PiP notification handlers
|
||||
ContentView.swift — SwiftUI host for Compose UIViewController
|
||||
HostBridge.swift — share sheet, deep links, widget/CarPlay → host
|
||||
Info.plist
|
||||
iosApp.entitlements
|
||||
CarPlay/CarPlaySceneDelegate.swift
|
||||
pip/ — desktop lyric PiP (from Flutter Runner/pip)
|
||||
WidgetExtension/ — White/Black home widgets (from Flutter HomeWidgetExample)
|
||||
project.yml — XcodeGen 源配置(改完跑 ./setup.sh)
|
||||
setup.sh
|
||||
iosApp.xcodeproj — 用这个打开
|
||||
iosApp/ — 主 App(UIKit + SwiftUI host)
|
||||
WidgetExtension/ — 桌面小组件 + 资源
|
||||
```
|
||||
|
||||
## Kotlin ↔ Swift bridge (remaining macOS work)
|
||||
## Notes
|
||||
|
||||
| Feature | Kotlin side | Swift side | Gap |
|
||||
|---|---|---|---|
|
||||
| Playback | `PlayerController.ios.kt` AVPlayer + MPRemoteCommandCenter | — | Done in Kotlin |
|
||||
| Home widget data | `IosHomeWidgetController` → UserDefaults | Widget reads keys | Wire widget tap → Kotlin player (export callback or observe `llmpHostTogglePlay`) |
|
||||
| PiP lyrics | `IosDesktopLyricController` posts notifications | `PipScreenManager` | Done via NotificationCenter |
|
||||
| Share | `IosShareBridge` posts `llmpShareRequest` | `HostBridge.presentShare` | Done |
|
||||
| Deep link `llmp://` | `IosDeepLinkHandler` | `SceneDelegate` + `HostBridge` | Handle payload in Kotlin UI layer |
|
||||
| CarPlay | `IosCarPlayHook.refreshCatalog()` writes JSON | `CarPlaySceneDelegate` | Play/group/menu callbacks need Kotlin export |
|
||||
|
||||
## CarPlay note
|
||||
|
||||
`CarPlaySceneDelegate` reads `carplay_*` JSON from the App Group. Full parity with Flutter `flutter_carplay` needs exported Kotlin handlers for `onPlay(musicId)` — until then, NotificationCenter events (`llmpHostCarPlayPlay`, etc.) are posted for the host to forward.
|
||||
|
||||
## Permissions
|
||||
|
||||
Add usage strings in Xcode as needed (camera for QR, photo library, etc.) — see Flutter `ios/Runner/Info.plist` for reference.
|
||||
- CarPlay entitlements 已挂在主 App(需账号侧已批准该 Bundle ID)。
|
||||
- PiP 用的 `black.mp4` 已在 `iosApp/pip/`。
|
||||
- SQLDelight 需要链接 `-lsqlite3`(已写在 `project.yml`)。
|
||||
- 日常运行请选 **iosApp** scheme。若选 **WidgetExtension**,Xcode 会回到主屏刷新小组件(调试 Widget 才用)。
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
module TencentOpenApi{
|
||||
umbrella header "TencentOpenApiUmbrellaHeader.h"
|
||||
export *
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
///
|
||||
/// \file QQApiInterface.h
|
||||
/// \brief QQApi接口简化封装
|
||||
///
|
||||
/// Created by Tencent on 12-5-15.
|
||||
/// Copyright (c) 2012年 Tencent. All rights reserved.
|
||||
///
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "QQApiInterfaceObject.h"
|
||||
|
||||
typedef void (^sendResultBlock)(NSDictionary *result);
|
||||
|
||||
// 发送消息回调是否发送成功
|
||||
typedef void(^QQApiInterfaceSendMessageResultBlock)(QQApiSendResultCode sendResultCode, NSString *message);
|
||||
|
||||
/**
|
||||
\brief 处理来至QQ的请求及响应的回调协议
|
||||
*/
|
||||
@protocol QQApiInterfaceDelegate <NSObject>
|
||||
|
||||
/**
|
||||
处理来至QQ的请求
|
||||
*/
|
||||
- (void)onReq:(QQBaseReq *)req;
|
||||
|
||||
/**
|
||||
处理来至QQ的响应
|
||||
*/
|
||||
- (void)onResp:(QQBaseResp *)resp;
|
||||
|
||||
/**
|
||||
处理QQ在线状态的回调
|
||||
*/
|
||||
- (void)isOnlineResponse:(NSDictionary *)response;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
\brief 对QQApi的简单封装类
|
||||
*/
|
||||
@interface QQApiInterface : NSObject
|
||||
|
||||
/**
|
||||
处理由手Q唤起的普通跳转请求
|
||||
\param url 待处理的url跳转请求
|
||||
\param delegate 第三方应用用于处理来至QQ请求及响应的委托对象
|
||||
\return 跳转请求处理结果,YES表示成功处理,NO表示不支持的请求协议或处理失败
|
||||
*/
|
||||
+ (BOOL)handleOpenURL:(NSURL *)url delegate:(id<QQApiInterfaceDelegate>)delegate;
|
||||
|
||||
/**
|
||||
处理由手Q唤起的universallink跳转请求
|
||||
\param universallink 待处理的universallink跳转请求
|
||||
\param delegate 第三方应用用于处理来至QQ请求及响应的委托对象
|
||||
\return 跳转请求处理结果,YES表示成功处理,NO表示不支持的请求协议或处理失败
|
||||
*/
|
||||
+ (BOOL)handleOpenUniversallink:(NSURL*)universallink delegate:(id<QQApiInterfaceDelegate>)delegate;
|
||||
|
||||
/**
|
||||
向手Q发起分享请求
|
||||
\param req 分享内容的请求
|
||||
\return 请求发送结果码
|
||||
*/
|
||||
+ (QQApiSendResultCode)sendReq:(QQBaseReq *)req;
|
||||
|
||||
|
||||
/**
|
||||
向手Q QZone结合版发起分享请求
|
||||
\note H5分享只支持单张网络图片的传递
|
||||
\param req 分享内容的请求
|
||||
\return 请求发送结果码
|
||||
*/
|
||||
+ (QQApiSendResultCode)SendReqToQZone:(QQBaseReq *)req;
|
||||
|
||||
/**
|
||||
向手Q发起设置QQ头像
|
||||
\param req 分享内容的请求
|
||||
\return 请求发送结果码
|
||||
*/
|
||||
+ (QQApiSendResultCode)sendMessageToQQAvatarWithReq:(QQBaseReq*)req;
|
||||
|
||||
|
||||
+ (QQApiSendResultCode)sendMessageToQQAuthWithReq:(QQBaseReq*)req;
|
||||
|
||||
|
||||
/**
|
||||
向手Q发起绑群请求
|
||||
\param req 请求的内容
|
||||
\param resultBlock 请求回调
|
||||
*/
|
||||
+ (void)sendThirdAppBindGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
|
||||
|
||||
/**
|
||||
向手Q发起加群请求
|
||||
\param req 请求的内容
|
||||
\param resultBlock 请求回调
|
||||
*/
|
||||
+ (void)sendThirdAppJoinGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
|
||||
|
||||
/**
|
||||
向手Q发起解绑群请求
|
||||
\param req 请求的内容
|
||||
\param resultBlock 请求回调
|
||||
*/
|
||||
+ (void)sendThirdAppUnBindGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
|
||||
|
||||
/**
|
||||
向手Q发起创建QQ频道的请求
|
||||
\param req 请求的内容
|
||||
\param resultBlock 回调发送结果
|
||||
\return void
|
||||
*/
|
||||
+ (void)sendMessageToCreateQQGroupProWithMessageRequest:(SendMessageToQQReq *)messageRequest sendResultBlock:(QQApiInterfaceSendMessageResultBlock)sendResultBlock;
|
||||
|
||||
|
||||
/**
|
||||
向手Q发起加入QQ频道的请求
|
||||
\param req 请求的内容
|
||||
\param resultBlock 回调发送结果
|
||||
\return void
|
||||
*/
|
||||
+ (void)sendMessageToJoinQQGroupProWithMessageRequest:(SendMessageToQQReq *)messageRequest sendResultBlock:(QQApiInterfaceSendMessageResultBlock)sendResultBlock;
|
||||
|
||||
|
||||
/**
|
||||
向手Q发起查询QQ频道openID的请求
|
||||
\param req 请求的内容
|
||||
\param resultBlock 请求回调
|
||||
*/
|
||||
+ (void)sendQueryQQGroupProInfo:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
|
||||
|
||||
/**
|
||||
向手Q发起组图分享到表情收藏
|
||||
\param req 分享内容的请求
|
||||
\return 请求发送结果码
|
||||
*/
|
||||
+ (QQApiSendResultCode)sendMessageToFaceCollectionWithReq:(QQBaseReq*)req;
|
||||
|
||||
/**
|
||||
检测是否已安装QQ
|
||||
\return 如果QQ已安装则返回YES,否则返回NO
|
||||
|
||||
\note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。
|
||||
只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。
|
||||
第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
|
||||
*/
|
||||
+ (BOOL)isQQInstalled;
|
||||
|
||||
/**
|
||||
检测是否已安装TIM
|
||||
\return 如果TIM已安装则返回YES,否则返回NO
|
||||
|
||||
\note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。
|
||||
只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。
|
||||
第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
|
||||
*/
|
||||
+ (BOOL)isTIMInstalled;
|
||||
|
||||
/**
|
||||
检测QQ是否支持API调用
|
||||
\return 如果当前安装QQ版本支持API调用则返回YES,否则返回NO
|
||||
*/
|
||||
+ (BOOL)isQQSupportApi;
|
||||
|
||||
/**
|
||||
检测TIM是否支持API调用
|
||||
\return 如果当前安装TIM版本支持API调用则返回YES,否则返回NO
|
||||
*/
|
||||
+ (BOOL)isTIMSupportApi __attribute__((deprecated("已过期, 建议删除调用,调用地方用YES替代。")));
|
||||
|
||||
/**
|
||||
检测是否支持分享
|
||||
\return 如果当前已安装QQ且QQ版本支持API调用 或者 当前已安装TIM且TIM版本支持API调用则返回YES,否则返回NO
|
||||
*/
|
||||
+ (BOOL)isSupportShareToQQ;
|
||||
|
||||
/**
|
||||
检测是否支持分享到QQ结合版QZone
|
||||
\return 如果当前已安装QQ且QQ版本支持API调用则返回YES,否则返回NO
|
||||
*/
|
||||
+ (BOOL)isSupportPushToQZone;
|
||||
|
||||
/**
|
||||
获取QQ下载地址
|
||||
|
||||
如果App通过<code>QQApiInterface#isQQInstalled</code>和<code>QQApiInterface#isQQSupportApi</code>检测发现QQ没安装或当前版本QQ不支持API调用,可引导用户通过打开此链接下载最新版QQ。
|
||||
\return iPhoneQQ下载地址
|
||||
*/
|
||||
+ (NSString *)getQQInstallUrl;
|
||||
|
||||
/**
|
||||
获取TIM下载地址
|
||||
|
||||
如果App通过<code>QQApiInterface#isTIMInstalled</code>检测发现TIM没安装或当前版本TIM不支持API调用,可引导用户通过打开此链接下载最新版TIM。
|
||||
\return iPhoneTIM下载地址
|
||||
*/
|
||||
+ (NSString *)getTIMInstallUrl;
|
||||
|
||||
#pragma mark - Log
|
||||
|
||||
/*! @brief 调用此函数可以导出QQSDK的Log到第三方中,用于定位问题
|
||||
|
||||
注意1:SDK会强引用这个block,注意不要导致内存泄漏,注意不要导致内存泄漏
|
||||
注意2:调用过一次startLog by block之后,如果再调用一次任意方式的startLoad,会释放上一次logBlock,不再回调上一个logBlock
|
||||
*
|
||||
* @param logBlock 打印log的回调block
|
||||
*/
|
||||
+ (void)startLogWithBlock:(QQApiLogBolock)logBlock;
|
||||
|
||||
///停止回调打印
|
||||
+ (void)stopLog;
|
||||
|
||||
///设置打印日志到文件开关on/off,如果不设置,默认不打印到文件
|
||||
+ (void)setSwitchPrintLogToFile:(BOOL)on;
|
||||
|
||||
///日志文件目录
|
||||
+ (NSString *)getLogFilePath;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,754 @@
|
||||
///
|
||||
/// \file QQApiInterfaceObject.h
|
||||
/// \brief QQApiInterface所依赖的请求及应答消息对象封装帮助类
|
||||
///
|
||||
/// Created by Tencent on 12-5-15.
|
||||
/// Copyright (c) 2012年 Tencent. All rights reserved.
|
||||
///
|
||||
|
||||
#ifndef QQApiInterface_QQAPIOBJECT_h
|
||||
#define QQApiInterface_QQAPIOBJECT_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, QQApiSendResultCode) {
|
||||
EQQAPISENDSUCESS = 0,
|
||||
EQQAPIQQNOTINSTALLED = 1, // QQ未安装
|
||||
EQQAPIQQNOTSUPPORTAPI = 2, // QQ api不支持
|
||||
EQQAPIMESSAGETYPEINVALID = 3,
|
||||
EQQAPIMESSAGECONTENTNULL = 4,
|
||||
EQQAPIMESSAGECONTENTINVALID = 5,
|
||||
EQQAPIAPPNOTREGISTED = 6,
|
||||
EQQAPIAPPSHAREASYNC = 7,
|
||||
EQQAPIQQNOTSUPPORTAPI_WITH_ERRORSHOW = 8, // QQ api不支持 && SDK显示error提示(已废弃)
|
||||
EQQAPIMESSAGEARKCONTENTNULL = 9, // ark内容为空
|
||||
EQQAPIMESSAGE_MINI_CONTENTNULL = 10, // 小程序参数为空
|
||||
EQQAPISENDFAILD = -1, // 发送失败
|
||||
EQQAPISHAREDESTUNKNOWN = -2, // 未指定分享到QQ或TIM
|
||||
EQQAPITIMSENDFAILD = -3, // 发送失败
|
||||
EQQAPITIMNOTINSTALLED = 11, // TIM未安装
|
||||
EQQAPITIMNOTSUPPORTAPI = 12, // TIM api不支持
|
||||
EQQAPI_INCOMING_PARAM_ERROR = 13, // 外部传参错误
|
||||
EQQAPI_THIRD_APP_GROUP_ERROR_APP_NOT_AUTHORIZIED = 14, // APP未获得授权
|
||||
EQQAPI_THIRD_APP_GROUP_ERROR_CGI_FAILED = 15, // CGI请求失败
|
||||
EQQAPI_THIRD_APP_GROUP_ERROR_HAS_BINDED = 16, // 该组织已经绑定群聊
|
||||
EQQAPI_THIRD_APP_GROUP_ERROR_NOT_BINDED = 17, // 该组织尚未绑定群聊
|
||||
EQQAPI_THIRD_APP_GROUP_ERROR_HAS_UNBINDED = 18, // 该组织已经解绑群聊
|
||||
EQQAPI_IMAGE_SIZE_OUT_OF_BOUNND = 19, // 图片大小超过限制(图片不能超过5M,缩略图不能超过1M)
|
||||
EQQAPI_TITLE_LENGTH_OUT_OF_BOUNND = 20, // 标题长度超过限制(不能超过128)
|
||||
EQQAPI_TITLE_NIL_ERROR = 21, // 标题不能为空
|
||||
EQQAPI_DESC_LENGTH_OUT_OF_BOUNND = 22, // 描述信息长度超过限制(不能超过512)
|
||||
EQQAPI_URL_LENGTH_OUT_OF_BOUNND = 23, // URL参数长度超过限制(不能超过1024)
|
||||
EQQAPI_URL_NIL_ERROR = 24, // URL参数不能为空
|
||||
EQQAPIQZONENOTSUPPORTTEXT = 10000, // qzone分享不支持text类型分享
|
||||
EQQAPIQZONENOTSUPPORTIMAGE = 10001, // qzone分享不支持image类型分享
|
||||
EQQAPIVERSIONNEEDUPDATE = 10002, // 当前QQ版本太低,需要更新至新版本才可以支持
|
||||
ETIMAPIVERSIONNEEDUPDATE = 10004, // 当前TIM版本太低,需要更新至新版本才可以支持
|
||||
EAPPURLTYPESILLEGALITY = 20000, // (>=3.3.8)第三方APP的info.plist中UrlTypes字段存在QQ的UrlScheme
|
||||
EQQAPI_ERROR_USER_NOT_AGREED_AUTHORIZATION = 30001, // 用户未同意隐私协议,用户同意隐私协议后,需要设置[TencentOAuth setIsUserAgreedAuthorization:YES];
|
||||
};
|
||||
|
||||
#pragma mark - QQApiObject(分享对象类型)
|
||||
|
||||
// QQApiObject control flags
|
||||
typedef NS_ENUM(NSUInteger,kQQAPICtrlFlag) {
|
||||
kQQAPICtrlFlagQZoneShareOnStart = 0x01,
|
||||
kQQAPICtrlFlagQZoneShareForbid = 0x02, //屏蔽好友选择器上的空间入口
|
||||
kQQAPICtrlFlagQQShare = 0x04,
|
||||
kQQAPICtrlFlagQQShareFavorites = 0x08, //收藏
|
||||
kQQAPICtrlFlagQQShareDataline = 0x10, //数据线
|
||||
kQQAPICtrlFlagQQShareEnableArk = 0x20, //支持ARK
|
||||
kQQAPICtrlFlagQQShareEnableMiniProgram = 0x40, //支持小程序
|
||||
};
|
||||
|
||||
// 分享到QQ或TIM
|
||||
typedef NS_ENUM(NSUInteger, ShareDestType) {
|
||||
ShareDestTypeQQ = 0,
|
||||
ShareDestTypeTIM,
|
||||
};
|
||||
|
||||
//小程序的类型
|
||||
typedef NS_ENUM(NSUInteger, MiniProgramType) {
|
||||
MiniProgramType_Develop = 0, // 开发版
|
||||
MiniProgramType_Test = 1, // 测试版
|
||||
MiniProgramType_Online = 3, // 正式版,默认
|
||||
MiniProgramType_Preview = 4, // 预览版
|
||||
};
|
||||
|
||||
/// 打印回调的block
|
||||
typedef void(^QQApiLogBolock)(NSString *logStr);
|
||||
|
||||
// QQApiObject
|
||||
/** \brief 所有在QQ及插件间发送的数据对象的根类。
|
||||
*/
|
||||
__attribute__((visibility("default"))) @interface QQApiObject : NSObject
|
||||
|
||||
@property (nonatomic, copy) NSString *title; ///< 标题,最长128个字符
|
||||
@property (nonatomic, copy) NSString *description; ///<简要描述,最长512个字符
|
||||
@property (nonatomic, copy) NSString *universalLink; ///(>=3.3.7)支持第三方传入在互联开放平台注册的universallink
|
||||
@property (nonatomic, assign) uint64_t cflag;
|
||||
//353新增两个字断给游戏侧使用,对齐微信sdk
|
||||
@property (nonatomic, copy) NSString *tagName;
|
||||
@property (nonatomic, copy) NSString *messageExt;
|
||||
/*
|
||||
* 分享到QQ/TIM
|
||||
* SDK根据是否安装对应客户端进行判断,判断顺序:QQ > TIM
|
||||
* 默认分享到QQ,如果QQ未安装检测TIM是否安装
|
||||
*/
|
||||
@property (nonatomic, assign) ShareDestType shareDestType;
|
||||
|
||||
/**
|
||||
* 检查参数是否完整有效
|
||||
* @return 检查参数结果
|
||||
*/
|
||||
- (QQApiSendResultCode)checkParamValid;
|
||||
|
||||
@end
|
||||
|
||||
// ArkObject
|
||||
/** \brief 支持Ark的根类。
|
||||
*/
|
||||
__attribute__((visibility("default"))) @interface ArkObject : NSObject
|
||||
@property (nonatomic, copy) NSString *arkData; ///< 显示Ark所需的数据,json串,长度暂不限制
|
||||
@property (nonatomic, strong) QQApiObject* qqApiObject; ///<原有老版本的QQApiObject
|
||||
|
||||
- (id)initWithData:(NSString *)arkData qqApiObject:(QQApiObject*)qqApiObject;
|
||||
+ (id)objectWithData:(NSString *)arkData qqApiObject:(QQApiObject*)qqApiObject;
|
||||
@end
|
||||
|
||||
#pragma mark QQ小程序
|
||||
//分享小程序消息 - QQ 8.0.8
|
||||
__attribute__((visibility("default"))) @interface QQApiMiniProgramObject : NSObject
|
||||
@property (nonatomic, strong) QQApiObject* qqApiObject; //原有老版本的QQApiObject
|
||||
@property (nonatomic, copy) NSString *miniAppID; //必填,小程序的AppId(注:必须在QQ互联平台中,将该小程序与分享的App绑定)
|
||||
@property (nonatomic, copy) NSString *miniPath; //必填,小程序的展示路径
|
||||
@property (nonatomic, copy) NSString *webpageUrl; //必填,兼容低版本的网页链接
|
||||
@property (nonatomic, assign) MiniProgramType miniprogramType; //非必填,小程序的类型,默认正式版(3),可选测试版(1)、预览版(4)
|
||||
@end
|
||||
|
||||
//唤起小程序 - QQ 8.1.8
|
||||
__attribute__((visibility("default"))) @interface QQApiLaunchMiniProgramObject : QQApiObject
|
||||
@property (nonatomic, copy) NSString *miniAppID; //必填,小程序的AppId(注:必须在QQ互联平台中,将该小程序与分享的App绑定)
|
||||
@property (nonatomic, copy) NSString *miniPath; //小程序的展示路径,不填展示默认小程序首页
|
||||
@property (nonatomic, assign) MiniProgramType miniprogramType; //非必填,小程序的类型,默认正式版(3),可选测试版(1)、开发版(0)
|
||||
@end
|
||||
|
||||
//小程序唤起第三方 - SDK 3.3.9
|
||||
__attribute__((visibility("default"))) @interface QQApiMiniProgramLaunchObject : QQApiObject
|
||||
@property (nonatomic, copy) NSString *appParameter; //小程序带来的数据,透传
|
||||
+ (instancetype)newWithAppParameter:(NSString *)parameter;
|
||||
@end
|
||||
|
||||
// QQApiResultObject
|
||||
/** \brief 用于请求回应的数据类型。
|
||||
<h3>可能错误码及描述如下:</h3>
|
||||
<TABLE>
|
||||
<TR><TD>error</TD><TD>errorDescription</TD><TD>注释</TD></TR>
|
||||
<TR><TD>0</TD><TD>nil</TD><TD>成功</TD></TR>
|
||||
<TR><TD>-1</TD><TD>param error</TD><TD>参数错误</TD></TR>
|
||||
<TR><TD>-2</TD><TD>group code is invalid</TD><TD>该群不在自己的群列表里面</TD></TR>
|
||||
<TR><TD>-3</TD><TD>upload photo failed</TD><TD>上传图片失败</TD></TR>
|
||||
<TR><TD>-4</TD><TD>user give up the current operation</TD><TD>用户放弃当前操作</TD></TR>
|
||||
<TR><TD>-5</TD><TD>client internal error</TD><TD>客户端内部处理错误</TD></TR>
|
||||
</TABLE>
|
||||
*/
|
||||
__attribute__((visibility("default"))) @interface QQApiResultObject : QQApiObject
|
||||
@property (nonatomic, copy) NSString *error; ///<错误
|
||||
@property (nonatomic, copy) NSString *errorDescription; ///<错误描述
|
||||
@property (nonatomic, copy) NSString *extendInfo; ///<扩展信息
|
||||
@property (nonatomic, copy) NSDictionary *otherInfo; ///<其他扩展信息
|
||||
@end
|
||||
|
||||
// QQApiTextObject
|
||||
/** \brief 文本对象
|
||||
*/
|
||||
@interface QQApiTextObject : QQApiObject
|
||||
@property (nonatomic, copy)NSString *text; ///<文本内容,必填,最长1536个字符
|
||||
|
||||
- (id)initWithText:(NSString *)text; ///<初始化方法
|
||||
+ (id)objectWithText:(NSString *)text;///<工厂方法,获取一个QQApiTextObject对象.
|
||||
|
||||
@end
|
||||
|
||||
// QQApiURLObject
|
||||
typedef NS_ENUM(NSUInteger, QQApiURLTargetType) {
|
||||
QQApiURLTargetTypeNotSpecified = 0x00,
|
||||
QQApiURLTargetTypeAudio = 0x01,
|
||||
QQApiURLTargetTypeVideo = 0x02,
|
||||
QQApiURLTargetTypeNews = 0x03
|
||||
};
|
||||
|
||||
/** @brief URL对象类型。
|
||||
|
||||
包括URL地址,URL地址所指向的目标类型及预览图像。
|
||||
*/
|
||||
__attribute__((visibility("default"))) @interface QQApiURLObject : QQApiObject
|
||||
/**
|
||||
URL地址所指向的目标类型.
|
||||
@note 参见QQApi.h 中的 QQApiURLTargetType 定义.
|
||||
*/
|
||||
@property (nonatomic)QQApiURLTargetType targetContentType;
|
||||
|
||||
@property (nonatomic, strong) NSURL *url; ///<URL地址,必填,最长512个字符
|
||||
@property (nonatomic, copy) NSData *previewImageData;///<预览图像数据,最大1M字节
|
||||
@property (nonatomic, strong) NSURL *previewImageURL; ///<预览图像URL **预览图像数据与预览图像URL可二选一
|
||||
|
||||
/**
|
||||
初始化方法
|
||||
*/
|
||||
- (id)initWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data targetContentType:(QQApiURLTargetType)targetContentType;
|
||||
|
||||
- (id)initWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL targetContentType:(QQApiURLTargetType)targetContentType;
|
||||
/**
|
||||
工厂方法,获取一个QQApiURLObject对象
|
||||
*/
|
||||
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data targetContentType:(QQApiURLTargetType)targetContentType;
|
||||
|
||||
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL targetContentType:(QQApiURLTargetType)targetContentType;
|
||||
|
||||
@end
|
||||
|
||||
// QQApiExtendObject
|
||||
/** @brief 扩展数据类型
|
||||
*/
|
||||
@interface QQApiExtendObject : QQApiObject
|
||||
@property (nonatomic, copy) NSData *data;///<具体数据内容,必填,最大5M字节
|
||||
@property (nonatomic, copy) NSData *previewImageData;///<预览图像,最大1M字节
|
||||
@property (nonatomic, copy) NSArray *imageDataArray;///图片数组(多图暂只支持分享到手机QQ收藏功能)
|
||||
|
||||
/**
|
||||
初始化方法
|
||||
@param data 数据内容
|
||||
@param previewImageData 用于预览的图片
|
||||
@param title 标题
|
||||
@param description 此对象,分享的描述
|
||||
*/
|
||||
- (id)initWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description;
|
||||
|
||||
/**
|
||||
初始化方法
|
||||
@param data 数据内容
|
||||
@param title 标题
|
||||
@param description 此对象,分享的描述
|
||||
@param imageDataArray 发送的多张图片队列
|
||||
*/
|
||||
- (id)initWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description imageDataArray:(NSArray *)imageDataArray;
|
||||
|
||||
|
||||
/**
|
||||
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
|
||||
@param data 数据内容
|
||||
@param previewImageData 用于预览的图片
|
||||
@param title 标题
|
||||
@param description 此对象,分享的描述
|
||||
@return
|
||||
一个自动释放的<code>QQApiExtendObject</code>实例
|
||||
*/
|
||||
+ (id)objectWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description;
|
||||
/**
|
||||
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
|
||||
@param data 数据内容
|
||||
@param previewImageData 用于预览的图片
|
||||
@param title 标题
|
||||
@param description 此对象,分享的描述
|
||||
@param imageDataArray 发送的多张图片队列
|
||||
@return
|
||||
一个自动释放的<code>QQApiExtendObject</code>实例
|
||||
*/
|
||||
+ (id)objectWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description imageDataArray:(NSArray *)imageDataArray;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// QQApiImageObject
|
||||
/** @brief 图片对象
|
||||
用于分享图片内容的对象,是一个指定为图片类型的<code>QQApiExtendObject</code>
|
||||
*/
|
||||
@interface QQApiImageObject : QQApiExtendObject
|
||||
@end
|
||||
|
||||
// QQApiImageForQQAvatarObject
|
||||
/** @brief 图片对象
|
||||
用于设置QQ头像内容的对象,是一个指定为图片类型的<code>QQApiExtendObject</code>
|
||||
*/
|
||||
@interface QQApiImageForQQAvatarObject : QQApiExtendObject
|
||||
@end
|
||||
/**
|
||||
* @brief 视频对象
|
||||
* 用于设置动态头像
|
||||
* assetURL可传ALAsset的ALAssetPropertyAssetURL,或者PHAsset的localIdentifier
|
||||
从手Q返回的错误码:
|
||||
//第三方设置动态头像结果
|
||||
@"ret=0"//设置成功
|
||||
@"ret=-10&error_des=user cancel"//用户取消设置
|
||||
@"ret=-11&error_des=pasteboard have no video data"//剪切板没有数据
|
||||
@"ret=-12&error_des=export data failed"//从剪切板导出数据到本地失败
|
||||
@"ret=-13&error_des=url param invalid"//sdk传递过来的数据有误
|
||||
@"ret=-14&error_des=video param invalid"//视频的参数不符合要求(检测第三方视频源方案:1、分辨率跟480*480保持一致;2、视频长度0.5s~8s)
|
||||
@"ret=-15&error_des=app authorised failed"//应用鉴权失败
|
||||
@"ret=-16&error_des=upload video failed"//设置头像,上传到后台失败
|
||||
@"ret=-17&error_des=account diff"//账号不一致
|
||||
*/
|
||||
@interface QQApiVideoForQQAvatarObject : QQApiExtendObject
|
||||
@property (nonatomic, copy) NSString *assetURL;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
//QQApiAuthObject 用于拉起手Q的授权详情页
|
||||
@interface QQApiAuthObject : QQApiObject
|
||||
@end
|
||||
|
||||
// QQApiImageArrayForFaceCollectionObject
|
||||
/** @brief 图片数组对象
|
||||
用于分享图片组到表情收藏,是一个指定为图片类型的<code>QQApiObject</code>
|
||||
*/
|
||||
@interface QQApiImageArrayForFaceCollectionObject : QQApiObject
|
||||
|
||||
@property (nonatomic, copy) NSArray *imageDataArray;///图片数组
|
||||
|
||||
/**
|
||||
初始化方法
|
||||
@param imageDataArray 图片数组
|
||||
*/
|
||||
- (id)initWithImageArrayData:(NSArray *)imageDataArray;
|
||||
/**
|
||||
helper方法获取一个autorelease的<code>QQApiObject</code>对象
|
||||
@param imageDataArray 发送的多张图片队列
|
||||
@return
|
||||
一个自动释放的<code>QQApiObject</code>实例
|
||||
*/
|
||||
+ (id)objectWithimageDataArray:(NSArray *)imageDataArray;
|
||||
|
||||
@end
|
||||
|
||||
// QQApiImageArrayForQZoneObject
|
||||
/** @brief 图片对象
|
||||
用于分享图片到空间,走写说说路径,是一个指定为图片类型的,当图片数组为空时,默认走文本写说说<code>QQApiObject</code>
|
||||
*/
|
||||
@interface QQApiImageArrayForQZoneObject : QQApiObject
|
||||
|
||||
@property (nonatomic, copy) NSArray *imageDataArray;///图片数组
|
||||
@property (nonatomic, copy) NSDictionary *extMap; // 扩展字段
|
||||
|
||||
/**
|
||||
初始化方法
|
||||
@param imageDataArray 图片数组
|
||||
@param title 写说说的内容,可以为空
|
||||
@param extMap 扩展字段
|
||||
*/
|
||||
- (id)initWithImageArrayData:(NSArray *)imageDataArray title:(NSString *)title extMap:(NSDictionary *)extMap;
|
||||
|
||||
/**
|
||||
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
|
||||
@param title 写说说的内容,可以为空
|
||||
@param imageDataArray 发送的多张图片队列
|
||||
@param extMap 扩展字段
|
||||
@return
|
||||
一个自动释放的<code>QQApiExtendObject</code>实例
|
||||
*/
|
||||
+ (id)objectWithimageDataArray:(NSArray *)imageDataArray title:(NSString *)title extMap:(NSDictionary *)extMap;
|
||||
|
||||
@end
|
||||
|
||||
// QQApiVideoForQZoneObject
|
||||
/** @brief 视频对象
|
||||
用于分享视频到空间,走写说说路径<code>QQApiObject</code>,assetURL和videoData两个参数必须设置至少一个参数,如果assetURL设置了忽略videoData参数
|
||||
@param assetURL可传ALAsset的ALAssetPropertyAssetURL,或者PHAsset的localIdentifier
|
||||
@param extMap 扩展字段
|
||||
@param videoData 视频数据,大小不超过50M
|
||||
*/
|
||||
@interface QQApiVideoForQZoneObject : QQApiObject
|
||||
|
||||
@property (nonatomic, copy) NSString *assetURL;
|
||||
@property (nonatomic, copy) NSDictionary *extMap; // 扩展字段
|
||||
@property (nonatomic, copy) NSData *videoData;
|
||||
|
||||
- (id)initWithAssetURL:(NSString *)assetURL title:(NSString *)title extMap:(NSDictionary *)extMap;
|
||||
|
||||
+ (id)objectWithAssetURL:(NSString *)assetURL title:(NSString *)title extMap:(NSDictionary *)extMap;
|
||||
|
||||
- (id)initWithVideoData:(NSData *)videoData title:(NSString *)title extMap:(NSDictionary *)extMap;
|
||||
|
||||
+ (id)objectWithVideoData:(NSData *)videoData title:(NSString *)title extMap:(NSDictionary *)extMap;
|
||||
|
||||
@end
|
||||
|
||||
// QQApiWebImageObject
|
||||
/** @brief 图片对象
|
||||
用于分享网络图片内容的对象,是一个指定网络图片url的: 该类型只在2.9.0的h5分享中才支持,
|
||||
原有的手q分享是不支持该类型的。
|
||||
*/
|
||||
@interface QQApiWebImageObject : QQApiObject
|
||||
|
||||
@property (nonatomic, strong) NSURL *previewImageURL; ///<预览图像URL
|
||||
|
||||
/**
|
||||
初始化方法
|
||||
@param previewImageURL 用于预览的图片
|
||||
@param title 标题
|
||||
@param description 此对象,分享的描述
|
||||
*/
|
||||
- (id)initWithPreviewImageURL:(NSURL *)previewImageURL title:(NSString *)title description:(NSString *)description;
|
||||
|
||||
/**
|
||||
helper方法获取一个autorelease的<code>QQApiWebImageObject</code>对象
|
||||
@param previewImageURL 用于预览的图片
|
||||
@param title 标题
|
||||
@param description 此对象,分享的描述
|
||||
*/
|
||||
+ (id)objectWithPreviewImageURL:(NSURL *)previewImageURL title:(NSString *)title description:(NSString *)description;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
//QQApiFileObject
|
||||
/** @brief 本地文件对象(暂只支持分享到手机QQ数据线功能)
|
||||
用于分享文件内容的对象,是一个指定为文件类型的<code>QQApiExtendObject</code>
|
||||
*/
|
||||
@interface QQApiFileObject : QQApiExtendObject {
|
||||
NSString *_fileName;
|
||||
}
|
||||
|
||||
@property (nonatomic, copy)NSString *fileName;
|
||||
|
||||
@end
|
||||
|
||||
// QQApiAudioObject
|
||||
/** @brief 音频URL对象
|
||||
用于分享目标内容为音频的URL的对象
|
||||
*/
|
||||
@interface QQApiAudioObject : QQApiURLObject
|
||||
|
||||
@property (nonatomic, strong) NSURL *flashURL; ///<音频URL地址,最长512个字符
|
||||
|
||||
/**
|
||||
获取一个autorelease的<code>QQApiAudioObject</code>
|
||||
@param url 音频内容的目标URL
|
||||
@param title 分享内容的标题
|
||||
@param description 分享内容的描述
|
||||
@param data 分享内容的预览图像
|
||||
@note 如果url为空,调用<code>QQApi#sendMessage:</code>时将返回FALSE
|
||||
*/
|
||||
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data;
|
||||
|
||||
/**
|
||||
获取一个autorelease的<code>QQApiAudioObject</code>
|
||||
@param url 音频内容的目标URL
|
||||
@param title 分享内容的标题
|
||||
@param description 分享内容的描述
|
||||
@param previewURL 分享内容的预览图像URL
|
||||
@note 如果url为空,调用<code>QQApi#sendMessage:</code>时将返回FALSE
|
||||
*/
|
||||
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL;
|
||||
|
||||
@end
|
||||
|
||||
// QQApiVideoObject
|
||||
/** @brief 视频URL对象
|
||||
用于分享目标内容为视频的URL的对象
|
||||
|
||||
QQApiVideoObject类型的分享,目前在Android和PC QQ上接收消息时,展现有待完善,待手机QQ版本以后更新支持
|
||||
目前如果要分享视频,推荐使用 QQApiNewsObject 类型
|
||||
*/
|
||||
@interface QQApiVideoObject : QQApiURLObject
|
||||
|
||||
@property (nonatomic, strong) NSURL *flashURL; ///<视频URL地址,最长512个字符
|
||||
|
||||
/**
|
||||
获取一个autorelease的<code>QQApiVideoObject</code>
|
||||
@param url 视频内容的目标URL
|
||||
@param title 分享内容的标题
|
||||
@param description 分享内容的描述
|
||||
@param data 分享内容的预览图像
|
||||
@note 如果url为空,调用<code>QQApi#sendMessage:</code>时将返回FALSE
|
||||
*/
|
||||
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data;
|
||||
/**
|
||||
获取一个autorelease的<code>QQApiVideoObject</code>
|
||||
@param url 视频内容的目标URL
|
||||
@param title 分享内容的标题
|
||||
@param description 分享内容的描述
|
||||
@param previewURL 分享内容的预览图像URL
|
||||
@note 如果url为空,调用<code>QQApi#sendMessage:</code>时将返回FALSE
|
||||
*/
|
||||
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL;
|
||||
|
||||
@end
|
||||
|
||||
// QQApiNewsObject
|
||||
/** @brief 新闻URL对象
|
||||
用于分享目标内容为新闻的URL的对象
|
||||
*/
|
||||
@interface QQApiNewsObject : QQApiURLObject
|
||||
/**
|
||||
获取一个autorelease的<code>QQApiNewsObject</code>
|
||||
@param url 视频内容的目标URL
|
||||
@param title 分享内容的标题
|
||||
@param description 分享内容的描述
|
||||
@param data 分享内容的预览图像
|
||||
@note 如果url为空,调用<code>QQApi#sendMessage:</code>时将返回FALSE
|
||||
*/
|
||||
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data;
|
||||
/**
|
||||
获取一个autorelease的<code>QQApiNewsObject</code>
|
||||
@param url 视频内容的目标URL
|
||||
@param title 分享内容的标题
|
||||
@param description 分享内容的描述
|
||||
@param previewURL 分享内容的预览图像URL
|
||||
@note 如果url为空,调用<code>QQApi#sendMessage:</code>时将返回FALSE
|
||||
*/
|
||||
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL;
|
||||
|
||||
@end
|
||||
|
||||
// QQApiCommonContentObject;
|
||||
/** @brief 通用模板类型对象
|
||||
用于分享一个固定显示模板的图文混排对象
|
||||
@note 图片列表和文本列表不能同时为空
|
||||
*/
|
||||
@interface QQApiCommonContentObject : QQApiObject
|
||||
/**
|
||||
预定义的界面布局类型
|
||||
*/
|
||||
@property (nonatomic,assign) unsigned int layoutType;
|
||||
@property (nonatomic, copy) NSData *previewImageData;///<预览图
|
||||
@property (nonatomic, copy) NSArray *textArray;///<文本列表
|
||||
@property (nonatomic, copy) NSArray *pictureDataArray;///<图片列表
|
||||
+ (id)objectWithLayoutType:(int)layoutType textArray:(NSArray *)textArray pictureArray:(NSArray *)pictureArray previewImageData:(NSData *)data;
|
||||
/**
|
||||
将一个NSDictionary对象转化为QQApiCommomContentObject,如果无法转换,则返回空
|
||||
*/
|
||||
+ (id)objectWithDictionary:(NSDictionary *)dic;
|
||||
- (NSDictionary *)toDictionary;
|
||||
@end
|
||||
|
||||
// QQApiExtraServiceObject; 通用业务消息处理类(旧版,后续使用QQApiCommonServiceObject)
|
||||
/**
|
||||
@brief OpenSDK扩展支持的服务,通用接口,后续会扩充能力
|
||||
@param serviceID [必选] 扩展支持的服务类型ID,参考官方文档说明
|
||||
@param openID [必选] 授权登录后对该用户的唯一标识
|
||||
@param toUin [可选] 对方的QQ号码
|
||||
@param extraInfo [可选] 扩展字段
|
||||
@note 该接口的使用须先登录
|
||||
*/
|
||||
@interface QQApiExtraServiceObject : QQApiObject
|
||||
@property (nonatomic, copy) NSString *serviceID;
|
||||
@property (nonatomic, copy) NSString *openID;
|
||||
@property (nonatomic, copy) NSString *toUin;
|
||||
@property (nonatomic, copy) NSDictionary *extraInfo;
|
||||
|
||||
- (id)initWithOpenID:(NSString *)openID serviceID:(NSString *)serviceID;
|
||||
+ (id)objecWithOpenID:(NSString *)openID serviceID:(NSString *)serviceID;
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
* QQApiCommonServiceObject; 通用业务消息处理类(新),可以适用所有的需要通过互联SDK发消息给手Q的业务去处理。
|
||||
* 使用前需要申请serviceID,每个业务功能有个对应的serviceID
|
||||
*/
|
||||
@interface QQApiCommonServiceObject : QQApiObject
|
||||
|
||||
// [必选] 授权登录后对该用户的唯一标识
|
||||
@property (nonatomic, copy) NSString *openID;
|
||||
|
||||
// [必选] 扩展支持的服务类型ID,参考官方文档说明
|
||||
@property (nonatomic, copy) NSString *serviceID;
|
||||
|
||||
// [可选] 扩展字段,由调用方跟具体的业务方协定具体的字段
|
||||
@property (nonatomic, copy) NSDictionary *extendInfo;
|
||||
|
||||
- (instancetype)initWithOpenID:(NSString *)openID
|
||||
serviceID:(NSString *)serviceID
|
||||
extendInfo:(NSDictionary *)extendInfo;
|
||||
|
||||
+ (instancetype)objecWithOpenID:(NSString *)openID
|
||||
serviceID:(NSString *)serviceID
|
||||
extendInfo:(NSDictionary *)extendInfo;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - QQApi请求消息类型
|
||||
|
||||
/**
|
||||
QQApi请求消息类型
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, QQApiInterfaceReqType) {
|
||||
EGETMESSAGEFROMQQREQTYPE = 0, /// < 手Q -> 第三方应用,请求第三方应用向手Q发送消息
|
||||
ESENDMESSAGETOQQREQTYPE = 1, /// < 第三方应用 -> 手Q,第三方应用向手Q分享消息
|
||||
ESHOWMESSAGEFROMQQREQTYPE = 2, /// < 手Q -> 第三方应用,请求第三方应用展现消息中的数据
|
||||
ESENDMESSAGEARKTOQQREQTYPE = 3, /// < 第三方应用 -> 手Q,第三方应用向手Q分享Ark消息
|
||||
ESENDMESSAGE_MINI_TOQQREQTYPE = 4 /// < 第三方应用 -> 手Q,第三方应用向手Q分享小程序消息
|
||||
};
|
||||
|
||||
/**
|
||||
QQApi应答消息类型
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, QQApiInterfaceRespType) {
|
||||
ESHOWMESSAGEFROMQQRESPTYPE = 0, /// < 第三方应用 -> 手Q,第三方应用应答消息展现结果
|
||||
EGETMESSAGEFROMQQRESPTYPE = 1, /// < 第三方应用 -> 手Q,第三方应用回应发往手Q的消息
|
||||
ESENDMESSAGETOQQRESPTYPE = 2 /// < 手Q -> 第三方应用,手Q应答处理分享消息的结果
|
||||
};
|
||||
|
||||
/**
|
||||
QQApi请求消息基类
|
||||
*/
|
||||
@interface QQBaseReq : NSObject
|
||||
|
||||
/** 请求消息类型,参见\ref QQApiInterfaceReqType */
|
||||
@property (nonatomic, assign) int type;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
QQApi应答消息基类
|
||||
*/
|
||||
@interface QQBaseResp : NSObject
|
||||
|
||||
/** 请求处理结果 */
|
||||
@property (nonatomic, copy) NSString *result;
|
||||
|
||||
/** 具体错误描述信息 */
|
||||
@property (nonatomic, copy) NSString *errorDescription;
|
||||
|
||||
/** 应答消息类型,参见\ref QQApiInterfaceRespType */
|
||||
@property (nonatomic, assign) int type;
|
||||
|
||||
/** 扩展信息 */
|
||||
@property (nonatomic, copy) NSString *extendInfo;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
GetMessageFromQQReq请求帮助类
|
||||
*/
|
||||
@interface GetMessageFromQQReq : QQBaseReq
|
||||
|
||||
/**
|
||||
创建一个GetMessageFromQQReq请求实例
|
||||
*/
|
||||
+ (GetMessageFromQQReq *)req;
|
||||
|
||||
@end
|
||||
|
||||
@interface SendMessageToQQReq : QQBaseReq
|
||||
|
||||
/**
|
||||
创建一个SendMessageToQQReq请求实例
|
||||
\param message 具体分享消息实例
|
||||
\return 新创建的SendMessageToQQReq请求实例
|
||||
*/
|
||||
+ (SendMessageToQQReq *)reqWithContent:(QQApiObject *)message;
|
||||
|
||||
/**
|
||||
创建一个支持Ark的SendMessageToQQReq请求实例
|
||||
\param message 具体分享消息实例
|
||||
\return 新创建的SendMessageToQQReq请求实例
|
||||
*/
|
||||
+ (SendMessageToQQReq *)reqWithArkContent:(ArkObject *)message;
|
||||
/**
|
||||
* 创建一个支持小程序的消息请求实例
|
||||
* @param miniMessage 小程序实例对象
|
||||
* @return 消息请求实例
|
||||
*/
|
||||
+ (SendMessageToQQReq *)reqWithMiniContent:(QQApiMiniProgramObject *)miniMessage;
|
||||
/** 具体分享消息 */
|
||||
@property (nonatomic, strong) QQApiObject *apiObject;
|
||||
|
||||
/** 支持Ark的具体分享消息 */
|
||||
@property (nonatomic, strong) ArkObject *arkObject;
|
||||
/** 支持小程序的具体分享消息 */
|
||||
@property (nonatomic, strong) QQApiMiniProgramObject *miniProgramObject;
|
||||
@end
|
||||
|
||||
/**
|
||||
SendMessageToQQResp应答帮助类
|
||||
*/
|
||||
@interface SendMessageToQQResp : QQBaseResp
|
||||
|
||||
/** 其他扩展信息 */
|
||||
@property (nonatomic, copy) NSDictionary *otherInfo;
|
||||
|
||||
/**
|
||||
创建一个SendMessageToQQResp应答实例
|
||||
\param result 请求处理结果
|
||||
\param errDesp 具体错误描述信息
|
||||
\param extendInfo 扩展信息
|
||||
\return 新创建的SendMessageToQQResp应答实例
|
||||
*/
|
||||
+ (SendMessageToQQResp *)respWithResult:(NSString *)result errorDescription:(NSString *)errDesp extendInfo:(NSString *)extendInfo;
|
||||
|
||||
+ (SendMessageToQQResp *) respWithResult:(NSString *)result errorDescription:(NSString *)errDesp extendInfo:(NSString *)extendInfo otherInfo:(NSDictionary *)otherInfo;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
ShowMessageFromQQReq请求帮助类
|
||||
*/
|
||||
@interface ShowMessageFromQQReq : QQBaseReq
|
||||
|
||||
/**
|
||||
创建一个ShowMessageFromQQReq请求实例
|
||||
\param message 具体待展现消息实例
|
||||
\return 新创建的ShowMessageFromQQReq请求实例
|
||||
*/
|
||||
+ (ShowMessageFromQQReq *)reqWithContent:(QQApiObject *)message;
|
||||
|
||||
/** 具体待展现消息 */
|
||||
@property (nonatomic, strong) QQApiObject *message;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark --一键加群&建群&解绑群
|
||||
// QQApiThirdAppBindGroupObject
|
||||
/** \brief 第三方app绑定群
|
||||
*/
|
||||
@interface QQApiThirdAppBindGroupObject : QQApiObject
|
||||
|
||||
@property (nonatomic, copy) NSString *accessToken;
|
||||
@property (nonatomic, copy) NSString *payToken;
|
||||
@property (nonatomic, copy) NSString *pfkey;
|
||||
@property (nonatomic, copy) NSString *unionID;
|
||||
@property (nonatomic, copy) NSString *appDisplayName;
|
||||
|
||||
- (id)initWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID appDisplayName:(NSString *)appDisplayName; ///<初始化方法
|
||||
+ (id)objectWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID appDisplayName:(NSString *)appDisplayName; ///<工厂方法,获取一个QQApiThirdAppBindGroupObject对象.
|
||||
|
||||
@end
|
||||
|
||||
// QQApiThirdAppJoinGroupObject
|
||||
/** \brief 第三方app加入群
|
||||
*/
|
||||
@interface QQApiThirdAppJoinGroupObject : QQApiObject
|
||||
|
||||
@property (nonatomic, copy) NSString *accessToken;
|
||||
@property (nonatomic, copy) NSString *payToken;
|
||||
@property (nonatomic, copy) NSString *pfkey;
|
||||
@property (nonatomic, copy) NSString *unionID;
|
||||
|
||||
- (id)initWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID; ///<初始化方法
|
||||
|
||||
+ (id)objectWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID; ///<工厂方法,获取一个QQApiThirdAppJoinGroupObject对象.
|
||||
|
||||
@end
|
||||
|
||||
// QQApiThirdAppUnBindGroupObject
|
||||
/** \brief 第三方app解绑群
|
||||
*/
|
||||
@interface QQApiThirdAppUnBindGroupObject : QQApiObject
|
||||
|
||||
@property (nonatomic, copy) NSString *accessToken;
|
||||
@property (nonatomic, copy) NSString *openId;
|
||||
@property (nonatomic, copy) NSString *payToken;
|
||||
@property (nonatomic, copy) NSString *pfkey;
|
||||
@property (nonatomic, copy) NSString *unionID;
|
||||
|
||||
- (id)initWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID openId:(NSString *)openId appId:(NSString *)appId; ///<初始化方法
|
||||
|
||||
+ (id)objectWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID openId:(NSString *)openId appId:(NSString *)appId; ///<工厂方法,获取一个QQApiThirdAppBindGroupObject对象.
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,555 @@
|
||||
///
|
||||
/// \file TencentOAuth.h
|
||||
/// \brief QQ互联开放平台授权登录及相关开放接口实现类
|
||||
///
|
||||
/// Created by Tencent on 12-12-21.
|
||||
/// Copyright (c) 2012年 Tencent. All rights reserved.
|
||||
///
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "SDKDef.h"
|
||||
|
||||
@protocol TencentSessionDelegate;
|
||||
@protocol TencentLoginDelegate;
|
||||
@protocol TencentApiInterfaceDelegate;
|
||||
@protocol TencentWebViewDelegate;
|
||||
|
||||
@class TencentApiReq;
|
||||
@class TencentApiResp;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TencentAuthorizeState) {
|
||||
kTencentNotAuthorizeState,
|
||||
kTencentSSOAuthorizeState,
|
||||
kTencentWebviewAuthorzieState,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TencentAuthMode) {
|
||||
kAuthModeClientSideToken,
|
||||
kAuthModeServerSideCode,
|
||||
};
|
||||
|
||||
#pragma mark - TencentOAuth(授权登录及相关开放接口调用)
|
||||
|
||||
/**
|
||||
* \brief TencentOpenAPI授权登录及相关开放接口调用
|
||||
*
|
||||
* TencentOAuth实现授权登录逻辑以及相关开放接口的请求调用
|
||||
*/
|
||||
@interface TencentOAuth : NSObject {
|
||||
NSMutableDictionary *_apiRequests;
|
||||
}
|
||||
|
||||
/** Access Token凭证,用于后续访问各开放接口 */
|
||||
@property(nonatomic, copy) NSString *accessToken;
|
||||
|
||||
/** Access Token的失效期 */
|
||||
@property(nonatomic, strong) NSDate *expirationDate;
|
||||
|
||||
/** 已实现的开放接口的回调委托对象 */
|
||||
@property(nonatomic, weak) id<TencentSessionDelegate> sessionDelegate;
|
||||
|
||||
/** 第三方应用在开发过程中设置的URLSchema,用于浏览器登录后后跳到第三方应用 */
|
||||
@property(nonatomic, copy) NSString *localAppId;
|
||||
|
||||
/** 用户授权登录后对该用户的唯一标识 */
|
||||
@property(nonatomic, copy) NSString *openId;
|
||||
|
||||
/** 用户登录成功过后的跳转页面地址 */
|
||||
@property(nonatomic, copy) NSString *redirectURI;
|
||||
|
||||
/** 第三方应用在互联开放平台申请的appID */
|
||||
@property(nonatomic, copy) NSString *appId;
|
||||
|
||||
/** 第三方应用在互联开放平台注册的UniversalLink */
|
||||
@property(nonatomic, copy) NSString *universalLink;
|
||||
|
||||
/** 主要是互娱的游戏设置uin */
|
||||
@property(nonatomic, copy) NSString *uin;
|
||||
|
||||
/** 主要是互娱的游戏设置鉴定票据 */
|
||||
@property(nonatomic, copy) NSString *skey;
|
||||
|
||||
/** 登陆透传的数据 */
|
||||
@property(nonatomic, copy) NSDictionary *passData;
|
||||
|
||||
/** 授权方式(Client Side Token或者Server Side Code) */
|
||||
@property(nonatomic, assign) TencentAuthMode authMode;
|
||||
|
||||
/** union id */
|
||||
@property(nonatomic, copy) NSString *unionid;
|
||||
|
||||
/** 第三方在授权登录/分享 时选择 QQ,还是TIM 。在授权前一定要指定其中一个类型*/
|
||||
@property(nonatomic, assign) TencentAuthShareType authShareType;
|
||||
|
||||
/** SDK打开web登录页,支持自动填充账号 */
|
||||
@property (nonatomic, copy) NSString *defaultUin;
|
||||
|
||||
/**
|
||||
* 获取上次登录得到的token
|
||||
*
|
||||
**/
|
||||
- (NSString *)getCachedToken;
|
||||
|
||||
/**
|
||||
* 获取上次登录得到的openid
|
||||
*
|
||||
**/
|
||||
- (NSString *)getCachedOpenID;
|
||||
|
||||
/**
|
||||
* 获取上次登录的token过期日期
|
||||
*
|
||||
**/
|
||||
- (NSDate *)getCachedExpirationDate;
|
||||
|
||||
/**
|
||||
* 上次登录的token是否过期(本地判断)
|
||||
**/
|
||||
- (BOOL)isCachedTokenValid;
|
||||
|
||||
/**
|
||||
* 删除上次登录登录的token信息
|
||||
*
|
||||
**/
|
||||
- (BOOL)deleteCachedToken;
|
||||
|
||||
/**
|
||||
* 删除openid
|
||||
*
|
||||
**/
|
||||
- (void)deleteOpenId;
|
||||
|
||||
/**
|
||||
* 用来获得当前sdk的版本号
|
||||
* \return 返回sdk版本号
|
||||
**/
|
||||
|
||||
+ (NSString *)sdkVersion;
|
||||
|
||||
/**
|
||||
* 用来获得当前sdk的小版本号
|
||||
* \return 返回sdk小版本号
|
||||
**/
|
||||
|
||||
+ (NSString *)sdkSubVersion;
|
||||
|
||||
/**
|
||||
* 用来获得当前sdk的是否精简版
|
||||
* @return 返回YES表示精简版
|
||||
**/
|
||||
|
||||
+ (BOOL)isLiteSDK;
|
||||
|
||||
/**
|
||||
* 主要是用来帮助判断是否有登陆被发起,但是还没有过返回结果
|
||||
* \return
|
||||
* kTencentNotAuthorizeState:无授权
|
||||
* kTencentSSOAuthorizeState:有人发起了sso授权但无返回
|
||||
* kTencentWebviewAuthorzieState:有人发起了webview授权还未返回
|
||||
**/
|
||||
|
||||
+ (TencentAuthorizeState *)authorizeState;
|
||||
|
||||
/**
|
||||
* 获取TencentOAuth单例
|
||||
*/
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/**
|
||||
* 设置SDK参数
|
||||
*
|
||||
* @param appId 不可为nil,第三方应用在互联开放平台申请的唯一标识
|
||||
* @param enableUniveralLink 默认为NO,第三方应用是否将sdk和手机QQ的交互方式切换为UniversalLink方式,启用后则在iOS9及以上的系统都会生效UniversalLink方式;否则,默认仅在iOS13及以上的系统生效UniversalLink方式。
|
||||
* @param universalLink 可以为nil,第三方应用在互联开放平台注册的UniversalLink,和bundleID一一对应(当为nil时,互联平台会按规则生成UniversalLink,详见官网说明)
|
||||
* @param delegate 不可为nil,第三方应用用于接收请求返回结果的委托对象
|
||||
*
|
||||
* @note
|
||||
* 使用说明】
|
||||
* 1、支持sdk与手Q的交互切换为UniversalLink模式,主要目的"是为了避免手Q的UrlScheme被其他应用抢注后,导致sdk接口功能受到影响"。
|
||||
* 2 、由于手Q版本在 >=8.1.3 后才适配了UniversalLink,所以一旦开启了enabled开关,“务必做到”及时知会用户升级手Q版本。
|
||||
*
|
||||
*/
|
||||
- (void)setupAppId:(NSString *)appId
|
||||
enableUniveralLink:(BOOL)enableUniveralLink
|
||||
universalLink:(NSString *)universalLink
|
||||
delegate:(id<TencentSessionDelegate>)delegate;
|
||||
|
||||
/**
|
||||
* 初始化TencentOAuth对象
|
||||
* !!!注意:3.5.17版本开始,内部单例实现,多次调用返回同一实例
|
||||
*
|
||||
* @param appId 不可为nil,第三方应用在互联开放平台申请的唯一标识
|
||||
* @param delegate 不可为nil,第三方应用用于接收请求返回结果的委托对象
|
||||
* @return 初始化后的授权登录对象
|
||||
*
|
||||
* 推荐使用初始化方法,并且适配UniversalLink:
|
||||
* - (instancetype)initWithAppId:(NSString *)appId
|
||||
* enableUniveralLink:(BOOL)enabled
|
||||
* universalLink:(NSString *)universalLink
|
||||
* delegate:(id<TencentSessionDelegate>)delegate;
|
||||
*/
|
||||
- (instancetype)initWithAppId:(NSString *)appId
|
||||
andDelegate:(id<TencentSessionDelegate>)delegate __attribute__((deprecated("此接口即将下线!请使用setupAppId:enableUniveralLink:universalLink:delegate")));
|
||||
|
||||
/**
|
||||
* 初始化TencentOAuth对象(>=3.3.7)
|
||||
* !!!注意:3.5.17版本开始,内部单例实现,多次调用返回同一实例
|
||||
*
|
||||
* @param appId 不可为nil,第三方应用在互联开放平台申请的唯一标识
|
||||
* @param universalLink 可以为nil,第三方应用在互联开放平台注册的UniversalLink,和bundleID一一对应(当为nil时,互联平台会按规则生成universallink,详见官网说明)
|
||||
* @param delegate 不可为nil,第三方应用用于接收请求返回结果的委托对象
|
||||
* @return 初始化后的授权登录对象
|
||||
*
|
||||
* 【使用说明】
|
||||
* 1、支持BundleId与UniversalLink的一一对应,主要目的"是为了解决应用的iPhone版本和iPad HD版本共用同一个AppId,导致同时安装情况下的跳转问题"。
|
||||
* 2、由于手Q版本在 >=8.1.8 后才支持了这种对应方式,所以一旦使用,“务必做到”及时知会用户升级手Q版本。
|
||||
*
|
||||
* 推荐使用初始化方法,并且适配UniversalLink:
|
||||
* - (instancetype)initWithAppId:(NSString *)appId
|
||||
* enableUniveralLink:(BOOL)enabled
|
||||
* universalLink:(NSString *)universalLink
|
||||
* delegate:(id<TencentSessionDelegate>)delegate;
|
||||
*
|
||||
*/
|
||||
- (instancetype)initWithAppId:(NSString *)appId
|
||||
andUniversalLink:(NSString *)universalLink
|
||||
andDelegate:(id<TencentSessionDelegate>)delegate __attribute__((deprecated("此接口即将下线!请使用setupAppId:enableUniveralLink:universalLink:delegate")));
|
||||
|
||||
/**
|
||||
* 初始化TencentOAuth对象(>=3.3.8)
|
||||
* !!!注意:3.5.17版本开始,内部单例实现,多次调用返回同一实例
|
||||
*
|
||||
* @param appId 不可为nil,第三方应用在互联开放平台申请的唯一标识
|
||||
* @param enabled 默认为NO,第三方应用是否将sdk和手机QQ的交互方式切换为UniversalLink方式,启用后则在iOS9及以上的系统都会生效UniversalLink方式;否则,默认仅在iOS13及以上的系统生效UniversalLink方式。
|
||||
* @param universalLink 可以为nil,第三方应用在互联开放平台注册的UniversalLink,和bundleID一一对应(当为nil时,互联平台会按规则生成UniversalLink,详见官网说明)
|
||||
* @param delegate 不可为nil,第三方应用用于接收请求返回结果的委托对象
|
||||
* @return 初始化后的授权登录对象
|
||||
*
|
||||
* @note
|
||||
* 使用说明】
|
||||
* 1、支持sdk与手Q的交互切换为UniversalLink模式,主要目的"是为了避免手Q的UrlScheme被其他应用抢注后,导致sdk接口功能受到影响"。
|
||||
* 2 、由于手Q版本在 >=8.1.3 后才适配了UniversalLink,所以一旦开启了enabled开关,“务必做到”及时知会用户升级手Q版本。
|
||||
*
|
||||
*/
|
||||
- (instancetype)initWithAppId:(NSString *)appId
|
||||
enableUniveralLink:(BOOL)enabled
|
||||
universalLink:(NSString *)universalLink
|
||||
delegate:(id<TencentSessionDelegate>)delegate __attribute__((deprecated("此接口即将下线!请使用setupAppId:enableUniveralLink:universalLink:delegate")));
|
||||
|
||||
/**
|
||||
* 设置用户是否已经授权同意授权隐私协议,在主体应用中,用户同意授权隐私协议后再初始化互联SDK,默认未同意授权
|
||||
* 注意:如未同意授权隐私协议,则互联SDK的所有功能都无法使用,包括初始化!!!
|
||||
* 从3.5.7版本开始支持该方法
|
||||
*
|
||||
* @param isAgreedAuthorization 是否已经授权,isAgreedAuthorization=YES, 表示已经同意授权;isAgreedAuthorization=NO,表示未同意授权,互联SDK的所有功能都无法使用
|
||||
*/
|
||||
+ (void)setIsUserAgreedAuthorization:(BOOL)isUserAgreedAuthorization;
|
||||
|
||||
/**
|
||||
* 获取当前用户是否已经同意授权隐私协议
|
||||
* 从3.5.7版本开始支持该方法
|
||||
*/
|
||||
+ (BOOL)isUserAgreedAuthorization;
|
||||
|
||||
/**
|
||||
* 判断用户手机上是否安装手机QQ
|
||||
* @return YES:安装 NO:没安装
|
||||
*
|
||||
* @note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。
|
||||
* 只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。
|
||||
* 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
|
||||
*/
|
||||
+ (BOOL)iphoneQQInstalled;
|
||||
|
||||
/**
|
||||
* 判断用户手机上是否安装手机TIM
|
||||
* @return YES:安装 NO:没安装
|
||||
*
|
||||
* @note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。
|
||||
* 只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。
|
||||
* 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
|
||||
*/
|
||||
+ (BOOL)iphoneTIMInstalled;
|
||||
|
||||
/**
|
||||
* 发起授权登录,如果安装了QQ APP,则拉起QQ发起授权登录;如果为安装QQ APP则在第三方应用进入H5页面,输入账密授权登录。
|
||||
*
|
||||
* @param permissions 授权信息列
|
||||
* @return 发起调用是否成功
|
||||
*/
|
||||
- (BOOL)authorize:(NSArray *)permissions;
|
||||
|
||||
/**
|
||||
* 发起授权登录, 在第三方应用进入H5页面,显示二维码,通过扫码完成授权登录
|
||||
*
|
||||
* @param permissions 授权信息列
|
||||
* @return 发起调用结果
|
||||
*/
|
||||
- (BOOL)authorizeWithQRlogin:(NSArray *)permissions;
|
||||
|
||||
/**
|
||||
* 增量授权,因用户没有授予相应接口调用的权限,需要用户确认是否授权
|
||||
* @param permissions 需增量授权的信息列表
|
||||
* @return 发起增量授权调用是否成功
|
||||
*/
|
||||
- (BOOL)incrAuthWithPermissions:(NSArray *)permissions;
|
||||
|
||||
/**
|
||||
* 重新授权,因token废除或失效导致接口调用失败,需用户重新授权
|
||||
* @param permissions 授权信息列表,同登录授权
|
||||
* @return 重新授权调用是否成功
|
||||
*/
|
||||
- (BOOL)reauthorizeWithPermissions:(NSArray *)permissions;
|
||||
|
||||
/**
|
||||
* 获取UnindID,可以根据UnindID的比较来确定OpenID是否属于同一个用户
|
||||
* 通过代理方法:- (void)didGetUnionID;返回请求结果。
|
||||
*
|
||||
* @return NO未登录,信息不足;YES条件满足,发送请求成功,请等待回调
|
||||
*/
|
||||
- (BOOL)RequestUnionId;
|
||||
|
||||
/**
|
||||
* (静态方法)处理应用拉起协议
|
||||
* @param url 处理被其他应用呼起时的逻辑
|
||||
* @return 处理结果,YES表示成功,NO表示失败
|
||||
*/
|
||||
+ (BOOL)HandleOpenURL:(NSURL *)url;
|
||||
|
||||
/**
|
||||
* (静态方法)sdk是否可以处理应用拉起协议
|
||||
* @param url 处理被其他应用呼起时的逻辑
|
||||
* @return 处理结果,YES表示可以 NO表示不行
|
||||
*/
|
||||
+ (BOOL)CanHandleOpenURL:(NSURL *)url;
|
||||
|
||||
/**
|
||||
* (静态方法)处理应用的UniversalLink拉起协议
|
||||
* @param url 处理被其他应用呼起时的逻辑
|
||||
* @return 处理结果,YES表示成功,NO表示失败
|
||||
*/
|
||||
+ (BOOL)HandleUniversalLink:(NSURL *)url;
|
||||
|
||||
/**
|
||||
* (静态方法)sdk是否可以处理应用的Universallink拉起协议
|
||||
* @param url 处理被其他应用呼起时的逻辑(应用的Universallink链接须满足官网注册时的格式要求)
|
||||
* @return 处理结果,YES表示可以 NO表示不行
|
||||
* 注:在调用其他Universallink相关处理接口之前,均需进行此项判断
|
||||
*/
|
||||
+ (BOOL)CanHandleUniversalLink:(NSURL *)url;
|
||||
|
||||
/**
|
||||
* (静态方法)获取TencentOAuth调用的上一次错误信息
|
||||
*/
|
||||
+ (NSString *)getLastErrorMsg;
|
||||
|
||||
/**
|
||||
* 以Server Side Code模式授权登录时,通过此接口获取返回的code值;
|
||||
* 以Client Side Token模式授权登录时,忽略此接口。
|
||||
*/
|
||||
- (NSString *)getServerSideCode;
|
||||
|
||||
/**
|
||||
* 退出登录(退出登录后,TecentOAuth失效,需要重新初始化)
|
||||
* @param delegate 第三方应用用于接收请求返回结果的委托对象
|
||||
*/
|
||||
- (void)logout:(id<TencentSessionDelegate>)delegate;
|
||||
|
||||
/**
|
||||
* 判断登录态是否有效
|
||||
* @return 处理结果,YES表示有效,NO表示无效,请用户重新登录授权
|
||||
*/
|
||||
- (BOOL)isSessionValid;
|
||||
|
||||
/**
|
||||
* 获取用户个人信息
|
||||
* @return 处理结果,YES表示API调用成功,NO表示API调用失败,登录态失败,重新登录
|
||||
*/
|
||||
- (BOOL)getUserInfo;
|
||||
|
||||
/**
|
||||
* 退出指定API调用
|
||||
* @param userData 用户调用某条API的时候传入的保留参数
|
||||
* @return 处理结果,YES表示成功 NO表示失败
|
||||
*/
|
||||
- (BOOL)cancel:(id)userData;
|
||||
|
||||
/**
|
||||
* CGI类任务创建接口
|
||||
* @param apiURL CGI请求的URL地址
|
||||
* @param method CGI请求方式:"GET","POST"
|
||||
* @param params CGI请求参数字典
|
||||
* @param callback CGI请求结果的回调接口对象
|
||||
* @return CGI请求任务实例,用于取消任务,返回nil代表任务创建失败
|
||||
*/
|
||||
- (TCAPIRequest *)cgiRequestWithURL:(NSURL *)apiURL method:(NSString *)method params:(NSDictionary *)params callback:(id<TCAPIRequestDelegate>)callback;
|
||||
|
||||
/**
|
||||
* TencentOpenApi发送任务统一接口
|
||||
* @param request 请求发送的任务
|
||||
* @param callback 任务发送后的回调地址
|
||||
*/
|
||||
- (BOOL)sendAPIRequest:(TCAPIRequest *)request callback:(id<TCAPIRequestDelegate>)callback;
|
||||
|
||||
/**
|
||||
* 获得用户的openId,仅有内存缓存
|
||||
* @return 返回openId
|
||||
*/
|
||||
- (NSString *)getUserOpenID;
|
||||
|
||||
/**
|
||||
* 获取appSignToken
|
||||
* @return 返回appSignToken
|
||||
*/
|
||||
+ (NSString *)getAppSignToken;
|
||||
|
||||
/**
|
||||
* 设置appSignToken,跨进程的应用可以通过该方法手动设置appSignToken
|
||||
*/
|
||||
+ (void)setupAppSignToken:(NSString *)appSignToken;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - TencentLoginDelegate(授权登录回调协议)
|
||||
|
||||
/**
|
||||
* \brief TencentLoginDelegate iOS Open SDK 1.3 API回调协议
|
||||
*
|
||||
* 第三方应用实现登录的回调协议
|
||||
*/
|
||||
@protocol TencentLoginDelegate <NSObject>
|
||||
|
||||
@required
|
||||
|
||||
/**
|
||||
* 登录成功后的回调
|
||||
*/
|
||||
- (void)tencentDidLogin;
|
||||
|
||||
/**
|
||||
* 登录失败后的回调
|
||||
* \param cancelled 代表用户是否主动退出登录
|
||||
*/
|
||||
- (void)tencentDidNotLogin:(BOOL)cancelled;
|
||||
|
||||
/**
|
||||
* 登录时网络有问题的回调
|
||||
*/
|
||||
- (void)tencentDidNotNetWork;
|
||||
|
||||
@optional
|
||||
/**
|
||||
* 登录时权限信息的获得
|
||||
*/
|
||||
- (NSArray *)getAuthorizedPermissions:(NSArray *)permissions withExtraParams:(NSDictionary *)extraParams __attribute__((deprecated("该接口已过期, 建议删除调用")));
|
||||
|
||||
/**
|
||||
* unionID获得
|
||||
*/
|
||||
- (void)didGetUnionID;
|
||||
|
||||
/**
|
||||
* 强制网页登录,包括账号密码登录和二维码登录
|
||||
* return YES时,就算本地有手Q也会打开web界面
|
||||
*/
|
||||
- (BOOL)forceWebLogin;
|
||||
|
||||
/* 获得appSignToken回调 */
|
||||
- (void)tencentDidGetAppSignToken:(NSString *)appSignToken;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - TencentSessionDelegate(开放接口回调协议)
|
||||
|
||||
/**
|
||||
* \brief TencentSessionDelegate iOS Open SDK 1.3 API回调协议
|
||||
*
|
||||
* 第三方应用需要实现每条需要调用的API的回调协议
|
||||
*/
|
||||
@protocol TencentSessionDelegate <NSObject, TencentLoginDelegate, TencentWebViewDelegate>
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* 退出登录的回调
|
||||
*/
|
||||
- (void)tencentDidLogout;
|
||||
|
||||
/**
|
||||
* 因用户未授予相应权限而需要执行增量授权。在用户调用某个api接口时,如果服务器返回操作未被授权,则触发该回调协议接口,由第三方决定是否跳转到增量授权页面,让用户重新授权。
|
||||
* @param tencentOAuth 登录授权对象。
|
||||
* @param permissions 需增量授权的权限列表。
|
||||
* @return 是否仍然回调返回原始的api请求结果。
|
||||
* @note 不实现该协议接口则默认为不开启增量授权流程。若需要增量授权请调用\ref TencentOAuth#incrAuthWithPermissions: \n注意:增量授权时用户可能会修改登录的帐号
|
||||
*/
|
||||
- (BOOL)tencentNeedPerformIncrAuth:(TencentOAuth *)tencentOAuth withPermissions:(NSArray *)permissions;
|
||||
|
||||
/**
|
||||
* [该逻辑未实现]因token失效而需要执行重新登录授权。在用户调用某个api接口时,如果服务器返回token失效,则触发该回调协议接口,由第三方决定是否跳转到登录授权页面,让用户重新授权。
|
||||
* @param tencentOAuth 登录授权对象。
|
||||
* @return 是否仍然回调返回原始的api请求结果。
|
||||
* @note 不实现该协议接口则默认为不开启重新登录授权流程。若需要重新登录授权请调用\ref TencentOAuth#reauthorizeWithPermissions: \n注意:重新登录授权时用户可能会修改登录的帐号
|
||||
*/
|
||||
- (BOOL)tencentNeedPerformReAuth:(TencentOAuth *)tencentOAuth;
|
||||
|
||||
/**
|
||||
* 用户通过增量授权流程重新授权登录,token及有效期限等信息已被更新。
|
||||
* @param tencentOAuth token及有效期限等信息更新后的授权实例对象
|
||||
* @note 第三方应用需更新已保存的token及有效期限等信息。
|
||||
*/
|
||||
- (void)tencentDidUpdate:(TencentOAuth *)tencentOAuth;
|
||||
|
||||
/**
|
||||
* 用户增量授权过程中因取消或网络问题导致授权失败
|
||||
* @param reason 授权失败原因,具体失败原因参见sdkdef.h文件中\ref UpdateFailType
|
||||
*/
|
||||
- (void)tencentFailedUpdate:(UpdateFailType)reason;
|
||||
|
||||
/**
|
||||
* 获取用户个人信息回调
|
||||
* @param response API返回结果,具体定义参见sdkdef.h文件中\ref APIResponse
|
||||
* @remarks 正确返回示例: \snippet example/getUserInfoResponse.exp success
|
||||
* 错误返回示例: \snippet example/getUserInfoResponse.exp fail
|
||||
*/
|
||||
- (void)getUserInfoResponse:(APIResponse*) response;
|
||||
|
||||
/**
|
||||
* 社交API统一回调接口
|
||||
* @param response API返回结果,具体定义参见sdkdef.h文件中\ref APIResponse
|
||||
* @param message 响应的消息,目前支持‘SendStory’,‘AppInvitation’,‘AppChallenge’,‘AppGiftRequest’
|
||||
*/
|
||||
- (void)responseDidReceived:(APIResponse*)response forMessage:(NSString *)message;
|
||||
|
||||
/**
|
||||
* post请求的上传进度
|
||||
* @param tencentOAuth 返回回调的tencentOAuth对象
|
||||
* @param bytesWritten 本次回调上传的数据字节数
|
||||
* @param totalBytesWritten 总共已经上传的字节数
|
||||
* @param totalBytesExpectedToWrite 总共需要上传的字节数
|
||||
* @param userData 用户自定义数据
|
||||
*/
|
||||
- (void)tencentOAuth:(TencentOAuth *)tencentOAuth didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite userData:(id)userData;
|
||||
|
||||
|
||||
/**
|
||||
* 通知第三方界面需要被关闭
|
||||
* @param tencentOAuth 返回回调的tencentOAuth对象
|
||||
* @param viewController 需要关闭的viewController
|
||||
*/
|
||||
- (void)tencentOAuth:(TencentOAuth *)tencentOAuth doCloseViewController:(UIViewController *)viewController;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - TencentWebViewDelegate(H5登录webview旋转方向回调)
|
||||
|
||||
/**
|
||||
* @brief TencentWebViewDelegate: H5登录webview旋转方向回调协议
|
||||
*
|
||||
* 第三方应用可以根据自己APP的旋转方向限制,通过此协议设置
|
||||
*/
|
||||
@protocol TencentWebViewDelegate <NSObject>
|
||||
@optional
|
||||
- (BOOL) tencentWebViewShouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation;
|
||||
- (NSUInteger) tencentWebViewSupportedInterfaceOrientationsWithWebkit;
|
||||
- (BOOL) tencentWebViewShouldAutorotateWithWebkit;
|
||||
@end
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// TencentOpenApiUmbrellaHeader.h
|
||||
// TencentOpenApi_IOS
|
||||
//
|
||||
// Created by jyukeizhang(张储祺) on 2020/7/27.
|
||||
// Copyright © 2020 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef TencentOpenApiUmbrellaHeader_h
|
||||
#define TencentOpenApiUmbrellaHeader_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
FOUNDATION_EXPORT double StaticLibraryModuleVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char StaticLibraryModuleVersionString[];
|
||||
|
||||
#import "QQApiInterface.h"
|
||||
#import "QQApiInterfaceObject.h"
|
||||
#import "SDKDef.h"
|
||||
#import "TencentOAuth.h"
|
||||
|
||||
|
||||
#endif /* TencentOpenApiUmbrellaHeader_h */
|
||||
|
||||
@@ -0,0 +1,420 @@
|
||||
///
|
||||
/// \file sdkdef.h
|
||||
/// \brief SDK中相关常量定义
|
||||
///
|
||||
/// Created by Tencent on 12-12-25.
|
||||
/// Copyright (c) 2012年 Tencent. All rights reserved.
|
||||
///
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/**
|
||||
* \brief 设置sdk的log等级
|
||||
*/
|
||||
typedef enum {
|
||||
TCOLogLevel_Disabled = -1, // 关闭所有log
|
||||
TCOLogLevel_Error = 0,
|
||||
TCOLogLevel_Warning,
|
||||
TCOLogLevel_Info,
|
||||
TCOLogLevel_Debug,
|
||||
} TCOLogLevel;
|
||||
|
||||
/**
|
||||
* \breif 授权/分享 方式
|
||||
*/
|
||||
typedef enum TencentAuthShareType {
|
||||
AuthShareType_QQ,
|
||||
AuthShareType_TIM,
|
||||
}TencentAuthShareType;
|
||||
|
||||
/**
|
||||
* \brief APIResponse.retCode可能的枚举常量
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
URLREQUEST_SUCCEED = 0, /**< 网络请求成功发送至服务器,并且服务器返回数据格式正确
|
||||
* \note 这里包括所请求业务操作失败的情况,例如没有授权等原因导致
|
||||
*/
|
||||
|
||||
URLREQUEST_FAILED = 1, /**< 网络异常,或服务器返回的数据格式不正确导致无法解析 */
|
||||
} REPONSE_RESULT;
|
||||
|
||||
/**
|
||||
* \brief 增量授权失败原因
|
||||
*
|
||||
* \note 增量授权失败不影响原token的有效性(原token已失效的情况除外)
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
kUpdateFailUnknown = 1, ///< 未知原因
|
||||
kUpdateFailUserCancel, ///< 用户取消
|
||||
kUpdateFailNetwork, ///< 网络问题
|
||||
} UpdateFailType;
|
||||
|
||||
/**
|
||||
* \brief 封装服务器返回的结果
|
||||
*
|
||||
* APIResponse用于封装所有请求的返回结果,包括错误码、错误信息、原始返回数据以及返回数据的json格式字典
|
||||
*/
|
||||
@interface APIResponse : NSObject<NSSecureCoding> {
|
||||
int _detailRetCode;
|
||||
int _retCode;
|
||||
int _seq;
|
||||
NSString *_errorMsg;
|
||||
NSDictionary *_jsonResponse;
|
||||
NSString *_message;
|
||||
id _userData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增的详细错误码\n
|
||||
* detailRetCode主要用于区分不同的错误情况,参见\ref OpenSDKError
|
||||
*/
|
||||
@property (nonatomic, assign) int detailRetCode;
|
||||
|
||||
/**
|
||||
* 网络请求是否成功送达服务器,以及服务器返回的数据格式是否正确\n
|
||||
* retCode具体取值可参考\ref REPONSE_RESULT
|
||||
*/
|
||||
@property (nonatomic, assign) int retCode;
|
||||
|
||||
/**
|
||||
* 网络请求对应的递增序列号,方便内部管理
|
||||
*/
|
||||
@property (nonatomic, assign) int seq;
|
||||
|
||||
/**
|
||||
* 错误提示语
|
||||
*/
|
||||
@property (nonatomic, copy) NSString *errorMsg;
|
||||
|
||||
/**
|
||||
* 服务器返回数据的json格式字典\n
|
||||
* 字典内具体参数的命名和含义请参考\ref api_spec
|
||||
*/
|
||||
@property (nonatomic, copy) NSDictionary *jsonResponse;
|
||||
|
||||
/**
|
||||
* 服务器返回的原始数据字符串
|
||||
*/
|
||||
@property (nonatomic, copy) NSString *message;
|
||||
|
||||
/**
|
||||
* 用户保留数据
|
||||
*/
|
||||
@property (nonatomic, strong) id userData;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
* 用户自定义的保留字段
|
||||
*/
|
||||
FOUNDATION_EXTERN NSString * const PARAM_USER_DATA;
|
||||
|
||||
/**
|
||||
* \name 应用邀请参数字段定义
|
||||
*/
|
||||
///@{
|
||||
|
||||
/** 应用邀请展示图片url的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_APP_ICON;
|
||||
|
||||
/** 应用邀请描述文本的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_APP_DESC;
|
||||
|
||||
/** 应用邀请好友列表的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_APP_INVITED_OPENIDS;
|
||||
|
||||
///@}
|
||||
|
||||
/**
|
||||
* \name sendStory新分享参数字段定义
|
||||
*/
|
||||
///@{
|
||||
|
||||
/** 预填入接受人列表的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_RECEIVER;
|
||||
|
||||
/** 分享feeds标题的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_TITLE;
|
||||
|
||||
/** 分享feeds评论内容的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_COMMENT;
|
||||
|
||||
/** 分享feeds摘要的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_SUMMARY;
|
||||
|
||||
/** 分享feeds展示图片url的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_IMAGE;
|
||||
|
||||
/** 分享feeds跳转链接url的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_URL;
|
||||
|
||||
/** 分享feeds点击操作默认行为的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_ACT;
|
||||
|
||||
///@}
|
||||
|
||||
/**
|
||||
* \name 设置头像参数字段定义
|
||||
*/
|
||||
///@{
|
||||
|
||||
/** 头像图片数据的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_PIC;
|
||||
|
||||
/** 头像图片文件名的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_FILENAME;
|
||||
|
||||
///@}
|
||||
|
||||
/**
|
||||
* \name 服务器返回数据的参数字段定义
|
||||
*/
|
||||
///@{
|
||||
|
||||
/** 服务器返回码的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_RETCODE;
|
||||
|
||||
/** 服务器返回错误信息的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_MESSAGE;
|
||||
|
||||
/** 服务器返回额外数据的key */
|
||||
FOUNDATION_EXTERN NSString * const PARAM_DATA;
|
||||
|
||||
///@}
|
||||
|
||||
/**
|
||||
* \name 错误信息相关常量定义
|
||||
*/
|
||||
///@{
|
||||
|
||||
/** 详细错误信息字典中额外信息的key */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyExtraInfo;
|
||||
|
||||
/** 详细错误信息字典中返回码的key */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyRetCode;
|
||||
|
||||
/** 详细错误信息字典中错误语句的key */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyMsg;
|
||||
|
||||
/** 不支持的接口 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnsupportedAPI;
|
||||
|
||||
/** 操作成功 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSuccess;
|
||||
|
||||
/** 未知错误 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnknown;
|
||||
|
||||
/** 用户取消 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserCancel;
|
||||
|
||||
/** 请重新登录 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgReLogin;
|
||||
|
||||
/** 应用没有操作权限 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgOperationDeny;
|
||||
|
||||
/** 网络异常或没有网络 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgNetwork;
|
||||
|
||||
/** URL格式或协议错误 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgURL;
|
||||
|
||||
/** 解析数据出错 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgDataParse;
|
||||
|
||||
/** 传入参数有误 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgParam;
|
||||
|
||||
/** 连接超时 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgTimeout;
|
||||
|
||||
/** 安全问题 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSecurity;
|
||||
|
||||
/** 文件读写错误 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgIO;
|
||||
|
||||
/** 服务器端错误 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgServer;
|
||||
|
||||
/** 页面错误 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgWebPage;
|
||||
|
||||
/** 设置头像图片过大 */
|
||||
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserHeadPicLarge;
|
||||
|
||||
/** 用户未同意授权隐私协议 */
|
||||
FOUNDATION_EXPORT NSString * const TCOpenSDKErrorMsgUserNotAgreedAuthorization;
|
||||
|
||||
///@}
|
||||
|
||||
/**
|
||||
* \brief SDK新增详细错误常量
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
kOpenSDKInvalid = -1, ///< 无效的错误码
|
||||
kOpenSDKErrorUnsupportedAPI = -2, ///< 不支持的接口
|
||||
|
||||
/**
|
||||
* \name CommonErrorCode
|
||||
* 公共错误码
|
||||
*/
|
||||
///@{
|
||||
kOpenSDKErrorSuccess = 0, ///< 成功
|
||||
kOpenSDKErrorUnknown, ///< 未知错误
|
||||
kOpenSDKErrorUserCancel, ///< 用户取消
|
||||
kOpenSDKErrorReLogin, ///< token无效或用户未授权相应权限需要重新登录
|
||||
kOpenSDKErrorOperationDeny, ///< 第三方应用没有该api操作的权限
|
||||
///@}
|
||||
|
||||
/**
|
||||
* \name NetworkRelatedErrorCode
|
||||
* 网络相关错误码
|
||||
*/
|
||||
///@{
|
||||
kOpenSDKErrorNetwork, ///< 网络错误,网络不通或连接不到服务器
|
||||
kOpenSDKErrorURL, ///< URL格式或协议错误
|
||||
kOpenSDKErrorDataParse, ///< 数据解析错误,服务器返回的数据解析出错
|
||||
kOpenSDKErrorParam, ///< 传入参数错误
|
||||
kOpenSDKErrorConnTimeout, ///< http连接超时
|
||||
kOpenSDKErrorSecurity, ///< 安全问题
|
||||
kOpenSDKErrorIO, ///< 下载和文件IO错误
|
||||
kOpenSDKErrorServer, ///< 服务器端错误
|
||||
///@}
|
||||
|
||||
/**
|
||||
* \name WebViewRelatedError
|
||||
* webview特有错误
|
||||
*/
|
||||
///@{
|
||||
kOpenSDKErrorWebPage, ///< 页面错误
|
||||
///@}
|
||||
|
||||
/**
|
||||
* \name SetUserHeadRelatedErrorCode
|
||||
* 设置头像自定义错误码段
|
||||
*/
|
||||
///@{
|
||||
kOpenSDKErrorUserHeadPicLarge = 0x010000, ///< 图片过大 设置头像自定义错误码
|
||||
///@}
|
||||
} OpenSDKError;
|
||||
|
||||
/**
|
||||
* \name SDK版本(v1.3)支持的授权列表常量
|
||||
*/
|
||||
///@{
|
||||
|
||||
/** 发表一条说说到QQ空间(<b>需要申请权限</b>) */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_TOPIC;
|
||||
|
||||
/** 创建一个QQ空间相册(<b>需要申请权限</b>) */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_ALBUM;
|
||||
|
||||
/** 上传一张照片到QQ空间相册(<b>需要申请权限</b>) */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_UPLOAD_PIC;
|
||||
|
||||
/** 获取用户QQ空间相册列表(<b>需要申请权限</b>) */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_LIST_ALBUM;
|
||||
|
||||
/** 验证是否认证空间粉丝 */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_CHECK_PAGE_FANS;
|
||||
|
||||
/** 获取登录用户自己的详细信息 */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_INFO;
|
||||
|
||||
/** 获取其他用户的详细信息 */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_OTHER_INFO;
|
||||
|
||||
/** 获取会员用户基本信息 */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_INFO;
|
||||
|
||||
/** 获取会员用户详细信息 */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_RICH_INFO;
|
||||
|
||||
/** 获取用户信息 */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_USER_INFO;
|
||||
|
||||
/** 移动端获取用户信息 */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_SIMPLE_USER_INFO;
|
||||
|
||||
/** 移动端获取用户信息 */
|
||||
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ALL;
|
||||
///@}
|
||||
|
||||
|
||||
/**
|
||||
* \name CGI接口相关参数类型定义
|
||||
*/
|
||||
|
||||
/** 必填的字符串类型参数 */
|
||||
typedef NSString *TCRequiredStr;
|
||||
|
||||
/** 必填的UIImage类型参数 */
|
||||
typedef UIImage *TCRequiredImage;
|
||||
|
||||
/** 必填的整型参数 */
|
||||
typedef NSInteger TCRequiredInt;
|
||||
|
||||
/** 必填的数字类型 */
|
||||
typedef NSNumber *TCRequiredNumber;
|
||||
|
||||
/** 必填的NSData参数 */
|
||||
typedef NSData *TCRequiredData;
|
||||
|
||||
/** 可选的字符串类型参数 */
|
||||
typedef NSString *TCOptionalStr;
|
||||
|
||||
/** 可选的UIImage类型参数 */
|
||||
typedef UIImage *TCOptionalImage;
|
||||
|
||||
/** 可选的整型参数 */
|
||||
typedef NSInteger TCOptionalInt;
|
||||
|
||||
/** 可选的数字类型 */
|
||||
typedef NSNumber *TCOptionalNumber;
|
||||
|
||||
/** 可选的不定类型参数 */
|
||||
typedef id TCRequiredId;
|
||||
///@}
|
||||
|
||||
|
||||
/**
|
||||
* \brief CGI请求的参数字典封装辅助基类
|
||||
*
|
||||
* 将相应属性的值以key-value的形式保存到参数字典中
|
||||
*/
|
||||
@interface TCAPIRequest : NSMutableDictionary
|
||||
|
||||
/** CGI请求的URL地址 */
|
||||
@property (nonatomic, readonly) NSURL *apiURL;
|
||||
|
||||
/** CGI请求方式:"GET","POST" */
|
||||
@property (nonatomic, readonly) NSString *method;
|
||||
|
||||
/**
|
||||
* API参数中的保留字段,可以塞入任意字典支持的类型,再调用完成后会带回给调用方
|
||||
*/
|
||||
@property (nonatomic, strong) TCRequiredId paramUserData;
|
||||
|
||||
/**
|
||||
* APIResponse,API的返回结果
|
||||
*/
|
||||
@property (nonatomic, readonly) APIResponse *response;
|
||||
|
||||
/** 取消相应的CGI请求任务 */
|
||||
- (void)cancel;
|
||||
|
||||
@end
|
||||
|
||||
@protocol TCAPIRequestDelegate <NSObject>
|
||||
@optional
|
||||
- (void)cgiRequest:(TCAPIRequest *)request didResponse:(APIResponse *)response;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
module TencentOpenApi{
|
||||
umbrella header "TencentOpenApiUmbrellaHeader.h"
|
||||
export *
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 975 B |
|
After Width: | Height: | Size: 980 B |
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
|
||||
<meta charset="UTF-8">
|
||||
<title>社交渠道</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale = 1.0,user-scalable=no" />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<style>
|
||||
*{margin:0;padding:0}
|
||||
.header{text-align:center;height:44px;background:-webkit-gradient(linear,0 0,0 100%,from(#4c4c4c),to(#313131));border-top:#5b5b5b 1px solid;position:relative;line-height:44px}
|
||||
.header h1{color:#fff;font-weight:bold;font-size:20px}
|
||||
.header a{width:49px;height:29px;background-size:49px 29px;position:absolute;top:7px;display:block;text-indent:-999em;cursor:pointer}
|
||||
.header .back{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEkAAAAsCAYAAAApSpU1AAAFgElEQVR42u2bSWgsRRjH40XEg3rwIujBg6KCp4CH7AnZ933f9z0hK7kk8RCEhCwEMTmID81C8JIVQRJEfGQ5PEwOnpKDh+T0nomCGngolvUrpsaeYWbSnel5ODOv4Jfp/mrpqv9UffV1TyciwqYUGRn5keRjydeS7yWPgogfJOuSVsnrEXYn2ei7ku+Sk5NFZWWlaG1tFd3d3aK/vz9o6OnpES0tLaKoqEjIsfwu6bNToDzJbU1NjRgbGxPDw8NiYGAgqATSDA4OitHRUXXsEGvJDoEqJX8zcxCG2dPR0SHa2tqCkvb2dtHZ2Sl6e3vFyMiIKCwsRKgufwTKlTxFIKYqF2G6Njc3Bz2MA7EQTY7xRvLafQR6X/JHY2OjaoxGm5qaQgrEYlXk5OQgVJtVgV6W/FRRUaEEorFQE8goVFlZGSI9sCrS5wUFBaKrq0s1wmwKZZgMcsz7VgQqyszMVA4apRsaGkKe8vJyRHpoVqA3JL/U1dUpH4TKz0VyFegFybfEDuxmVK6vrw8LHD7JlEjdubm5aktEIGZTuFBaWnq3SLLAB0TUennV1taGFSUlJb5FkpkvcvNHQRw1lbj9CCfMiPQJy4ytngrV1dXPDEKMxcVFsbe390yv605xcbF3kWRGDPdlCMQMqqqq8gsCz5ubG7G7u+tiRwhscHJyIi4uLlQ5nW5vb8XU1JTXOlagntV+O250H3oS6BXJz3h2nBeFeQTiL4hA4lPb6DxCIA6Qh21jY0OJ46kd8km6jhm4BvWs9tlxk+tRpAcsMxw1BYk67eL4+Fh1Wp/TceO5GbRIVupwDepZ7a9HkaShOC0tTTlqBCKYshtCCX28s7Mjrq6uxMLCgld4bGGsTx2SlWsiEvWs9pVbMBeRHFH1NQqyxFhugUYP2FeijKc6+hwBPCXsxjLu7ZghPz//P5F0VJ2Xl6d2MgoQSNnF0NCQmJ+fd6LteiYZ8zQHBwdqsMbyug5Jn5+fn6tlbKzLOXZjGepZ7Td6GEXqycrKUo4agYgP7GR7e9vlW3a3M4i5uTmnnftDnC12b23pc8pgcy9jrOupjBmcIsk/bxFVs8RYh3YLdNcAr6+v1Sfp6OhICXR2dqZEYgb+X0RiqW2mpKSoDAKoQKEHqM8RCBvH6+vrShidODfThhbAvQx2X2XMwC5vXG6vSs5TU1NVABUo9AA5np2dVceTk5POfGaSTswmdllfbQACaBE02mYsg91qfx2Pb112tw8lf+KbiA8CwdbWlhogx3qpcYxQl5eXKm9tbU3BrMJGnrc2YH9/3ymKEey6DOfUs9rf7Oxsj3FSdVxcnIoPAoEeIMd0fHV11WlDsImJCWdZjhEKu7c2zKJFstpfJoy3iPvThIQEFSPYDd8uaXx83GnTQhHhu5fnh8Ll5WUXmxbJynW1SFb760skHpEcJSUlKe9uB319feL09FQNTn+ylDY3N50g1szMjEeor9ui7H0S9az2m2f6vp4CvCl5zI6Hh/eXpaUl1dGVlRV1Pj09LQ4PD112M1+J8rotLRI2s+gvxGq/MzIy7nyelMTjEtTEy/sLnfWV5wv3sgzYyrX5cnxd3xvp6emmHt+OxsbGKi8fjpgVSQWa7Hg4sXCDJyJmfy1RgWZiYqJyZOEEwbWVHydVoMmLWjizcIGNy7RIDqGqoqOjn4tkQqjPYmJilEMLB1g5ll6YcIj0kuRHHDlOLdTBD8vxfnGfl7jekfzGrUuoi8SqkWNtue/rgASaT1GaHSAU4bZMjvEJP7H5895kueSv+Ph45eBCjaioKERqsuMN3HTeeyYqDxVxmEEOgebtfJf7bcmu5B9CBJw6syvYoN8OcX7lPwMiApFkw+9JhiRfSb5h6wwyvpQ0evJB/wIFDhaTvqlwnwAAAABJRU5ErkJggg==);left:5px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container" id="container">
|
||||
<div class="header">
|
||||
<h1 id="title"></h1>
|
||||
<a href="javascript:void(0)" class="back" id="cancel">返回</a>
|
||||
<a href="javascript:void(0)" style="display:none" id="confirm"></a>
|
||||
</div>
|
||||
<div id="main"></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
try {
|
||||
document.domain = "qq.com";
|
||||
} catch (_) {
|
||||
alert("invalid domain");
|
||||
}
|
||||
function getParam(name) {
|
||||
var re = new RegExp("(?:\\?|#|&)" + name + "=([^&]*)(?:$|&|#)", "i"), m = re.exec(window.location.href);
|
||||
var ret = m ? m[1] : "";
|
||||
ret = ret.replace(/[+]/ig, "%20");
|
||||
return ret;
|
||||
}
|
||||
var iface = getParam("iface");
|
||||
switch (iface) {
|
||||
case "AppChallenge":
|
||||
case "AppInvitation":
|
||||
case "AppGiftRequest":
|
||||
case "SendStory": {
|
||||
var appid = getParam("oauth_consumer_key"), type = getParam("type");
|
||||
var url = "tencent" + appid + "://" + iface + "/cancel";
|
||||
document.getElementById("cancel").addEventListener("click", function () {
|
||||
setTimeout(function () {
|
||||
window.open(url, "_self");
|
||||
}, 0);
|
||||
}, false);
|
||||
document.getElementById("title").innerHTML = {
|
||||
AppChallenge : type == "pk" ? "发送挑战" : "发送炫耀",
|
||||
AppInvitation : "发送应用邀请",
|
||||
AppGiftRequest : type == "request" ? "发送应用请求" : "发送免费礼物",
|
||||
SendStory : "发送分享"
|
||||
}[iface];
|
||||
document.write('<script type="text/javascript" src="https://qzonestyle.gtimg.cn/open/mobile/' + {
|
||||
AppChallenge : "brag_ios/js/sdk_brag.js",
|
||||
AppInvitation : "invite_ios/js/sdk_invite.js",
|
||||
AppGiftRequest : "request_ios/js/sdk_request.js",
|
||||
SendStory : "story_ios/js/sdk_story.js"
|
||||
}[iface] + '"><\/script>');
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "widget_fav_click.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "widget_fav_click 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "widget_fav_click 2.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "widget_fav_unclick.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "widget_fav_unclick 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "widget_fav_unclick 2.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "player_play_next.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true,
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg height="14"
|
||||
viewBox="0 0 16 14"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m13.7695312 0h.7421876c.6040624 0 1.09375.4896875 1.09375 1.09375v11.5820312c0 .6040625-.4896876 1.09375-1.09375 1.09375h-.7421876c-.6040624 0-1.09375-.4896875-1.09375-1.09375v-11.5820312c0-.6040625.4896876-1.09375 1.09375-1.09375zm-3.2946484 7.54279297-8.24812499 5.92578123c-.63074218.4531446-1.50941406.3091797-1.96257812-.3215625-.17177985-.2391048-.26417969-.5260941-.26417969-.8205078v-10.9190039c0-.77664062.62958984-1.40625 1.40625-1.40625.27772132 0 .54922472.08223197.78027344.23632813l8.25707026 5.50701171c.5025391.33515625.6382227 1.01425782.3030665 1.51679688-.0729441.10937328-.165009.20469996-.2717774.28140625z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 748 B |
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "player_play_prev.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true,
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<svg height="14"
|
||||
viewBox="0 0 16 14"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="m13.7695312 0h.7421876c.6040624 0 1.09375.4896875 1.09375 1.09375v11.5820312c0 .6040625-.4896876 1.09375-1.09375 1.09375h-.7421876c-.6040624 0-1.09375-.4896875-1.09375-1.09375v-11.5820312c0-.6040625.4896876-1.09375 1.09375-1.09375zm-3.2946484 7.54279297-8.24812499 5.92578123c-.63074218.4531446-1.50941406.3091797-1.96257812-.3215625-.17177985-.2391048-.26417969-.5260941-.26417969-.8205078v-10.9190039c0-.77664062.62958984-1.40625 1.40625-1.40625.27772132 0 .54922472.08223197.78027344.23632813l8.25707026 5.50701171c.5025391.33515625.6382227 1.01425782.3030665 1.51679688-.0729441.10937328-.165009.20469996-.2717774.28140625z"
|
||||
transform="matrix(-1 0 0 1 16 0)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 798 B |
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "cd.png",
|
||||
"idiom": "universal",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"filename": "cd 1.png",
|
||||
"idiom": "universal",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"filename": "cd 2.png",
|
||||
"idiom": "universal",
|
||||
"scale": "3x"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 210 KiB |
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "cd.png",
|
||||
"idiom": "universal",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"filename": "cd 1.png",
|
||||
"idiom": "universal",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"filename": "cd 2.png",
|
||||
"idiom": "universal",
|
||||
"scale": "3x"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 210 KiB |
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "logo.png",
|
||||
"idiom": "universal",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"filename": "logo 1.png",
|
||||
"idiom": "universal",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"filename": "logo 2.png",
|
||||
"idiom": "universal",
|
||||
"scale": "3x"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 29 KiB |
@@ -3,7 +3,7 @@ import Foundation
|
||||
|
||||
@available(iOS 17, *)
|
||||
public struct BackgroundIntent: AppIntent {
|
||||
static public var title: LocalizedStringResource = "LLMP Widget Action"
|
||||
static public var title: LocalizedStringResource = "HomeWidget Background Intent"
|
||||
|
||||
@Parameter(title: "Widget URI")
|
||||
var url: URL?
|
||||
@@ -16,7 +16,7 @@ public struct BackgroundIntent: AppIntent {
|
||||
|
||||
public func perform() async throws -> some IntentResult {
|
||||
NotificationCenter.default.post(
|
||||
name: Notification.Name("fromWidgetToHost"),
|
||||
name: Notification.Name("fromWigetToRunner"),
|
||||
object: nil,
|
||||
userInfo: ["url": url?.absoluteString ?? ""]
|
||||
)
|
||||
@@ -24,6 +24,7 @@ public struct BackgroundIntent: AppIntent {
|
||||
}
|
||||
}
|
||||
|
||||
/// 在宿主进程中继续执行,使小组件点击能通知主 App(对齐 Flutter Runner/BackgroundIntent)。
|
||||
@available(iOS 17, *)
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
extension BackgroundIntent: ForegroundContinuableIntent {}
|
||||
|
||||
@@ -1,48 +1,41 @@
|
||||
//
|
||||
// BackgroundIntent.swift
|
||||
// Runner
|
||||
//
|
||||
// Created by hoshizora-rin on 2024/9/19.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import WidgetKit
|
||||
|
||||
private let widgetGroupId = "group.top.zhushenwudi.llmp"
|
||||
private var lastLyricLine1 = ""
|
||||
private var lastLyricLine2 = ""
|
||||
private let widgetGroupId = "group.com.zhushenwudi.lovelivemusicplayer"
|
||||
|
||||
struct Provider: TimelineProvider {
|
||||
func placeholder(in context: Context) -> ExampleEntry {
|
||||
ExampleEntry(
|
||||
date: Date(),
|
||||
widgetFamily: context.family,
|
||||
songName: "",
|
||||
songArtist: "",
|
||||
isFavorite: false,
|
||||
isPlaying: false,
|
||||
playText: "",
|
||||
lyricLine1: "",
|
||||
lyricLine2: "",
|
||||
currentLine: 1,
|
||||
isShutdown: true,
|
||||
bgColor: "255,255,255"
|
||||
)
|
||||
sampleEntry(family: context.family, isPreview: true)
|
||||
}
|
||||
|
||||
func getSnapshot(in context: Context, completion: @escaping (ExampleEntry) -> Void) {
|
||||
completion(readEntry(family: context.family))
|
||||
// 与 Flutter 一样读 App Group;isPreview 仅用于避免 Button(intent) 把添加页打成鱼骨。
|
||||
completion(readEntry(family: context.family, isPreview: true))
|
||||
}
|
||||
|
||||
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
|
||||
completion(Timeline(entries: [readEntry(family: context.family)], policy: .atEnd))
|
||||
// 数据由主应用写入 App Group 后主动 reload;不要让单条当前时间的
|
||||
// timeline 以 .atEnd 反复申请无意义的系统刷新预算。
|
||||
completion(Timeline(entries: [readEntry(family: context.family, isPreview: false)], policy: .never))
|
||||
}
|
||||
|
||||
private func readEntry(family: WidgetFamily) -> ExampleEntry {
|
||||
private func readEntry(family: WidgetFamily, isPreview: Bool) -> ExampleEntry {
|
||||
let data = UserDefaults(suiteName: widgetGroupId)
|
||||
let songName = data?.string(forKey: "songName") ?? ""
|
||||
let songArtist = data?.string(forKey: "songArtist") ?? ""
|
||||
let isFavorite = data?.bool(forKey: "songFavorite") ?? false
|
||||
let isPlaying = data?.bool(forKey: "isPlaying") ?? false
|
||||
let playText = data?.string(forKey: "playText") ?? "播放中,已暂停"
|
||||
if let line1 = data?.string(forKey: "lyricLine1") { lastLyricLine1 = line1 }
|
||||
if let line2 = data?.string(forKey: "lyricLine2") { lastLyricLine2 = line2 }
|
||||
let currentLine = data?.integer(forKey: "currentLine") ?? 1
|
||||
let playText = data?.string(forKey: "playText") ?? ""
|
||||
let isShutdown = data?.bool(forKey: "isShutdown") ?? true
|
||||
let bgColor = data?.string(forKey: "bgColor") ?? "255,255,255"
|
||||
|
||||
return ExampleEntry(
|
||||
date: Date(),
|
||||
widgetFamily: family,
|
||||
@@ -51,15 +44,28 @@ struct Provider: TimelineProvider {
|
||||
isFavorite: isFavorite,
|
||||
isPlaying: isPlaying,
|
||||
playText: playText,
|
||||
lyricLine1: lastLyricLine1,
|
||||
lyricLine2: lastLyricLine2,
|
||||
currentLine: currentLine,
|
||||
isShutdown: isShutdown,
|
||||
bgColor: bgColor
|
||||
bgColor: bgColor,
|
||||
isPreview: isPreview
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func sampleEntry(family: WidgetFamily, isPreview: Bool) -> ExampleEntry {
|
||||
ExampleEntry(
|
||||
date: Date(),
|
||||
widgetFamily: family,
|
||||
songName: "",
|
||||
songArtist: "",
|
||||
isFavorite: false,
|
||||
isPlaying: false,
|
||||
playText: "",
|
||||
isShutdown: true,
|
||||
bgColor: "255,255,255",
|
||||
isPreview: isPreview
|
||||
)
|
||||
}
|
||||
|
||||
struct ExampleEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let widgetFamily: WidgetFamily
|
||||
@@ -68,168 +74,303 @@ struct ExampleEntry: TimelineEntry {
|
||||
let isFavorite: Bool
|
||||
let isPlaying: Bool
|
||||
let playText: String
|
||||
let lyricLine1: String
|
||||
let lyricLine2: String
|
||||
let currentLine: Int
|
||||
let isShutdown: Bool
|
||||
let bgColor: String
|
||||
let isPreview: Bool
|
||||
}
|
||||
|
||||
struct HomeWidgetExampleEntryView: View {
|
||||
var entry: Provider.Entry
|
||||
let isWhiteBackground: Bool
|
||||
var isWhiteBackground = true
|
||||
private let actionButtonSize: CGFloat = 34
|
||||
private let transportIconSize: CGFloat = 20
|
||||
|
||||
init(entry: Provider.Entry, isWhiteBackground: Bool) {
|
||||
self.entry = entry
|
||||
self.isWhiteBackground = isWhiteBackground
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
let canvas = familyCanvasSize
|
||||
let cdSize: CGFloat = 140
|
||||
let coverSize = cdSize * 0.55
|
||||
let coverAndCdDiffSize = (cdSize - coverSize) / 2
|
||||
let playButtonAndCdDiffSize = (cdSize - 34) / 2
|
||||
let offsetX = calcCdOffsetX(entry: entry, geometry: geometry)
|
||||
let offsetY = calcCdOffsetY(entry: entry, geometry: geometry)
|
||||
let lyricMaxWidth = geometry.size.width - cdSize
|
||||
let coverSize: CGFloat = cdSize * 0.55
|
||||
let coverAndCdDiffSize: CGFloat = (cdSize - coverSize) / 2
|
||||
let playButtonAndCdDiffSize: CGFloat = (cdSize - 34) / 2 // width:18 + padding:8x2
|
||||
let offsetX = calcCdOffsetX(entry: entry, canvas: canvas)
|
||||
let offsetY = calcCdOffsetY(entry: entry, canvas: canvas)
|
||||
let bgColor = calcBgColor(entry: entry)
|
||||
let playText = calcPlayText(entry: entry)
|
||||
|
||||
|
||||
ZStack {
|
||||
// 填充整个布局颜色
|
||||
if isWhiteBackground {
|
||||
Rectangle()
|
||||
.fill(
|
||||
AngularGradient(
|
||||
gradient: Gradient(colors: [
|
||||
Color(red: 229/255, green: 233/255, blue: 235/255),
|
||||
Color(red: 219/255, green: 223/255, blue: 225/255),
|
||||
bgColor,
|
||||
]),
|
||||
gradient: Gradient(
|
||||
colors: [
|
||||
Color(red: 229/255, green: 233/255, blue: 235/255),
|
||||
Color(red: 219/255, green: 223/255, blue: 225/255),
|
||||
bgColor
|
||||
]
|
||||
),
|
||||
center: .topLeading,
|
||||
angle: .degrees(225)
|
||||
angle: .degrees(180 + 45)
|
||||
)
|
||||
)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
} else {
|
||||
Rectangle()
|
||||
.fill(Color(red: 49/255, green: 49/255, blue: 49/255))
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 绘制右上角唱片
|
||||
ZStack(alignment: .topTrailing) {
|
||||
Image(isWhiteBackground ? "cd_white" : "cd_black")
|
||||
.resizable()
|
||||
.interpolation(.high)
|
||||
.scaledToFit()
|
||||
.frame(width: cdSize, height: cdSize)
|
||||
.clipped()
|
||||
.offset(x: offsetX, y: offsetY)
|
||||
|
||||
|
||||
if let image = loadImageFromFile() {
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.interpolation(.high)
|
||||
.scaledToFit()
|
||||
.frame(width: coverSize, height: coverSize)
|
||||
.clipShape(Circle())
|
||||
.clipped()
|
||||
.offset(x: -coverAndCdDiffSize + offsetX, y: coverAndCdDiffSize + offsetY)
|
||||
}
|
||||
|
||||
if #available(iOSApplicationExtension 17, *) {
|
||||
Button(intent: BackgroundIntent(url: URL(string: "homeWidgetExample://toggle_play"))) {
|
||||
Image(systemName: entry.isPlaying ? "pause.fill" : "play.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 18, height: 18)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(8)
|
||||
.offset(x: -playButtonAndCdDiffSize + offsetX, y: playButtonAndCdDiffSize + offsetY)
|
||||
}
|
||||
|
||||
playButton(
|
||||
offsetX: -playButtonAndCdDiffSize + offsetX,
|
||||
offsetY: playButtonAndCdDiffSize + offsetY
|
||||
)
|
||||
}
|
||||
|
||||
.contentTransition(.interpolate)
|
||||
.animation(.smooth(duration: 0.45), value: entry.isPlaying)
|
||||
|
||||
// 绘制左侧一列歌曲信息
|
||||
VStack(alignment: .leading) {
|
||||
Image("logo")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 30, height: 30)
|
||||
.clipped()
|
||||
.padding(.bottom, 5)
|
||||
|
||||
if entry.widgetFamily == .systemMedium {
|
||||
Text(entry.lyricLine1)
|
||||
.font(.system(size: 15))
|
||||
.foregroundColor(lyricColor(for: entry.currentLine, active: 1))
|
||||
.frame(maxWidth: entry.isPlaying ? .infinity : lyricMaxWidth, alignment: .leading)
|
||||
Text(entry.lyricLine2)
|
||||
.font(.system(size: 15))
|
||||
.foregroundColor(lyricColor(for: entry.currentLine, active: 2))
|
||||
.frame(maxWidth: entry.isPlaying ? .infinity : lyricMaxWidth, alignment: .leading)
|
||||
}
|
||||
|
||||
|
||||
Spacer()
|
||||
Text(playText).font(.system(size: 10)).bold().foregroundColor(.gray)
|
||||
Text(entry.songName).font(.system(size: 12)).bold()
|
||||
.foregroundColor(isWhiteBackground ? .black : .white)
|
||||
Text(entry.songArtist).font(.system(size: 12, weight: .light)).foregroundColor(.gray)
|
||||
|
||||
Text(playText)
|
||||
.font(.system(size: 10))
|
||||
.bold()
|
||||
.foregroundColor(.gray)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.trailing, actionReservedWidth)
|
||||
.padding(.bottom, 3)
|
||||
|
||||
Text(entry.songName)
|
||||
.font(.system(size: 12))
|
||||
.bold()
|
||||
.foregroundColor(lyricForeground(isPrimary: true))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.trailing, actionReservedWidth)
|
||||
.padding(.bottom, 2)
|
||||
|
||||
Text(entry.songArtist)
|
||||
.font(.system(size: 12, weight: .light))
|
||||
.foregroundColor(.gray)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.trailing, actionReservedWidth)
|
||||
|
||||
}
|
||||
.padding()
|
||||
|
||||
|
||||
VStack {
|
||||
Spacer()
|
||||
if #available(iOSApplicationExtension 17, *) {
|
||||
Button(intent: BackgroundIntent(url: URL(string: "homeWidgetExample://toggle_love"))) {
|
||||
Image(entry.isFavorite ? "FavoriteClick" : "FavoriteUnClick")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 34, height: 34)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.frame(maxWidth: .infinity, alignment: .bottomTrailing)
|
||||
.padding(8)
|
||||
}
|
||||
}
|
||||
|
||||
if entry.isShutdown {
|
||||
Link(destination: URL(string: "llmp://")!) { Color.clear }
|
||||
favoriteButton()
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.widgetURL(shutdownOpenURL)
|
||||
}
|
||||
|
||||
/// 关机态点卡片打开 App;用 widgetURL 而不是全屏 Link,避免桌面白卡渲染成鱼骨。
|
||||
var shutdownOpenURL: URL? {
|
||||
if entry.isShutdown && !entry.isPreview {
|
||||
return URL(string: "llmp://")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var familyCanvasSize: CGSize {
|
||||
switch entry.widgetFamily {
|
||||
case .systemMedium:
|
||||
return CGSize(width: 338, height: 158)
|
||||
default:
|
||||
return CGSize(width: 158, height: 158)
|
||||
}
|
||||
}
|
||||
|
||||
private func lyricColor(for currentLine: Int, active: Int) -> Color {
|
||||
let highlighted = currentLine == -1 || currentLine == active
|
||||
if highlighted {
|
||||
return isWhiteBackground ? .black : .white
|
||||
var actionReservedWidth: CGFloat {
|
||||
entry.widgetFamily == .systemMedium ? 110 : 72
|
||||
}
|
||||
|
||||
/// 黑卡必须用明确 RGB,系统 `.white` 在 Widget 预览里可能被折成浅色叠在白底上,歌词会消失。
|
||||
func lyricForeground(isPrimary: Bool) -> Color {
|
||||
if isPrimary {
|
||||
return isWhiteBackground
|
||||
? Color(red: 0, green: 0, blue: 0)
|
||||
: Color(red: 1, green: 1, blue: 1)
|
||||
}
|
||||
return Color(red: 0.7, green: 0.7, blue: 0.7, opacity: 0.7)
|
||||
}
|
||||
|
||||
func calcCdOffsetX(entry: ExampleEntry, geometry: GeometryProxy) -> CGFloat {
|
||||
func calcCdOffsetX(entry: ExampleEntry, canvas: CGSize) -> CGFloat {
|
||||
var offset = 0.0
|
||||
switch entry.widgetFamily {
|
||||
case .systemSmall: return (geometry.size.width - 70) / 2
|
||||
case .systemMedium: return (geometry.size.width - 152) / 2 + (entry.isPlaying ? 40 : 0)
|
||||
default: return 0
|
||||
case .systemSmall:
|
||||
offset = (canvas.width - 70) / 2
|
||||
case .systemMedium:
|
||||
offset = (canvas.width - 152) / 2 + (entry.isPlaying ? 55 : 15)
|
||||
default:
|
||||
break
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
func calcCdOffsetY(entry: ExampleEntry, geometry: GeometryProxy) -> CGFloat {
|
||||
func calcCdOffsetY(entry: ExampleEntry, canvas: CGSize) -> CGFloat {
|
||||
var offset = 0.0
|
||||
switch entry.widgetFamily {
|
||||
case .systemSmall: return (70 - geometry.size.height) / 2
|
||||
case .systemMedium: return (152 - geometry.size.height) / 2 - (entry.isPlaying ? 40 : 0)
|
||||
default: return 0
|
||||
case .systemSmall:
|
||||
offset = (70 - canvas.height) / 2
|
||||
case .systemMedium:
|
||||
offset = (152 - canvas.height) / 2 - (entry.isPlaying ? 55 : 15)
|
||||
default:
|
||||
break
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
|
||||
func calcBgColor(entry: ExampleEntry) -> Color {
|
||||
let parts = entry.bgColor.split(separator: ",").compactMap { Double($0) }
|
||||
let rgb = parts.count >= 3 ? parts : [230, 215, 210]
|
||||
return Color(red: rgb[0]/255, green: rgb[1]/255, blue: rgb[2]/255)
|
||||
var intColors: [Double] = [230.0, 215.0, 210.0]
|
||||
if entry.bgColor.contains(",") {
|
||||
intColors = entry.bgColor.components(separatedBy: ",").compactMap(Double.init)
|
||||
}
|
||||
return Color(
|
||||
red: Double(intColors[0]) / 255,
|
||||
green: Double(intColors[1]) / 255,
|
||||
blue: Double(intColors[2]) / 255
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
func loadImageFromFile() -> UIImage? {
|
||||
guard let fileURL = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: widgetGroupId)?
|
||||
.appendingPathComponent("sharedImage.png") else { return nil }
|
||||
// 没歌/关机/添加页预览都不该读 App Group 里的旧 sharedImage
|
||||
//(和 Flutter 共用 group,重装后文件还在)。
|
||||
if entry.isPreview || entry.isShutdown || entry.songName.isEmpty {
|
||||
return nil
|
||||
}
|
||||
guard let fileURL = FileManager.default.containerURL(
|
||||
forSecurityApplicationGroupIdentifier: widgetGroupId
|
||||
)?.appendingPathComponent("sharedImage.png") else { return nil }
|
||||
return UIImage(contentsOfFile: fileURL.path)
|
||||
}
|
||||
|
||||
|
||||
func calcPlayText(entry: ExampleEntry) -> String {
|
||||
let parts = entry.playText.split(separator: ",").map(String.init)
|
||||
let playing = parts.first ?? "播放中"
|
||||
let paused = parts.count > 1 ? parts[1] : "已暂停"
|
||||
return entry.isPlaying ? playing : paused
|
||||
var playTextArr: [String] = [WidgetI18n.t("playing"), WidgetI18n.t("paused")]
|
||||
if entry.playText.contains(",") {
|
||||
playTextArr = entry.playText.components(separatedBy: ",")
|
||||
}
|
||||
return entry.isPlaying ? playTextArr[0] : playTextArr[1]
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func transportButton(assetName: String, action: String) -> some View {
|
||||
let icon = Image(assetName)
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.foregroundColor(.white)
|
||||
.frame(width: transportIconSize, height: transportIconSize)
|
||||
.frame(width: actionButtonSize, height: actionButtonSize)
|
||||
if entry.isPreview || entry.isShutdown {
|
||||
icon
|
||||
} else if #available(iOSApplicationExtension 17, *) {
|
||||
Button(
|
||||
intent: BackgroundIntent(
|
||||
url: URL(string: "homeWidgetExample://\(action)")
|
||||
)
|
||||
) {
|
||||
icon
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
} else {
|
||||
icon
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func playButton(offsetX: CGFloat, offsetY: CGFloat) -> some View {
|
||||
let icon = Image(systemName: entry.isPlaying ? "pause.fill" : "play.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 18, height: 18)
|
||||
.foregroundColor(.white)
|
||||
// 预览和关机态都不能挂 App Intent,否则桌面白卡会掉回 placeholder 鱼骨。
|
||||
if entry.isPreview || entry.isShutdown {
|
||||
icon
|
||||
.padding(8)
|
||||
.offset(x: offsetX, y: offsetY)
|
||||
} else if #available(iOSApplicationExtension 17, *) {
|
||||
Button(intent: BackgroundIntent(url: URL(string: "homeWidgetExample://toggle_play"))) {
|
||||
icon
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(8)
|
||||
.offset(x: offsetX, y: offsetY)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func favoriteButton() -> some View {
|
||||
HStack(spacing: 8) {
|
||||
if entry.widgetFamily == .systemMedium {
|
||||
transportButton(
|
||||
assetName: "PlayerPrev",
|
||||
action: "previous"
|
||||
)
|
||||
}
|
||||
transportButton(
|
||||
assetName: "PlayerNext",
|
||||
action: "next"
|
||||
)
|
||||
favoriteControl()
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .bottomTrailing)
|
||||
.padding(8)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func favoriteControl() -> some View {
|
||||
let icon = Image(entry.isFavorite ? "FavoriteClick" : "FavoriteUnClick")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: actionButtonSize, height: actionButtonSize)
|
||||
if entry.isPreview || entry.isShutdown {
|
||||
icon
|
||||
} else if #available(iOSApplicationExtension 17, *) {
|
||||
Button(intent: BackgroundIntent(url: URL(string: "homeWidgetExample://toggle_love"))) {
|
||||
icon
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
} else {
|
||||
icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,14 +378,13 @@ struct HomeWidgetExampleWhite: Widget {
|
||||
let kind = "HomeWidgetExampleWhite"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: kind, provider: Provider()) { entry in
|
||||
HomeWidgetExampleEntryView(entry: entry, isWhiteBackground: true)
|
||||
.containerBackground(.fill.tertiary, for: .widget)
|
||||
StaticConfiguration(kind: kind, provider: Provider()) { (entry) in
|
||||
widgetRoot(entry: entry, isWhiteBackground: true)
|
||||
}
|
||||
.configurationDisplayName("LLMP音乐组件")
|
||||
.contentMarginsDisabled()
|
||||
.supportedFamilies([.systemSmall, .systemMedium])
|
||||
.description("LoveLive!媒体播放器")
|
||||
.configurationDisplayName(WidgetI18n.t("widget_white"))
|
||||
.contentMarginsDisabled()
|
||||
.supportedFamilies([.systemSmall, .systemMedium])
|
||||
.description(WidgetI18n.t("widget_desc"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,13 +392,111 @@ struct HomeWidgetExampleBlack: Widget {
|
||||
let kind = "HomeWidgetExampleBlack"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: kind, provider: Provider()) { entry in
|
||||
HomeWidgetExampleEntryView(entry: entry, isWhiteBackground: false)
|
||||
.containerBackground(.fill.tertiary, for: .widget)
|
||||
StaticConfiguration(kind: kind, provider: Provider()) { (entry) in
|
||||
widgetRoot(entry: entry, isWhiteBackground: false)
|
||||
}
|
||||
.configurationDisplayName("LLMP音乐组件")
|
||||
.contentMarginsDisabled()
|
||||
.supportedFamilies([.systemSmall, .systemMedium])
|
||||
.description("LoveLive!媒体播放器")
|
||||
.configurationDisplayName(WidgetI18n.t("widget_black"))
|
||||
.contentMarginsDisabled()
|
||||
.supportedFamilies([.systemSmall, .systemMedium])
|
||||
.description(WidgetI18n.t("widget_desc"))
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func widgetRoot(entry: ExampleEntry, isWhiteBackground: Bool) -> some View {
|
||||
let view = HomeWidgetExampleEntryView(entry: entry, isWhiteBackground: isWhiteBackground)
|
||||
.containerBackground(for: .widget) {
|
||||
widgetContainerFill(entry: entry, isWhiteBackground: isWhiteBackground)
|
||||
}
|
||||
if entry.isPreview {
|
||||
view.unredacted()
|
||||
} else {
|
||||
view
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func widgetContainerFill(entry: ExampleEntry, isWhiteBackground: Bool) -> some View {
|
||||
if isWhiteBackground {
|
||||
Color(red: 229/255, green: 233/255, blue: 235/255)
|
||||
} else {
|
||||
Color(red: 49/255, green: 49/255, blue: 49/255)
|
||||
}
|
||||
}
|
||||
|
||||
private func sampleBgColor(_ bgColor: String) -> Color {
|
||||
var intColors: [Double] = [230.0, 215.0, 210.0]
|
||||
if bgColor.contains(",") {
|
||||
intColors = bgColor.components(separatedBy: ",").compactMap(Double.init)
|
||||
}
|
||||
return Color(
|
||||
red: Double(intColors[0]) / 255,
|
||||
green: Double(intColors[1]) / 255,
|
||||
blue: Double(intColors[2]) / 255
|
||||
)
|
||||
}
|
||||
|
||||
/// Widget 扩展未链接 ComposeApp,按系统语言对齐主工程四套文案。
|
||||
private enum WidgetI18n {
|
||||
static func t(_ key: String) -> String {
|
||||
let table: [String: String]
|
||||
switch Locale.current.language.languageCode?.identifier {
|
||||
case "en":
|
||||
table = [
|
||||
"playing": "Playing",
|
||||
"paused": "Paused",
|
||||
"widget_white": "LLMP Music Widget (White)",
|
||||
"widget_black": "LLMP Music Widget (Black)",
|
||||
"widget_desc": "LoveLive! music player",
|
||||
]
|
||||
case "ja":
|
||||
table = [
|
||||
"playing": "放送中",
|
||||
"paused": "停止しました",
|
||||
"widget_white": "LLMP 音楽ウィジェット (白)",
|
||||
"widget_black": "LLMP 音楽ウィジェット (黒)",
|
||||
"widget_desc": "LoveLive!メディアプレーヤー",
|
||||
]
|
||||
case "de":
|
||||
table = [
|
||||
"playing": "Wird abgespielt",
|
||||
"paused": "Pausiert",
|
||||
"widget_white": "LLMP Musik-Widget (Weiß)",
|
||||
"widget_black": "LLMP Musik-Widget (Schwarz)",
|
||||
"widget_desc": "LoveLive! Musikplayer",
|
||||
]
|
||||
default:
|
||||
table = [
|
||||
"playing": "播放中",
|
||||
"paused": "已暂停",
|
||||
"widget_white": "LLMP 音乐组件 (白)",
|
||||
"widget_black": "LLMP 音乐组件 (黑)",
|
||||
"widget_desc": "LoveLive!媒体播放器",
|
||||
]
|
||||
}
|
||||
return table[key] ?? key
|
||||
}
|
||||
}
|
||||
|
||||
#Preview(as: .systemSmall) {
|
||||
HomeWidgetExampleWhite()
|
||||
} timeline: {
|
||||
ExampleEntry(
|
||||
date: Date(),
|
||||
widgetFamily: .systemSmall,
|
||||
songName: "Song Name",
|
||||
songArtist: "Song Artist",
|
||||
isFavorite: true,
|
||||
isPlaying: true,
|
||||
playText: "playing,paused",
|
||||
isShutdown: true,
|
||||
bgColor: "255,255,255",
|
||||
isPreview: true
|
||||
)
|
||||
}
|
||||
|
||||
#Preview(as: .systemMedium) {
|
||||
HomeWidgetExampleWhite()
|
||||
} timeline: {
|
||||
sampleEntry(family: .systemMedium, isPreview: true)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import SwiftUI
|
||||
//
|
||||
// HomeWidgetExampleBundle.swift
|
||||
// HomeWidgetExample
|
||||
//
|
||||
// Created by hoshizora-rin on 2024/9/19.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct HomeWidgetExampleBundle: WidgetBundle {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// HomeWidgetExampleControl.swift
|
||||
// HomeWidgetExample
|
||||
//
|
||||
// Created by hoshizora-rin on 2024/9/19.
|
||||
//
|
||||
|
||||
import AppIntents
|
||||
import SwiftUI
|
||||
import WidgetKit
|
||||
|
||||
@available(iOS 18.0, *)
|
||||
struct HomeWidgetExampleControl: ControlWidget {
|
||||
static let kind: String = "com.zhushenwudi.lovelivemusicplayer.HomeWidgetExample"
|
||||
|
||||
var body: some ControlWidgetConfiguration {
|
||||
AppIntentControlConfiguration(
|
||||
kind: Self.kind,
|
||||
provider: Provider()
|
||||
) { value in
|
||||
ControlWidgetToggle(
|
||||
"Start Timer",
|
||||
isOn: value.isRunning,
|
||||
action: StartTimerIntent(value.name)
|
||||
) { isRunning in
|
||||
Label(isRunning ? "On" : "Off", systemImage: "timer")
|
||||
}
|
||||
}
|
||||
.displayName("Timer")
|
||||
.description("A an example control that runs a timer.")
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 18.0, *)
|
||||
extension HomeWidgetExampleControl {
|
||||
struct Value {
|
||||
var isRunning: Bool
|
||||
var name: String
|
||||
}
|
||||
|
||||
struct Provider: AppIntentControlValueProvider {
|
||||
func previewValue(configuration: TimerConfiguration) -> Value {
|
||||
HomeWidgetExampleControl.Value(isRunning: false, name: configuration.timerName)
|
||||
}
|
||||
|
||||
func currentValue(configuration: TimerConfiguration) async throws -> Value {
|
||||
let isRunning = true // Check if the timer is running
|
||||
return HomeWidgetExampleControl.Value(isRunning: isRunning, name: configuration.timerName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 18.0, *)
|
||||
struct TimerConfiguration: ControlConfigurationIntent {
|
||||
static let title: LocalizedStringResource = "Timer Name Configuration"
|
||||
|
||||
@Parameter(title: "Timer Name", default: "Timer")
|
||||
var timerName: String
|
||||
}
|
||||
|
||||
@available(iOS 18.0, *)
|
||||
struct StartTimerIntent: SetValueIntent {
|
||||
static let title: LocalizedStringResource = "Start a timer"
|
||||
|
||||
@Parameter(title: "Timer Name")
|
||||
var name: String
|
||||
|
||||
@Parameter(title: "Timer is running")
|
||||
var value: Bool
|
||||
|
||||
init() {}
|
||||
|
||||
init(_ name: String) {
|
||||
self.name = name
|
||||
}
|
||||
|
||||
func perform() async throws -> some IntentResult {
|
||||
// Start the timer…
|
||||
return .result()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// HomeWidgetExampleLiveActivity.swift
|
||||
// HomeWidgetExample
|
||||
//
|
||||
// Created by hoshizora-rin on 2024/9/19.
|
||||
//
|
||||
|
||||
import ActivityKit
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct HomeWidgetExampleAttributes: ActivityAttributes {
|
||||
public struct ContentState: Codable, Hashable {
|
||||
// Dynamic stateful properties about your activity go here!
|
||||
var emoji: String
|
||||
}
|
||||
|
||||
// Fixed non-changing properties about your activity go here!
|
||||
var name: String
|
||||
}
|
||||
|
||||
@available(iOS 16.2, *)
|
||||
struct HomeWidgetExampleLiveActivity: Widget {
|
||||
var body: some WidgetConfiguration {
|
||||
ActivityConfiguration(for: HomeWidgetExampleAttributes.self) { context in
|
||||
// Lock screen/banner UI goes here
|
||||
VStack {
|
||||
Text("Hello \(context.state.emoji)")
|
||||
}
|
||||
.activityBackgroundTint(Color.cyan)
|
||||
.activitySystemActionForegroundColor(Color.black)
|
||||
|
||||
} dynamicIsland: { context in
|
||||
DynamicIsland {
|
||||
// Expanded UI goes here. Compose the expanded UI through
|
||||
// various regions, like leading/trailing/center/bottom
|
||||
DynamicIslandExpandedRegion(.leading) {
|
||||
Text("Leading")
|
||||
}
|
||||
DynamicIslandExpandedRegion(.trailing) {
|
||||
Text("Trailing")
|
||||
}
|
||||
DynamicIslandExpandedRegion(.bottom) {
|
||||
Text("Bottom \(context.state.emoji)")
|
||||
// more content
|
||||
}
|
||||
} compactLeading: {
|
||||
Text("L")
|
||||
} compactTrailing: {
|
||||
Text("T \(context.state.emoji)")
|
||||
} minimal: {
|
||||
Text(context.state.emoji)
|
||||
}
|
||||
.widgetURL(URL(string: "http://www.apple.com"))
|
||||
.keylineTint(Color.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension HomeWidgetExampleAttributes {
|
||||
fileprivate static var preview: HomeWidgetExampleAttributes {
|
||||
HomeWidgetExampleAttributes(name: "World")
|
||||
}
|
||||
}
|
||||
|
||||
extension HomeWidgetExampleAttributes.ContentState {
|
||||
fileprivate static var smiley: HomeWidgetExampleAttributes.ContentState {
|
||||
HomeWidgetExampleAttributes.ContentState(emoji: "😀")
|
||||
}
|
||||
|
||||
fileprivate static var starEyes: HomeWidgetExampleAttributes.ContentState {
|
||||
HomeWidgetExampleAttributes.ContentState(emoji: "🤩")
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,24 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>LLMP</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.2.0</string>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.top.zhushenwudi.llmp</string>
|
||||
<string>group.com.zhushenwudi.lovelivemusicplayer</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,867 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
02E790E4779E846963D3DC78 /* PipScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0480DE3601E8C37A2D4745EA /* PipScreenView.swift */; };
|
||||
22EA1CB33A17DCC6FC82406D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F543015C899423D4B01CEB2E /* Assets.xcassets */; };
|
||||
2D0291E964949C7C8D4DF324 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 88A292AA7607F14E8C66291E /* Assets.xcassets */; };
|
||||
4701F6E8155C8E093C8CF81B /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8F846972E53BC33C1001D3 /* GradientView.swift */; };
|
||||
56B0305B5D3847FC6B326502 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE8509E5A49E4A5CAAEB3ADB /* ContentView.swift */; };
|
||||
5A902F589276392B4CBC0015 /* HomeWidgetExampleBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30942AA1A2E3A1AB832DD959 /* HomeWidgetExampleBundle.swift */; };
|
||||
76ED3117C0B97CD5572EB808 /* HomeWidgetExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B1CCBD46108D407DF0C9A3 /* HomeWidgetExample.swift */; };
|
||||
8070A4B4DD1D3DD823A8F923 /* WidgetAppUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0142951DE661E55DC3207327 /* WidgetAppUtils.swift */; };
|
||||
B7C8D9E0F1A234567890ABCD /* InAppWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F6789012345678 /* InAppWebViewController.swift */; };
|
||||
C8D9E0F1A2B345678901BCDE /* WebViewContentBlocker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C3D4E5F67890123456789 /* WebViewContentBlocker.swift */; };
|
||||
A1B2C3D4E5F60718293A4B5C /* BackgroundIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59CDB1C6A666078B3272B409 /* BackgroundIntent.swift */; };
|
||||
88ABACE0259DE83261737B99 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1285E8A7B7E7359EA11B9591 /* SceneDelegate.swift */; };
|
||||
93FF51B22016695CB74F0AD6 /* HostBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6B3C131D45A3E52B69B531 /* HostBridge.swift */; };
|
||||
A59815941DC5A1C2B9F349CB /* PipScreenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2016716493FA6F4B6E30EB17 /* PipScreenManager.swift */; };
|
||||
C8353E6C8BE98C27FD768A59 /* WidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = E6AF7C77765E074EE1C7E597 /* WidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
EAC4398962E17AAE02F2DB68 /* black.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = E2D6A135D7E87F67C8285D07 /* black.mp4 */; };
|
||||
F9A23E2F5F7514E5D6A3D9C0 /* BackgroundIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59CDB1C6A666078B3272B409 /* BackgroundIntent.swift */; };
|
||||
FA6C12E55F9D41237B4BDAF1 /* CarPlaySceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 310E847101B97B7A36776025 /* CarPlaySceneDelegate.swift */; };
|
||||
FDE52B8D57A097A60A4CF366 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B6C1F485E0CF8BB70C9DDD2 /* AppDelegate.swift */; };
|
||||
D1E2F3A4B5C60718293A4B02 /* QqShareHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E2F3A4B5C60718293A4B01 /* QqShareHost.swift */; };
|
||||
D1E2F3A4B5C60718293A4B04 /* TencentOpenAPI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1E2F3A4B5C60718293A4B03 /* TencentOpenAPI.framework */; };
|
||||
D1E2F3A4B5C60718293A4B06 /* TencentOpenApi_IOS_Bundle.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D1E2F3A4B5C60718293A4B05 /* TencentOpenApi_IOS_Bundle.bundle */; };
|
||||
I18N0PLIST0BUILD0FILE001 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = I18N0PLIST0VARNT0GROUP01 /* InfoPlist.strings */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
50F517066DF80E213FC4D6D2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 242FB5348943441C388468CE /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 8803DE44F41D95FEAAC1346B;
|
||||
remoteInfo = WidgetExtension;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
E99160E501832DB2CECE6CFF /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
C8353E6C8BE98C27FD768A59 /* WidgetExtension.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0142951DE661E55DC3207327 /* WidgetAppUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetAppUtils.swift; sourceTree = "<group>"; };
|
||||
A1B2C3D4E5F6789012345678 /* InAppWebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppWebViewController.swift; sourceTree = "<group>"; };
|
||||
B2C3D4E5F67890123456789 /* WebViewContentBlocker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewContentBlocker.swift; sourceTree = "<group>"; };
|
||||
0480DE3601E8C37A2D4745EA /* PipScreenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PipScreenView.swift; sourceTree = "<group>"; };
|
||||
1285E8A7B7E7359EA11B9591 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
2016716493FA6F4B6E30EB17 /* PipScreenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PipScreenManager.swift; sourceTree = "<group>"; };
|
||||
30942AA1A2E3A1AB832DD959 /* HomeWidgetExampleBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeWidgetExampleBundle.swift; sourceTree = "<group>"; };
|
||||
310E847101B97B7A36776025 /* CarPlaySceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarPlaySceneDelegate.swift; sourceTree = "<group>"; };
|
||||
4B8F846972E53BC33C1001D3 /* GradientView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = "<group>"; };
|
||||
59CDB1C6A666078B3272B409 /* BackgroundIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundIntent.swift; sourceTree = "<group>"; };
|
||||
7B6B3C131D45A3E52B69B531 /* HostBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostBridge.swift; sourceTree = "<group>"; };
|
||||
86038AE1032E284E79B32AD6 /* iosApp.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
88A292AA7607F14E8C66291E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
8B6C1F485E0CF8BB70C9DDD2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
B8B1CCBD46108D407DF0C9A3 /* HomeWidgetExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeWidgetExample.swift; sourceTree = "<group>"; };
|
||||
CE8509E5A49E4A5CAAEB3ADB /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
E2D6A135D7E87F67C8285D07 /* black.mp4 */ = {isa = PBXFileReference; path = black.mp4; sourceTree = "<group>"; };
|
||||
E6AF7C77765E074EE1C7E597 /* WidgetExtension.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F543015C899423D4B01CEB2E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
D1E2F3A4B5C60718293A4B01 /* QqShareHost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QqShareHost.swift; sourceTree = "<group>"; };
|
||||
D1E2F3A4B5C60718293A4B03 /* TencentOpenAPI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TencentOpenAPI.framework; path = Vendor/TencentOpenAPI.framework; sourceTree = "<group>"; };
|
||||
D1E2F3A4B5C60718293A4B05 /* TencentOpenApi_IOS_Bundle.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = TencentOpenApi_IOS_Bundle.bundle; path = Vendor/TencentOpenApi_IOS_Bundle.bundle; sourceTree = "<group>"; };
|
||||
I18N0PLIST0FILE0REF0ZH01 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
I18N0PLIST0FILE0REF0EN01 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
I18N0PLIST0FILE0REF0JA01 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
I18N0PLIST0FILE0REF0DE01 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
D1E2F3A4B5C60718293A4B07 /* iosApp-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "iosApp-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0ECDA917A71DFD183A635257 /* CarPlay */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
310E847101B97B7A36776025 /* CarPlaySceneDelegate.swift */,
|
||||
);
|
||||
path = CarPlay;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1DB4B685CAEE8BEFEBBAEF94 /* iosApp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0ECDA917A71DFD183A635257 /* CarPlay */,
|
||||
5B4BD3733F8124940306FE19 /* pip */,
|
||||
8B6C1F485E0CF8BB70C9DDD2 /* AppDelegate.swift */,
|
||||
F543015C899423D4B01CEB2E /* Assets.xcassets */,
|
||||
CE8509E5A49E4A5CAAEB3ADB /* ContentView.swift */,
|
||||
A1B2C3D4E5F6789012345678 /* InAppWebViewController.swift */,
|
||||
B2C3D4E5F67890123456789 /* WebViewContentBlocker.swift */,
|
||||
7B6B3C131D45A3E52B69B531 /* HostBridge.swift */,
|
||||
D1E2F3A4B5C60718293A4B01 /* QqShareHost.swift */,
|
||||
D1E2F3A4B5C60718293A4B07 /* iosApp-Bridging-Header.h */,
|
||||
1285E8A7B7E7359EA11B9591 /* SceneDelegate.swift */,
|
||||
0142951DE661E55DC3207327 /* WidgetAppUtils.swift */,
|
||||
I18N0PLIST0VARNT0GROUP01 /* InfoPlist.strings */,
|
||||
);
|
||||
path = iosApp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
38B69C3F79CD97858CF7B609 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1DB4B685CAEE8BEFEBBAEF94 /* iosApp */,
|
||||
D1E2F3A4B5C60718293A4B09 /* Vendor */,
|
||||
A795A079A61B73EB2CA424AE /* WidgetExtension */,
|
||||
94C98B8B3A2BFC1966447269 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D1E2F3A4B5C60718293A4B09 /* Vendor */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D1E2F3A4B5C60718293A4B03 /* TencentOpenAPI.framework */,
|
||||
D1E2F3A4B5C60718293A4B05 /* TencentOpenApi_IOS_Bundle.bundle */,
|
||||
);
|
||||
name = Vendor;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5B4BD3733F8124940306FE19 /* pip */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E2D6A135D7E87F67C8285D07 /* black.mp4 */,
|
||||
4B8F846972E53BC33C1001D3 /* GradientView.swift */,
|
||||
2016716493FA6F4B6E30EB17 /* PipScreenManager.swift */,
|
||||
0480DE3601E8C37A2D4745EA /* PipScreenView.swift */,
|
||||
);
|
||||
path = pip;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
94C98B8B3A2BFC1966447269 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
86038AE1032E284E79B32AD6 /* iosApp.app */,
|
||||
E6AF7C77765E074EE1C7E597 /* WidgetExtension.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A795A079A61B73EB2CA424AE /* WidgetExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
88A292AA7607F14E8C66291E /* Assets.xcassets */,
|
||||
59CDB1C6A666078B3272B409 /* BackgroundIntent.swift */,
|
||||
B8B1CCBD46108D407DF0C9A3 /* HomeWidgetExample.swift */,
|
||||
30942AA1A2E3A1AB832DD959 /* HomeWidgetExampleBundle.swift */,
|
||||
);
|
||||
path = WidgetExtension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
78B53CC79860CBFC2F5543C6 /* iosApp */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 29DCA9F0B3A40325067EB163 /* Build configuration list for PBXNativeTarget "iosApp" */;
|
||||
buildPhases = (
|
||||
3EC48E5D6B774AB34AC4F9CF /* Build ComposeApp.framework */,
|
||||
297B59D9CE634556D6007C40 /* Sources */,
|
||||
D1E2F3A4B5C60718293A4B08 /* Frameworks */,
|
||||
1623B02D10928C8101C1F806 /* Resources */,
|
||||
E99160E501832DB2CECE6CFF /* Embed Foundation Extensions */,
|
||||
C516F4DE7FC107135868F830 /* Verify Widget Extension Embedded */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
8C46D4887752213A420B4355 /* PBXTargetDependency */,
|
||||
);
|
||||
name = iosApp;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = iosApp;
|
||||
productReference = 86038AE1032E284E79B32AD6 /* iosApp.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
8803DE44F41D95FEAAC1346B /* WidgetExtension */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = F78545275CCD4B3EFDC1B6CD /* Build configuration list for PBXNativeTarget "WidgetExtension" */;
|
||||
buildPhases = (
|
||||
F846647070D4E44D5C5AEB29 /* Sources */,
|
||||
0E7A1DC2920D6844E8A58BE1 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = WidgetExtension;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = WidgetExtension;
|
||||
productReference = E6AF7C77765E074EE1C7E597 /* WidgetExtension.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
242FB5348943441C388468CE /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1430;
|
||||
TargetAttributes = {
|
||||
78B53CC79860CBFC2F5543C6 = {
|
||||
DevelopmentTeam = "";
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
8803DE44F41D95FEAAC1346B = {
|
||||
DevelopmentTeam = "";
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = FF7CA1CA754D017066F1822F /* Build configuration list for PBXProject "iosApp" */;
|
||||
compatibilityVersion = "Xcode 14.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
Base,
|
||||
en,
|
||||
"zh-Hans",
|
||||
ja,
|
||||
de,
|
||||
);
|
||||
mainGroup = 38B69C3F79CD97858CF7B609;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
preferredProjectObjectVersion = 54;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8803DE44F41D95FEAAC1346B /* WidgetExtension */,
|
||||
78B53CC79860CBFC2F5543C6 /* iosApp */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
0E7A1DC2920D6844E8A58BE1 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2D0291E964949C7C8D4DF324 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
1623B02D10928C8101C1F806 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
22EA1CB33A17DCC6FC82406D /* Assets.xcassets in Resources */,
|
||||
EAC4398962E17AAE02F2DB68 /* black.mp4 in Resources */,
|
||||
D1E2F3A4B5C60718293A4B06 /* TencentOpenApi_IOS_Bundle.bundle in Resources */,
|
||||
I18N0PLIST0BUILD0FILE001 /* InfoPlist.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D1E2F3A4B5C60718293A4B08 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D1E2F3A4B5C60718293A4B04 /* TencentOpenAPI.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3EC48E5D6B774AB34AC4F9CF /* Build ComposeApp.framework */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Build ComposeApp.framework";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\nset -euo pipefail\nif [ \"YES\" = \"${OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED:-}\" ]; then\n echo \"Skipping Gradle build (OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED=YES)\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode";
|
||||
};
|
||||
C516F4DE7FC107135868F830 /* Verify Widget Extension Embedded */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Verify Widget Extension Embedded";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "set -euo pipefail\nAPPEX=\"${TARGET_BUILD_DIR}/${WRAPPER_NAME}/PlugIns/WidgetExtension.appex\"\nif [ ! -d \"$APPEX\" ]; then\n echo \"error: WidgetExtension.appex is missing from ${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n echo \"error: The app will install without widgets. Check WidgetExtension code signing.\"\n exit 1\nfi\necho \"Widget extension embedded: $APPEX\"\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
I18N0PLIST0VARNT0GROUP01 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
I18N0PLIST0FILE0REF0ZH01 /* zh-Hans */,
|
||||
I18N0PLIST0FILE0REF0EN01 /* en */,
|
||||
I18N0PLIST0FILE0REF0JA01 /* ja */,
|
||||
I18N0PLIST0FILE0REF0DE01 /* de */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
297B59D9CE634556D6007C40 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FDE52B8D57A097A60A4CF366 /* AppDelegate.swift in Sources */,
|
||||
FA6C12E55F9D41237B4BDAF1 /* CarPlaySceneDelegate.swift in Sources */,
|
||||
56B0305B5D3847FC6B326502 /* ContentView.swift in Sources */,
|
||||
4701F6E8155C8E093C8CF81B /* GradientView.swift in Sources */,
|
||||
93FF51B22016695CB74F0AD6 /* HostBridge.swift in Sources */,
|
||||
A59815941DC5A1C2B9F349CB /* PipScreenManager.swift in Sources */,
|
||||
02E790E4779E846963D3DC78 /* PipScreenView.swift in Sources */,
|
||||
88ABACE0259DE83261737B99 /* SceneDelegate.swift in Sources */,
|
||||
8070A4B4DD1D3DD823A8F923 /* WidgetAppUtils.swift in Sources */,
|
||||
B7C8D9E0F1A234567890ABCD /* InAppWebViewController.swift in Sources */,
|
||||
C8D9E0F1A2B345678901BCDE /* WebViewContentBlocker.swift in Sources */,
|
||||
A1B2C3D4E5F60718293A4B5C /* BackgroundIntent.swift in Sources */,
|
||||
D1E2F3A4B5C60718293A4B02 /* QqShareHost.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F846647070D4E44D5C5AEB29 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F9A23E2F5F7514E5D6A3D9C0 /* BackgroundIntent.swift in Sources */,
|
||||
76ED3117C0B97CD5572EB808 /* HomeWidgetExample.swift in Sources */,
|
||||
5A902F589276392B4CBC0015 /* HomeWidgetExampleBundle.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
8C46D4887752213A420B4355 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 8803DE44F41D95FEAAC1346B /* WidgetExtension */;
|
||||
targetProxy = 50F517066DF80E213FC4D6D2 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
11814011E7D6EC822BFBC66D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 74;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = V73L9LHPCA;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_DEBUG_DYLIB = NO;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||
MARKETING_VERSION = 1.7.4;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
18EBBCFA772E47B10DD95C22 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = iosApp/iosApp.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
|
||||
"$(SRCROOT)/Vendor",
|
||||
);
|
||||
INFOPLIST_FILE = iosApp/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = LLMP;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lz",
|
||||
"-lc++",
|
||||
"-framework",
|
||||
ComposeApp,
|
||||
"-framework",
|
||||
TencentOpenAPI,
|
||||
"-framework",
|
||||
Security,
|
||||
"-framework",
|
||||
SystemConfiguration,
|
||||
"-framework",
|
||||
CoreTelephony,
|
||||
"-framework",
|
||||
WebKit,
|
||||
"-lsqlite3",
|
||||
);
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "iosApp/iosApp-Bridging-Header.h";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.zhushenwudi.lovelivemusicplayer;
|
||||
PRODUCT_NAME = LLMP;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "LLMP-M-AD-HOC";
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
44097AC4C7F9A2BBDFDF3082 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||
"EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited)";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = LLMP;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.zhushenwudi.lovelivemusicplayer.HomeWidgetExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "LLMP-M-DEVELOP-WIDGET";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
4EC94FBC5B79F3C98C54650C /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 74;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = V73L9LHPCA;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_DEBUG_DYLIB = NO;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||
MARKETING_VERSION = 1.7.4;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
6C824A6EB4BAD43AE2A4BB29 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 74;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = V73L9LHPCA;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_DEBUG_DYLIB = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
"DEBUG=1",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||
MARKETING_VERSION = 1.7.4;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
730BE128FD55F23ECB060431 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||
"EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited)";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = LLMP;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.zhushenwudi.lovelivemusicplayer.HomeWidgetExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "LLMP-M-RELEASE-WIDGET";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
9221A1FDC06803E8A2FC6E61 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = iosApp/iosApp.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
|
||||
"$(SRCROOT)/Vendor",
|
||||
);
|
||||
INFOPLIST_FILE = iosApp/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = LLMP;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lz",
|
||||
"-lc++",
|
||||
"-framework",
|
||||
ComposeApp,
|
||||
"-framework",
|
||||
TencentOpenAPI,
|
||||
"-framework",
|
||||
Security,
|
||||
"-framework",
|
||||
SystemConfiguration,
|
||||
"-framework",
|
||||
CoreTelephony,
|
||||
"-framework",
|
||||
WebKit,
|
||||
"-lsqlite3",
|
||||
);
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "iosApp/iosApp-Bridging-Header.h";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.zhushenwudi.lovelivemusicplayer;
|
||||
PRODUCT_NAME = LLMP;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "LLMP-M-DEVELOP";
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
AF835BFF1220CA5AB5B867E4 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = iosApp/iosApp.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
|
||||
"$(SRCROOT)/Vendor",
|
||||
);
|
||||
INFOPLIST_FILE = iosApp/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = LLMP;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lz",
|
||||
"-lc++",
|
||||
"-framework",
|
||||
ComposeApp,
|
||||
"-framework",
|
||||
TencentOpenAPI,
|
||||
"-framework",
|
||||
Security,
|
||||
"-framework",
|
||||
SystemConfiguration,
|
||||
"-framework",
|
||||
CoreTelephony,
|
||||
"-framework",
|
||||
WebKit,
|
||||
"-lsqlite3",
|
||||
);
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "iosApp/iosApp-Bridging-Header.h";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.zhushenwudi.lovelivemusicplayer;
|
||||
PRODUCT_NAME = LLMP;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "LLMP-M-RELEASE";
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
BC1BDC6B2E0AEE85A44B675A /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||
"EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited)";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = LLMP;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.zhushenwudi.lovelivemusicplayer.HomeWidgetExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "LLMP-M-AD-HOC-WIDGET";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
29DCA9F0B3A40325067EB163 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
9221A1FDC06803E8A2FC6E61 /* Debug */,
|
||||
18EBBCFA772E47B10DD95C22 /* Profile */,
|
||||
AF835BFF1220CA5AB5B867E4 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Debug;
|
||||
};
|
||||
F78545275CCD4B3EFDC1B6CD /* Build configuration list for PBXNativeTarget "WidgetExtension" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
44097AC4C7F9A2BBDFDF3082 /* Debug */,
|
||||
BC1BDC6B2E0AEE85A44B675A /* Profile */,
|
||||
730BE128FD55F23ECB060431 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Debug;
|
||||
};
|
||||
FF7CA1CA754D017066F1822F /* Build configuration list for PBXProject "iosApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
6C824A6EB4BAD43AE2A4BB29 /* Debug */,
|
||||
4EC94FBC5B79F3C98C54650C /* Profile */,
|
||||
11814011E7D6EC822BFBC66D /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Debug;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 242FB5348943441C388468CE /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8803DE44F41D95FEAAC1346B"
|
||||
BuildableName = "WidgetExtension.appex"
|
||||
BlueprintName = "WidgetExtension"
|
||||
ReferencedContainer = "container:iosApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "NO"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "78B53CC79860CBFC2F5543C6"
|
||||
BuildableName = "LLMP.app"
|
||||
BlueprintName = "iosApp"
|
||||
ReferencedContainer = "container:iosApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8803DE44F41D95FEAAC1346B"
|
||||
BuildableName = "WidgetExtension.appex"
|
||||
BlueprintName = "WidgetExtension"
|
||||
ReferencedContainer = "container:iosApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8803DE44F41D95FEAAC1346B"
|
||||
BuildableName = "WidgetExtension.appex"
|
||||
BlueprintName = "WidgetExtension"
|
||||
ReferencedContainer = "container:iosApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetFamily"
|
||||
value = "systemMedium"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetKind"
|
||||
value = "HomeWidgetExampleWhite"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8803DE44F41D95FEAAC1346B"
|
||||
BuildableName = "WidgetExtension.appex"
|
||||
BlueprintName = "WidgetExtension"
|
||||
ReferencedContainer = "container:iosApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "78B53CC79860CBFC2F5543C6"
|
||||
BuildableName = "LLMP.app"
|
||||
BlueprintName = "iosApp"
|
||||
ReferencedContainer = "container:iosApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "78B53CC79860CBFC2F5543C6"
|
||||
BuildableName = "LLMP.app"
|
||||
BlueprintName = "iosApp"
|
||||
ReferencedContainer = "container:iosApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "78B53CC79860CBFC2F5543C6"
|
||||
BuildableName = "LLMP.app"
|
||||
BlueprintName = "iosApp"
|
||||
ReferencedContainer = "container:iosApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "78B53CC79860CBFC2F5543C6"
|
||||
BuildableName = "LLMP.app"
|
||||
BlueprintName = "iosApp"
|
||||
ReferencedContainer = "container:iosApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,9 +1,12 @@
|
||||
import UIKit
|
||||
import WidgetKit
|
||||
|
||||
let widgetGroupId = "group.top.zhushenwudi.llmp"
|
||||
let widgetGroupId = "group.com.zhushenwudi.lovelivemusicplayer"
|
||||
|
||||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
private var widgetReloadWorkItem: DispatchWorkItem?
|
||||
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||||
@@ -28,15 +31,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
options: UIScene.ConnectionOptions
|
||||
) -> UISceneConfiguration {
|
||||
if connectingSceneSession.role == UISceneSession.Role.carTemplateApplication {
|
||||
return UISceneConfiguration(
|
||||
let config = UISceneConfiguration(
|
||||
name: "CarPlay Configuration",
|
||||
sessionRole: connectingSceneSession.role
|
||||
)
|
||||
config.delegateClass = CarPlaySceneDelegate.self
|
||||
return config
|
||||
}
|
||||
return UISceneConfiguration(
|
||||
let config = UISceneConfiguration(
|
||||
name: "Default Configuration",
|
||||
sessionRole: connectingSceneSession.role
|
||||
)
|
||||
config.delegateClass = SceneDelegate.self
|
||||
return config
|
||||
}
|
||||
|
||||
private func registerHostNotifications() {
|
||||
@@ -48,18 +55,62 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
HostBridge.shared.presentShare(from: notification.userInfo)
|
||||
}
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: Notification.Name("fromWidgetToHost"),
|
||||
forName: Notification.Name("fromWigetToRunner"),
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { notification in
|
||||
HostBridge.shared.handleWidgetAction(from: notification.userInfo)
|
||||
}
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: Notification.Name("llmpCarPlayAction"),
|
||||
forName: Notification.Name("llmpWidgetSaveCover"),
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { notification in
|
||||
HostBridge.shared.handleCarPlayAction(from: notification.userInfo)
|
||||
guard let path = notification.userInfo?["path"] as? String else { return }
|
||||
WidgetAppUtils.shared.saveImageToFile(imagePath: path) { [weak self] in
|
||||
self?.scheduleWidgetReload()
|
||||
}
|
||||
}
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: Notification.Name("llmpReloadWidgets"),
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.scheduleWidgetReload()
|
||||
}
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: Notification.Name("llmpOpenWebView"),
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { notification in
|
||||
HostBridge.shared.presentWebView(from: notification.userInfo)
|
||||
}
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: Notification.Name("llmpShowToast"),
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { notification in
|
||||
guard let message = notification.userInfo?["message"] as? String else { return }
|
||||
HostBridge.shared.showToast(message)
|
||||
}
|
||||
QqShareHost.shared.prepare()
|
||||
}
|
||||
|
||||
func application(
|
||||
_ app: UIApplication,
|
||||
open url: URL,
|
||||
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
|
||||
) -> Bool {
|
||||
QqShareHost.shared.handleOpen(url)
|
||||
}
|
||||
|
||||
/// 合并切歌、播放状态、歌词行和封面完成时挤在一起的刷新请求。
|
||||
private func scheduleWidgetReload() {
|
||||
widgetReloadWorkItem?.cancel()
|
||||
let workItem = DispatchWorkItem {
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}
|
||||
widgetReloadWorkItem = workItem
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: workItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"size": "20x20",
|
||||
"idiom": "iphone",
|
||||
"filename": "icon-20@2x.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"size": "20x20",
|
||||
"idiom": "iphone",
|
||||
"filename": "icon-20@3x.png",
|
||||
"scale": "3x"
|
||||
},
|
||||
{
|
||||
"size": "29x29",
|
||||
"idiom": "iphone",
|
||||
"filename": "icon-29.png",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"size": "29x29",
|
||||
"idiom": "iphone",
|
||||
"filename": "icon-29@2x.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"size": "29x29",
|
||||
"idiom": "iphone",
|
||||
"filename": "icon-29@3x.png",
|
||||
"scale": "3x"
|
||||
},
|
||||
{
|
||||
"size": "40x40",
|
||||
"idiom": "iphone",
|
||||
"filename": "icon-40@2x.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"size": "40x40",
|
||||
"idiom": "iphone",
|
||||
"filename": "icon-40@3x.png",
|
||||
"scale": "3x"
|
||||
},
|
||||
{
|
||||
"size": "60x60",
|
||||
"idiom": "iphone",
|
||||
"filename": "icon-60@2x.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"size": "60x60",
|
||||
"idiom": "iphone",
|
||||
"filename": "icon-60@3x.png",
|
||||
"scale": "3x"
|
||||
},
|
||||
{
|
||||
"size": "20x20",
|
||||
"idiom": "ipad",
|
||||
"filename": "icon-20-ipad.png",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"size": "20x20",
|
||||
"idiom": "ipad",
|
||||
"filename": "icon-20@2x-ipad.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"size": "29x29",
|
||||
"idiom": "ipad",
|
||||
"filename": "icon-29-ipad.png",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"size": "29x29",
|
||||
"idiom": "ipad",
|
||||
"filename": "icon-29@2x-ipad.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"size": "40x40",
|
||||
"idiom": "ipad",
|
||||
"filename": "icon-40.png",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"size": "40x40",
|
||||
"idiom": "ipad",
|
||||
"filename": "icon-40@2x.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"size": "76x76",
|
||||
"idiom": "ipad",
|
||||
"filename": "icon-76.png",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"size": "76x76",
|
||||
"idiom": "ipad",
|
||||
"filename": "icon-76@2x.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"size": "83.5x83.5",
|
||||
"idiom": "ipad",
|
||||
"filename": "icon-83.5@2x.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"size": "1024x1024",
|
||||
"idiom": "ios-marketing",
|
||||
"filename": "icon-1024.png",
|
||||
"scale": "1x"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "icon.wuruihong.com"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 701 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 6.4 KiB |