andysu-react-native 0.0.0-stage → 0.1.90
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/android/build.gradle +24 -0
- package/android/src/main/java/com/andysu/reactnative/AndySUModule.java +96 -0
- package/android/src/main/java/com/andysu/reactnative/AndySUPackage.java +21 -0
- package/app.plugin.js +13 -0
- package/lib/index.d.ts +40 -0
- package/lib/index.js +61 -0
- package/package.json +29 -4
- package/src/index.ts +80 -0
- package/README.md +0 -3
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// React Native autolinks this module. Inside the AndySU repo it compiles against :api; in an app it
|
|
2
|
+
// uses the published API artifact (compileOnly: the host provides the one runtime copy).
|
|
3
|
+
apply plugin: "com.android.library"
|
|
4
|
+
|
|
5
|
+
def andysuVersion = project.findProperty("andysuVersion") ?: "0.1.90"
|
|
6
|
+
|
|
7
|
+
android {
|
|
8
|
+
namespace "com.andysu.reactnative"
|
|
9
|
+
compileSdk 36
|
|
10
|
+
defaultConfig { minSdk 21 }
|
|
11
|
+
compileOptions {
|
|
12
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
13
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
dependencies {
|
|
18
|
+
compileOnly "com.facebook.react:react-android:0.88.1"
|
|
19
|
+
if (findProject(":api") != null) {
|
|
20
|
+
compileOnly project(":api")
|
|
21
|
+
} else {
|
|
22
|
+
compileOnly "io.github.dixonsolutions.andysu:api:${andysuVersion}"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
package com.andysu.reactnative;
|
|
2
|
+
|
|
3
|
+
import com.andysu.api.Host;
|
|
4
|
+
import com.andysu.api.HostApi;
|
|
5
|
+
import com.facebook.react.bridge.Promise;
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
7
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
8
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
9
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
10
|
+
|
|
11
|
+
import org.json.JSONObject;
|
|
12
|
+
|
|
13
|
+
/** NativeModules.AndySU: a thin bridge to the AndySU host (com.andysu.api.Host). */
|
|
14
|
+
public class AndySUModule extends ReactContextBaseJavaModule {
|
|
15
|
+
public AndySUModule(ReactApplicationContext ctx) {
|
|
16
|
+
super(ctx);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Override
|
|
20
|
+
public String getName() {
|
|
21
|
+
return "AndySU";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@ReactMethod
|
|
25
|
+
public void info(Promise p) {
|
|
26
|
+
HostApi h = Host.current();
|
|
27
|
+
try {
|
|
28
|
+
JSONObject o = new JSONObject().put("present", h != null);
|
|
29
|
+
if (h != null) {
|
|
30
|
+
o.put("apiLevel", h.apiLevel()).put("version", h.version()).put("isFactory", h.isFactory())
|
|
31
|
+
.put("slotDir", h.slotDir().getPath());
|
|
32
|
+
}
|
|
33
|
+
p.resolve(o.toString());
|
|
34
|
+
} catch (Exception e) {
|
|
35
|
+
p.reject("andysu", e);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@ReactMethod
|
|
40
|
+
public void status(Promise p) {
|
|
41
|
+
HostApi h = Host.current();
|
|
42
|
+
p.resolve(h == null ? "{\"state\":\"idle\"}" : h.updates().status());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@ReactMethod
|
|
46
|
+
public void checkNow() {
|
|
47
|
+
HostApi h = Host.current();
|
|
48
|
+
if (h != null) h.updates().checkNow();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@ReactMethod
|
|
52
|
+
public void applyNow() {
|
|
53
|
+
HostApi h = Host.current();
|
|
54
|
+
if (h != null) h.updates().applyNow();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@ReactMethod
|
|
58
|
+
public void restartNow() {
|
|
59
|
+
HostApi h = Host.current();
|
|
60
|
+
if (h != null) h.updates().restartNow();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@ReactMethod
|
|
64
|
+
public void setApplyPolicy(String policy) {
|
|
65
|
+
HostApi h = Host.current();
|
|
66
|
+
if (h != null) h.updates().setApplyPolicy(policy);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@ReactMethod
|
|
70
|
+
public void applyPolicy(Promise p) {
|
|
71
|
+
HostApi h = Host.current();
|
|
72
|
+
p.resolve(h == null ? "live" : h.updates().applyPolicy());
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@ReactMethod
|
|
76
|
+
public void markHealthy() {
|
|
77
|
+
HostApi h = Host.current();
|
|
78
|
+
if (h != null) h.markHealthy();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@ReactMethod
|
|
82
|
+
public void listen() {
|
|
83
|
+
HostApi h = Host.current();
|
|
84
|
+
if (h == null) return;
|
|
85
|
+
h.updates().setListener(json -> getReactApplicationContext()
|
|
86
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
87
|
+
.emit("AndySUStatus", json));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Required by NativeEventEmitter on the JS side.
|
|
91
|
+
@ReactMethod
|
|
92
|
+
public void addListener(String eventName) {}
|
|
93
|
+
|
|
94
|
+
@ReactMethod
|
|
95
|
+
public void removeListeners(double count) {}
|
|
96
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
package com.andysu.reactnative;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage;
|
|
4
|
+
import com.facebook.react.bridge.NativeModule;
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
7
|
+
|
|
8
|
+
import java.util.Collections;
|
|
9
|
+
import java.util.List;
|
|
10
|
+
|
|
11
|
+
public class AndySUPackage implements ReactPackage {
|
|
12
|
+
@Override
|
|
13
|
+
public List<NativeModule> createNativeModules(ReactApplicationContext ctx) {
|
|
14
|
+
return Collections.singletonList(new AndySUModule(ctx));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@Override
|
|
18
|
+
public List<ViewManager> createViewManagers(ReactApplicationContext ctx) {
|
|
19
|
+
return Collections.emptyList();
|
|
20
|
+
}
|
|
21
|
+
}
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Expo config plugin. AndySU needs nothing in the app's own manifest: the frozen host is produced
|
|
2
|
+
// from the finished APK by `andysu split --transparent` (or the Gradle plugin). The plugin records
|
|
3
|
+
// the andysu.toml location in the Android app's gradle.properties so build scripts can find it.
|
|
4
|
+
const { withGradleProperties } = require('@expo/config-plugins');
|
|
5
|
+
|
|
6
|
+
module.exports = function withAndySU(config, props = {}) {
|
|
7
|
+
return withGradleProperties(config, (c) => {
|
|
8
|
+
const key = 'andysu.toml';
|
|
9
|
+
c.modResults = c.modResults.filter((p) => !(p.type === 'property' && p.key === key));
|
|
10
|
+
c.modResults.push({ type: 'property', key, value: props.toml || '../andysu.toml' });
|
|
11
|
+
return c;
|
|
12
|
+
});
|
|
13
|
+
};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Update status as reported by the AndySU host. */
|
|
2
|
+
export interface Status {
|
|
3
|
+
state: 'idle' | 'checking' | 'up_to_date' | 'deferred' | 'available' | 'downloading' | 'staged' | 'applying' | 'shell_update' | 'error';
|
|
4
|
+
release?: string;
|
|
5
|
+
hot_only?: boolean;
|
|
6
|
+
mandatory?: boolean;
|
|
7
|
+
download_bytes?: number;
|
|
8
|
+
code?: string;
|
|
9
|
+
message?: string;
|
|
10
|
+
[k: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface HostInfo {
|
|
13
|
+
/** False when the app is not running under an AndySU host (e.g. a debug build). */
|
|
14
|
+
present: boolean;
|
|
15
|
+
apiLevel?: number;
|
|
16
|
+
version?: string;
|
|
17
|
+
isFactory?: boolean;
|
|
18
|
+
slotDir?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Information about the running payload. */
|
|
21
|
+
export declare function info(): Promise<HostInfo>;
|
|
22
|
+
/** The updater's current status. */
|
|
23
|
+
export declare function status(): Promise<Status>;
|
|
24
|
+
/** Check now (download and stage according to the configured mode). */
|
|
25
|
+
export declare function checkNow(): void;
|
|
26
|
+
/** Apply a staged update now (live if possible, otherwise by restarting). */
|
|
27
|
+
export declare function applyNow(): void;
|
|
28
|
+
/** Restart into the newest staged or committed payload. */
|
|
29
|
+
export declare function restartNow(): void;
|
|
30
|
+
export type ApplyPolicy = 'live' | 'restart' | 'next_start';
|
|
31
|
+
/**
|
|
32
|
+
* How staged updates apply on this install: swap live (the default), restart the app, or wait for
|
|
33
|
+
* the next start. Pass null to return to the policy configured in andysu.toml.
|
|
34
|
+
*/
|
|
35
|
+
export declare function setApplyPolicy(policy: ApplyPolicy | null): void;
|
|
36
|
+
export declare function applyPolicy(): Promise<ApplyPolicy>;
|
|
37
|
+
/** Commit this payload generation (call once the app is known to work). */
|
|
38
|
+
export declare function markHealthy(): void;
|
|
39
|
+
/** Listen for status changes. Returns an unsubscribe function. */
|
|
40
|
+
export declare function onStatus(cb: (s: Status) => void): () => void;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.info = info;
|
|
4
|
+
exports.status = status;
|
|
5
|
+
exports.checkNow = checkNow;
|
|
6
|
+
exports.applyNow = applyNow;
|
|
7
|
+
exports.restartNow = restartNow;
|
|
8
|
+
exports.setApplyPolicy = setApplyPolicy;
|
|
9
|
+
exports.applyPolicy = applyPolicy;
|
|
10
|
+
exports.markHealthy = markHealthy;
|
|
11
|
+
exports.onStatus = onStatus;
|
|
12
|
+
const react_native_1 = require("react-native");
|
|
13
|
+
const native = react_native_1.Platform.OS === 'android' ? react_native_1.NativeModules.AndySU : undefined;
|
|
14
|
+
/** Information about the running payload. */
|
|
15
|
+
async function info() {
|
|
16
|
+
if (!native)
|
|
17
|
+
return { present: false };
|
|
18
|
+
return JSON.parse(await native.info());
|
|
19
|
+
}
|
|
20
|
+
/** The updater's current status. */
|
|
21
|
+
async function status() {
|
|
22
|
+
if (!native)
|
|
23
|
+
return { state: 'idle' };
|
|
24
|
+
return JSON.parse(await native.status());
|
|
25
|
+
}
|
|
26
|
+
/** Check now (download and stage according to the configured mode). */
|
|
27
|
+
function checkNow() {
|
|
28
|
+
native === null || native === void 0 ? void 0 : native.checkNow();
|
|
29
|
+
}
|
|
30
|
+
/** Apply a staged update now (live if possible, otherwise by restarting). */
|
|
31
|
+
function applyNow() {
|
|
32
|
+
native === null || native === void 0 ? void 0 : native.applyNow();
|
|
33
|
+
}
|
|
34
|
+
/** Restart into the newest staged or committed payload. */
|
|
35
|
+
function restartNow() {
|
|
36
|
+
native === null || native === void 0 ? void 0 : native.restartNow();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* How staged updates apply on this install: swap live (the default), restart the app, or wait for
|
|
40
|
+
* the next start. Pass null to return to the policy configured in andysu.toml.
|
|
41
|
+
*/
|
|
42
|
+
function setApplyPolicy(policy) {
|
|
43
|
+
native === null || native === void 0 ? void 0 : native.setApplyPolicy(policy);
|
|
44
|
+
}
|
|
45
|
+
async function applyPolicy() {
|
|
46
|
+
if (!native)
|
|
47
|
+
return 'live';
|
|
48
|
+
return native.applyPolicy();
|
|
49
|
+
}
|
|
50
|
+
/** Commit this payload generation (call once the app is known to work). */
|
|
51
|
+
function markHealthy() {
|
|
52
|
+
native === null || native === void 0 ? void 0 : native.markHealthy();
|
|
53
|
+
}
|
|
54
|
+
/** Listen for status changes. Returns an unsubscribe function. */
|
|
55
|
+
function onStatus(cb) {
|
|
56
|
+
if (!native)
|
|
57
|
+
return () => { };
|
|
58
|
+
const sub = new react_native_1.NativeEventEmitter(native).addListener('AndySUStatus', (json) => cb(JSON.parse(json)));
|
|
59
|
+
native.listen();
|
|
60
|
+
return () => sub.remove();
|
|
61
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "andysu-react-native",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.90",
|
|
4
|
+
"description": "AndySU self-updates for React Native and Expo apps on Android (status, check, apply)",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/dixonSolutions/AndySU",
|
|
9
|
+
"directory": "bindings/react-native"
|
|
10
|
+
},
|
|
11
|
+
"main": "lib/index.js",
|
|
12
|
+
"types": "lib/index.d.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"lib",
|
|
15
|
+
"src",
|
|
16
|
+
"android/build.gradle",
|
|
17
|
+
"android/src",
|
|
18
|
+
"app.plugin.js",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -p tsconfig.json",
|
|
23
|
+
"test": "node test/smoke.js"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react-native": ">=0.70"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"typescript": "^5.6.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
|
|
2
|
+
|
|
3
|
+
/** Update status as reported by the AndySU host. */
|
|
4
|
+
export interface Status {
|
|
5
|
+
state: 'idle' | 'checking' | 'up_to_date' | 'deferred' | 'available' | 'downloading' | 'staged' |
|
|
6
|
+
'applying' | 'shell_update' | 'error';
|
|
7
|
+
release?: string;
|
|
8
|
+
hot_only?: boolean;
|
|
9
|
+
mandatory?: boolean;
|
|
10
|
+
download_bytes?: number;
|
|
11
|
+
code?: string;
|
|
12
|
+
message?: string;
|
|
13
|
+
[k: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface HostInfo {
|
|
17
|
+
/** False when the app is not running under an AndySU host (e.g. a debug build). */
|
|
18
|
+
present: boolean;
|
|
19
|
+
apiLevel?: number;
|
|
20
|
+
version?: string;
|
|
21
|
+
isFactory?: boolean;
|
|
22
|
+
slotDir?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const native = Platform.OS === 'android' ? NativeModules.AndySU : undefined;
|
|
26
|
+
|
|
27
|
+
/** Information about the running payload. */
|
|
28
|
+
export async function info(): Promise<HostInfo> {
|
|
29
|
+
if (!native) return { present: false };
|
|
30
|
+
return JSON.parse(await native.info());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The updater's current status. */
|
|
34
|
+
export async function status(): Promise<Status> {
|
|
35
|
+
if (!native) return { state: 'idle' };
|
|
36
|
+
return JSON.parse(await native.status());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Check now (download and stage according to the configured mode). */
|
|
40
|
+
export function checkNow(): void {
|
|
41
|
+
native?.checkNow();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Apply a staged update now (live if possible, otherwise by restarting). */
|
|
45
|
+
export function applyNow(): void {
|
|
46
|
+
native?.applyNow();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Restart into the newest staged or committed payload. */
|
|
50
|
+
export function restartNow(): void {
|
|
51
|
+
native?.restartNow();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type ApplyPolicy = 'live' | 'restart' | 'next_start';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* How staged updates apply on this install: swap live (the default), restart the app, or wait for
|
|
58
|
+
* the next start. Pass null to return to the policy configured in andysu.toml.
|
|
59
|
+
*/
|
|
60
|
+
export function setApplyPolicy(policy: ApplyPolicy | null): void {
|
|
61
|
+
native?.setApplyPolicy(policy);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function applyPolicy(): Promise<ApplyPolicy> {
|
|
65
|
+
if (!native) return 'live';
|
|
66
|
+
return native.applyPolicy();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Commit this payload generation (call once the app is known to work). */
|
|
70
|
+
export function markHealthy(): void {
|
|
71
|
+
native?.markHealthy();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Listen for status changes. Returns an unsubscribe function. */
|
|
75
|
+
export function onStatus(cb: (s: Status) => void): () => void {
|
|
76
|
+
if (!native) return () => {};
|
|
77
|
+
const sub = new NativeEventEmitter(native).addListener('AndySUStatus', (json: string) => cb(JSON.parse(json)));
|
|
78
|
+
native.listen();
|
|
79
|
+
return () => sub.remove();
|
|
80
|
+
}
|
package/README.md
DELETED