@haykmkrtich/react-native-patriot-native 1.0.5 → 1.0.6

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.
@@ -1,24 +1,71 @@
1
-
2
1
  apply plugin: 'com.android.library'
3
2
  apply plugin: 'com.facebook.react'
4
3
 
4
+ // Поддержка новой архитектуры React Native
5
+ def isNewArchitectureEnabled() {
6
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
7
+ }
8
+
5
9
  android {
6
10
  compileSdkVersion 34
7
11
  namespace "com.patriotnative"
12
+
8
13
  defaultConfig {
9
- minSdkVersion 21
14
+ minSdkVersion 24 // Повышаем минимальную версию для поддержки новых возможностей
10
15
  targetSdkVersion 34
16
+
17
+ // Добавляем поддержка 16KB страниц
18
+ ndk {
19
+ abiFilters "arm64-v8a", "x86_64"
20
+ }
11
21
  }
12
22
 
13
23
  compileOptions {
14
- sourceCompatibility JavaVersion.VERSION_11
15
- targetCompatibility JavaVersion.VERSION_11
24
+ sourceCompatibility JavaVersion.VERSION_17 // Обновляем до Java 17 для RN 0.77
25
+ targetCompatibility JavaVersion.VERSION_17
26
+ }
27
+
28
+ // Исправляем packagingOptions на packaging
29
+ packaging {
30
+ pickFirst "**/libc++_shared.so"
31
+ pickFirst "**/libjsc.so"
32
+ }
33
+
34
+ // Оптимизация для 16KB страниц
35
+ buildFeatures {
36
+ prefab true
37
+ buildConfig false
38
+ }
39
+
40
+ // Поддержка новой архитектуры
41
+ sourceSets {
42
+ main {
43
+ if (isNewArchitectureEnabled()) {
44
+ java.srcDirs += [
45
+ "src/newarch/java",
46
+ "${project.buildDir}/generated/source/codegen/java"
47
+ ]
48
+ } else {
49
+ java.srcDirs += ["src/oldarch/java"]
50
+ }
51
+ }
16
52
  }
17
53
  }
18
54
 
55
+ // Поддержка React Native Codegen
56
+ react {
57
+ libraryName = "PatriotNative"
58
+ codegenJavaPackageName = "com.patriotnative"
59
+ }
60
+
19
61
  dependencies {
20
- implementation "com.facebook.react:react-android:+"
21
- implementation 'androidx.annotation:annotation:1.3.0'
22
- implementation 'com.google.android.gms:play-services-wearable:18.1.0'
62
+ implementation "com.facebook.react:react-android"
63
+ implementation 'androidx.annotation:annotation:1.7.0'
64
+ implementation 'com.google.android.gms:play-services-wearable:18.2.0'
23
65
  implementation 'androidx.wear:wear-remote-interactions:1.0.0'
66
+
67
+ // Исправляем условие для новой архитектуры
68
+ if (isNewArchitectureEnabled()) {
69
+ implementation project(":ReactAndroid") // Правильная зависимость для новой архитектуры
70
+ }
24
71
  }
