Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/android/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ dependencies {
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
implementation project(':capacitor-cordova-android-plugins')

// Firebase BoM - manages all Firebase versions
implementation platform('com.google.firebase:firebase-bom:34.8.0')

// Firebase Cloud Messaging for push notifications
implementation 'com.google.firebase:firebase-messaging'

// Security library for encrypted storage (zero-trust secure storage)
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
}

apply from: 'capacitor.build.gradle'
Expand Down
29 changes: 29 additions & 0 deletions apps/android/android/app/google-services.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "107236466397",
"project_id": "pagespace-f328e",
"storage_bucket": "pagespace-f328e.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:107236466397:android:931c03aeb94c42dd904534",
"android_client_info": {
"package_name": "ai.pagespace.android"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyDsbaTral3mH28xMxesfO5FMIz3-ORzcLg"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package ai.pagespace.android;

import android.os.Bundle;
import com.getcapacitor.BridgeActivity;

public class MainActivity extends BridgeActivity {}
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
registerPlugin(PageSpaceSecureStoragePlugin.class);
super.onCreate(savedInstanceState);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package ai.pagespace.android;

import android.content.Context;
import android.content.SharedPreferences;
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKey;
import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;

/**
* Zero-trust secure storage plugin using Android EncryptedSharedPreferences.
* Provides the same JS interface as the iOS PageSpaceKeychainPlugin.
*
* Security properties:
* - AES-256-GCM encryption for values
* - AES-256-SIV encryption for keys
* - Android Keystore-backed master key
* - Hardware security module used when available
* - No cloud sync - data stays on device
*/
@CapacitorPlugin(name = "PageSpaceKeychain")
public class PageSpaceSecureStoragePlugin extends Plugin {
private SharedPreferences sharedPreferences;
private String initializationError;
private static final String PREFS_NAME = "ai.pagespace.secure";

@Override
public void load() {
try {
Context context = getContext();
MasterKey masterKey = new MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();

sharedPreferences = EncryptedSharedPreferences.create(
context,
PREFS_NAME,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
initializationError = null;
} catch (Exception e) {
sharedPreferences = null;
initializationError = "Secure storage unavailable: " + e.getMessage();
}
}

private boolean rejectIfNotInitialized(PluginCall call) {
if (sharedPreferences == null) {
call.reject(initializationError != null ? initializationError : "Secure storage not initialized");
return true;
}
return false;
}

@PluginMethod
public void get(PluginCall call) {
if (rejectIfNotInitialized(call)) return;
String key = call.getString("key");
if (key == null) {
call.reject("Missing key");
return;
}
String value = sharedPreferences.getString(key, null);
JSObject result = new JSObject();
result.put("value", value);
call.resolve(result);
}

@PluginMethod
public void set(PluginCall call) {
if (rejectIfNotInitialized(call)) return;
String key = call.getString("key");
String value = call.getString("value");
if (key == null || value == null) {
call.reject("Missing key or value");
return;
}
sharedPreferences.edit().putString(key, value).apply();
JSObject result = new JSObject();
result.put("success", true);
call.resolve(result);
}

@PluginMethod
public void remove(PluginCall call) {
if (rejectIfNotInitialized(call)) return;
String key = call.getString("key");
if (key == null) {
call.reject("Missing key");
return;
}
sharedPreferences.edit().remove(key).apply();
JSObject result = new JSObject();
result.put("success", true);
call.resolve(result);
}
}
4 changes: 2 additions & 2 deletions apps/android/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.7.2'
classpath 'com.google.gms:google-services:4.4.2'
classpath 'com.android.tools.build:gradle:8.9.1'
classpath 'com.google.gms:google-services:4.4.4'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand Down
4 changes: 2 additions & 2 deletions apps/android/android/variables.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ext {
minSdkVersion = 23
compileSdkVersion = 35
targetSdkVersion = 35
compileSdkVersion = 36
targetSdkVersion = 36
androidxActivityVersion = '1.9.2'
androidxAppCompatVersion = '1.7.0'
androidxCoordinatorLayoutVersion = '1.2.0'
Expand Down
2 changes: 1 addition & 1 deletion apps/android/capacitor.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const config: CapacitorConfig = {
SocialLogin: {
google: {
// You'll need to add your Android client ID from Google Cloud Console
androidClientId: 'YOUR_ANDROID_CLIENT_ID.apps.googleusercontent.com',
androidClientId: '636969838408-s5s3ts6nubc6c29ur81o2ipf6tmu9gqq.apps.googleusercontent.com',
},
},
PushNotifications: {
Expand Down