Improvements
This commit is contained in:
parent
16ae642243
commit
5930f71fe6
|
@ -112,7 +112,6 @@ import app.fedilab.android.sqlite.TimelineCacheDAO;
|
||||||
|
|
||||||
import static app.fedilab.android.helper.ECDH.kp_private;
|
import static app.fedilab.android.helper.ECDH.kp_private;
|
||||||
import static app.fedilab.android.helper.ECDH.kp_public;
|
import static app.fedilab.android.helper.ECDH.kp_public;
|
||||||
import static app.fedilab.android.helper.ECDH.newPair;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5866,7 +5865,7 @@ public class API {
|
||||||
String strPriv = prefs.getString(kp_private, "");
|
String strPriv = prefs.getString(kp_private, "");
|
||||||
ECDH ecdh = ECDH.getInstance();
|
ECDH ecdh = ECDH.getInstance();
|
||||||
if (strPub.trim().isEmpty() || strPriv.trim().isEmpty()) {
|
if (strPub.trim().isEmpty() || strPriv.trim().isEmpty()) {
|
||||||
newPair(context);
|
ecdh.newPair(context);
|
||||||
}
|
}
|
||||||
String pubKey = ecdh.getPublicKey(context);
|
String pubKey = ecdh.getPublicKey(context);
|
||||||
byte[] randBytes = new byte[16];
|
byte[] randBytes = new byte[16];
|
||||||
|
|
|
@ -19,28 +19,21 @@ import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
|
||||||
import org.spongycastle.asn1.ASN1ObjectIdentifier;
|
import org.spongycastle.asn1.ASN1ObjectIdentifier;
|
||||||
import org.spongycastle.asn1.x9.ECNamedCurveTable;
|
import org.spongycastle.asn1.x9.ECNamedCurveTable;
|
||||||
import org.spongycastle.asn1.x9.X9ECParameters;
|
import org.spongycastle.asn1.x9.X9ECParameters;
|
||||||
import org.spongycastle.crypto.params.ECNamedDomainParameters;
|
import org.spongycastle.crypto.params.ECNamedDomainParameters;
|
||||||
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||||
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
|
||||||
import org.spongycastle.jce.spec.ECNamedCurveSpec;
|
import org.spongycastle.jce.spec.ECNamedCurveSpec;
|
||||||
import org.spongycastle.jce.spec.ECParameterSpec;
|
import org.spongycastle.jce.spec.ECParameterSpec;
|
||||||
import org.spongycastle.jce.spec.ECPrivateKeySpec;
|
import org.spongycastle.jce.spec.ECPrivateKeySpec;
|
||||||
import org.spongycastle.jce.spec.ECPublicKeySpec;
|
import org.spongycastle.jce.spec.ECPublicKeySpec;
|
||||||
import org.spongycastle.jce.spec.IEKeySpec;
|
|
||||||
import org.spongycastle.math.ec.ECCurve;
|
import org.spongycastle.math.ec.ECCurve;
|
||||||
import org.spongycastle.math.ec.ECPoint;
|
import org.spongycastle.math.ec.ECPoint;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.KeyPairGenerator;
|
import java.security.KeyPairGenerator;
|
||||||
|
@ -51,20 +44,16 @@ import java.security.PublicKey;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.security.interfaces.ECPublicKey;
|
import java.security.interfaces.ECPublicKey;
|
||||||
import java.security.spec.ECGenParameterSpec;
|
import java.security.spec.ECGenParameterSpec;
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.CipherInputStream;
|
|
||||||
import javax.crypto.KeyAgreement;
|
import javax.crypto.KeyAgreement;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
|
|
||||||
import app.fedilab.android.client.Entities.Account;
|
import app.fedilab.android.client.Entities.Account;
|
||||||
import app.fedilab.android.sqlite.AccountDAO;
|
import app.fedilab.android.sqlite.AccountDAO;
|
||||||
import app.fedilab.android.sqlite.Sqlite;
|
import app.fedilab.android.sqlite.Sqlite;
|
||||||
|
|
||||||
// https://github.com/nelenkov/ecdh-kx/blob/master/src/org/nick/ecdhkx/Crypto.java
|
|
||||||
|
|
||||||
public class ECDH {
|
public class ECDH {
|
||||||
|
|
||||||
|
@ -76,24 +65,26 @@ public class ECDH {
|
||||||
public static final String kp_private = "kp_private";
|
public static final String kp_private = "kp_private";
|
||||||
public static final String KEGEN_ALG = "ECDH";
|
public static final String KEGEN_ALG = "ECDH";
|
||||||
|
|
||||||
|
public static final String name = "prime256v1";
|
||||||
|
|
||||||
private static final String kp_public_affine_x = "kp_public_affine_x";
|
private static final String kp_public_affine_x = "kp_public_affine_x";
|
||||||
private static final String kp_public_affine_y = "kp_public_affine_y";
|
private static final String kp_public_affine_y = "kp_public_affine_y";
|
||||||
|
|
||||||
private static ECDH instance;
|
private static ECDH instance;
|
||||||
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());
|
Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static KeyFactory kf = null;
|
public final KeyFactory kf;
|
||||||
private static KeyPairGenerator kpg = null;
|
private final KeyPairGenerator kpg;
|
||||||
|
|
||||||
|
|
||||||
public ECDH() {
|
public ECDH() {
|
||||||
try {
|
try {
|
||||||
kf = KeyFactory.getInstance(KEGEN_ALG, PROVIDER);
|
kf = KeyFactory.getInstance(KEGEN_ALG, PROVIDER);
|
||||||
kpg = KeyPairGenerator.getInstance(KEGEN_ALG, PROVIDER);
|
kpg = KeyPairGenerator.getInstance(KEGEN_ALG, PROVIDER);
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
|
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
@ -104,7 +95,6 @@ public class ECDH {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new ECDH();
|
instance = new ECDH();
|
||||||
}
|
}
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,16 +109,15 @@ public class ECDH {
|
||||||
return Base64.decode(str, Base64.DEFAULT);
|
return Base64.decode(str, Base64.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static synchronized KeyPair generateKeyPair()
|
synchronized KeyPair generateKeyPair()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
ECGenParameterSpec ecParamSpec = new ECGenParameterSpec("prime256v1");
|
ECGenParameterSpec ecParamSpec = new ECGenParameterSpec(name);
|
||||||
kpg.initialize(ecParamSpec);
|
kpg.initialize(ecParamSpec);
|
||||||
|
|
||||||
return kpg.generateKeyPair();
|
return kpg.generateKeyPair();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] generateSecret(PrivateKey myPrivKey, PublicKey otherPubKey) throws Exception {
|
private byte[] generateSecret(PrivateKey myPrivKey, PublicKey otherPubKey) throws Exception {
|
||||||
|
|
||||||
KeyAgreement keyAgreement = KeyAgreement.getInstance(KEGEN_ALG);
|
KeyAgreement keyAgreement = KeyAgreement.getInstance(KEGEN_ALG);
|
||||||
keyAgreement.init(myPrivKey);
|
keyAgreement.init(myPrivKey);
|
||||||
keyAgreement.doPhase(otherPubKey, true);
|
keyAgreement.doPhase(otherPubKey, true);
|
||||||
|
@ -137,13 +126,13 @@ public class ECDH {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static synchronized KeyPair readKeyPair(Context context)
|
synchronized KeyPair readKeyPair(Context context)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
return new KeyPair(readMyPublicKey(context), readMyPrivateKey(context));
|
return new KeyPair(readMyPublicKey(context), readMyPrivateKey(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ApplySharedPref")
|
@SuppressLint("ApplySharedPref")
|
||||||
public static KeyPair newPair(Context context) {
|
public KeyPair newPair(Context context) {
|
||||||
SharedPreferences.Editor prefsEditor = PreferenceManager
|
SharedPreferences.Editor prefsEditor = PreferenceManager
|
||||||
.getDefaultSharedPreferences(context).edit();
|
.getDefaultSharedPreferences(context).edit();
|
||||||
KeyPair kp;
|
KeyPair kp;
|
||||||
|
@ -159,8 +148,8 @@ public class ECDH {
|
||||||
byte[] y = key.getW().getAffineY().toByteArray();
|
byte[] y = key.getW().getAffineY().toByteArray();
|
||||||
BigInteger xbi = new BigInteger(1, x);
|
BigInteger xbi = new BigInteger(1, x);
|
||||||
BigInteger ybi = new BigInteger(1, y);
|
BigInteger ybi = new BigInteger(1, y);
|
||||||
X9ECParameters x9 = ECNamedCurveTable.getByName("prime256v1");
|
X9ECParameters x9 = ECNamedCurveTable.getByName(name);
|
||||||
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID("prime256v1");
|
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name);
|
||||||
|
|
||||||
ECCurve curve = x9.getCurve();
|
ECCurve curve = x9.getCurve();
|
||||||
ECPoint point = curve.createPoint(xbi, ybi);
|
ECPoint point = curve.createPoint(xbi, ybi);
|
||||||
|
@ -184,10 +173,10 @@ public class ECDH {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static synchronized PublicKey readMyPublicKey(Context context) throws Exception {
|
synchronized PublicKey readMyPublicKey(Context context) throws Exception {
|
||||||
|
|
||||||
X9ECParameters x9 = ECNamedCurveTable.getByName("prime256v1");
|
X9ECParameters x9 = ECNamedCurveTable.getByName(name);
|
||||||
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID("prime256v1");
|
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name);
|
||||||
|
|
||||||
SharedPreferences prefs = PreferenceManager
|
SharedPreferences prefs = PreferenceManager
|
||||||
.getDefaultSharedPreferences(context);
|
.getDefaultSharedPreferences(context);
|
||||||
|
@ -198,24 +187,23 @@ public class ECDH {
|
||||||
x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
|
x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
|
||||||
|
|
||||||
|
|
||||||
ECNamedCurveSpec ecNamedCurveSpec = new ECNamedCurveSpec("prime256v1", dParams.getCurve(), dParams.getG(), dParams.getN());
|
ECNamedCurveSpec ecNamedCurveSpec = new ECNamedCurveSpec(name, dParams.getCurve(), dParams.getG(), dParams.getN());
|
||||||
java.security.spec.ECPoint w = new java.security.spec.ECPoint(xbi, ybi);
|
java.security.spec.ECPoint w = new java.security.spec.ECPoint(xbi, ybi);
|
||||||
return kf.generatePublic(new java.security.spec.ECPublicKeySpec(w, ecNamedCurveSpec));
|
return kf.generatePublic(new java.security.spec.ECPublicKeySpec(w, ecNamedCurveSpec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String uncryptMessage(Context context, String cyphered, String slug) {
|
public String uncryptMessage(Context context, String cyphered, String slug) {
|
||||||
getInstance();
|
getInstance();
|
||||||
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
String[] slugArray = slug.split("@");
|
String[] slugArray = slug.split("@");
|
||||||
Account account = new AccountDAO(context, db).getUniqAccountUsernameInstance(slugArray[0], slugArray[1]);
|
Account account = new AccountDAO(context, db).getUniqAccountUsernameInstance(slugArray[0], slugArray[1]);
|
||||||
byte[] privateKey = getSharedSecret(context, account);
|
byte[] privateKey = getSharedSecret(context, account);
|
||||||
try {
|
try {
|
||||||
Cipher outCipher = Cipher.getInstance("ECIES", "SC");
|
Cipher outCipher = Cipher.getInstance("ECIES", PROVIDER);
|
||||||
// outCipher.init(Cipher.DECRYPT_MODE, readPrivateKey(privateKey));
|
// outCipher.init(Cipher.DECRYPT_MODE, readPrivateKey(privateKey));
|
||||||
outCipher.init(Cipher.DECRYPT_MODE, readPrivateKey(privateKey));
|
outCipher.init(Cipher.DECRYPT_MODE, readPrivateKey(privateKey));
|
||||||
|
byte[] plaintext = outCipher.doFinal(Base64.decode(cyphered, Base64.DEFAULT));
|
||||||
byte[] plaintext = outCipher.doFinal(cyphered.getBytes(StandardCharsets.UTF_8));
|
|
||||||
String finalText = new String(plaintext);
|
String finalText = new String(plaintext);
|
||||||
return finalText;
|
return finalText;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -226,9 +214,8 @@ public class ECDH {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static PublicKey readPublicKey(String keyStr) throws Exception {
|
public PublicKey readPublicKey(String keyStr) throws Exception {
|
||||||
KeyFactory kf = KeyFactory.getInstance("ECDH", new BouncyCastleProvider());
|
ECParameterSpec parameterSpec = org.spongycastle.jce.ECNamedCurveTable.getParameterSpec(name);
|
||||||
ECParameterSpec parameterSpec = org.spongycastle.jce.ECNamedCurveTable.getParameterSpec("prime256v1");
|
|
||||||
ECCurve curve = parameterSpec.getCurve();
|
ECCurve curve = parameterSpec.getCurve();
|
||||||
ECPoint point = curve.decodePoint(base64Decode(keyStr));
|
ECPoint point = curve.decodePoint(base64Decode(keyStr));
|
||||||
ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, parameterSpec);
|
ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, parameterSpec);
|
||||||
|
@ -236,33 +223,32 @@ public class ECDH {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static PrivateKey readPrivateKey(byte[] key) throws Exception {
|
public PrivateKey readPrivateKey(byte[] key) throws Exception {
|
||||||
KeyFactory kf = KeyFactory.getInstance("ECDH", new BouncyCastleProvider());
|
ECParameterSpec parameterSpec = org.spongycastle.jce.ECNamedCurveTable.getParameterSpec(name);
|
||||||
ECParameterSpec parameterSpec = org.spongycastle.jce.ECNamedCurveTable.getParameterSpec("prime256v1");
|
|
||||||
ECPrivateKeySpec pubSpec = new ECPrivateKeySpec(new BigInteger(1, key), parameterSpec);
|
ECPrivateKeySpec pubSpec = new ECPrivateKeySpec(new BigInteger(1, key), parameterSpec);
|
||||||
return kf.generatePrivate(pubSpec);
|
return kf.generatePrivate(pubSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
static synchronized PrivateKey readMyPrivateKey(Context context) throws Exception {
|
synchronized PrivateKey readMyPrivateKey(Context context) throws Exception {
|
||||||
X9ECParameters x9 = ECNamedCurveTable.getByName("prime256v1");
|
X9ECParameters x9 = ECNamedCurveTable.getByName(name);
|
||||||
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID("prime256v1");
|
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name);
|
||||||
|
|
||||||
SharedPreferences prefs = PreferenceManager
|
SharedPreferences prefs = PreferenceManager
|
||||||
.getDefaultSharedPreferences(context);
|
.getDefaultSharedPreferences(context);
|
||||||
BigInteger ybi = new BigInteger(prefs.getString(kp_public_affine_y, "0"));
|
BigInteger ybi = new BigInteger(prefs.getString(kp_public_affine_y, "0"));
|
||||||
ECNamedDomainParameters dParams = new ECNamedDomainParameters(oid,
|
ECNamedDomainParameters dParams = new ECNamedDomainParameters(oid,
|
||||||
x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
|
x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
|
||||||
ECNamedCurveSpec ecNamedCurveSpec = new ECNamedCurveSpec("prime256v1", dParams.getCurve(), dParams.getG(), dParams.getN());
|
ECNamedCurveSpec ecNamedCurveSpec = new ECNamedCurveSpec(name, dParams.getCurve(), dParams.getG(), dParams.getN());
|
||||||
return kf.generatePrivate(new java.security.spec.ECPrivateKeySpec(ybi, ecNamedCurveSpec));
|
return kf.generatePrivate(new java.security.spec.ECPrivateKeySpec(ybi, ecNamedCurveSpec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static synchronized KeyPair getPair(Context context) {
|
private synchronized KeyPair getPair(Context context) {
|
||||||
SharedPreferences prefs = PreferenceManager
|
SharedPreferences prefs = PreferenceManager
|
||||||
.getDefaultSharedPreferences(context);
|
.getDefaultSharedPreferences(context);
|
||||||
String strPub = prefs.getString(kp_public, "");
|
String strPub = prefs.getString(kp_public, "");
|
||||||
String strPriv = prefs.getString(kp_private, "");
|
String strPriv = prefs.getString(kp_private, "");
|
||||||
if (strPub.trim().isEmpty() || strPriv.trim().isEmpty() || 1 == 1) {
|
if (strPub.trim().isEmpty() || strPriv.trim().isEmpty()) {
|
||||||
return newPair(context);
|
return newPair(context);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -273,7 +259,7 @@ public class ECDH {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PublicKey getServerKey(Context context, Account account) throws Exception {
|
PublicKey getServerKey(Context context, Account account) throws Exception {
|
||||||
SharedPreferences prefs = PreferenceManager
|
SharedPreferences prefs = PreferenceManager
|
||||||
.getDefaultSharedPreferences(context);
|
.getDefaultSharedPreferences(context);
|
||||||
String serverKey = prefs.getString(peer_public + account.getId() + account.getInstance(), "");
|
String serverKey = prefs.getString(peer_public + account.getId() + account.getInstance(), "");
|
||||||
|
@ -281,13 +267,17 @@ public class ECDH {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "RedundantSuppression"})
|
@SuppressWarnings({"unused", "RedundantSuppression"})
|
||||||
public static byte[] getSharedSecret(Context context, Account account) {
|
public byte[] getSharedSecret(Context context, Account account) {
|
||||||
try {
|
try {
|
||||||
return generateSecret(getPair(context).getPrivate(), getServerKey(context, account));
|
KeyPair keyPair = getPair(context);
|
||||||
|
if (keyPair != null) {
|
||||||
|
return generateSecret(keyPair.getPrivate(), getServerKey(context, account));
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPublicKey(Context context) {
|
public String getPublicKey(Context context) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.unifiedpush.android.connector.MessagingReceiverHandler;
|
||||||
|
|
||||||
|
|
||||||
import app.fedilab.android.client.Entities.Account;
|
import app.fedilab.android.client.Entities.Account;
|
||||||
|
import app.fedilab.android.helper.ECDH;
|
||||||
import app.fedilab.android.helper.NotificationsHelper;
|
import app.fedilab.android.helper.NotificationsHelper;
|
||||||
import app.fedilab.android.helper.PushNotifications;
|
import app.fedilab.android.helper.PushNotifications;
|
||||||
import app.fedilab.android.sqlite.AccountDAO;
|
import app.fedilab.android.sqlite.AccountDAO;
|
||||||
|
@ -39,7 +40,8 @@ class handler implements MessagingReceiverHandler {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
String[] slugArray = slug.split("@");
|
String[] slugArray = slug.split("@");
|
||||||
//ECDH.uncryptMessage(context, s, slug);
|
ECDH ecdh = ECDH.getInstance();
|
||||||
|
//ecdh.uncryptMessage(context, s, slug);
|
||||||
Account account = new AccountDAO(context, db).getUniqAccountUsernameInstance(slugArray[0], slugArray[1]);
|
Account account = new AccountDAO(context, db).getUniqAccountUsernameInstance(slugArray[0], slugArray[1]);
|
||||||
NotificationsHelper.task(context, account);
|
NotificationsHelper.task(context, account);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue