@napacitor/app 0.0.0-stage → 0.1.2-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -3
- package/dist/definitions.d.ts +48 -0
- package/dist/definitions.js +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +38 -0
- package/dist/web.d.ts +19 -0
- package/dist/web.js +60 -0
- package/maui/AppPlugin.cs +184 -0
- package/maui/Napacitor.App.csproj +25 -0
- package/package.json +13 -6
package/README.md
CHANGED
|
@@ -1,3 +1,64 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# @napacitor/app
|
|
2
|
+
|
|
3
|
+
应用生命周期插件(与 [@capacitor/app](https://github.com/ionic-team/capacitor-plugins/tree/main/app) 对应)。
|
|
4
|
+
|
|
5
|
+
## 用法
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { App } from '@napacitor/app';
|
|
9
|
+
|
|
10
|
+
const { isActive } = await App.getState();
|
|
11
|
+
const { value: language } = await App.getAppLanguage(); // 'zh-CN'
|
|
12
|
+
|
|
13
|
+
App.addListener('appStateChange', ({ isActive }) => console.log('active:', isActive));
|
|
14
|
+
App.addListener('pause', () => console.log('进入后台'));
|
|
15
|
+
App.addListener('resume', () => console.log('回到前台'));
|
|
16
|
+
App.addListener('appUrlOpen', ({ url }) => console.log('深度链接:', url));
|
|
17
|
+
App.addListener('backButton', ({ canGoBack }) => console.log('返回键'));
|
|
18
|
+
|
|
19
|
+
const { url } = await App.getLaunchUrl();
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 结构
|
|
23
|
+
|
|
24
|
+
- `src/` —— TypeScript 源码(index / definitions / web)
|
|
25
|
+
- `dist/` —— 构建产物(CJS + d.ts,已提交)
|
|
26
|
+
- `maui/` —— .NET MAUI C# 实现(程序集 `Napacitor.App`,运行时反射自动发现)
|
|
27
|
+
|
|
28
|
+
原生实现映射:Window 生命周期事件 `Resumed / Stopped / Backgrounding`
|
|
29
|
+
(appStateChange / pause / resume)、`Application.Quit`(exitApp)、
|
|
30
|
+
`CultureInfo.CurrentUICulture`(getAppLanguage)。首个生命周期监听器注册(或首次
|
|
31
|
+
`getState`)后惰性挂接主 Window。
|
|
32
|
+
|
|
33
|
+
## 与 Capacitor 的差异
|
|
34
|
+
|
|
35
|
+
- **exitApp**:映射 `Application.Quit`,仅 Windows/macOS 有原生语义;Android/iOS 系统
|
|
36
|
+
不鼓励程序化退出(HIG/Play 政策),如需强退需宿主平台代码;
|
|
37
|
+
- **appUrlOpen / getLaunchUrl(深度链接)**:MAUI 无跨平台的 App Link 事件,宿主需
|
|
38
|
+
①注册 URL scheme,②在 `App.xaml.cs` 转发:
|
|
39
|
+
|
|
40
|
+
```csharp
|
|
41
|
+
protected override void OnAppLinkRequestReceived(Uri uri)
|
|
42
|
+
{
|
|
43
|
+
base.OnAppLinkRequestReceived(uri);
|
|
44
|
+
Napacitor.Plugins.App.AppPlugin.NotifyAppLink(uri);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
URL scheme 注册:Android 在 `AndroidManifest.xml` 的 MainActivity 加
|
|
49
|
+
`<intent-filter>`(`<data android:scheme="your-scheme" />`);iOS 在 `Info.plist` 加
|
|
50
|
+
`CFBundleURLTypes`;Windows 打包应用在 `Package.appxmanifest` 声明协议;
|
|
51
|
+
- **backButton(Android)**:宿主在 `MainActivity` 重写返回键并转发:
|
|
52
|
+
|
|
53
|
+
```csharp
|
|
54
|
+
public override void OnBackPressed()
|
|
55
|
+
{
|
|
56
|
+
if (!AppPlugin.NotifyBackButton())
|
|
57
|
+
{
|
|
58
|
+
base.OnBackPressed(); // 无 JS 监听器时走默认行为
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`NotifyBackButton` 返回 true 表示 JS 已监听(宿主应拦截本次返回键);
|
|
64
|
+
- **appRestoredResult / minimizeApp / edgeGesture**:暂未实现。
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @napacitor/app 类型定义。
|
|
3
|
+
* 应用生命周期 / 退出 / 语言 / 启动链接
|
|
4
|
+
* (映射 MAUI Window 生命周期事件 + Application.Quit + CultureInfo)。
|
|
5
|
+
*/
|
|
6
|
+
import type { PluginListenerHandle } from '@napacitor/core';
|
|
7
|
+
/** 应用状态。 */
|
|
8
|
+
export interface AppState {
|
|
9
|
+
/** 应用是否处于活跃状态(前台且可交互)。 */
|
|
10
|
+
isActive: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** getAppLanguage() 返回值。 */
|
|
13
|
+
export interface AppLanguage {
|
|
14
|
+
/** 应用语言(BCP-47 风格,如 'zh-CN';无法识别时退回 ISO 双字母)。 */
|
|
15
|
+
value: string;
|
|
16
|
+
}
|
|
17
|
+
/** getLaunchUrl() / appUrlOpen 事件载荷。 */
|
|
18
|
+
export interface AppUrl {
|
|
19
|
+
/** 深度链接地址;未收到过时为空字符串。 */
|
|
20
|
+
url: string;
|
|
21
|
+
}
|
|
22
|
+
/** backButton 事件载荷。 */
|
|
23
|
+
export interface BackButtonResponse {
|
|
24
|
+
/** WebView 历史是否可回退(宿主未提供时为 false)。 */
|
|
25
|
+
canGoBack: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface AppPlugin {
|
|
28
|
+
/** 退出应用(Windows/macOS 有效;Android/iOS 系统不鼓励程序化退出,见 README)。 */
|
|
29
|
+
exitApp(): Promise<void>;
|
|
30
|
+
/** 获取应用当前状态。 */
|
|
31
|
+
getState(): Promise<AppState>;
|
|
32
|
+
/** 获取触发应用启动的深度链接(需宿主注册 URL scheme 并转发,见 README)。 */
|
|
33
|
+
getLaunchUrl(): Promise<AppUrl>;
|
|
34
|
+
/** 获取应用语言。 */
|
|
35
|
+
getAppLanguage(): Promise<AppLanguage>;
|
|
36
|
+
/** 应用前后台切换时派发(payload 为 {isActive})。 */
|
|
37
|
+
addListener(eventName: 'appStateChange', listenerFunc: (state: AppState) => void): Promise<PluginListenerHandle> & PluginListenerHandle;
|
|
38
|
+
/** 应用进入后台时派发。 */
|
|
39
|
+
addListener(eventName: 'pause', listenerFunc: () => void): Promise<PluginListenerHandle> & PluginListenerHandle;
|
|
40
|
+
/** 应用回到前台时派发。 */
|
|
41
|
+
addListener(eventName: 'resume', listenerFunc: () => void): Promise<PluginListenerHandle> & PluginListenerHandle;
|
|
42
|
+
/** 应用经深度链接打开/唤起时派发(需宿主转发,见 README)。 */
|
|
43
|
+
addListener(eventName: 'appUrlOpen', listenerFunc: (data: AppUrl) => void): Promise<PluginListenerHandle> & PluginListenerHandle;
|
|
44
|
+
/** Android 硬件返回键按下时派发(需宿主转发,见 README)。 */
|
|
45
|
+
addListener(eventName: 'backButton', listenerFunc: (data: BackButtonResponse) => void): Promise<PluginListenerHandle> & PluginListenerHandle;
|
|
46
|
+
/** 移除该插件全部监听器。 */
|
|
47
|
+
removeAllListeners(): Promise<void>;
|
|
48
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
26
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.App = void 0;
|
|
30
|
+
/**
|
|
31
|
+
* @napacitor/app 入口。
|
|
32
|
+
*/
|
|
33
|
+
const core_1 = require("@napacitor/core");
|
|
34
|
+
const App = (0, core_1.registerPlugin)('App', {
|
|
35
|
+
web: () => Promise.resolve().then(() => __importStar(require('./web'))).then((m) => new m.AppWeb()),
|
|
36
|
+
});
|
|
37
|
+
exports.App = App;
|
|
38
|
+
__exportStar(require("./definitions"), exports);
|
package/dist/web.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @napacitor/app 浏览器实现(基于 document.visibilitychange 映射生命周期)。
|
|
3
|
+
*/
|
|
4
|
+
import type { PluginListenerHandle } from '@napacitor/core';
|
|
5
|
+
import type { AppLanguage, AppState, AppUrl } from './definitions';
|
|
6
|
+
type AnyCallback = (...args: any[]) => void;
|
|
7
|
+
export declare class AppWeb {
|
|
8
|
+
private _listeners;
|
|
9
|
+
private _visibilityHandler;
|
|
10
|
+
exitApp(): Promise<void>;
|
|
11
|
+
getState(): Promise<AppState>;
|
|
12
|
+
getLaunchUrl(): Promise<AppUrl>;
|
|
13
|
+
getAppLanguage(): Promise<AppLanguage>;
|
|
14
|
+
addListener(eventName: string, callback: AnyCallback): Promise<PluginListenerHandle>;
|
|
15
|
+
removeAllListeners(): Promise<void>;
|
|
16
|
+
private _emit;
|
|
17
|
+
private _ensureVisibility;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
package/dist/web.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AppWeb = void 0;
|
|
4
|
+
class AppWeb {
|
|
5
|
+
constructor() {
|
|
6
|
+
this._listeners = {};
|
|
7
|
+
this._visibilityHandler = null;
|
|
8
|
+
}
|
|
9
|
+
async exitApp() {
|
|
10
|
+
throw new Error('App.exitApp:浏览器不支持退出应用');
|
|
11
|
+
}
|
|
12
|
+
async getState() {
|
|
13
|
+
return { isActive: !document.hidden };
|
|
14
|
+
}
|
|
15
|
+
async getLaunchUrl() {
|
|
16
|
+
return { url: window.location.href };
|
|
17
|
+
}
|
|
18
|
+
async getAppLanguage() {
|
|
19
|
+
return { value: navigator.language || 'en' };
|
|
20
|
+
}
|
|
21
|
+
async addListener(eventName, callback) {
|
|
22
|
+
if (!this._listeners[eventName]) {
|
|
23
|
+
this._listeners[eventName] = [];
|
|
24
|
+
}
|
|
25
|
+
this._listeners[eventName].push(callback);
|
|
26
|
+
this._ensureVisibility();
|
|
27
|
+
return {
|
|
28
|
+
remove: async () => {
|
|
29
|
+
const arr = this._listeners[eventName];
|
|
30
|
+
if (arr) {
|
|
31
|
+
const idx = arr.indexOf(callback);
|
|
32
|
+
if (idx >= 0)
|
|
33
|
+
arr.splice(idx, 1);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async removeAllListeners() {
|
|
39
|
+
this._listeners = {};
|
|
40
|
+
if (this._visibilityHandler) {
|
|
41
|
+
document.removeEventListener('visibilitychange', this._visibilityHandler);
|
|
42
|
+
this._visibilityHandler = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
_emit(event, data) {
|
|
46
|
+
var _a;
|
|
47
|
+
((_a = this._listeners[event]) !== null && _a !== void 0 ? _a : []).forEach((cb) => cb(data));
|
|
48
|
+
}
|
|
49
|
+
_ensureVisibility() {
|
|
50
|
+
if (this._visibilityHandler)
|
|
51
|
+
return;
|
|
52
|
+
this._visibilityHandler = () => {
|
|
53
|
+
const active = !document.hidden;
|
|
54
|
+
this._emit('appStateChange', { isActive: active });
|
|
55
|
+
this._emit(active ? 'resume' : 'pause');
|
|
56
|
+
};
|
|
57
|
+
document.addEventListener('visibilitychange', this._visibilityHandler);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.AppWeb = AppWeb;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
using System.Globalization;
|
|
2
|
+
using Microsoft.Maui.ApplicationModel;
|
|
3
|
+
using Microsoft.Maui.Controls;
|
|
4
|
+
|
|
5
|
+
namespace Napacitor.Plugins.App;
|
|
6
|
+
|
|
7
|
+
/* ─── 返回 / 事件 DTO ─────────────────────────────────────── */
|
|
8
|
+
|
|
9
|
+
/// <summary>应用状态。</summary>
|
|
10
|
+
public class AppStateResult
|
|
11
|
+
{
|
|
12
|
+
public bool IsActive { get; set; } = true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/// <summary>getAppLanguage() 返回值。</summary>
|
|
16
|
+
public class AppLanguageResult
|
|
17
|
+
{
|
|
18
|
+
public string Value { get; set; } = string.Empty;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// <summary>getLaunchUrl() / appUrlOpen 事件载荷。</summary>
|
|
22
|
+
public class AppUrlResult
|
|
23
|
+
{
|
|
24
|
+
public string Url { get; set; } = string.Empty;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// <summary>backButton 事件载荷。</summary>
|
|
28
|
+
public class AppBackButtonResult
|
|
29
|
+
{
|
|
30
|
+
public bool CanGoBack { get; set; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* ─── 插件主体 ─────────────────────────────────────────────── */
|
|
34
|
+
|
|
35
|
+
/// <summary>
|
|
36
|
+
/// App 插件:应用生命周期 / 退出 / 语言 / 启动链接
|
|
37
|
+
/// (映射 MAUI Window 生命周期事件 + Application.Quit + CultureInfo)。
|
|
38
|
+
///
|
|
39
|
+
/// 事件管线:首个 JS 监听器注册(或首次 getState)后,惰性挂接主 Window 的
|
|
40
|
+
/// Resumed / Stopped / Backgrounding 事件(移动端前后台 + 桌面 Stop/Display),
|
|
41
|
+
/// 状态变化经 appStateChange / pause / resume 派发。
|
|
42
|
+
///
|
|
43
|
+
/// 平台差异说明:appUrlOpen(深度链接)与 backButton(Android 返回键)MAUI 无
|
|
44
|
+
/// 跨平台可订阅事件,宿主需少量平台代码转发到 <see cref="NotifyAppLink"/> /
|
|
45
|
+
/// <see cref="NotifyBackButton"/>(见插件 README)。
|
|
46
|
+
/// </summary>
|
|
47
|
+
[NapacitorPlugin("App")]
|
|
48
|
+
public class AppPlugin : NapacitorPluginBase
|
|
49
|
+
{
|
|
50
|
+
private const string AppStateChangeEvent = "appStateChange";
|
|
51
|
+
private const string PauseEvent = "pause";
|
|
52
|
+
private const string ResumeEvent = "resume";
|
|
53
|
+
private const string AppUrlOpenEvent = "appUrlOpen";
|
|
54
|
+
private const string BackButtonEvent = "backButton";
|
|
55
|
+
|
|
56
|
+
/// <summary>当前插件实例(供宿主静态转发入口使用)。</summary>
|
|
57
|
+
private static AppPlugin? _current;
|
|
58
|
+
|
|
59
|
+
/// <summary>启动 / 最近一次收到的深度链接(宿主经 NotifyAppLink 写入)。</summary>
|
|
60
|
+
private static string? _launchUrl;
|
|
61
|
+
|
|
62
|
+
private Window? _window;
|
|
63
|
+
private bool _hooked;
|
|
64
|
+
private bool _isActive = true;
|
|
65
|
+
|
|
66
|
+
public AppPlugin()
|
|
67
|
+
{
|
|
68
|
+
_current = this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// <summary>获取应用当前状态(顺带惰性挂接生命周期钩子)。</summary>
|
|
72
|
+
[NapacitorMethod]
|
|
73
|
+
public Task<AppStateResult> GetState()
|
|
74
|
+
{
|
|
75
|
+
EnsureWindowHooked();
|
|
76
|
+
return Task.FromResult(new AppStateResult { IsActive = _isActive });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// <summary>获取应用语言(CultureInfo.CurrentUICulture)。</summary>
|
|
80
|
+
[NapacitorMethod]
|
|
81
|
+
public Task<AppLanguageResult> GetAppLanguage()
|
|
82
|
+
{
|
|
83
|
+
var culture = CultureInfo.CurrentUICulture;
|
|
84
|
+
var value = string.IsNullOrEmpty(culture.Name)
|
|
85
|
+
? culture.TwoLetterISOLanguageName
|
|
86
|
+
: culture.Name;
|
|
87
|
+
return Task.FromResult(new AppLanguageResult { Value = value });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// <summary>获取启动深度链接(未收到过时 url 为空字符串)。</summary>
|
|
91
|
+
[NapacitorMethod]
|
|
92
|
+
public Task<AppUrlResult> GetLaunchUrl()
|
|
93
|
+
{
|
|
94
|
+
return Task.FromResult(new AppUrlResult { Url = _launchUrl ?? string.Empty });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// <summary>退出应用(Windows/macOS 有效;移动端见 README 平台差异)。</summary>
|
|
98
|
+
[NapacitorMethod]
|
|
99
|
+
public Task ExitApp()
|
|
100
|
+
{
|
|
101
|
+
MainThread.BeginInvokeOnMainThread(() => Application.Current?.Quit());
|
|
102
|
+
return Task.CompletedTask;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// <summary>生命周期事件首个监听器注册时惰性挂接主 Window。</summary>
|
|
106
|
+
protected override void OnListenerAdded(string eventName)
|
|
107
|
+
{
|
|
108
|
+
EnsureWindowHooked();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* ── 宿主转发入口(深度链接 / Android 返回键)── */
|
|
112
|
+
|
|
113
|
+
/// <summary>
|
|
114
|
+
/// 宿主在 App.xaml.cs 的 OnAppLinkRequestReceived 重写中调用:
|
|
115
|
+
/// 记录启动链接并向 JS 派发 appUrlOpen 事件(插件实例尚未创建时仅记录,
|
|
116
|
+
/// getLaunchUrl 仍可读到)。
|
|
117
|
+
/// </summary>
|
|
118
|
+
public static void NotifyAppLink(Uri uri)
|
|
119
|
+
{
|
|
120
|
+
_launchUrl = uri.ToString();
|
|
121
|
+
_current?.NotifyListeners(AppUrlOpenEvent, new AppUrlResult { Url = _launchUrl });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// <summary>
|
|
125
|
+
/// 宿主在 Android MainActivity 的 OnBackPressed 重写中调用:
|
|
126
|
+
/// 向 JS 派发 backButton 事件(无 JS 监听器时返回 false,走系统默认行为)。
|
|
127
|
+
/// </summary>
|
|
128
|
+
public static bool NotifyBackButton(bool canGoBack = false)
|
|
129
|
+
{
|
|
130
|
+
var plugin = _current;
|
|
131
|
+
if (plugin is null || plugin.ListenerCount(BackButtonEvent) == 0)
|
|
132
|
+
{
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
plugin.NotifyListeners(BackButtonEvent, new AppBackButtonResult { CanGoBack = canGoBack });
|
|
136
|
+
return true; // 已消费:宿主应拦截本次返回键
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* ── Window 生命周期挂接 ── */
|
|
140
|
+
|
|
141
|
+
private void EnsureWindowHooked()
|
|
142
|
+
{
|
|
143
|
+
if (_hooked)
|
|
144
|
+
{
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
MainThread.BeginInvokeOnMainThread(() =>
|
|
148
|
+
{
|
|
149
|
+
if (_hooked)
|
|
150
|
+
{
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
var window = Application.Current?.Windows?.FirstOrDefault();
|
|
154
|
+
if (window is null)
|
|
155
|
+
{
|
|
156
|
+
return; // 窗口尚未创建:下次 getState / OnListenerAdded 再试
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
_window = window;
|
|
160
|
+
_hooked = true;
|
|
161
|
+
window.Resumed += OnWindowResumed; // 移动端回到前台
|
|
162
|
+
window.Stopped += OnWindowStopped; // 移动端/桌面进入后台
|
|
163
|
+
window.Backgrounding += OnWindowBackgrounding; // iOS 即将转后台
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private void OnWindowResumed(object? sender, EventArgs e) => SetActive(true);
|
|
168
|
+
|
|
169
|
+
private void OnWindowStopped(object? sender, EventArgs e) => SetActive(false);
|
|
170
|
+
|
|
171
|
+
private void OnWindowBackgrounding(object? sender, BackgroundingEventArgs e) =>
|
|
172
|
+
SetActive(false);
|
|
173
|
+
|
|
174
|
+
private void SetActive(bool active)
|
|
175
|
+
{
|
|
176
|
+
if (_isActive == active)
|
|
177
|
+
{
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
_isActive = active;
|
|
181
|
+
NotifyListeners(AppStateChangeEvent, new AppStateResult { IsActive = active });
|
|
182
|
+
NotifyListeners(active ? ResumeEvent : PauseEvent, null);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
|
2
|
+
|
|
3
|
+
<PropertyGroup>
|
|
4
|
+
<TargetFramework>net10.0</TargetFramework>
|
|
5
|
+
<RootNamespace>Napacitor.Plugins.App</RootNamespace>
|
|
6
|
+
<AssemblyName>Napacitor.App</AssemblyName>
|
|
7
|
+
<Nullable>enable</Nullable>
|
|
8
|
+
<ImplicitUsings>enable</ImplicitUsings>
|
|
9
|
+
<LangVersion>latest</LangVersion>
|
|
10
|
+
<Description>Napacitor App 插件(.NET MAUI 实现)</Description>
|
|
11
|
+
<Version>0.1.0</Version>
|
|
12
|
+
</PropertyGroup>
|
|
13
|
+
|
|
14
|
+
<ItemGroup>
|
|
15
|
+
<!-- Window 生命周期事件 / Application.Quit 需要 Controls 层 -->
|
|
16
|
+
<PackageReference Include="Microsoft.Maui.Essentials" Version="10.0.20" />
|
|
17
|
+
<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.20" />
|
|
18
|
+
</ItemGroup>
|
|
19
|
+
|
|
20
|
+
<ItemGroup>
|
|
21
|
+
<ProjectReference Include="..\..\maui\src\Napacitor.csproj" Condition="Exists('..\..\maui\src\Napacitor.csproj')" />
|
|
22
|
+
<ProjectReference Include="..\..\..\maui\src\Napacitor.csproj" Condition="!Exists('..\..\maui\src\Napacitor.csproj')" />
|
|
23
|
+
</ItemGroup>
|
|
24
|
+
|
|
25
|
+
</Project>
|
package/package.json
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@napacitor/app",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@napacitor/app",
|
|
3
|
+
"version": "0.1.2-beta.3",
|
|
4
|
+
"description": "Napacitor App 插件(应用生命周期/退出/语言/启动链接,映射 MAUI 应用生命周期)",
|
|
5
|
+
"homepage": "https://github.com/jevonsflash/napacitor",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"files": ["dist/", "maui/", "!maui/bin/", "!maui/obj/"],
|
|
10
|
+
"scripts": { "build": "tsc", "type-check": "tsc --noEmit" },
|
|
11
|
+
"peerDependencies": { "@napacitor/core": "^0.1.0" },
|
|
12
|
+
"devDependencies": { "@napacitor/core": "^0.1.0", "typescript": "~5.4.5" }
|
|
13
|
+
}
|