@symbiote-native/cellular 0.0.1
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/LICENSE +21 -0
- package/README.md +232 -0
- package/build/angular/index.d.ts +2 -0
- package/build/angular/index.js +4 -0
- package/build/angular/services/permissions.service/index.d.ts +8 -0
- package/build/angular/services/permissions.service/index.js +76 -0
- package/build/core/cellular.d.ts +40 -0
- package/build/core/cellular.js +116 -0
- package/build/core/index.d.ts +2 -0
- package/build/core/index.js +2 -0
- package/build/core/native-module.d.ts +13 -0
- package/build/core/native-module.js +3 -0
- package/build/core/types.d.ts +14 -0
- package/build/core/types.js +19 -0
- package/build/react/hooks/use-permissions/index.d.ts +6 -0
- package/build/react/hooks/use-permissions/index.js +28 -0
- package/build/react/index.d.ts +2 -0
- package/build/react/index.js +4 -0
- package/build/vue/composables/use-permissions/index.d.ts +7 -0
- package/build/vue/composables/use-permissions/index.js +21 -0
- package/build/vue/index.d.ts +2 -0
- package/build/vue/index.js +4 -0
- package/build-ngc/angular/index.d.ts +2 -0
- package/build-ngc/angular/index.js +5 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/angular/services/permissions.service/index.d.ts +11 -0
- package/build-ngc/angular/services/permissions.service/index.js +35 -0
- package/build-ngc/angular/services/permissions.service/index.js.map +1 -0
- package/build-ngc/core/cellular.d.ts +40 -0
- package/build-ngc/core/cellular.js +117 -0
- package/build-ngc/core/cellular.js.map +1 -0
- package/build-ngc/core/index.d.ts +2 -0
- package/build-ngc/core/index.js +3 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +13 -0
- package/build-ngc/core/native-module.js +4 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/types.d.ts +14 -0
- package/build-ngc/core/types.js +20 -0
- package/build-ngc/core/types.js.map +1 -0
- package/native-link.json +12 -0
- package/package.json +107 -0
- package/src/angular/index.ts +4 -0
- package/src/angular/services/permissions.service/index.ts +35 -0
- package/src/angular/services/permissions.service/permissions.service.test.ts +109 -0
- package/src/core/cellular.test.ts +193 -0
- package/src/core/cellular.ts +131 -0
- package/src/core/index.ts +16 -0
- package/src/core/native-module.ts +24 -0
- package/src/core/types.ts +23 -0
- package/src/react/hooks/use-permissions/index.ts +39 -0
- package/src/react/hooks/use-permissions/use-permissions.test.tsx +115 -0
- package/src/react/index.ts +4 -0
- package/src/vue/composables/use-permissions/index.ts +34 -0
- package/src/vue/composables/use-permissions/use-permissions.test.ts +103 -0
- package/src/vue/index.ts +4 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// @symbiote-native/cellular/vue: the Vue entry over the framework-agnostic core, plus
|
|
2
|
+
// usePermissions — the only stateful surface this package needs a lifecycle wrapper for.
|
|
3
|
+
export { usePermissions } from './composables/use-permissions/index.js';
|
|
4
|
+
export * from '../core/index.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// @symbiote-native/cellular/angular: the Angular entry over the framework-agnostic core, plus
|
|
2
|
+
// PermissionsService — the only stateful surface this package needs a lifecycle wrapper for.
|
|
3
|
+
export { PermissionsService } from './services/permissions.service';
|
|
4
|
+
export * from '../core';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/angular/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,6FAA6F;AAC7F,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Signal } from '@angular/core';
|
|
2
|
+
import { type PermissionResponse } from '../../../core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
export declare class PermissionsService {
|
|
5
|
+
private readonly status;
|
|
6
|
+
connect(): Signal<PermissionResponse | null>;
|
|
7
|
+
get(): Promise<PermissionResponse>;
|
|
8
|
+
request(): Promise<PermissionResponse>;
|
|
9
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PermissionsService, never>;
|
|
10
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<PermissionsService>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Angular twin of React's `usePermissions` hook and Vue's `usePermissions` composable —
|
|
2
|
+
// see BatteryStateService's connect() pattern for the shape rationale.
|
|
3
|
+
//
|
|
4
|
+
// readonly status = inject(PermissionsService).connect();
|
|
5
|
+
// // template: {{ status()?.granted }}
|
|
6
|
+
import { Injectable, signal } from '@angular/core';
|
|
7
|
+
import { getPermissionsAsync, requestPermissionsAsync, } from '../../../core';
|
|
8
|
+
import * as i0 from "@angular/core";
|
|
9
|
+
export class PermissionsService {
|
|
10
|
+
status = signal(null, /* @ts-ignore */
|
|
11
|
+
...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
|
|
12
|
+
connect() {
|
|
13
|
+
if (this.status() === null) {
|
|
14
|
+
void this.get();
|
|
15
|
+
}
|
|
16
|
+
return this.status.asReadonly();
|
|
17
|
+
}
|
|
18
|
+
async get() {
|
|
19
|
+
const response = await getPermissionsAsync();
|
|
20
|
+
this.status.set(response);
|
|
21
|
+
return response;
|
|
22
|
+
}
|
|
23
|
+
async request() {
|
|
24
|
+
const response = await requestPermissionsAsync();
|
|
25
|
+
this.status.set(response);
|
|
26
|
+
return response;
|
|
27
|
+
}
|
|
28
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: PermissionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
29
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: PermissionsService, providedIn: 'root' });
|
|
30
|
+
}
|
|
31
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: PermissionsService, decorators: [{
|
|
32
|
+
type: Injectable,
|
|
33
|
+
args: [{ providedIn: 'root' }]
|
|
34
|
+
}] });
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/permissions.service/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,uEAAuE;AACvE,EAAE;AACF,4DAA4D;AAC5D,yCAAyC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAChE,OAAO,EACL,mBAAmB,EACnB,uBAAuB,GAExB,MAAM,eAAe,CAAC;;AAGvB,MAAM,OAAO,kBAAkB;IACZ,MAAM,GAAG,MAAM,CAA4B,IAAI;+EAAC,CAAC;IAElE,OAAO;QACL,IAAI,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;YAC3B,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;QAClB,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,GAAG;QACP,MAAM,QAAQ,GAAG,MAAM,mBAAmB,EAAE,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1B,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,QAAQ,GAAG,MAAM,uBAAuB,EAAE,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1B,OAAO,QAAQ,CAAC;IAClB,CAAC;uGApBU,kBAAkB;2GAAlB,kBAAkB,cADL,MAAM;;2FACnB,kBAAkB;kBAD9B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type PermissionResponse } from 'expo-modules-core';
|
|
2
|
+
import { CellularGeneration } from './types';
|
|
3
|
+
/** Gets the generation of the device's current connection. */
|
|
4
|
+
export declare function getCellularGenerationAsync(): Promise<CellularGeneration>;
|
|
5
|
+
/**
|
|
6
|
+
* Tells if the carrier of the SIM card allows to make VoIP calls on its network.
|
|
7
|
+
* @deprecated This method is deprecated and will be removed in a future SDK version.
|
|
8
|
+
* @platform android
|
|
9
|
+
*/
|
|
10
|
+
export declare function allowsVoipAsync(): Promise<boolean | null>;
|
|
11
|
+
/**
|
|
12
|
+
* Gets the ISO country code of the current registered operator's MCC (Mobile Country Code).
|
|
13
|
+
* @platform android
|
|
14
|
+
*/
|
|
15
|
+
export declare function getIsoCountryCodeAsync(): Promise<string | null>;
|
|
16
|
+
/**
|
|
17
|
+
* Gets the name of the user's cellular service provider.
|
|
18
|
+
* @platform android
|
|
19
|
+
*/
|
|
20
|
+
export declare function getCarrierNameAsync(): Promise<string | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Gets the mobile country code (MCC) of the device's current registered operator.
|
|
23
|
+
* @platform android
|
|
24
|
+
*/
|
|
25
|
+
export declare function getMobileCountryCodeAsync(): Promise<string | null>;
|
|
26
|
+
/**
|
|
27
|
+
* Gets the mobile network code (MNC) of the device's current registered operator.
|
|
28
|
+
* @platform android
|
|
29
|
+
*/
|
|
30
|
+
export declare function getMobileNetworkCodeAsync(): Promise<string | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Checks user's permissions for accessing cellular information. Android only — iOS/web need no
|
|
33
|
+
* permission for cellular info, so this always resolves granted there.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getPermissionsAsync(): Promise<PermissionResponse>;
|
|
36
|
+
/**
|
|
37
|
+
* Asks the user to grant permissions for accessing cellular information. Android only — iOS/web
|
|
38
|
+
* need no permission for cellular info, so this always resolves granted there.
|
|
39
|
+
*/
|
|
40
|
+
export declare function requestPermissionsAsync(): Promise<PermissionResponse>;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Hand-ported from .vendors/expo/packages/expo-cellular/src/Cellular.ts (sdk-57). Carrier/SIM
|
|
2
|
+
// info (ISO country code, carrier name, MCC/MNC) and allowsVoipAsync are Android-only upstream —
|
|
3
|
+
// each short-circuits to `null` on iOS before ever reaching the native module, since iOS exposes
|
|
4
|
+
// none of this. getPermissionsAsync/requestPermissionsAsync mirror that same split: Android
|
|
5
|
+
// delegates to the native module, every other platform needs no permission for cellular info at
|
|
6
|
+
// all and resolves a plain GRANTED literal. Upstream's web branch (navigator.connection) is out
|
|
7
|
+
// of scope — this repo has no web target for this package's native-module resolution path.
|
|
8
|
+
import { Platform, PermissionStatus, UnavailabilityError, } from 'expo-modules-core';
|
|
9
|
+
import { expoCellular } from './native-module';
|
|
10
|
+
import { CellularGeneration } from './types';
|
|
11
|
+
const NATIVE_MODULE_NAME = 'expo-cellular';
|
|
12
|
+
const GRANTED_PERMISSION_RESPONSE = {
|
|
13
|
+
status: PermissionStatus.GRANTED,
|
|
14
|
+
expires: 'never',
|
|
15
|
+
granted: true,
|
|
16
|
+
canAskAgain: true,
|
|
17
|
+
};
|
|
18
|
+
/** Gets the generation of the device's current connection. */
|
|
19
|
+
export async function getCellularGenerationAsync() {
|
|
20
|
+
if (!expoCellular.getCellularGenerationAsync) {
|
|
21
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getCellularGenerationAsync');
|
|
22
|
+
}
|
|
23
|
+
return expoCellular.getCellularGenerationAsync();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Tells if the carrier of the SIM card allows to make VoIP calls on its network.
|
|
27
|
+
* @deprecated This method is deprecated and will be removed in a future SDK version.
|
|
28
|
+
* @platform android
|
|
29
|
+
*/
|
|
30
|
+
export async function allowsVoipAsync() {
|
|
31
|
+
if (Platform.OS === 'ios') {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (!expoCellular.allowsVoipAsync) {
|
|
35
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'allowsVoipAsync');
|
|
36
|
+
}
|
|
37
|
+
return expoCellular.allowsVoipAsync();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Gets the ISO country code of the current registered operator's MCC (Mobile Country Code).
|
|
41
|
+
* @platform android
|
|
42
|
+
*/
|
|
43
|
+
export async function getIsoCountryCodeAsync() {
|
|
44
|
+
if (Platform.OS === 'ios') {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (!expoCellular.getIsoCountryCodeAsync) {
|
|
48
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getIsoCountryCodeAsync');
|
|
49
|
+
}
|
|
50
|
+
return expoCellular.getIsoCountryCodeAsync();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Gets the name of the user's cellular service provider.
|
|
54
|
+
* @platform android
|
|
55
|
+
*/
|
|
56
|
+
export async function getCarrierNameAsync() {
|
|
57
|
+
if (Platform.OS === 'ios') {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (!expoCellular.getCarrierNameAsync) {
|
|
61
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getCarrierNameAsync');
|
|
62
|
+
}
|
|
63
|
+
return expoCellular.getCarrierNameAsync();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Gets the mobile country code (MCC) of the device's current registered operator.
|
|
67
|
+
* @platform android
|
|
68
|
+
*/
|
|
69
|
+
export async function getMobileCountryCodeAsync() {
|
|
70
|
+
if (Platform.OS === 'ios') {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
if (!expoCellular.getMobileCountryCodeAsync) {
|
|
74
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getMobileCountryCodeAsync');
|
|
75
|
+
}
|
|
76
|
+
return expoCellular.getMobileCountryCodeAsync();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Gets the mobile network code (MNC) of the device's current registered operator.
|
|
80
|
+
* @platform android
|
|
81
|
+
*/
|
|
82
|
+
export async function getMobileNetworkCodeAsync() {
|
|
83
|
+
if (Platform.OS === 'ios') {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
if (!expoCellular.getMobileNetworkCodeAsync) {
|
|
87
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getMobileNetworkCodeAsync');
|
|
88
|
+
}
|
|
89
|
+
return expoCellular.getMobileNetworkCodeAsync();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Checks user's permissions for accessing cellular information. Android only — iOS/web need no
|
|
93
|
+
* permission for cellular info, so this always resolves granted there.
|
|
94
|
+
*/
|
|
95
|
+
export async function getPermissionsAsync() {
|
|
96
|
+
if (Platform.OS === 'android') {
|
|
97
|
+
if (!expoCellular.getPermissionsAsync) {
|
|
98
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getPermissionsAsync');
|
|
99
|
+
}
|
|
100
|
+
return expoCellular.getPermissionsAsync();
|
|
101
|
+
}
|
|
102
|
+
return GRANTED_PERMISSION_RESPONSE;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Asks the user to grant permissions for accessing cellular information. Android only — iOS/web
|
|
106
|
+
* need no permission for cellular info, so this always resolves granted there.
|
|
107
|
+
*/
|
|
108
|
+
export async function requestPermissionsAsync() {
|
|
109
|
+
if (Platform.OS === 'android') {
|
|
110
|
+
if (!expoCellular.requestPermissionsAsync) {
|
|
111
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'requestPermissionsAsync');
|
|
112
|
+
}
|
|
113
|
+
return expoCellular.requestPermissionsAsync();
|
|
114
|
+
}
|
|
115
|
+
return GRANTED_PERMISSION_RESPONSE;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=cellular.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cellular.js","sourceRoot":"","sources":["../../src/core/cellular.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { getCellularGenerationAsync, allowsVoipAsync, getIsoCountryCodeAsync, getCarrierNameAsync, getMobileCountryCodeAsync, getMobileNetworkCodeAsync, getPermissionsAsync, requestPermissionsAsync, } from './cellular';
|
|
2
|
+
export { CellularGeneration, PermissionStatus, type PermissionResponse, type PermissionExpiration, } from './types';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { getCellularGenerationAsync, allowsVoipAsync, getIsoCountryCodeAsync, getCarrierNameAsync, getMobileCountryCodeAsync, getMobileNetworkCodeAsync, getPermissionsAsync, requestPermissionsAsync, } from './cellular';
|
|
2
|
+
export { CellularGeneration, PermissionStatus, } from './types';
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,yBAAyB,EACzB,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,kBAAkB,EAClB,gBAAgB,GAGjB,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PermissionResponse } from 'expo-modules-core';
|
|
2
|
+
import type { CellularGeneration } from './types';
|
|
3
|
+
export type INativeCellularModule = {
|
|
4
|
+
getCellularGenerationAsync?(): Promise<CellularGeneration>;
|
|
5
|
+
allowsVoipAsync?(): Promise<boolean>;
|
|
6
|
+
getIsoCountryCodeAsync?(): Promise<string | null>;
|
|
7
|
+
getCarrierNameAsync?(): Promise<string | null>;
|
|
8
|
+
getMobileCountryCodeAsync?(): Promise<string | null>;
|
|
9
|
+
getMobileNetworkCodeAsync?(): Promise<string | null>;
|
|
10
|
+
getPermissionsAsync?(): Promise<PermissionResponse>;
|
|
11
|
+
requestPermissionsAsync?(): Promise<PermissionResponse>;
|
|
12
|
+
};
|
|
13
|
+
export declare const expoCellular: INativeCellularModule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAIxD,MAAM,yBAAyB,GAAG,cAAc,CAAC;AAmBjD,MAAM,CAAC,MAAM,YAAY,GAAG,mBAAmB,CAAwB,yBAAyB,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { PermissionStatus, type PermissionResponse, type PermissionExpiration, } from 'expo-modules-core';
|
|
2
|
+
/** Telecommunication technology used to establish a cellular connection. */
|
|
3
|
+
export declare enum CellularGeneration {
|
|
4
|
+
/** The device's connection generation could not be determined. */
|
|
5
|
+
UNKNOWN = 0,
|
|
6
|
+
/** 2nd generation (GPRS/EDGE-class) connection. */
|
|
7
|
+
CELLULAR_2G = 1,
|
|
8
|
+
/** 3rd generation (UMTS/HSPA-class) connection. */
|
|
9
|
+
CELLULAR_3G = 2,
|
|
10
|
+
/** 4th generation (LTE-class) connection. */
|
|
11
|
+
CELLULAR_4G = 3,
|
|
12
|
+
/** 5th generation (NR-class) connection. */
|
|
13
|
+
CELLULAR_5G = 4
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Hand-ported from .vendors/expo/packages/expo-cellular/src/Cellular.types.ts (sdk-57).
|
|
2
|
+
// PermissionResponse/PermissionStatus/PermissionExpiration come straight from
|
|
3
|
+
// expo-modules-core (which re-exports PermissionsInterface) — never from the `expo`
|
|
4
|
+
// meta-package, unlike upstream's own Cellular.ts.
|
|
5
|
+
export { PermissionStatus, } from 'expo-modules-core';
|
|
6
|
+
/** Telecommunication technology used to establish a cellular connection. */
|
|
7
|
+
export var CellularGeneration;
|
|
8
|
+
(function (CellularGeneration) {
|
|
9
|
+
/** The device's connection generation could not be determined. */
|
|
10
|
+
CellularGeneration[CellularGeneration["UNKNOWN"] = 0] = "UNKNOWN";
|
|
11
|
+
/** 2nd generation (GPRS/EDGE-class) connection. */
|
|
12
|
+
CellularGeneration[CellularGeneration["CELLULAR_2G"] = 1] = "CELLULAR_2G";
|
|
13
|
+
/** 3rd generation (UMTS/HSPA-class) connection. */
|
|
14
|
+
CellularGeneration[CellularGeneration["CELLULAR_3G"] = 2] = "CELLULAR_3G";
|
|
15
|
+
/** 4th generation (LTE-class) connection. */
|
|
16
|
+
CellularGeneration[CellularGeneration["CELLULAR_4G"] = 3] = "CELLULAR_4G";
|
|
17
|
+
/** 5th generation (NR-class) connection. */
|
|
18
|
+
CellularGeneration[CellularGeneration["CELLULAR_5G"] = 4] = "CELLULAR_5G";
|
|
19
|
+
})(CellularGeneration || (CellularGeneration = {}));
|
|
20
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,8EAA8E;AAC9E,oFAAoF;AACpF,mDAAmD;AACnD,OAAO,EACL,gBAAgB,GAGjB,MAAM,mBAAmB,CAAC;AAE3B,4EAA4E;AAC5E,MAAM,CAAN,IAAY,kBAWX;AAXD,WAAY,kBAAkB;IAC5B,kEAAkE;IAClE,iEAAW,CAAA;IACX,mDAAmD;IACnD,yEAAe,CAAA;IACf,mDAAmD;IACnD,yEAAe,CAAA;IACf,6CAA6C;IAC7C,yEAAe,CAAA;IACf,4CAA4C;IAC5C,yEAAe,CAAA;AACjB,CAAC,EAXW,kBAAkB,KAAlB,kBAAkB,QAW7B"}
|
package/native-link.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@symbiote-native/cellular",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "expo-cellular wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Cellular generation (2G/3G/4G/5G), carrier/SIM info on Android (ISO country code, carrier name, MCC/MNC), VoIP-support detection, and the permission get/request pair gating carrier info on Android.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/OneEyed1366/symbiote-native.git",
|
|
9
|
+
"directory": "packages/cellular"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/OneEyed1366/symbiote-native/tree/master/packages/cellular#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/OneEyed1366/symbiote-native/issues"
|
|
14
|
+
},
|
|
15
|
+
"author": "Andrey Prokopenko <psevdoproger@gmail.com>",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "./build/core/index.js",
|
|
18
|
+
"module": "./build/core/index.js",
|
|
19
|
+
"types": "./build/core/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./build/core/index.d.ts",
|
|
23
|
+
"default": "./build/core/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./vue": {
|
|
26
|
+
"types": "./build/vue/index.d.ts",
|
|
27
|
+
"default": "./build/vue/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./react": {
|
|
30
|
+
"types": "./build/react/index.d.ts",
|
|
31
|
+
"default": "./build/react/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./angular": {
|
|
34
|
+
"types": "./build-ngc/angular/index.d.ts",
|
|
35
|
+
"react-native": "./build-ngc/angular/index.js",
|
|
36
|
+
"default": "./src/angular/index.ts"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"src",
|
|
41
|
+
"build",
|
|
42
|
+
"build-ngc",
|
|
43
|
+
"native-link.json"
|
|
44
|
+
],
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"expo-cellular": "57.0.1",
|
|
50
|
+
"expo-modules-core": "57.0.5"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@symbiote-native/engine": ">=0.1.7",
|
|
54
|
+
"@angular/core": ">=20",
|
|
55
|
+
"@vue/runtime-core": "^3.5.13",
|
|
56
|
+
"react": ">=19.0.0",
|
|
57
|
+
"react-native": ">=0.86",
|
|
58
|
+
"vue": ">=3.5.0",
|
|
59
|
+
"@symbiote-native/angular": "0.6.1",
|
|
60
|
+
"@symbiote-native/react": "0.2.8",
|
|
61
|
+
"@symbiote-native/vue": "0.3.8"
|
|
62
|
+
},
|
|
63
|
+
"peerDependenciesMeta": {
|
|
64
|
+
"@symbiote-native/angular": {
|
|
65
|
+
"optional": true
|
|
66
|
+
},
|
|
67
|
+
"@symbiote-native/react": {
|
|
68
|
+
"optional": true
|
|
69
|
+
},
|
|
70
|
+
"@symbiote-native/vue": {
|
|
71
|
+
"optional": true
|
|
72
|
+
},
|
|
73
|
+
"@angular/core": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"@vue/runtime-core": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"react": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"vue": {
|
|
83
|
+
"optional": true
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"devDependencies": {
|
|
87
|
+
"@angular/compiler": "^22",
|
|
88
|
+
"@angular/compiler-cli": "^22",
|
|
89
|
+
"@angular/core": "^22",
|
|
90
|
+
"@types/node": "^26.0.0",
|
|
91
|
+
"@types/react": "^19.2.0",
|
|
92
|
+
"@vue/runtime-core": "^3.5.13",
|
|
93
|
+
"react": "19.2.3",
|
|
94
|
+
"typescript": "~6.0.0",
|
|
95
|
+
"@symbiote-native/vue": "0.3.8",
|
|
96
|
+
"@symbiote-native/angular": "0.6.1",
|
|
97
|
+
"@symbiote-native/react": "0.2.8",
|
|
98
|
+
"@symbiote-native/test-utils": "0.1.6",
|
|
99
|
+
"@symbiote-native/engine": "0.1.7"
|
|
100
|
+
},
|
|
101
|
+
"scripts": {
|
|
102
|
+
"typecheck": "tsc --build",
|
|
103
|
+
"clean": "rm -rf build-ngc",
|
|
104
|
+
"ng:build": "pnpm run clean && ngc -p tsconfig.angular.json",
|
|
105
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// @symbiote-native/cellular/angular: the Angular entry over the framework-agnostic core, plus
|
|
2
|
+
// PermissionsService — the only stateful surface this package needs a lifecycle wrapper for.
|
|
3
|
+
export { PermissionsService } from './services/permissions.service';
|
|
4
|
+
export * from '../core';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Angular twin of React's `usePermissions` hook and Vue's `usePermissions` composable —
|
|
2
|
+
// see BatteryStateService's connect() pattern for the shape rationale.
|
|
3
|
+
//
|
|
4
|
+
// readonly status = inject(PermissionsService).connect();
|
|
5
|
+
// // template: {{ status()?.granted }}
|
|
6
|
+
import { Injectable, signal, type Signal } from '@angular/core';
|
|
7
|
+
import {
|
|
8
|
+
getPermissionsAsync,
|
|
9
|
+
requestPermissionsAsync,
|
|
10
|
+
type PermissionResponse,
|
|
11
|
+
} from '../../../core';
|
|
12
|
+
|
|
13
|
+
@Injectable({ providedIn: 'root' })
|
|
14
|
+
export class PermissionsService {
|
|
15
|
+
private readonly status = signal<PermissionResponse | null>(null);
|
|
16
|
+
|
|
17
|
+
connect(): Signal<PermissionResponse | null> {
|
|
18
|
+
if (this.status() === null) {
|
|
19
|
+
void this.get();
|
|
20
|
+
}
|
|
21
|
+
return this.status.asReadonly();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async get(): Promise<PermissionResponse> {
|
|
25
|
+
const response = await getPermissionsAsync();
|
|
26
|
+
this.status.set(response);
|
|
27
|
+
return response;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async request(): Promise<PermissionResponse> {
|
|
31
|
+
const response = await requestPermissionsAsync();
|
|
32
|
+
this.status.set(response);
|
|
33
|
+
return response;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Co-located Angular-driven test (ADR 0025) for PermissionsService. See
|
|
2
|
+
// packages/battery/src/angular/services/battery-state.service/battery-state.service.test.ts for
|
|
3
|
+
// the shared rationale, adapted here to test the get/request permission pair instead of a
|
|
4
|
+
// listener subscription.
|
|
5
|
+
|
|
6
|
+
import '@angular/compiler';
|
|
7
|
+
import { Component, inject, type Signal } from '@angular/core';
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
9
|
+
import { mount, unmount } from '@symbiote-native/angular';
|
|
10
|
+
import { installFabric } from '@symbiote-native/test-utils';
|
|
11
|
+
import { PermissionsService } from './index';
|
|
12
|
+
|
|
13
|
+
type IPermissionResponse = {
|
|
14
|
+
status: string;
|
|
15
|
+
expires: 'never';
|
|
16
|
+
granted: boolean;
|
|
17
|
+
canAskAgain: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const GRANTED: IPermissionResponse = {
|
|
21
|
+
status: 'granted',
|
|
22
|
+
expires: 'never',
|
|
23
|
+
granted: true,
|
|
24
|
+
canAskAgain: true,
|
|
25
|
+
};
|
|
26
|
+
const DENIED: IPermissionResponse = {
|
|
27
|
+
status: 'denied',
|
|
28
|
+
expires: 'never',
|
|
29
|
+
granted: false,
|
|
30
|
+
canAskAgain: true,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getPermissionsAsyncMock = vi.fn(async () => GRANTED);
|
|
34
|
+
const requestPermissionsAsyncMock = vi.fn(async () => GRANTED);
|
|
35
|
+
|
|
36
|
+
vi.mock('../../../core', () => ({
|
|
37
|
+
getPermissionsAsync: () => getPermissionsAsyncMock(),
|
|
38
|
+
requestPermissionsAsync: () => requestPermissionsAsyncMock(),
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
const ROOT_TAG = 973;
|
|
42
|
+
const fabric = installFabric();
|
|
43
|
+
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
44
|
+
|
|
45
|
+
let capturedResult: Signal<IPermissionResponse | null> | undefined;
|
|
46
|
+
let capturedService: PermissionsService | undefined;
|
|
47
|
+
|
|
48
|
+
@Component({
|
|
49
|
+
selector: 'symbiote-permissions-host',
|
|
50
|
+
standalone: true,
|
|
51
|
+
template: '',
|
|
52
|
+
})
|
|
53
|
+
class PermissionsHost {
|
|
54
|
+
private readonly service = inject(PermissionsService);
|
|
55
|
+
readonly status = this.service.connect();
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
capturedResult = this.status;
|
|
59
|
+
capturedService = this.service;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
fabric.reset();
|
|
65
|
+
capturedResult = undefined;
|
|
66
|
+
capturedService = undefined;
|
|
67
|
+
getPermissionsAsyncMock.mockClear();
|
|
68
|
+
requestPermissionsAsyncMock.mockClear();
|
|
69
|
+
getPermissionsAsyncMock.mockResolvedValue(GRANTED);
|
|
70
|
+
requestPermissionsAsyncMock.mockResolvedValue(GRANTED);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterEach(() => unmount(ROOT_TAG));
|
|
74
|
+
|
|
75
|
+
describe('PermissionsService.connect', () => {
|
|
76
|
+
it('starts at null before the initial get() resolves', () => {
|
|
77
|
+
mount(ROOT_TAG, PermissionsHost);
|
|
78
|
+
|
|
79
|
+
expect(capturedResult?.()).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('fetches the permission status once connect() is called', async () => {
|
|
83
|
+
mount(ROOT_TAG, PermissionsHost);
|
|
84
|
+
await tick();
|
|
85
|
+
|
|
86
|
+
expect(capturedResult?.()).toEqual(GRANTED);
|
|
87
|
+
expect(getPermissionsAsyncMock).toHaveBeenCalledTimes(1);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('request() re-fetches and updates the signal', async () => {
|
|
91
|
+
mount(ROOT_TAG, PermissionsHost);
|
|
92
|
+
await tick();
|
|
93
|
+
|
|
94
|
+
requestPermissionsAsyncMock.mockResolvedValueOnce(DENIED);
|
|
95
|
+
await capturedService?.request();
|
|
96
|
+
|
|
97
|
+
expect(capturedResult?.()).toEqual(DENIED);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('get() re-fetches and updates the signal', async () => {
|
|
101
|
+
mount(ROOT_TAG, PermissionsHost);
|
|
102
|
+
await tick();
|
|
103
|
+
|
|
104
|
+
getPermissionsAsyncMock.mockResolvedValueOnce(DENIED);
|
|
105
|
+
await capturedService?.get();
|
|
106
|
+
|
|
107
|
+
expect(capturedResult?.()).toEqual(DENIED);
|
|
108
|
+
});
|
|
109
|
+
});
|