react-native-pdf-jsi 2.2.7 → 3.0.0

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.
@@ -0,0 +1,123 @@
1
+ package org.wonday.pdf;
2
+
3
+ import android.app.DownloadManager;
4
+ import android.content.Intent;
5
+ import android.net.Uri;
6
+ import android.util.Log;
7
+
8
+ import com.facebook.react.bridge.Promise;
9
+ import com.facebook.react.bridge.ReactApplicationContext;
10
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
11
+ import com.facebook.react.bridge.ReactMethod;
12
+
13
+ /**
14
+ * FileManager - Native module for file operations like opening folders
15
+ */
16
+ public class FileManager extends ReactContextBaseJavaModule {
17
+ private static final String TAG = "FileManager";
18
+ private static final String FOLDER_NAME = "PDFDemoApp";
19
+ private final ReactApplicationContext reactContext;
20
+
21
+ public FileManager(ReactApplicationContext reactContext) {
22
+ super(reactContext);
23
+ this.reactContext = reactContext;
24
+ }
25
+
26
+ @Override
27
+ public String getName() {
28
+ return "FileManager";
29
+ }
30
+
31
+ /**
32
+ * Open the Downloads/PDFDemoApp folder in the file manager
33
+ * Multiple fallback strategies for maximum compatibility
34
+ */
35
+ @ReactMethod
36
+ public void openDownloadsFolder(Promise promise) {
37
+ try {
38
+ Log.i(TAG, "📂 [OPEN FOLDER] Attempting to open Downloads/" + FOLDER_NAME);
39
+
40
+ // Strategy 1: Try to open specific Downloads/PDFDemoApp folder
41
+ try {
42
+ Intent specificIntent = new Intent(Intent.ACTION_VIEW);
43
+ Uri folderUri = Uri.parse("content://com.android.externalstorage.documents/document/primary:Download/" + FOLDER_NAME);
44
+ specificIntent.setDataAndType(folderUri, "resource/folder");
45
+ specificIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
46
+
47
+ if (specificIntent.resolveActivity(reactContext.getPackageManager()) != null) {
48
+ reactContext.startActivity(specificIntent);
49
+ Log.i(TAG, "✅ [OPEN FOLDER] Opened specific folder via DocumentsUI");
50
+ promise.resolve(true);
51
+ return;
52
+ }
53
+ } catch (Exception e) {
54
+ Log.i(TAG, "📂 [OPEN FOLDER] Strategy 1 failed, trying fallback...");
55
+ }
56
+
57
+ // Strategy 2: Open Downloads app
58
+ try {
59
+ Log.i(TAG, "📂 [OPEN FOLDER] Trying Downloads app");
60
+ Intent downloadsIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
61
+ downloadsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
62
+
63
+ if (downloadsIntent.resolveActivity(reactContext.getPackageManager()) != null) {
64
+ reactContext.startActivity(downloadsIntent);
65
+ Log.i(TAG, "✅ [OPEN FOLDER] Opened Downloads app");
66
+ promise.resolve(true);
67
+ return;
68
+ }
69
+ } catch (Exception e) {
70
+ Log.i(TAG, "📂 [OPEN FOLDER] Strategy 2 failed, trying fallback...");
71
+ }
72
+
73
+ // Strategy 3: Open Files app with generic CATEGORY_APP_FILES intent
74
+ try {
75
+ Log.i(TAG, "📂 [OPEN FOLDER] Trying Files app");
76
+ Intent filesIntent = new Intent(Intent.ACTION_VIEW);
77
+ filesIntent.addCategory(Intent.CATEGORY_DEFAULT);
78
+ filesIntent.setType("resource/folder");
79
+ filesIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
80
+
81
+ if (filesIntent.resolveActivity(reactContext.getPackageManager()) != null) {
82
+ reactContext.startActivity(filesIntent);
83
+ Log.i(TAG, "✅ [OPEN FOLDER] Opened Files app");
84
+ promise.resolve(true);
85
+ return;
86
+ }
87
+ } catch (Exception e) {
88
+ Log.i(TAG, "📂 [OPEN FOLDER] Strategy 3 failed");
89
+ }
90
+
91
+ // Strategy 4: Try to launch any file manager using generic intent
92
+ try {
93
+ Log.i(TAG, "📂 [OPEN FOLDER] Trying generic file manager");
94
+ Intent genericIntent = new Intent(Intent.ACTION_GET_CONTENT);
95
+ genericIntent.setType("*/*");
96
+ genericIntent.addCategory(Intent.CATEGORY_OPENABLE);
97
+ genericIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
98
+
99
+ if (genericIntent.resolveActivity(reactContext.getPackageManager()) != null) {
100
+ reactContext.startActivity(Intent.createChooser(genericIntent, "Open File Manager"));
101
+ Log.i(TAG, "✅ [OPEN FOLDER] Opened file picker");
102
+ promise.resolve(true);
103
+ return;
104
+ }
105
+ } catch (Exception e) {
106
+ Log.i(TAG, "📂 [OPEN FOLDER] Strategy 4 failed");
107
+ }
108
+
109
+ // All strategies failed
110
+ Log.w(TAG, "⚠️ [OPEN FOLDER] All strategies failed - no file manager available");
111
+ promise.reject("NO_FILE_MANAGER", "No file manager app available on this device");
112
+
113
+ } catch (Exception e) {
114
+ Log.e(TAG, "❌ [OPEN FOLDER] ERROR", e);
115
+ promise.reject("OPEN_FOLDER_ERROR", e.getMessage());
116
+ }
117
+ }
118
+ }
119
+
120
+
121
+
122
+
123
+
@@ -0,0 +1,311 @@
1
+ package org.wonday.pdf;
2
+
3
+ import android.content.Context;
4
+ import android.content.pm.ApplicationInfo;
5
+ import android.content.pm.PackageManager;
6
+ import android.util.Log;
7
+
8
+ import com.facebook.react.bridge.ReactApplicationContext;
9
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
10
+ import com.facebook.react.bridge.ReactMethod;
11
+ import com.facebook.react.bridge.Promise;
12
+ import com.facebook.react.bridge.ReadableMap;
13
+ import com.facebook.react.bridge.WritableMap;
14
+ import com.facebook.react.bridge.Arguments;
15
+
16
+ import java.security.MessageDigest;
17
+ import java.security.NoSuchAlgorithmException;
18
+ import java.util.HashMap;
19
+ import java.util.Map;
20
+
21
+ import javax.crypto.Mac;
22
+ import javax.crypto.spec.SecretKeySpec;
23
+
24
+ public class LicenseVerifier extends ReactContextBaseJavaModule {
25
+ private static final String TAG = "LicenseVerifier";
26
+
27
+ // License info cache
28
+ private String currentLicenseKey = null;
29
+ private String currentTier = null;
30
+ private long expiryTimestamp = 0;
31
+ private String currentEmail = null;
32
+
33
+ // Secret key for HMAC verification (from app config)
34
+ private String secretKey = null;
35
+
36
+ public LicenseVerifier(ReactApplicationContext reactContext) {
37
+ super(reactContext);
38
+ loadSecretKey();
39
+ }
40
+
41
+ @Override
42
+ public String getName() {
43
+ return "LicenseVerifier";
44
+ }
45
+
46
+ /**
47
+ * Load secret key from AndroidManifest meta-data
48
+ */
49
+ private void loadSecretKey() {
50
+ try {
51
+ Context context = getReactApplicationContext();
52
+ ApplicationInfo appInfo = context.getPackageManager()
53
+ .getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
54
+
55
+ if (appInfo.metaData != null) {
56
+ secretKey = appInfo.metaData.getString("com.reactnativepdf.license.secret");
57
+ }
58
+
59
+ if (secretKey == null) {
60
+ Log.w(TAG, "License secret not found in AndroidManifest");
61
+ secretKey = "default-secret-change-in-production";
62
+ }
63
+ } catch (Exception e) {
64
+ Log.e(TAG, "Error loading license secret", e);
65
+ secretKey = "default-secret-change-in-production";
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Set license information from JS
71
+ */
72
+ @ReactMethod
73
+ public void setLicenseInfo(ReadableMap licenseInfo, Promise promise) {
74
+ try {
75
+ if (licenseInfo == null) {
76
+ promise.reject("INVALID_LICENSE", "License info is null");
77
+ return;
78
+ }
79
+
80
+ String licenseKey = licenseInfo.getString("key");
81
+ String tier = licenseInfo.getString("tier");
82
+ String email = licenseInfo.getString("email");
83
+ String expiresAt = licenseInfo.getString("expiresAt");
84
+
85
+ if (licenseKey == null || tier == null) {
86
+ promise.reject("INVALID_LICENSE", "Missing required license fields");
87
+ return;
88
+ }
89
+
90
+ // Verify license key format and checksum
91
+ if (!verifyLicenseKey(licenseKey)) {
92
+ promise.reject("INVALID_LICENSE", "Invalid license key format or checksum");
93
+ return;
94
+ }
95
+
96
+ // Parse expiry timestamp
97
+ long expiry = 0;
98
+ if (expiresAt != null && !expiresAt.isEmpty()) {
99
+ try {
100
+ expiry = java.time.Instant.parse(expiresAt).toEpochMilli();
101
+ } catch (Exception e) {
102
+ Log.w(TAG, "Invalid expiry date format", e);
103
+ }
104
+ }
105
+
106
+ // Cache license info
107
+ this.currentLicenseKey = licenseKey;
108
+ this.currentTier = tier;
109
+ this.currentEmail = email;
110
+ this.expiryTimestamp = expiry;
111
+
112
+ Log.i(TAG, "License set: " + tier + " for " + email);
113
+ promise.resolve(true);
114
+
115
+ } catch (Exception e) {
116
+ Log.e(TAG, "Error setting license info", e);
117
+ promise.reject("LICENSE_ERROR", e.getMessage());
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Require Pro license for a feature
123
+ */
124
+ @ReactMethod
125
+ public void requirePro(String featureName, Promise promise) {
126
+ try {
127
+ if (!isProActive()) {
128
+ WritableMap error = Arguments.createMap();
129
+ error.putString("code", "LICENSE_REQUIRED");
130
+ error.putString("feature", featureName);
131
+ error.putString("message", featureName + " requires a Pro license");
132
+ promise.reject("LICENSE_REQUIRED", error);
133
+ return;
134
+ }
135
+
136
+ promise.resolve(true);
137
+ } catch (Exception e) {
138
+ Log.e(TAG, "Error checking Pro license", e);
139
+ promise.reject("LICENSE_ERROR", e.getMessage());
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Check if Pro license is active
145
+ */
146
+ public boolean isProActive() {
147
+ // 🧪 TESTING MODE: Always return true for testing
148
+ Log.i(TAG, "🧪 TESTING MODE: isProActive() returning true for testing");
149
+ return true;
150
+
151
+ /* PRODUCTION CODE (commented out for testing):
152
+ if (currentLicenseKey == null || currentTier == null) {
153
+ Log.w(TAG, "No license set");
154
+ return false;
155
+ }
156
+
157
+ // Check if license is expired
158
+ if (expiryTimestamp > 0 && System.currentTimeMillis() > expiryTimestamp) {
159
+ Log.w(TAG, "License expired");
160
+ return false;
161
+ }
162
+
163
+ // Check if tier is Pro or higher
164
+ return isProTier(currentTier);
165
+ */
166
+ }
167
+
168
+ /**
169
+ * Check if tier is Pro or higher
170
+ */
171
+ private boolean isProTier(String tier) {
172
+ if (tier == null) return false;
173
+
174
+ switch (tier.toLowerCase()) {
175
+ case "solo":
176
+ case "professional":
177
+ case "team":
178
+ case "enterprise":
179
+ return true;
180
+ default:
181
+ return false;
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Get current license tier
187
+ */
188
+ @ReactMethod
189
+ public void getTier(Promise promise) {
190
+ try {
191
+ if (currentTier == null) {
192
+ promise.resolve("free");
193
+ } else {
194
+ promise.resolve(currentTier);
195
+ }
196
+ } catch (Exception e) {
197
+ Log.e(TAG, "Error getting tier", e);
198
+ promise.reject("LICENSE_ERROR", e.getMessage());
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Check if license is expired
204
+ */
205
+ @ReactMethod
206
+ public void isExpired(Promise promise) {
207
+ try {
208
+ boolean expired = expiryTimestamp > 0 && System.currentTimeMillis() > expiryTimestamp;
209
+ promise.resolve(expired);
210
+ } catch (Exception e) {
211
+ Log.e(TAG, "Error checking expiry", e);
212
+ promise.reject("LICENSE_ERROR", e.getMessage());
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Verify license key format and checksum
218
+ */
219
+ private boolean verifyLicenseKey(String licenseKey) {
220
+ if (licenseKey == null || licenseKey.length() != 19) {
221
+ return false;
222
+ }
223
+
224
+ // Check format: X###-####-####-####
225
+ if (!licenseKey.matches("^[SPTE][A-F0-9]{3}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}$")) {
226
+ return false;
227
+ }
228
+
229
+ // Verify checksum
230
+ try {
231
+ String[] parts = licenseKey.split("-");
232
+ String prefix = parts[0].substring(0, 1);
233
+ String seg1 = parts[0].substring(1);
234
+ String seg2 = parts[1];
235
+ String seg3 = parts[2];
236
+ String checksum = parts[3];
237
+
238
+ String data = prefix + seg1 + seg2 + seg3;
239
+ String expectedChecksum = generateChecksum(data);
240
+
241
+ return checksum.equals(expectedChecksum);
242
+ } catch (Exception e) {
243
+ Log.e(TAG, "Error verifying license key", e);
244
+ return false;
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Generate checksum for license key
250
+ */
251
+ private String generateChecksum(String data) {
252
+ try {
253
+ Mac mac = Mac.getInstance("HmacSHA256");
254
+ SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
255
+ mac.init(secretKeySpec);
256
+
257
+ byte[] hash = mac.doFinal(data.getBytes());
258
+ StringBuilder hexString = new StringBuilder();
259
+
260
+ for (byte b : hash) {
261
+ String hex = Integer.toHexString(0xff & b);
262
+ if (hex.length() == 1) {
263
+ hexString.append('0');
264
+ }
265
+ hexString.append(hex);
266
+ }
267
+
268
+ return hexString.toString().toUpperCase().substring(0, 4);
269
+ } catch (Exception e) {
270
+ Log.e(TAG, "Error generating checksum", e);
271
+ return "0000";
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Get license info for debugging
277
+ */
278
+ @ReactMethod
279
+ public void getLicenseInfo(Promise promise) {
280
+ try {
281
+ WritableMap info = Arguments.createMap();
282
+ info.putString("key", currentLicenseKey);
283
+ info.putString("tier", currentTier);
284
+ info.putString("email", currentEmail);
285
+ info.putDouble("expiresAt", expiryTimestamp);
286
+ info.putBoolean("isPro", isProActive());
287
+ promise.resolve(info);
288
+ } catch (Exception e) {
289
+ Log.e(TAG, "Error getting license info", e);
290
+ promise.reject("LICENSE_ERROR", e.getMessage());
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Clear license info
296
+ */
297
+ @ReactMethod
298
+ public void clearLicense(Promise promise) {
299
+ try {
300
+ currentLicenseKey = null;
301
+ currentTier = null;
302
+ currentEmail = null;
303
+ expiryTimestamp = 0;
304
+ Log.i(TAG, "License cleared");
305
+ promise.resolve(true);
306
+ } catch (Exception e) {
307
+ Log.e(TAG, "Error clearing license", e);
308
+ promise.reject("LICENSE_ERROR", e.getMessage());
309
+ }
310
+ }
311
+ }