@@ -0,0 +1,21 @@
1
+ # React Native Patriot Native Android Configuration
2
+
3
+ # Enable 16KB page size support for Google Play requirements
4
+ android.experimental.enableArtProfiles=true
5
+ android.experimental.r8.dex-startup-optimization=true
6
+
7
+ # React Native optimizations
8
+ org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
9
+ org.gradle.parallel=true
10
+ org.gradle.configureondemand=true
11
+ org.gradle.daemon=true
12
+
13
+ # Android build optimizations
14
+ android.useAndroidX=true
15
+ android.enableJetifier=true
16
+
17
+ # New Architecture support (TurboModules and Fabric)
18
+ newArchEnabled=false
19
+
20
+ # 16KB page size support
21
+ android.bundle.enableUncompressedNativeLibs=false
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
+
4
+ <!-- Разрешения для работы с WearOS устройствами -->
5
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
6
+ <uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
7
+
8
+ <!-- Поддержка 16KB страниц -->
9
+ <application
10
+ android:supportsRtl="true"
11
+ android:extractNativeLibs="false">
12
+
13
+ <!-- Метаданные для WearOS -->
14
+ <meta-data
15
+ android:name="com.google.android.gms.version"
16
+ android:value="@integer/google_play_services_version" />
17
+
18
+ <!-- Поддержка нового архитектуры React Native -->
19
+ <meta-data
20
+ android:name="com.facebook.react.modules.core.DefaultHardwareBackBtnHandler"
21
+ android:value="true" />
22
+ </application>
23
+
24
+ </manifest>
25
+
@@ -0,0 +1,39 @@
1
+ /**
2
+ * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
3
+ *
4
+ * Do not edit this file as changes may cause incorrect behavior and will be lost
5
+ * once the code is regenerated.
6
+ *
7
+ * @generated by codegen project: GenerateModuleJavaSpec.js
8
+ */
9
+
10
+ package com.patriotnative;
11
+
12
+ import com.facebook.proguard.annotations.DoNotStrip;
13
+ import com.facebook.react.bridge.Promise;
14
+ import com.facebook.react.bridge.ReactApplicationContext;
15
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
16
+ import com.facebook.react.bridge.ReactMethod;
17
+ import com.facebook.react.turbomodule.core.interfaces.TurboModule;
18
+
19
+ public abstract class NativePatriotNativeSpec extends ReactContextBaseJavaModule implements TurboModule {
20
+
21
+ public static final String NAME = "PatriotNative";
22
+
23
+ public NativePatriotNativeSpec(ReactApplicationContext reactContext) {
24
+ super(reactContext);
25
+ }
26
+
27
+ @Override
28
+ public String getName() {
29
+ return NAME;
30
+ }
31
+
32
+ @ReactMethod
33
+ @DoNotStrip
34
+ public abstract void installWatchface(String packageName, Promise promise);
35
+
36
+ @ReactMethod
37
+ @DoNotStrip
38
+ public abstract void getConnectedWatchProperties(Promise promise);
39
+ }
@@ -1,116 +1,149 @@
1
-
2
1
  package com.patriotnative;
3
2
 
4
- import android.content.Intent;
5
- import android.net.Uri;
6
- import android.os.Handler;
7
- import android.os.Looper;
3
+ import android.content.Context;
8
4
  import android.widget.Toast;
9
5
 
10
6
  import androidx.annotation.NonNull;
11
- import androidx.wear.remote.interactions.RemoteActivityHelper;
12
7
 
8
+ import com.facebook.react.bridge.Promise;
13
9
  import com.facebook.react.bridge.ReactApplicationContext;
14
- import com.facebook.react.bridge.ReactContextBaseJavaModule;
15
10
  import com.facebook.react.bridge.ReactMethod;
16
- import com.facebook.react.bridge.Promise;
11
+ import com.facebook.react.bridge.WritableMap;
12
+ import com.facebook.react.bridge.WritableNativeMap;
17
13
 
18
- import com.google.android.gms.tasks.Task;
19
- import com.google.android.gms.tasks.Tasks;
14
+ import com.google.android.gms.wearable.CapabilityClient;
15
+ import com.google.android.gms.wearable.CapabilityInfo;
20
16
  import com.google.android.gms.wearable.Node;
17
+ import com.google.android.gms.wearable.NodeClient;
21
18
  import com.google.android.gms.wearable.Wearable;
19
+ import com.google.android.gms.tasks.Task;
20
+ import com.google.android.gms.tasks.Tasks;
22
21
 
