strata-storage 2.4.0 → 2.4.2
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 +295 -0
- package/android/src/main/java/com/strata/storage/EncryptedStorage.java +27 -18
- package/android/src/main/java/com/strata/storage/SQLiteStorage.java +85 -58
- package/android/src/main/java/com/strata/storage/SharedPreferencesStorage.java +9 -2
- package/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +83 -10
- package/dist/README.md +295 -0
- package/dist/adapters/web/MemoryAdapter.d.ts.map +1 -1
- package/dist/adapters/web/MemoryAdapter.js +7 -1
- package/dist/adapters/web/SessionStorageAdapter.d.ts.map +1 -1
- package/dist/adapters/web/SessionStorageAdapter.js +3 -2
- package/dist/android/src/main/java/com/strata/storage/EncryptedStorage.java +27 -18
- package/dist/android/src/main/java/com/strata/storage/SQLiteStorage.java +85 -58
- package/dist/android/src/main/java/com/strata/storage/SharedPreferencesStorage.java +9 -2
- package/dist/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +83 -10
- package/dist/core/Strata.d.ts.map +1 -1
- package/dist/core/Strata.js +4 -6
- package/dist/firebase.d.ts.map +1 -1
- package/dist/firebase.js +75 -13
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/ios/Plugin/SQLiteStorage.swift +100 -30
- package/dist/ios/Plugin/UserDefaultsStorage.swift +13 -2
- package/dist/package.json +5 -5
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +31 -6
- package/dist/plugin/web.d.ts +5 -1
- package/dist/plugin/web.d.ts.map +1 -1
- package/dist/plugin/web.js +55 -32
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +7 -2
- package/ios/Plugin/SQLiteStorage.swift +100 -30
- package/ios/Plugin/UserDefaultsStorage.swift +13 -2
- package/package.json +29 -19
package/README.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# Strata Storage
|
|
2
|
+
|
|
3
|
+
> Zero-dependency universal storage plugin providing a unified API for all storage operations across web, Android, and iOS platforms.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/strata-storage)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://github.com/aoneahsan/strata-storage)
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **🚀 Zero Dependencies** - No external runtime dependencies, pure TypeScript implementation
|
|
13
|
+
- **🌐 Universal API** - Single consistent API across web, iOS, and Android
|
|
14
|
+
- **🔒 Built-in Encryption** - Secure data storage using native crypto APIs
|
|
15
|
+
- **📦 Compression** - Automatic data compression for large objects
|
|
16
|
+
- **⏱️ TTL Support** - Automatic expiration with time-to-live
|
|
17
|
+
- **🔄 Cross-Tab Sync** - Real-time synchronization across browser tabs
|
|
18
|
+
- **🎯 Advanced Queries** - Tag-based querying and filtering
|
|
19
|
+
- **📱 Mobile Ready** - Native iOS and Android storage with Capacitor
|
|
20
|
+
- **💾 Multiple Adapters** - localStorage, IndexedDB, SQLite, Keychain, and more
|
|
21
|
+
- **🎨 Framework Integrations** - React hooks, Vue composables, Angular services
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install strata-storage
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or using yarn:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
yarn add strata-storage
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or using pnpm:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pnpm add strata-storage
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
### Basic Usage
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { Strata } from 'strata-storage';
|
|
47
|
+
|
|
48
|
+
// Create and initialize storage
|
|
49
|
+
const storage = new Strata();
|
|
50
|
+
await storage.initialize();
|
|
51
|
+
|
|
52
|
+
// Store data
|
|
53
|
+
await storage.set('username', 'john_doe');
|
|
54
|
+
await storage.set('user', {
|
|
55
|
+
id: 123,
|
|
56
|
+
name: 'John Doe',
|
|
57
|
+
email: 'john@example.com'
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Retrieve data
|
|
61
|
+
const username = await storage.get('username');
|
|
62
|
+
const user = await storage.get('user');
|
|
63
|
+
|
|
64
|
+
// Remove data
|
|
65
|
+
await storage.remove('username');
|
|
66
|
+
|
|
67
|
+
// Clear all data
|
|
68
|
+
await storage.clear();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Advanced Features
|
|
72
|
+
|
|
73
|
+
#### Encryption
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const storage = new Strata({
|
|
77
|
+
encryption: {
|
|
78
|
+
enabled: true,
|
|
79
|
+
password: 'your-secure-password'
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await storage.initialize();
|
|
84
|
+
await storage.set('sensitiveData', { token: 'secret' });
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### Time-To-Live (TTL)
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// Data expires in 1 hour
|
|
91
|
+
await storage.set('sessionData', data, {
|
|
92
|
+
ttl: 60 * 60 * 1000
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Compression
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const storage = new Strata({
|
|
100
|
+
compression: {
|
|
101
|
+
enabled: true,
|
|
102
|
+
threshold: 1024 // Compress data larger than 1KB
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await storage.initialize();
|
|
107
|
+
await storage.set('largeData', bigObject);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Cross-Tab Sync
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
const storage = new Strata({
|
|
114
|
+
sync: { enabled: true }
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
await storage.initialize();
|
|
118
|
+
|
|
119
|
+
// Subscribe to changes from other tabs
|
|
120
|
+
storage.subscribe((change) => {
|
|
121
|
+
console.log(`Key ${change.key} changed to:`, change.newValue);
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Platform Support
|
|
126
|
+
|
|
127
|
+
### Web Browsers
|
|
128
|
+
- **localStorage** - Simple key-value storage
|
|
129
|
+
- **sessionStorage** - Session-scoped storage
|
|
130
|
+
- **IndexedDB** - Large structured data
|
|
131
|
+
- **Cookies** - Cookie-based storage
|
|
132
|
+
- **Cache API** - HTTP cache storage
|
|
133
|
+
- **Memory** - In-memory storage
|
|
134
|
+
|
|
135
|
+
### iOS (via Capacitor)
|
|
136
|
+
- **UserDefaults** - User preferences
|
|
137
|
+
- **Keychain** - Secure credential storage
|
|
138
|
+
- **SQLite** - Database storage
|
|
139
|
+
- **FileManager** - File-based storage
|
|
140
|
+
|
|
141
|
+
### Android (via Capacitor)
|
|
142
|
+
- **SharedPreferences** - Simple key-value storage
|
|
143
|
+
- **EncryptedSharedPreferences** - Secure storage
|
|
144
|
+
- **SQLite** - Database storage
|
|
145
|
+
- **File Storage** - File-based storage
|
|
146
|
+
|
|
147
|
+
## Framework Integrations
|
|
148
|
+
|
|
149
|
+
### React
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { useStrata } from 'strata-storage/react';
|
|
153
|
+
|
|
154
|
+
function MyComponent() {
|
|
155
|
+
const { data, loading, set, remove } = useStrata('myKey');
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<div>
|
|
159
|
+
<p>{data}</p>
|
|
160
|
+
<button onClick={() => set('newValue')}>Update</button>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Vue
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { useStrata } from 'strata-storage/vue';
|
|
170
|
+
|
|
171
|
+
export default {
|
|
172
|
+
setup() {
|
|
173
|
+
const { data, loading, set, remove } = useStrata('myKey');
|
|
174
|
+
|
|
175
|
+
return { data, loading, set, remove };
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Angular
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { StrataService } from 'strata-storage/angular';
|
|
184
|
+
|
|
185
|
+
@Component({ /* ... */ })
|
|
186
|
+
export class MyComponent {
|
|
187
|
+
constructor(private storage: StrataService) {}
|
|
188
|
+
|
|
189
|
+
async saveData() {
|
|
190
|
+
await this.storage.set('key', 'value');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Documentation
|
|
196
|
+
|
|
197
|
+
- **[Getting Started](docs/getting-started/installation.md)** - Installation and setup guide
|
|
198
|
+
- **[Quick Start](docs/getting-started/quick-start.md)** - Get up and running in minutes
|
|
199
|
+
- **[API Reference](docs/api/README.md)** - Complete API documentation
|
|
200
|
+
- **[Platform Guides](docs/guides/platforms/web.md)** - Platform-specific information
|
|
201
|
+
- **[Examples](docs/examples/README.md)** - Real-world usage examples
|
|
202
|
+
- **[Migration Guide](docs/MIGRATION.md)** - Migrating from other storage solutions
|
|
203
|
+
|
|
204
|
+
## Storage Adapters
|
|
205
|
+
|
|
206
|
+
| Adapter | Platform | Use Case |
|
|
207
|
+
|---------|----------|----------|
|
|
208
|
+
| `localStorage` | Web | Simple key-value, persistent |
|
|
209
|
+
| `sessionStorage` | Web | Session-scoped data |
|
|
210
|
+
| `indexedDB` | Web | Large structured data |
|
|
211
|
+
| `cookies` | Web | Cookie-based storage |
|
|
212
|
+
| `cache` | Web | HTTP cache |
|
|
213
|
+
| `memory` | All | Temporary in-memory |
|
|
214
|
+
| `preferences` | Mobile | User preferences (UserDefaults/SharedPreferences) |
|
|
215
|
+
| `secure` | Mobile | Encrypted storage (Keychain/EncryptedSharedPreferences) |
|
|
216
|
+
| `sqlite` | Mobile | Database storage |
|
|
217
|
+
| `filesystem` | Mobile | File-based storage |
|
|
218
|
+
|
|
219
|
+
## Requirements
|
|
220
|
+
|
|
221
|
+
- **Node.js**: 18.0.0 or higher
|
|
222
|
+
- **TypeScript**: 5.0+ (optional, but recommended)
|
|
223
|
+
- **Capacitor**: 5.x or 6.x (for mobile platforms)
|
|
224
|
+
|
|
225
|
+
## Browser Support
|
|
226
|
+
|
|
227
|
+
- Chrome/Edge: Latest 2 versions
|
|
228
|
+
- Firefox: Latest 2 versions
|
|
229
|
+
- Safari: Latest 2 versions
|
|
230
|
+
- iOS Safari: iOS 13+
|
|
231
|
+
- Android WebView: Android 8+
|
|
232
|
+
|
|
233
|
+
## Why Strata Storage?
|
|
234
|
+
|
|
235
|
+
### Zero Dependencies
|
|
236
|
+
Unlike other storage solutions that depend on multiple packages, Strata Storage has **zero runtime dependencies**. Everything is built from scratch, ensuring:
|
|
237
|
+
- Smaller bundle size
|
|
238
|
+
- No dependency conflicts
|
|
239
|
+
- Better security
|
|
240
|
+
- Full control over implementation
|
|
241
|
+
|
|
242
|
+
### Universal API
|
|
243
|
+
One API works everywhere. No need to learn different APIs for web and mobile, or switch between libraries:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
// Same code works on web, iOS, and Android
|
|
247
|
+
await storage.set('key', 'value');
|
|
248
|
+
const value = await storage.get('key');
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Built-in Features
|
|
252
|
+
Advanced features included out of the box:
|
|
253
|
+
- Encryption (Web Crypto API / Native Crypto)
|
|
254
|
+
- Compression (LZ-string algorithm)
|
|
255
|
+
- TTL/Expiration
|
|
256
|
+
- Cross-tab sync
|
|
257
|
+
- Advanced queries
|
|
258
|
+
- Data migrations
|
|
259
|
+
|
|
260
|
+
## Contributing
|
|
261
|
+
|
|
262
|
+
Contributions are welcome! Please read the [Contributing Guide](CONTRIBUTING.md) for details.
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
|
|
267
|
+
|
|
268
|
+
## Author
|
|
269
|
+
|
|
270
|
+
**Ahsan Mahmood**
|
|
271
|
+
- Email: aoneahsan@gmail.com
|
|
272
|
+
- LinkedIn: [linkedin.com/in/aoneahsan](https://linkedin.com/in/aoneahsan)
|
|
273
|
+
- Portfolio: [aoneahsan.com](https://aoneahsan.com)
|
|
274
|
+
- GitHub: [@aoneahsan](https://github.com/aoneahsan)
|
|
275
|
+
- NPM: [npmjs.com/~aoneahsan](https://www.npmjs.com/~aoneahsan)
|
|
276
|
+
- Phone/WhatsApp: +923046619706
|
|
277
|
+
|
|
278
|
+
## Links
|
|
279
|
+
|
|
280
|
+
- **NPM Package**: [npmjs.com/package/strata-storage](https://www.npmjs.com/package/strata-storage)
|
|
281
|
+
- **GitHub Repository**: [github.com/aoneahsan/strata-storage](https://github.com/aoneahsan/strata-storage)
|
|
282
|
+
- **Issue Tracker**: [github.com/aoneahsan/strata-storage/issues](https://github.com/aoneahsan/strata-storage/issues)
|
|
283
|
+
- **Documentation**: [github.com/aoneahsan/strata-storage/tree/main/docs](https://github.com/aoneahsan/strata-storage/tree/main/docs)
|
|
284
|
+
|
|
285
|
+
## Support
|
|
286
|
+
|
|
287
|
+
If you encounter any issues or have questions:
|
|
288
|
+
|
|
289
|
+
1. Check the [FAQ](docs/reference/faq.md)
|
|
290
|
+
2. Search [existing issues](https://github.com/aoneahsan/strata-storage/issues)
|
|
291
|
+
3. Create a [new issue](https://github.com/aoneahsan/strata-storage/issues/new)
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
Made with ❤️ by [Ahsan Mahmood](https://aoneahsan.com)
|
|
@@ -3,8 +3,11 @@ package com.strata.storage;
|
|
|
3
3
|
import android.content.Context;
|
|
4
4
|
import android.content.SharedPreferences;
|
|
5
5
|
import android.os.Build;
|
|
6
|
+
import android.util.Log;
|
|
6
7
|
import androidx.security.crypto.EncryptedSharedPreferences;
|
|
7
8
|
import androidx.security.crypto.MasterKey;
|
|
9
|
+
import java.security.GeneralSecurityException;
|
|
10
|
+
import java.io.IOException;
|
|
8
11
|
import java.util.Set;
|
|
9
12
|
import java.util.HashSet;
|
|
10
13
|
import java.util.Map;
|
|
@@ -18,28 +21,34 @@ public class EncryptedStorage {
|
|
|
18
21
|
private SharedPreferences.Editor editor;
|
|
19
22
|
private static final String DEFAULT_NAME = "StrataSecureStorage";
|
|
20
23
|
|
|
21
|
-
public EncryptedStorage(Context context) throws
|
|
24
|
+
public EncryptedStorage(Context context) throws GeneralSecurityException, IOException {
|
|
22
25
|
this(context, DEFAULT_NAME);
|
|
23
26
|
}
|
|
24
|
-
|
|
25
|
-
public EncryptedStorage(Context context, String name) throws
|
|
27
|
+
|
|
28
|
+
public EncryptedStorage(Context context, String name) throws GeneralSecurityException, IOException {
|
|
26
29
|
String fileName = name != null ? name : DEFAULT_NAME;
|
|
27
|
-
|
|
30
|
+
|
|
28
31
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
29
|
-
|
|
30
|
-
.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
try {
|
|
33
|
+
MasterKey masterKey = new MasterKey.Builder(context)
|
|
34
|
+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
|
35
|
+
.build();
|
|
36
|
+
|
|
37
|
+
encryptedPrefs = EncryptedSharedPreferences.create(
|
|
38
|
+
context,
|
|
39
|
+
fileName,
|
|
40
|
+
masterKey,
|
|
41
|
+
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
42
|
+
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
editor = encryptedPrefs.edit();
|
|
46
|
+
} catch (GeneralSecurityException | IOException e) {
|
|
47
|
+
Log.e("StrataStorage", "Failed to initialize encrypted storage", e);
|
|
48
|
+
throw e;
|
|
49
|
+
}
|
|
42
50
|
} else {
|
|
51
|
+
Log.w("StrataStorage", "API < 23, using unencrypted SharedPreferences");
|
|
43
52
|
// Fallback to regular SharedPreferences for older devices
|
|
44
53
|
encryptedPrefs = context.getSharedPreferences(fileName, Context.MODE_PRIVATE);
|
|
45
54
|
editor = encryptedPrefs.edit();
|
|
@@ -68,7 +77,7 @@ public class EncryptedStorage {
|
|
|
68
77
|
editor.putString(key, stringValue);
|
|
69
78
|
return editor.commit();
|
|
70
79
|
} catch (Exception e) {
|
|
71
|
-
e
|
|
80
|
+
Log.e("StrataStorage", "Failed to set value in encrypted storage", e);
|
|
72
81
|
return false;
|
|
73
82
|
}
|
|
74
83
|
}
|
|
@@ -5,6 +5,7 @@ import android.database.Cursor;
|
|
|
5
5
|
import android.database.sqlite.SQLiteDatabase;
|
|
6
6
|
import android.database.sqlite.SQLiteOpenHelper;
|
|
7
7
|
import android.content.ContentValues;
|
|
8
|
+
import android.util.Log;
|
|
8
9
|
import java.util.ArrayList;
|
|
9
10
|
import java.util.List;
|
|
10
11
|
import java.util.HashMap;
|
|
@@ -78,7 +79,7 @@ public class SQLiteStorage extends SQLiteOpenHelper {
|
|
|
78
79
|
valueBytes = json.getBytes(StandardCharsets.UTF_8);
|
|
79
80
|
}
|
|
80
81
|
} catch (Exception e) {
|
|
81
|
-
e
|
|
82
|
+
Log.e("StrataStorage", "Failed to set value in SQLite", e);
|
|
82
83
|
return false;
|
|
83
84
|
}
|
|
84
85
|
SQLiteDatabase db = this.getWritableDatabase();
|
|
@@ -112,36 +113,41 @@ public class SQLiteStorage extends SQLiteOpenHelper {
|
|
|
112
113
|
|
|
113
114
|
public Map<String, Object> get(String key) {
|
|
114
115
|
SQLiteDatabase db = this.getReadableDatabase();
|
|
115
|
-
Cursor cursor =
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
result =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
116
|
+
Cursor cursor = null;
|
|
117
|
+
try {
|
|
118
|
+
cursor = db.query(TABLE_NAME, null, KEY_ID + "=?",
|
|
119
|
+
new String[]{key}, null, null, null, null);
|
|
120
|
+
|
|
121
|
+
Map<String, Object> result = null;
|
|
122
|
+
if (cursor != null && cursor.moveToFirst()) {
|
|
123
|
+
result = new HashMap<>();
|
|
124
|
+
result.put("key", key);
|
|
125
|
+
result.put("value", cursor.getBlob(cursor.getColumnIndex(KEY_VALUE)));
|
|
126
|
+
result.put("created", cursor.getLong(cursor.getColumnIndex(KEY_CREATED)));
|
|
127
|
+
result.put("updated", cursor.getLong(cursor.getColumnIndex(KEY_UPDATED)));
|
|
128
|
+
|
|
129
|
+
int expiresIndex = cursor.getColumnIndex(KEY_EXPIRES);
|
|
130
|
+
if (!cursor.isNull(expiresIndex)) {
|
|
131
|
+
result.put("expires", cursor.getLong(expiresIndex));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
int tagsIndex = cursor.getColumnIndex(KEY_TAGS);
|
|
135
|
+
if (!cursor.isNull(tagsIndex)) {
|
|
136
|
+
result.put("tags", cursor.getString(tagsIndex));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
int metadataIndex = cursor.getColumnIndex(KEY_METADATA);
|
|
140
|
+
if (!cursor.isNull(metadataIndex)) {
|
|
141
|
+
result.put("metadata", cursor.getString(metadataIndex));
|
|
142
|
+
}
|
|
134
143
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
|
|
144
|
+
return result;
|
|
145
|
+
} finally {
|
|
146
|
+
if (cursor != null) {
|
|
147
|
+
cursor.close();
|
|
139
148
|
}
|
|
140
|
-
|
|
141
|
-
cursor.close();
|
|
149
|
+
db.close();
|
|
142
150
|
}
|
|
143
|
-
db.close();
|
|
144
|
-
return result;
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
public boolean remove(String key) {
|
|
@@ -179,52 +185,73 @@ public class SQLiteStorage extends SQLiteOpenHelper {
|
|
|
179
185
|
List<String> keys = new ArrayList<>();
|
|
180
186
|
String selectQuery;
|
|
181
187
|
String[] selectionArgs = null;
|
|
182
|
-
|
|
188
|
+
|
|
183
189
|
if (pattern != null) {
|
|
184
190
|
selectQuery = "SELECT " + KEY_ID + " FROM " + TABLE_NAME + " WHERE " + KEY_ID + " LIKE ?";
|
|
185
|
-
selectionArgs = new String[]{"%
|
|
191
|
+
selectionArgs = new String[]{"%" + pattern + "%"};
|
|
186
192
|
} else {
|
|
187
193
|
selectQuery = "SELECT " + KEY_ID + " FROM " + TABLE_NAME;
|
|
188
194
|
}
|
|
189
|
-
|
|
195
|
+
|
|
190
196
|
SQLiteDatabase db = this.getReadableDatabase();
|
|
191
|
-
Cursor cursor =
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
Cursor cursor = null;
|
|
198
|
+
try {
|
|
199
|
+
cursor = db.rawQuery(selectQuery, selectionArgs);
|
|
200
|
+
|
|
201
|
+
if (cursor != null && cursor.moveToFirst()) {
|
|
202
|
+
do {
|
|
203
|
+
keys.add(cursor.getString(0));
|
|
204
|
+
} while (cursor.moveToNext());
|
|
205
|
+
}
|
|
206
|
+
return keys;
|
|
207
|
+
} finally {
|
|
208
|
+
if (cursor != null) {
|
|
209
|
+
cursor.close();
|
|
210
|
+
}
|
|
211
|
+
db.close();
|
|
197
212
|
}
|
|
198
|
-
cursor.close();
|
|
199
|
-
db.close();
|
|
200
|
-
return keys;
|
|
201
213
|
}
|
|
202
214
|
|
|
203
215
|
public boolean has(String key) {
|
|
204
216
|
SQLiteDatabase db = this.getReadableDatabase();
|
|
205
|
-
Cursor cursor =
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
217
|
+
Cursor cursor = null;
|
|
218
|
+
try {
|
|
219
|
+
cursor = db.query(TABLE_NAME, new String[]{KEY_ID}, KEY_ID + "=?",
|
|
220
|
+
new String[]{key}, null, null, null, null);
|
|
221
|
+
boolean exists = cursor != null && cursor.getCount() > 0;
|
|
222
|
+
return exists;
|
|
223
|
+
} finally {
|
|
224
|
+
if (cursor != null) {
|
|
225
|
+
cursor.close();
|
|
226
|
+
}
|
|
227
|
+
db.close();
|
|
228
|
+
}
|
|
211
229
|
}
|
|
212
230
|
|
|
213
231
|
public SizeInfo size() {
|
|
214
232
|
SQLiteDatabase db = this.getReadableDatabase();
|
|
215
|
-
Cursor cursor =
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
233
|
+
Cursor cursor = null;
|
|
234
|
+
try {
|
|
235
|
+
cursor = db.rawQuery("SELECT COUNT(*), SUM(LENGTH(" + KEY_VALUE + ")) FROM " + TABLE_NAME, null);
|
|
236
|
+
|
|
237
|
+
long totalSize = 0;
|
|
238
|
+
int count = 0;
|
|
239
|
+
|
|
240
|
+
if (cursor != null && cursor.moveToFirst()) {
|
|
241
|
+
count = cursor.getInt(0);
|
|
242
|
+
// Check for NULL values from SQL SUM function
|
|
243
|
+
if (!cursor.isNull(1)) {
|
|
244
|
+
totalSize = cursor.getLong(1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return new SizeInfo(totalSize, count);
|
|
249
|
+
} finally {
|
|
250
|
+
if (cursor != null) {
|
|
251
|
+
cursor.close();
|
|
252
|
+
}
|
|
253
|
+
db.close();
|
|
224
254
|
}
|
|
225
|
-
|
|
226
|
-
db.close();
|
|
227
|
-
return new SizeInfo(totalSize, count);
|
|
228
255
|
}
|
|
229
256
|
|
|
230
257
|
/**
|
|
@@ -2,6 +2,7 @@ package com.strata.storage;
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context;
|
|
4
4
|
import android.content.SharedPreferences;
|
|
5
|
+
import android.util.Log;
|
|
5
6
|
import java.util.Map;
|
|
6
7
|
import java.util.Set;
|
|
7
8
|
import java.util.HashSet;
|
|
@@ -37,7 +38,13 @@ public class SharedPreferencesStorage {
|
|
|
37
38
|
} else if (value instanceof Boolean) {
|
|
38
39
|
editor.putBoolean(key, (Boolean) value);
|
|
39
40
|
} else if (value instanceof Set) {
|
|
40
|
-
|
|
41
|
+
// Safely convert to Set<String>
|
|
42
|
+
Set<?> rawSet = (Set<?>) value;
|
|
43
|
+
Set<String> stringSet = new HashSet<>();
|
|
44
|
+
for (Object item : rawSet) {
|
|
45
|
+
stringSet.add(item != null ? item.toString() : "null");
|
|
46
|
+
}
|
|
47
|
+
editor.putStringSet(key, stringSet);
|
|
41
48
|
} else {
|
|
42
49
|
// Convert complex objects to JSON
|
|
43
50
|
String json;
|
|
@@ -56,7 +63,7 @@ public class SharedPreferencesStorage {
|
|
|
56
63
|
}
|
|
57
64
|
return editor.commit();
|
|
58
65
|
} catch (Exception e) {
|
|
59
|
-
e
|
|
66
|
+
Log.e("StrataStorage", "Failed to set value in SharedPreferences", e);
|
|
60
67
|
return false;
|
|
61
68
|
}
|
|
62
69
|
}
|