Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Activity filter memo and maximum amount #2807

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
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
17 changes: 17 additions & 0 deletions android/app/src/main/java/com/zeus/LndMobile.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
Expand All @@ -69,6 +70,7 @@
// TODO break this class up
class LndMobile extends ReactContextBaseJavaModule {
private final String TAG = "LndMobile";
public static Map<String, String> translationCache = new HashMap<>();
Messenger messenger;
private boolean lndMobileServiceBound = false;
private Messenger lndMobileServiceMessenger; // The service
Expand Down Expand Up @@ -245,6 +247,21 @@ public String getName() {
return "LndMobile";
}

@ReactMethod
public void updateTranslationCache(String locale, ReadableMap translations) {
translationCache.clear();
ReadableMapKeySetIterator iterator = translations.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
translationCache.put(key, translations.getString(key));
}

// Get service instance and rebuild notification
Intent intent = new Intent(getReactApplicationContext(), LndMobileService.class);
intent.setAction("app.zeusln.zeus.android.intent.action.UPDATE_NOTIFICATION");
getReactApplicationContext().startService(intent);
}

@ReactMethod
public void checkLndMobileServiceConnected(Promise p) {
if (lndMobileServiceBound) {
Expand Down
150 changes: 111 additions & 39 deletions android/app/src/main/java/com/zeus/LndMobileService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
import android.database.sqlite.SQLiteDatabase;
import android.os.IBinder;
import android.os.Build;
Expand All @@ -34,6 +35,14 @@
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.json.JSONException;
import org.json.JSONObject;

import com.reactnativecommunity.asyncstorage.AsyncLocalStorageUtil;
import com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier;

Expand Down Expand Up @@ -486,59 +495,122 @@ private boolean getPersistentServicesEnabled(Context context) {
return false;
}

private String getLocalizedString(String key) {
String translation = LndMobile.translationCache.get(key);
return translation != null ? translation : "MISSING STRING";
}

private String fetchLocalizedStringFromBundle(String locale, String key) throws IOException, JSONException {
String assetPath = "stored-locales/" + locale + ".json";
Log.d(TAG, "Attempting to load: " + assetPath);
AssetManager assetManager = getApplicationContext().getAssets();
String[] mainAssets = assetManager.list("");
for (String file : mainAssets) {
Log.d(TAG, "Root asset: " + file);
if (file.equals("assets")) {
String[] assetFiles = assetManager.list("assets");
for (String assetFile : assetFiles) {
Log.d(TAG, "Asset file: " + assetFile);
}
}
}
try (InputStream is = assetManager.open(assetPath);
BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
JSONObject json = new JSONObject(result.toString());

String[] keyParts = key.split("\\.");
JSONObject current = json;
for (int i = 0; i < keyParts.length - 1; i++) {
current = current.getJSONObject(keyParts[i]);
}
return current.getString(keyParts[keyParts.length - 1]);
}
}

@Override
public int onStartCommand(Intent intent, int flags, int startid) {
// Hyperlog.v(TAG, "onStartCommand()");
if (intent != null && intent.getAction() != null && intent.getAction().equals("app.zeusln.zeus.android.intent.action.STOP")) {
Log.i(TAG, "Received stopForeground Intent");
stopForeground(true);
stopSelf();
return START_NOT_STICKY;
} else {
boolean persistentServicesEnabled = getPersistentServicesEnabled(this);
// persistent services on, start service as foreground-svc
if (persistentServicesEnabled) {
Notification.Builder notificationBuilder = null;
Intent notificationIntent = new Intent (this, MainActivity.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel chan = new NotificationChannel(BuildConfig.APPLICATION_ID, "ZEUS", NotificationManager.IMPORTANCE_NONE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
if (intent != null && intent.getAction() != null) {
if (intent.getAction().equals("app.zeusln.zeus.android.intent.action.STOP")) {
Log.i(TAG, "Received stopForeground Intent");
stopForeground(true);
stopSelf();
return START_NOT_STICKY;
} else if (intent.getAction().equals("app.zeusln.zeus.android.intent.action.GRACEFUL_STOP")) {
Handler handler = new Handler(msg -> {
if (msg.what == MSG_STOP_LND_RESULT) {
stopForeground(true);
stopSelf();
Process.killProcess(Process.myPid());
return true;
}
return false;
});
Messenger messenger = new Messenger(handler);
mClients.add(messenger);
stopLnd(messenger, -1);
return START_NOT_STICKY;
} else if (intent.getAction().equals("app.zeusln.zeus.android.intent.action.UPDATE_NOTIFICATION")) {
if (notificationManager == null) {
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert notificationManager != null;
notificationManager.createNotificationChannel(chan);

notificationBuilder = new Notification.Builder(this, BuildConfig.APPLICATION_ID);
} else {
notificationBuilder = new Notification.Builder(this);
}

notificationBuilder
.setContentTitle("ZEUS")
.setContentText("LND is running in the background")
.setSmallIcon(R.drawable.ic_stat_ic_notification_lnd)
.setContentIntent(pendingIntent)
.setOngoing(true);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
notificationBuilder.setForegroundServiceBehavior(FOREGROUND_SERVICE_IMMEDIATE);
}
notificationManager.notify(ONGOING_NOTIFICATION_ID, buildNotification());
return START_NOT_STICKY;
}
}
boolean persistentServicesEnabled = getPersistentServicesEnabled(this);
// persistent services on, start service as foreground-svc
if (persistentServicesEnabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel chan = new NotificationChannel(BuildConfig.APPLICATION_ID, "ZEUS", NotificationManager.IMPORTANCE_NONE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert notificationManager != null;
notificationManager.createNotificationChannel(chan);
}

Notification notification = notificationBuilder.build();
Notification notification = buildNotification();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(ONGOING_NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
} else {
startForeground(ONGOING_NOTIFICATION_ID, notification);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(ONGOING_NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
} else {
startForeground(ONGOING_NOTIFICATION_ID, notification);
}
}

// else noop, instead of calling startService, start will be handled by binding
return startid;
}

private Notification buildNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
Intent stopIntent = new Intent(this, LndMobileService.class);
stopIntent.setAction("app.zeusln.zeus.android.intent.action.GRACEFUL_STOP");
PendingIntent stopPendingIntent = PendingIntent.getService(this, 0, stopIntent, PendingIntent.FLAG_IMMUTABLE);
Notification.Builder notificationBuilder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationBuilder = new Notification.Builder(this, BuildConfig.APPLICATION_ID);
} else {
notificationBuilder = new Notification.Builder(this);
}
notificationBuilder
.setContentText(getLocalizedString("androidNotification.lndRunningBackground"))
.setSmallIcon(R.drawable.ic_stat_ic_notification_lnd)
.setContentIntent(pendingIntent)
.setOngoing(true)
.addAction(0, getLocalizedString("androidNotification.shutdown"), stopPendingIntent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
notificationBuilder.setForegroundServiceBehavior(FOREGROUND_SERVICE_IMMEDIATE);
}
return notificationBuilder.build();
}

@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
Expand Down
4 changes: 4 additions & 0 deletions lndmobile/LndMobile.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export interface ILndMobile {
unbindLndMobileService(): Promise<void>; // TODO(hsjoberg): function looks broken
sendPongToLndMobileservice(): Promise<{ data: string }>;
checkLndMobileServiceConnected(): Promise<boolean>;
updateTranslationCache(
locale: string,
translations: { [key: string]: string }
): void;
}

export interface ILndMobileTools {
Expand Down
8 changes: 7 additions & 1 deletion locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,10 @@
"views.ActivityFilter.lightningPayments": "Lightning payments",
"views.ActivityFilter.onChainPayments": "On-chain payments",
"views.ActivityFilter.minimumAmount": "Minimum Amount (sats)",
"views.ActivityFilter.maximumAmount": "Maximum Amount (sats)",
"views.ActivityFilter.maximumAmountPlaceHolder":"No limit",
"views.ActivityFilter.memo": "Memo/Note",
"views.ActivityFilter.memoPlaceHolder":"Enter Memo/Note",
"views.ActivityFilter.inTransit": "In transit payments",
"views.ActivityFilter.isFailed": "Failed payments",
"views.ActivityFilter.standardInvoices": "Standard invoices",
Expand Down Expand Up @@ -1362,5 +1366,7 @@
"views.OnChainAddresses.sortBy.balanceAscending": "Balance (ascending)",
"views.OnChainAddresses.sortBy.balanceDescending": "Balance (descending)",
"views.OnChainAddresses.createAddress": "Create address",
"views.OnChainAddresses.changeAddresses": "Change addresses"
"views.OnChainAddresses.changeAddresses": "Change addresses",
"androidNotification.lndRunningBackground": "LND is running in the background",
"androidNotification.shutdown": "Shutdown"
}
38 changes: 28 additions & 10 deletions stores/ActivityStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ export interface Filter {
unconfirmed: boolean;
zeusPay: boolean;
minimumAmount: number;
maximumAmount?: number;
startDate?: Date;
endDate?: Date;
memo: string;
}

export const DEFAULT_FILTERS = {
Expand All @@ -48,8 +50,10 @@ export const DEFAULT_FILTERS = {
ampInvoices: true,
zeusPay: true,
minimumAmount: 0,
maximumAmount: undefined,
startDate: undefined,
endDate: undefined
endDate: undefined,
memo: ''
};

export default class ActivityStore {
Expand Down Expand Up @@ -94,8 +98,10 @@ export default class ActivityStore {
unconfirmed: true,
zeusPay: true,
minimumAmount: 0,
maximumAmount: undefined,
startDate: undefined,
endDate: undefined
endDate: undefined,
memo: ''
};
await Storage.setItem(ACTIVITY_FILTERS_KEY, this.filters);
};
Expand All @@ -106,6 +112,12 @@ export default class ActivityStore {
this.setFilters(this.filters);
};

@action
public setMaximumAmountFilter = (filter: any) => {
this.filters.maximumAmount = filter;
this.setFilters(this.filters);
};

@action
public setStartDateFilter = (filter: any) => {
this.filters.startDate = filter;
Expand All @@ -130,7 +142,13 @@ export default class ActivityStore {
this.setFilters(this.filters);
};

private getSortedActivity = () => {
@action
public setMemoFilter = (filter: any) => {
this.filters.memo = filter;
this.setFilters(this.filters);
};

getSortedActivity = () => {
const activity: any[] = [];
const payments = this.paymentsStore.payments;
const transactions = this.transactionsStore.transactions;
Expand Down Expand Up @@ -185,15 +203,15 @@ export default class ActivityStore {
try {
const filters = await Storage.getItem(ACTIVITY_FILTERS_KEY);
if (filters) {
runInAction(() => {
this.filters = JSON.parse(filters, (key, value) =>
(key === 'startDate' || key === 'endDate') && value
? new Date(value)
: value
);
});
const parsedFilters = JSON.parse(filters, (key, value) =>
(key === 'startDate' || key === 'endDate') && value
? new Date(value)
: value
);
this.filters = { ...DEFAULT_FILTERS, ...parsedFilters };
} else {
console.log('No activity filters stored');
this.filters = DEFAULT_FILTERS;
}
} catch (error) {
console.log('Loading activity filters failed', error);
Expand Down
Loading