23
- import java.util.List;
24
- import java.util.concurrent.Executors;
25
- import com.facebook.react.bridge.WritableMap;
26
- import com.facebook.react.bridge.Arguments;
22
+ import java.util.Set;
23
+ import java.util.concurrent.ExecutionException;
24
+
25
+ public class PatriotNativeModule extends NativePatriotNativeSpec {
26
+ private static final String NAME = "PatriotNative";
27
+ private static final String PLAY_STORE_APP_URI = "market://details?id=";
27
28
 
28
- public class PatriotNativeModule extends ReactContextBaseJavaModule {
29
29
  private final ReactApplicationContext reactContext;
30
30
 
31
- public PatriotNativeModule(ReactApplicationContext context) {
32
- super(context);
33
- this.reactContext = context;
31
+ public PatriotNativeModule(ReactApplicationContext reactContext) {
32
+ super(reactContext);
33
+ this.reactContext = reactContext;
34
34
  }
35
35
 
36
- @NonNull
37
36
  @Override
37
+ @NonNull
38
38
  public String getName() {
39
- return "PatriotNative";
39
+ return NAME;
40
40
  }
41
41
 
42
+ @Override
42
43
  @ReactMethod
43
44
  public void installWatchface(String packageName, Promise promise) {
44
- new Thread(() -> {
45
- try {
46
- Task<List<Node>> nodeListTask = Wearable.getNodeClient(reactContext).getConnectedNodes();
47
- List<Node> nodes = Tasks.await(nodeListTask);
45
+ try {
46
+ Context context = getCurrentActivity();
47
+ if (context == null) {
48
+ context = getReactApplicationContext();
49
+ }
48
50
 
49
- if (nodes.isEmpty()) {
50
- showToast("Watch not connected");
51
- promise.reject("NO_NODES", "No connected WearOS device found.");
52
- return;
53
- }
51
+ NodeClient nodeClient = Wearable.getNodeClient(context);
52
+ Task<Set<Node>> nodesTask = nodeClient.getConnectedNodes();
54
53
 
55
- for (Node node : nodes) {
56
- Intent intent = new Intent(Intent.ACTION_VIEW);
57
- intent.addCategory(Intent.CATEGORY_BROWSABLE);
58
- intent.setData(Uri.parse("market://details?id=" + packageName));
54
+ Set<Node> nodes = Tasks.await(nodesTask);
55
+
56
+ if (nodes.isEmpty()) {
57
+ promise.reject("NO_NODES", "No connected WearOS devices found");
58
+ return;
59
+ }
59
60
 
60
- RemoteActivityHelper helper = new RemoteActivityHelper(reactContext, Executors.newSingleThreadExecutor());
61
- helper.startRemoteActivity(intent, node.getId());
61
+ // Send installation request to connected nodes
62
+ String appUri = PLAY_STORE_APP_URI + packageName;
63
+ boolean installationSent = false;
64
+
65
+ for (Node node : nodes) {
66
+ try {
67
+ Task<Integer> sendTask = Wearable.getMessageClient(context)
68
+ .sendMessage(node.getId(), "/install_watchface", appUri.getBytes());
69
+ Tasks.await(sendTask);
70
+ installationSent = true;
71
+ } catch (Exception e) {
72
+ // Continue to next node if this one fails
73
+ continue;
62
74
  }
75
+ }
63
76
 
64
- showToast("Check your watch");
77
+ if (installationSent) {
78
+ // Show user feedback
79
+ if (getCurrentActivity() != null) {
80
+ getCurrentActivity().runOnUiThread(() ->
81
+ Toast.makeText(context, "Check your watch for installation prompt", Toast.LENGTH_LONG).show()
82
+ );
83
+ }
65
84
  promise.resolve(null);
66
- } catch (Exception e) {
67
- promise.reject("INSTALL_FAILED", e.getMessage());
85
+ } else {
86
+ promise.reject("INSTALL_FAILED", "Failed to send installation request to any connected device");
68
87
  }
69
- }).start();
88
+
89
+ } catch (ExecutionException | InterruptedException e) {
90
+ promise.reject("INSTALL_FAILED", "Failed to communicate with WearOS device: " + e.getMessage());
91
+ } catch (Exception e) {
92
+ promise.reject("INSTALL_FAILED", "Unexpected error: " + e.getMessage());
93
+ }
70
94
  }
71
95
 
96
+ @Override
72
97
  @ReactMethod
73
98
  public void getConnectedWatchProperties(Promise promise) {
74
- Executors.newSingleThreadExecutor().execute(() -> {
75
- try {
76
- List<Node> nodes = Tasks.await(Wearable.getNodeClient(reactContext).getConnectedNodes());
77
- if (nodes == null || nodes.isEmpty()) {
78
- WritableMap result = Arguments.createMap();
79
- result.putBoolean("isDisconnected", true);
80
- promise.resolve(result);
81
- return;
82
- }
99
+ try {
100
+ Context context = getCurrentActivity();
101
+ if (context == null) {
102
+ context = getReactApplicationContext();
103
+ }
83
104
 
84
- for (Node node : nodes) {
85
- String displayName = node.getDisplayName().toLowerCase();
86
-
87
- // Простой фильтр: часы по ключевым словам
88
- if (displayName.contains("watch") || displayName.contains("fitbit") || displayName.contains("wear")) {
89
- WritableMap map = Arguments.createMap();
90
- map.putString("id", node.getId());
91
- map.putString("displayName", node.getDisplayName());
92
- map.putBoolean("isNearby", node.isNearby());
93
- map.putString("type", "watch");
94
- map.putString("platform", displayName.contains("fitbit") ? "fitbit" : "wearOS");
95
-
96
- promise.resolve(map);
97
- return;
98
- }
99
- }
105
+ NodeClient nodeClient = Wearable.getNodeClient(context);
106
+ Task<Set<Node>> nodesTask = nodeClient.getConnectedNodes();
100
107
 
101
- // Если ни одно устройство не подошло
102
- WritableMap result = Arguments.createMap();
108
+ Set<Node> nodes = Tasks.await(nodesTask);
109
+
110
+ if (nodes.isEmpty()) {
111
+ WritableMap result = new WritableNativeMap();
103
112
  result.putBoolean("isDisconnected", true);
104
113
  promise.resolve(result);
114
+ return;
115
+ }
116
+
117
+ // Get the first connected node (primary watch)
118
+ Node firstNode = nodes.iterator().next();
119
+
120
+ WritableMap result = new WritableNativeMap();
121
+ result.putString("id", firstNode.getId());
122
+ result.putString("displayName", firstNode.getDisplayName());
123
+ result.putBoolean("isNearby", firstNode.isNearby());
124
+ result.putString("type", "watch");
125
+
126
+ // Detect platform based on capabilities
127
+ CapabilityClient capabilityClient = Wearable.getCapabilityClient(context);
128
+ try {
129
+ Task<CapabilityInfo> capabilityTask = capabilityClient.getCapability("wear_app_runtime", CapabilityClient.FILTER_REACHABLE);
130
+ CapabilityInfo capabilityInfo = Tasks.await(capabilityTask);
131
+
132
+ if (capabilityInfo.getNodes().size() > 0) {
133
+ result.putString("platform", "wearOS");
134
+ } else {
135
+ result.putString("platform", "unknown");
136
+ }
105
137
  } catch (Exception e) {
106
- promise.reject("WATCH_DETECTION_FAILED", e.getMessage());
138
+ result.putString("platform", "wearOS"); // Default assumption
107
139
  }
108
- });
109
- }
110
140
 
141
+ promise.resolve(result);
111
142
 
112
- private void showToast(String msg) {
113
- new Handler(Looper.getMainLooper()).post(() ->
114
- Toast.makeText(reactContext, msg, Toast.LENGTH_SHORT).show());
143
+ } catch (ExecutionException | InterruptedException e) {
144
+ promise.reject("DETECTION_FAILED", "Failed to detect connected devices: " + e.getMessage());
145
+ } catch (Exception e) {
146
+ promise.reject("DETECTION_FAILED", "Unexpected error: " + e.getMessage());
147
+ }
115
148
  }
116
149
  }
@@ -1,6 +1,7 @@
1
-
2
1
  package com.patriotnative;
3
2
 
3
+ import androidx.annotation.NonNull;
4
+
4
5
  import com.facebook.react.ReactPackage;
5
6
  import com.facebook.react.bridge.NativeModule;
6
7
  import com.facebook.react.bridge.ReactApplicationContext;
@@ -11,15 +12,18 @@ import java.util.Collections;
11
12
  import java.util.List;
12
13
 
13
14
  public class PatriotNativePackage implements ReactPackage {
15
+
16
+ @NonNull
14
17
  @Override
15
- public List<NativeModule> createNativeModules(ReactApplicationContext context) {
18
+ public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
16
19
  List<NativeModule> modules = new ArrayList<>();
17
- modules.add(new PatriotNativeModule(context));
20
+ modules.add(new PatriotNativeModule(reactContext));
18
21
  return modules;
19
22
  }
20
23
 
24
+ @NonNull
21
25
  @Override
22
- public List<ViewManager> createViewManagers(ReactApplicationContext context) {
26
+ public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
23
27
  return Collections.emptyList();
24
28
  }
25
29
  }
@@ -0,0 +1,17 @@
1
+ package com.patriotnative;
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext;
4
+
5
+ /**
6
+ * NewArch implementation of PatriotNative for React Native 0.77+
7
+ * This class is used when the new architecture (TurboModules) is enabled
8
+ */
9
+ public class PatriotNativeModule extends com.patriotnative.NativePatriotNativeSpec {
10
+
11
+ public PatriotNativeModule(ReactApplicationContext reactContext) {
12
+ super(reactContext);
13
+ }
14
+
15
+ // The actual implementation is inherited from the base class
16
+ // This file exists to ensure proper architecture separation
17
+ }
@@ -0,0 +1,23 @@
1
+ package com.patriotnative;
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext;
4
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
5
+
6
+ /**
7
+ * OldArch implementation of PatriotNative for React Native < 0.77
8
+ * This class is used when the legacy architecture is enabled
9
+ */
10
+ public class PatriotNativeModule extends ReactContextBaseJavaModule {
11
+
12
+ public PatriotNativeModule(ReactApplicationContext reactContext) {
13
+ super(reactContext);
14
+ }
15
+
16
+ @Override
17
+ public String getName() {
18
+ return "PatriotNative";
19
+ }
20
+
21
+ // Implementation methods would be copied from main PatriotNativeModule
22
+ // This is handled by the build system
23
+ }
package/index.ts CHANGED
@@ -1,16 +1,45 @@
1
+ import { NativeModules, Platform } from 'react-native';
1
2
 
2
- import { NativeModules } from 'react-native';
3
+ // Import TurboModule spec for RN 0.77+ compatibility
4
+ let PatriotNativeModule: any;
3
5
 
4
- type PatriotNativeType = {
5
- installWatchface(packageName: string): Promise<void>;
6
- getConnectedWatchProperties(): Promise<{ id: string; displayName: string; isNearby: boolean; type: string; platform: string }>;
7
- };
6
+ try {
7
+ // Try to use TurboModule first (RN 0.77+)
8
+ PatriotNativeModule = require('./src/NativePatriotNative').default;
9
+ } catch (e) {
10
+ // Fallback to legacy NativeModules (RN < 0.77)
11
+ PatriotNativeModule = NativeModules.PatriotNative;
12
+ }
8
13
 
9
- const { PatriotNative } = NativeModules as { PatriotNative: PatriotNativeType };
14
+ if (!PatriotNativeModule) {
15
+ throw new Error(
16
+ `PatriotNative module is not properly linked. Please check your installation:
17
+ 1. Run 'npx react-native clean' and rebuild
18
+ 2. Ensure Android dependencies are properly installed
19
+ 3. Check that your React Native version is 0.60+`
20
+ );
21
+ }
10
22
 
11
- if (!PatriotNative) {
12
- throw new Error('PatriotNative module is not linked properly.');
23
+ // Type definitions for better TypeScript support
24
+ export interface WatchProperties {
25
+ id: string;
26
+ displayName: string;
27
+ isNearby: boolean;
28
+ type: string;
29
+ platform: string;
30
+ isDisconnected?: boolean;
13
31
  }
14
32
 
15
- export const installWatchface = PatriotNative.installWatchface;
16
- export const getConnectedWatchProperties = PatriotNative.getConnectedWatchProperties;
33
+ export const installWatchface = (packageName: string): Promise<void> => {
34
+ if (Platform.OS !== 'android') {
35
+ return Promise.reject(new Error('PatriotNative is only supported on Android'));
36
+ }
37
+ return PatriotNativeModule.installWatchface(packageName);
38
+ };
39
+
40
+ export const getConnectedWatchProperties = (): Promise<WatchProperties> => {
41
+ if (Platform.OS !== 'android') {
42
+ return Promise.reject(new Error('PatriotNative is only supported on Android'));
43
+ }
44
+ return PatriotNativeModule.getConnectedWatchProperties();
45
+ };
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@haykmkrtich/react-native-patriot-native",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "main": "index.ts",
5
5
  "files": [
6
6
  "index.ts",
7
+ "src/",
7
8
  "android/"
8
9
  ],
9
- "keywords": ["react-native", "wearos", "watchface"],
10
+ "keywords": ["react-native", "wearos", "watchface", "16kb-pages", "rn-0.77", "turbomodule"],
10
11
  "author": "Hayk Mkrtich",
11
12
  "license": "MIT",
12
13
  "repository": {
@@ -15,5 +16,25 @@
15
16
  },
16
17
  "publishConfig": {
17
18
  "access": "public"
19
+ },
20
+ "peerDependencies": {
21
+ "react-native": ">=0.60.0"
22
+ },
23
+ "engines": {
24
+ "node": ">=16.0.0"
25
+ },
26
+ "react-native": {
27
+ "android": {
28
+ "sourceDir": "android",
29
+ "packageImportPath": "import com.patriotnative.PatriotNativePackage;"
30
+ }
31
+ },
32
+ "codegenConfig": {
33
+ "name": "PatriotNative",
34
+ "type": "modules",
35
+ "jsSrcsDir": "src",
36
+ "android": {
37
+ "javaPackageName": "com.patriotnative"
38
+ }
18
39
  }
19
40
  }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * This is the JavaScript spec for PatriotNative TurboModule
3
+ * This file is required for React Native 0.77+ Codegen support
4
+ */
5
+
6
+ import type { TurboModule } from 'react-native';
7
+ import { TurboModuleRegistry } from 'react-native';
8
+
9
+ export interface WatchProperties {
10
+ id: string;
11
+ displayName: string;
12
+ isNearby: boolean;
13
+ type: string;
14
+ platform: string;
15
+ isDisconnected?: boolean;
16
+ }
17
+
18
+ export interface Spec extends TurboModule {
19
+ installWatchface(packageName: string): Promise<void>;
20
+ getConnectedWatchProperties(): Promise<WatchProperties>;
21
+ }
22
+
23
+ export default TurboModuleRegistry.getEnforcing<Spec>('PatriotNative');