mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-24 16:49:55 +01:00
Compare commits
1 Commits
fix_nonloc
...
1905-use-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d433f7478a |
14
.idea/dictionaries/briar.xml
generated
14
.idea/dictionaries/briar.xml
generated
@@ -1,14 +0,0 @@
|
|||||||
<component name="ProjectDictionaryState">
|
|
||||||
<dictionary name="briar">
|
|
||||||
<words>
|
|
||||||
<w>briar</w>
|
|
||||||
<w>briarproject</w>
|
|
||||||
<w>emoji</w>
|
|
||||||
<w>encrypter</w>
|
|
||||||
<w>identicon</w>
|
|
||||||
<w>introducee</w>
|
|
||||||
<w>introducer</w>
|
|
||||||
<w>onboarding</w>
|
|
||||||
</words>
|
|
||||||
</dictionary>
|
|
||||||
</component>
|
|
||||||
@@ -31,7 +31,6 @@ public class AndroidUtils {
|
|||||||
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
|
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
|
||||||
|
|
||||||
private static final String STORED_REPORTS = "dev-reports";
|
private static final String STORED_REPORTS = "dev-reports";
|
||||||
private static final String STORED_LOGCAT = "dev-logcat";
|
|
||||||
|
|
||||||
public static Collection<String> getSupportedArchitectures() {
|
public static Collection<String> getSupportedArchitectures() {
|
||||||
List<String> abis = new ArrayList<>();
|
List<String> abis = new ArrayList<>();
|
||||||
@@ -108,10 +107,6 @@ public class AndroidUtils {
|
|||||||
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
|
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getLogcatFile(Context ctx) {
|
|
||||||
return new File(ctx.getFilesDir(), STORED_LOGCAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of supported content types for image attachments.
|
* Returns an array of supported content types for image attachments.
|
||||||
* GIFs can't be compressed on API < 24 so they're not supported.
|
* GIFs can't be compressed on API < 24 so they're not supported.
|
||||||
|
|||||||
@@ -19,10 +19,4 @@ public interface StreamDecrypterFactory {
|
|||||||
*/
|
*/
|
||||||
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
|
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link StreamDecrypter} for decrypting a log stream.
|
|
||||||
*/
|
|
||||||
StreamDecrypter createLogStreamDecrypter(InputStream in,
|
|
||||||
SecretKey headerKey);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,6 @@ public interface StreamEncrypterFactory {
|
|||||||
* Creates a {@link StreamEncrypter} for encrypting a contact exchange
|
* Creates a {@link StreamEncrypter} for encrypting a contact exchange
|
||||||
* stream.
|
* stream.
|
||||||
*/
|
*/
|
||||||
StreamEncrypter createContactExchangeStreamEncrypter(OutputStream out,
|
StreamEncrypter createContactExchangeStreamDecrypter(OutputStream out,
|
||||||
SecretKey headerKey);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link StreamEncrypter} for encrypting a log stream.
|
|
||||||
*/
|
|
||||||
StreamEncrypter createLogStreamEncrypter(OutputStream out,
|
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,4 @@ public interface DevConfig {
|
|||||||
String getDevOnionAddress();
|
String getDevOnionAddress();
|
||||||
|
|
||||||
File getReportDir();
|
File getReportDir();
|
||||||
|
|
||||||
File getLogcatFile();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,4 @@ public interface StreamReaderFactory {
|
|||||||
*/
|
*/
|
||||||
InputStream createContactExchangeStreamReader(InputStream in,
|
InputStream createContactExchangeStreamReader(InputStream in,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an {@link InputStream} for reading from a log stream.
|
|
||||||
*/
|
|
||||||
InputStream createLogStreamReader(InputStream in, SecretKey headerKey);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,19 +7,17 @@ import java.io.OutputStream;
|
|||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface StreamWriterFactory {
|
public interface StreamWriterFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link StreamWriter} for writing to a transport stream.
|
* Creates an {@link OutputStream OutputStream} for writing to a
|
||||||
|
* transport stream
|
||||||
*/
|
*/
|
||||||
StreamWriter createStreamWriter(OutputStream out, StreamContext ctx);
|
StreamWriter createStreamWriter(OutputStream out, StreamContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link StreamWriter} for writing to a contact exchange stream.
|
* Creates an {@link OutputStream OutputStream} for writing to a contact
|
||||||
|
* exchange stream.
|
||||||
*/
|
*/
|
||||||
StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link StreamWriter} for writing to a log stream.
|
|
||||||
*/
|
|
||||||
StreamWriter createLogStreamWriter(OutputStream out, SecretKey headerKey);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,16 +9,14 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.lang.Math.min;
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
|
|
||||||
class ScryptKdf implements PasswordBasedKdf {
|
class ScryptKdf implements PasswordBasedKdf {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(ScryptKdf.class.getName());
|
Logger.getLogger(ScryptKdf.class.getName());
|
||||||
|
|
||||||
private static final int MIN_COST = 256; // Min parameter N
|
private static final int MIN_COST = 256; // Min parameter N
|
||||||
private static final int MAX_COST = 1024 * 1024; // Max parameter N
|
private static final int MAX_COST = 1024 * 1024; // Max parameter N
|
||||||
@@ -35,20 +33,10 @@ class ScryptKdf implements PasswordBasedKdf {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int chooseCostParameter() {
|
public int chooseCostParameter() {
|
||||||
// Scrypt uses at least 128 * N * r bytes of memory. Don't use more
|
|
||||||
// than half of the JVM's max heap size or we may run out of memory.
|
|
||||||
// https://blog.filippo.io/the-scrypt-parameters/
|
|
||||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
|
||||||
long maxCost = min(MAX_COST, maxMemory / BLOCK_SIZE / 256);
|
|
||||||
if (LOG.isLoggable(INFO) && maxCost < MAX_COST) {
|
|
||||||
LOG.info("Max cost capped at " + maxCost
|
|
||||||
+ " due to max heap size " + maxMemory);
|
|
||||||
}
|
|
||||||
// Increase the cost from min to max while measuring performance
|
// Increase the cost from min to max while measuring performance
|
||||||
int cost = MIN_COST;
|
int cost = MIN_COST;
|
||||||
while (cost * 2 <= maxCost && measureDuration(cost) * 2 <= TARGET_MS) {
|
while (cost * 2 <= MAX_COST && measureDuration(cost) * 2 <= TARGET_MS)
|
||||||
cost *= 2;
|
cost *= 2;
|
||||||
}
|
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("KDF cost parameter " + cost);
|
LOG.info("KDF cost parameter " + cost);
|
||||||
return cost;
|
return cost;
|
||||||
|
|||||||
@@ -36,10 +36,4 @@ class StreamDecrypterFactoryImpl implements StreamDecrypterFactory {
|
|||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
|
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public StreamDecrypter createLogStreamDecrypter(InputStream in,
|
|
||||||
SecretKey headerKey) {
|
|
||||||
return createContactExchangeStreamDecrypter(in, headerKey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamEncrypter createContactExchangeStreamEncrypter(
|
public StreamEncrypter createContactExchangeStreamDecrypter(
|
||||||
OutputStream out, SecretKey headerKey) {
|
OutputStream out, SecretKey headerKey) {
|
||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
@@ -60,10 +60,4 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
|
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
|
||||||
headerKey, frameKey);
|
headerKey, frameKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public StreamEncrypter createLogStreamEncrypter(OutputStream out,
|
|
||||||
SecretKey headerKey) {
|
|
||||||
return createContactExchangeStreamEncrypter(out, headerKey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,21 +24,15 @@ class StreamReaderFactoryImpl implements StreamReaderFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream createStreamReader(InputStream in, StreamContext ctx) {
|
public InputStream createStreamReader(InputStream in, StreamContext ctx) {
|
||||||
return new StreamReaderImpl(streamDecrypterFactory
|
return new StreamReaderImpl(
|
||||||
.createStreamDecrypter(in, ctx));
|
streamDecrypterFactory.createStreamDecrypter(in, ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream createContactExchangeStreamReader(InputStream in,
|
public InputStream createContactExchangeStreamReader(InputStream in,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamReaderImpl(streamDecrypterFactory
|
return new StreamReaderImpl(
|
||||||
.createContactExchangeStreamDecrypter(in, headerKey));
|
streamDecrypterFactory.createContactExchangeStreamDecrypter(in,
|
||||||
}
|
headerKey));
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream createLogStreamReader(InputStream in,
|
|
||||||
SecretKey headerKey) {
|
|
||||||
return new StreamReaderImpl(streamDecrypterFactory
|
|
||||||
.createLogStreamDecrypter(in, headerKey));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,21 +26,15 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
|
|||||||
@Override
|
@Override
|
||||||
public StreamWriter createStreamWriter(OutputStream out,
|
public StreamWriter createStreamWriter(OutputStream out,
|
||||||
StreamContext ctx) {
|
StreamContext ctx) {
|
||||||
return new StreamWriterImpl(streamEncrypterFactory
|
return new StreamWriterImpl(
|
||||||
.createStreamEncrypter(out, ctx));
|
streamEncrypterFactory.createStreamEncrypter(out, ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
public StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamWriterImpl(streamEncrypterFactory
|
return new StreamWriterImpl(
|
||||||
.createContactExchangeStreamEncrypter(out, headerKey));
|
streamEncrypterFactory.createContactExchangeStreamDecrypter(out,
|
||||||
}
|
headerKey));
|
||||||
|
|
||||||
@Override
|
|
||||||
public StreamWriter createLogStreamWriter(OutputStream out,
|
|
||||||
SecretKey headerKey) {
|
|
||||||
return new StreamWriterImpl(streamEncrypterFactory
|
|
||||||
.createLogStreamEncrypter(out, headerKey));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,6 @@ dependencies {
|
|||||||
testImplementation "org.jmock:jmock:$jmockVersion"
|
testImplementation "org.jmock:jmock:$jmockVersion"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
||||||
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
||||||
testAnnotationProcessor "com.google.dagger:dagger-compiler:2.24"
|
|
||||||
|
|
||||||
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class BriarAccountManager extends AndroidAccountManager {
|
|||||||
public void deleteAccount() {
|
public void deleteAccount() {
|
||||||
synchronized (stateChangeLock) {
|
synchronized (stateChangeLock) {
|
||||||
super.deleteAccount();
|
super.deleteAccount();
|
||||||
Localizer.reinitialize(appContext);
|
Localizer.reinitialize();
|
||||||
UiUtils.setTheme(appContext,
|
UiUtils.setTheme(appContext,
|
||||||
appContext.getString(R.string.pref_theme_light_value));
|
appContext.getString(R.string.pref_theme_light_value));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import org.briarproject.briar.BriarCoreModule;
|
|||||||
import org.briarproject.briar.android.attachment.AttachmentModule;
|
import org.briarproject.briar.android.attachment.AttachmentModule;
|
||||||
import org.briarproject.briar.android.attachment.media.MediaModule;
|
import org.briarproject.briar.android.attachment.media.MediaModule;
|
||||||
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
|
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
|
||||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
|
||||||
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
||||||
import org.briarproject.briar.android.view.EmojiTextInputView;
|
import org.briarproject.briar.android.view.EmojiTextInputView;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
@@ -180,10 +179,6 @@ public interface AndroidComponent
|
|||||||
|
|
||||||
AndroidWakeLockManager wakeLockManager();
|
AndroidWakeLockManager wakeLockManager();
|
||||||
|
|
||||||
CachingLogHandler logHandler();
|
|
||||||
|
|
||||||
Thread.UncaughtExceptionHandler exceptionHandler();
|
|
||||||
|
|
||||||
void inject(SignInReminderReceiver briarService);
|
void inject(SignInReminderReceiver briarService);
|
||||||
|
|
||||||
void inject(BriarService briarService);
|
void inject(BriarService briarService);
|
||||||
|
|||||||
@@ -579,7 +579,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void updateContactAddedNotification() {
|
private void updateContactAddedNotification() {
|
||||||
if (contactAddedTotal == 0) return;
|
|
||||||
BriarNotificationBuilder b =
|
BriarNotificationBuilder b =
|
||||||
new BriarNotificationBuilder(appContext, CONTACT_CHANNEL_ID);
|
new BriarNotificationBuilder(appContext, CONTACT_CHANNEL_ID);
|
||||||
b.setSmallIcon(R.drawable.notification_contact_added);
|
b.setSmallIcon(R.drawable.notification_contact_added);
|
||||||
@@ -714,16 +713,4 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
public void unblockAllBlogPostNotifications() {
|
public void unblockAllBlogPostNotifications() {
|
||||||
androidExecutor.runOnUiThread((Runnable) () -> blockBlogs = false);
|
androidExecutor.runOnUiThread((Runnable) () -> blockBlogs = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void restartNotifications(boolean locked, boolean mayAlertAgain) {
|
|
||||||
androidExecutor.runOnUiThread(() -> {
|
|
||||||
updateForegroundNotification(locked);
|
|
||||||
updateContactNotification(mayAlertAgain);
|
|
||||||
updateBlogPostNotification(mayAlertAgain);
|
|
||||||
updateForumPostNotification(mayAlertAgain);
|
|
||||||
updateGroupMessageNotification(mayAlertAgain);
|
|
||||||
updateContactAddedNotification();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ import org.briarproject.briar.android.account.LockManagerImpl;
|
|||||||
import org.briarproject.briar.android.account.SetupModule;
|
import org.briarproject.briar.android.account.SetupModule;
|
||||||
import org.briarproject.briar.android.contact.ContactListModule;
|
import org.briarproject.briar.android.contact.ContactListModule;
|
||||||
import org.briarproject.briar.android.forum.ForumModule;
|
import org.briarproject.briar.android.forum.ForumModule;
|
||||||
|
import org.briarproject.briar.android.introduction.IntroductionModule;
|
||||||
import org.briarproject.briar.android.keyagreement.ContactExchangeModule;
|
import org.briarproject.briar.android.keyagreement.ContactExchangeModule;
|
||||||
import org.briarproject.briar.android.logging.LoggingModule;
|
|
||||||
import org.briarproject.briar.android.login.LoginModule;
|
import org.briarproject.briar.android.login.LoginModule;
|
||||||
import org.briarproject.briar.android.navdrawer.NavDrawerModule;
|
import org.briarproject.briar.android.navdrawer.NavDrawerModule;
|
||||||
import org.briarproject.briar.android.privategroup.conversation.GroupConversationModule;
|
import org.briarproject.briar.android.privategroup.conversation.GroupConversationModule;
|
||||||
@@ -75,13 +75,13 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
|||||||
SetupModule.class,
|
SetupModule.class,
|
||||||
DozeHelperModule.class,
|
DozeHelperModule.class,
|
||||||
ContactExchangeModule.class,
|
ContactExchangeModule.class,
|
||||||
LoggingModule.class,
|
|
||||||
LoginModule.class,
|
LoginModule.class,
|
||||||
NavDrawerModule.class,
|
NavDrawerModule.class,
|
||||||
ViewModelModule.class,
|
ViewModelModule.class,
|
||||||
SettingsModule.class,
|
SettingsModule.class,
|
||||||
DevReportModule.class,
|
DevReportModule.class,
|
||||||
ContactListModule.class,
|
ContactListModule.class,
|
||||||
|
IntroductionModule.class,
|
||||||
// below need to be within same scope as ViewModelProvider.Factory
|
// below need to be within same scope as ViewModelProvider.Factory
|
||||||
ForumModule.class,
|
ForumModule.class,
|
||||||
GroupListModule.class,
|
GroupListModule.class,
|
||||||
@@ -194,11 +194,6 @@ public class AppModule {
|
|||||||
public File getReportDir() {
|
public File getReportDir() {
|
||||||
return AndroidUtils.getReportDir(app.getApplicationContext());
|
return AndroidUtils.getReportDir(app.getApplicationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getLogcatFile() {
|
|
||||||
return AndroidUtils.getLogcatFile(app.getApplicationContext());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
return devConfig;
|
return devConfig;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import android.content.SharedPreferences;
|
|||||||
import org.briarproject.bramble.BrambleApplication;
|
import org.briarproject.bramble.BrambleApplication;
|
||||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.logging.LogRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This exists so that the Application object will not necessarily be cast
|
* This exists so that the Application object will not necessarily be cast
|
||||||
* directly to the Briar application object.
|
* directly to the Briar application object.
|
||||||
@@ -14,6 +17,8 @@ public interface BriarApplication extends BrambleApplication {
|
|||||||
|
|
||||||
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;
|
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;
|
||||||
|
|
||||||
|
Collection<LogRecord> getRecentLogRecords();
|
||||||
|
|
||||||
AndroidComponent getApplicationComponent();
|
AndroidComponent getApplicationComponent();
|
||||||
|
|
||||||
SharedPreferences getDefaultSharedPreferences();
|
SharedPreferences getDefaultSharedPreferences();
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ import org.briarproject.bramble.BrambleCoreEagerSingletons;
|
|||||||
import org.briarproject.briar.BriarCoreEagerSingletons;
|
import org.briarproject.briar.BriarCoreEagerSingletons;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
import org.briarproject.briar.android.logging.CachingLogHandler;
|
||||||
|
import org.briarproject.briar.android.reporting.BriarExceptionHandler;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
|
||||||
import java.lang.Thread.UncaughtExceptionHandler;
|
import java.util.Collection;
|
||||||
import java.util.logging.Handler;
|
import java.util.logging.Handler;
|
||||||
|
import java.util.logging.LogRecord;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -40,11 +42,16 @@ public class BriarApplicationImpl extends Application
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(BriarApplicationImpl.class.getName());
|
getLogger(BriarApplicationImpl.class.getName());
|
||||||
|
|
||||||
|
private final CachingLogHandler logHandler = new CachingLogHandler();
|
||||||
|
private final BriarExceptionHandler exceptionHandler =
|
||||||
|
new BriarExceptionHandler(this);
|
||||||
|
|
||||||
private AndroidComponent applicationComponent;
|
private AndroidComponent applicationComponent;
|
||||||
private volatile SharedPreferences prefs;
|
private volatile SharedPreferences prefs;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void attachBaseContext(Context base) {
|
protected void attachBaseContext(Context base) {
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
|
||||||
if (prefs == null)
|
if (prefs == null)
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(base);
|
prefs = PreferenceManager.getDefaultSharedPreferences(base);
|
||||||
// Loading the language needs to be done here.
|
// Loading the language needs to be done here.
|
||||||
@@ -60,11 +67,6 @@ public class BriarApplicationImpl extends Application
|
|||||||
|
|
||||||
if (IS_DEBUG_BUILD) enableStrictMode();
|
if (IS_DEBUG_BUILD) enableStrictMode();
|
||||||
|
|
||||||
applicationComponent = createApplicationComponent();
|
|
||||||
UncaughtExceptionHandler exceptionHandler =
|
|
||||||
applicationComponent.exceptionHandler();
|
|
||||||
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
|
|
||||||
|
|
||||||
Logger rootLogger = getLogger("");
|
Logger rootLogger = getLogger("");
|
||||||
Handler[] handlers = rootLogger.getHandlers();
|
Handler[] handlers = rootLogger.getHandlers();
|
||||||
// Disable the Android logger for release builds
|
// Disable the Android logger for release builds
|
||||||
@@ -76,13 +78,12 @@ public class BriarApplicationImpl extends Application
|
|||||||
// Restore the default handlers after the level raising handler
|
// Restore the default handlers after the level raising handler
|
||||||
for (Handler handler : handlers) rootLogger.addHandler(handler);
|
for (Handler handler : handlers) rootLogger.addHandler(handler);
|
||||||
}
|
}
|
||||||
CachingLogHandler logHandler = applicationComponent.logHandler();
|
|
||||||
rootLogger.addHandler(logHandler);
|
rootLogger.addHandler(logHandler);
|
||||||
rootLogger.setLevel(IS_DEBUG_BUILD ? FINE : INFO);
|
rootLogger.setLevel(IS_DEBUG_BUILD ? FINE : INFO);
|
||||||
|
|
||||||
LOG.info("Created");
|
LOG.info("Created");
|
||||||
Localizer.getInstance().setLocaleLegacy(this);
|
|
||||||
|
|
||||||
|
applicationComponent = createApplicationComponent();
|
||||||
EmojiManager.install(new GoogleEmojiProvider());
|
EmojiManager.install(new GoogleEmojiProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,9 +106,7 @@ public class BriarApplicationImpl extends Application
|
|||||||
@Override
|
@Override
|
||||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||||
super.onConfigurationChanged(newConfig);
|
super.onConfigurationChanged(newConfig);
|
||||||
Localizer.getInstance().applicationConfigurationChanged(this, newConfig,
|
Localizer.getInstance().setLocale(this);
|
||||||
applicationComponent.androidNotificationManager(),
|
|
||||||
applicationComponent.lockManager().isLocked());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setTheme(Context ctx, SharedPreferences prefs) {
|
private void setTheme(Context ctx, SharedPreferences prefs) {
|
||||||
@@ -137,6 +136,11 @@ public class BriarApplicationImpl extends Application
|
|||||||
return applicationComponent;
|
return applicationComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<LogRecord> getRecentLogRecords() {
|
||||||
|
return logHandler.getRecentLogRecords();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AndroidComponent getApplicationComponent() {
|
public AndroidComponent getApplicationComponent() {
|
||||||
return applicationComponent;
|
return applicationComponent;
|
||||||
|
|||||||
@@ -6,16 +6,12 @@ import android.content.res.Configuration;
|
|||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import androidx.core.text.TextUtilsCompat;
|
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR;
|
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.LANGUAGE;
|
import static org.briarproject.briar.android.settings.SettingsFragment.LANGUAGE;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -25,7 +21,6 @@ public class Localizer {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private static Localizer INSTANCE;
|
private static Localizer INSTANCE;
|
||||||
private final Locale systemLocale;
|
private final Locale systemLocale;
|
||||||
@Nullable
|
|
||||||
private final Locale locale;
|
private final Locale locale;
|
||||||
|
|
||||||
private Localizer(SharedPreferences sharedPreferences) {
|
private Localizer(SharedPreferences sharedPreferences) {
|
||||||
@@ -35,17 +30,10 @@ public class Localizer {
|
|||||||
|
|
||||||
private Localizer(Locale systemLocale, @Nullable Locale userLocale) {
|
private Localizer(Locale systemLocale, @Nullable Locale userLocale) {
|
||||||
this.systemLocale = systemLocale;
|
this.systemLocale = systemLocale;
|
||||||
locale = userLocale;
|
if (userLocale == null) locale = systemLocale;
|
||||||
setLocaleAndSystemConfiguration(locale);
|
else locale = userLocale;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Localizer(Locale systemLocale) {
|
|
||||||
this.systemLocale = systemLocale;
|
|
||||||
locale = null;
|
|
||||||
setLocaleAndSystemConfiguration(systemLocale);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Instantiate the Localizer.
|
// Instantiate the Localizer.
|
||||||
public static synchronized void initialize(SharedPreferences prefs) {
|
public static synchronized void initialize(SharedPreferences prefs) {
|
||||||
if (INSTANCE == null)
|
if (INSTANCE == null)
|
||||||
@@ -53,11 +41,9 @@ public class Localizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reinstantiate the Localizer with the system locale
|
// Reinstantiate the Localizer with the system locale
|
||||||
public static synchronized void reinitialize(Context appContext) {
|
public static synchronized void reinitialize() {
|
||||||
if (INSTANCE != null && INSTANCE.locale != null) {
|
if (INSTANCE != null)
|
||||||
INSTANCE = new Localizer(INSTANCE.systemLocale);
|
INSTANCE = new Localizer(INSTANCE.systemLocale, null);
|
||||||
INSTANCE.forceLocale(appContext, INSTANCE.systemLocale);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current instance.
|
// Get the current instance.
|
||||||
@@ -78,98 +64,29 @@ public class Localizer {
|
|||||||
if (tag.contains("-")) {
|
if (tag.contains("-")) {
|
||||||
String[] langArray = tag.split("-");
|
String[] langArray = tag.split("-");
|
||||||
return new Locale(langArray[0], langArray[1]);
|
return new Locale(langArray[0], langArray[1]);
|
||||||
} else {
|
} else
|
||||||
return new Locale(tag);
|
return new Locale(tag);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the localized version of context
|
// Returns the localized version of context
|
||||||
public Context setLocale(Context context) {
|
public Context setLocale(Context context) {
|
||||||
if (locale == null || SDK_INT < 17) return context;
|
|
||||||
Resources res = context.getResources();
|
Resources res = context.getResources();
|
||||||
Configuration conf = res.getConfiguration();
|
Configuration conf = res.getConfiguration();
|
||||||
updateConfiguration(conf, locale);
|
Locale currentLocale;
|
||||||
return context.createConfigurationContext(conf);
|
if (SDK_INT >= 24) {
|
||||||
}
|
currentLocale = conf.getLocales().get(0);
|
||||||
|
} else
|
||||||
// For API < 17 only.
|
currentLocale = conf.locale;
|
||||||
public void setLocaleLegacy(Context appContext) {
|
if (locale.equals(currentLocale))
|
||||||
if (SDK_INT >= 17 || locale == null) return;
|
return context;
|
||||||
forceLocale(appContext, locale);
|
Locale.setDefault(locale);
|
||||||
}
|
|
||||||
|
|
||||||
// Forces the update of the resources through the deprecated API.
|
|
||||||
private void forceLocale(Context context, Locale locale) {
|
|
||||||
Resources res = context.getResources();
|
|
||||||
Configuration conf = res.getConfiguration();
|
|
||||||
updateConfiguration(conf, locale);
|
|
||||||
res.updateConfiguration(conf, res.getDisplayMetrics());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateConfiguration(Configuration conf, Locale locale) {
|
|
||||||
if (SDK_INT >= 17) {
|
if (SDK_INT >= 17) {
|
||||||
conf.setLocale(locale);
|
conf.setLocale(locale);
|
||||||
} else {
|
context.createConfigurationContext(conf);
|
||||||
|
} else
|
||||||
conf.locale = locale;
|
conf.locale = locale;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setLocaleAndSystemConfiguration(@Nullable Locale locale) {
|
|
||||||
if (locale == null) return;
|
|
||||||
Locale.setDefault(locale);
|
|
||||||
if (SDK_INT >= 23) return;
|
|
||||||
Resources systemResources = Resources.getSystem();
|
|
||||||
Configuration systemConfiguration = systemResources.getConfiguration();
|
|
||||||
updateConfiguration(systemConfiguration, locale);
|
|
||||||
// DateUtils uses the system resources, so we need to update them too.
|
|
||||||
//noinspection deprecation
|
//noinspection deprecation
|
||||||
systemResources.updateConfiguration(systemConfiguration,
|
res.updateConfiguration(conf, res.getDisplayMetrics());
|
||||||
systemResources.getDisplayMetrics());
|
return context;
|
||||||
}
|
|
||||||
|
|
||||||
private Locale getLocaleFromConfig(Configuration config) {
|
|
||||||
if (SDK_INT >= 24) {
|
|
||||||
return config.getLocales().get(0);
|
|
||||||
} else {
|
|
||||||
//noinspection deprecation
|
|
||||||
return config.locale;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applicationConfigurationChanged(Context appContext,
|
|
||||||
Configuration newConfig,
|
|
||||||
AndroidNotificationManager androidNotificationManager,
|
|
||||||
boolean locked) {
|
|
||||||
Locale newLocale = getLocaleFromConfig(newConfig);
|
|
||||||
if (locale == null && newLocale != systemLocale) {
|
|
||||||
androidNotificationManager.restartNotifications(locked, false);
|
|
||||||
}
|
|
||||||
if (newLocale == locale) return;
|
|
||||||
setLocaleAndSystemConfiguration(locale);
|
|
||||||
if (SDK_INT < 17) setLocaleLegacy(appContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the language represented by locale
|
|
||||||
* should be offered to the user on this device.
|
|
||||||
* * Android doesn't pick up Asturian on API < 21
|
|
||||||
* * RTL languages are supported since API >= 17
|
|
||||||
*/
|
|
||||||
public static boolean isLocaleSupported(Locale locale) {
|
|
||||||
if (SDK_INT >= 21) return true;
|
|
||||||
if (locale.getLanguage().equals("ast")) return false;
|
|
||||||
if (SDK_INT >= 17) return true;
|
|
||||||
return isLeftToRight(locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exclude RTL locales on API < 17, they won't be laid out correctly
|
|
||||||
private static boolean isLeftToRight(Locale locale) {
|
|
||||||
// TextUtilsCompat returns the wrong direction for Hebrew on some phones
|
|
||||||
String language = locale.getLanguage();
|
|
||||||
if (language.equals("iw") || language.equals("he")) return false;
|
|
||||||
int direction =
|
|
||||||
TextUtilsCompat.getLayoutDirectionFromLocale(locale);
|
|
||||||
return direction == LAYOUT_DIRECTION_LTR;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.contact;
|
package org.briarproject.briar.android.contact;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.briarproject.briar.android.util.BriarAdapter;
|
import org.briarproject.briar.android.util.BriarAdapter;
|
||||||
|
|
||||||
@@ -45,8 +44,4 @@ public abstract class BaseContactListAdapter<I extends ContactItem, VH extends C
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface OnContactClickListener<I> {
|
|
||||||
void onItemClick(View view, I item);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import android.view.ViewGroup;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.NullSafety;
|
import org.briarproject.bramble.api.nullsafety.NullSafety;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
|
||||||
|
|
||||||
import androidx.recyclerview.widget.DiffUtil.ItemCallback;
|
import androidx.recyclerview.widget.DiffUtil.ItemCallback;
|
||||||
import androidx.recyclerview.widget.ListAdapter;
|
import androidx.recyclerview.widget.ListAdapter;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
|
||||||
import org.briarproject.briar.android.contact.add.remote.AddContactActivity;
|
import org.briarproject.briar.android.contact.add.remote.AddContactActivity;
|
||||||
import org.briarproject.briar.android.contact.add.remote.PendingContactListActivity;
|
import org.briarproject.briar.android.contact.add.remote.PendingContactListActivity;
|
||||||
import org.briarproject.briar.android.conversation.ConversationActivity;
|
import org.briarproject.briar.android.conversation.ConversationActivity;
|
||||||
@@ -102,6 +101,8 @@ public class ContactListFragment extends BaseFragment
|
|||||||
.observe(getViewLifecycleOwner(), result -> {
|
.observe(getViewLifecycleOwner(), result -> {
|
||||||
result.onError(this::handleException).onSuccess(items -> {
|
result.onError(this::handleException).onSuccess(items -> {
|
||||||
adapter.submitList(items);
|
adapter.submitList(items);
|
||||||
|
// TODO: this should not be required any longer due to
|
||||||
|
// changes in BriarRecyclerView
|
||||||
if (requireNonNull(items).size() == 0) list.showData();
|
if (requireNonNull(items).size() == 0) list.showData();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
|||||||
@@ -3,70 +3,41 @@ package org.briarproject.briar.android.contact;
|
|||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
|
||||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
|
||||||
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
|
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
|
||||||
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
|
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
|
||||||
import org.briarproject.bramble.api.db.TransactionManager;
|
import org.briarproject.bramble.api.db.TransactionManager;
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
|
||||||
import org.briarproject.briar.android.viewmodel.LiveResult;
|
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent;
|
|
||||||
import org.briarproject.briar.api.client.MessageTracker;
|
|
||||||
import org.briarproject.briar.api.conversation.ConversationManager;
|
import org.briarproject.briar.api.conversation.ConversationManager;
|
||||||
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
|
||||||
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
|
|
||||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
|
||||||
import org.briarproject.briar.api.identity.AuthorManager;
|
import org.briarproject.briar.api.identity.AuthorManager;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.arch.core.util.Function;
|
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ContactListViewModel extends DbViewModel implements EventListener {
|
class ContactListViewModel extends ContactsViewModel {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(ContactListViewModel.class.getName());
|
getLogger(ContactListViewModel.class.getName());
|
||||||
|
|
||||||
private final ContactManager contactManager;
|
|
||||||
private final AuthorManager authorManager;
|
|
||||||
private final ConversationManager conversationManager;
|
|
||||||
private final ConnectionRegistry connectionRegistry;
|
|
||||||
private final EventBus eventBus;
|
|
||||||
private final AndroidNotificationManager notificationManager;
|
private final AndroidNotificationManager notificationManager;
|
||||||
|
|
||||||
private final MutableLiveData<LiveResult<List<ContactListItem>>>
|
|
||||||
contactListItems = new MutableLiveData<>();
|
|
||||||
|
|
||||||
private final MutableLiveData<Boolean> hasPendingContacts =
|
private final MutableLiveData<Boolean> hasPendingContacts =
|
||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
|
|
||||||
@@ -79,99 +50,25 @@ class ContactListViewModel extends DbViewModel implements EventListener {
|
|||||||
ConversationManager conversationManager,
|
ConversationManager conversationManager,
|
||||||
ConnectionRegistry connectionRegistry, EventBus eventBus,
|
ConnectionRegistry connectionRegistry, EventBus eventBus,
|
||||||
AndroidNotificationManager notificationManager) {
|
AndroidNotificationManager notificationManager) {
|
||||||
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
|
super(application, dbExecutor, lifecycleManager, db, androidExecutor,
|
||||||
this.contactManager = contactManager;
|
contactManager, authorManager, conversationManager,
|
||||||
this.authorManager = authorManager;
|
connectionRegistry, eventBus);
|
||||||
this.conversationManager = conversationManager;
|
|
||||||
this.connectionRegistry = connectionRegistry;
|
|
||||||
this.eventBus = eventBus;
|
|
||||||
this.notificationManager = notificationManager;
|
this.notificationManager = notificationManager;
|
||||||
this.eventBus.addListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCleared() {
|
|
||||||
super.onCleared();
|
|
||||||
eventBus.removeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadContacts() {
|
|
||||||
loadList(this::loadContacts, contactListItems::setValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ContactListItem> loadContacts(Transaction txn)
|
|
||||||
throws DbException {
|
|
||||||
long start = now();
|
|
||||||
List<ContactListItem> contacts = new ArrayList<>();
|
|
||||||
for (Contact c : contactManager.getContacts(txn)) {
|
|
||||||
ContactId id = c.getId();
|
|
||||||
AuthorInfo authorInfo = authorManager.getAuthorInfo(txn, c);
|
|
||||||
MessageTracker.GroupCount count =
|
|
||||||
conversationManager.getGroupCount(txn, id);
|
|
||||||
boolean connected = connectionRegistry.isConnected(c.getId());
|
|
||||||
contacts.add(new ContactListItem(c, authorInfo, connected, count));
|
|
||||||
}
|
|
||||||
Collections.sort(contacts);
|
|
||||||
logDuration(LOG, "Full load", start);
|
|
||||||
return contacts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof ContactAddedEvent) {
|
super.eventOccurred(e);
|
||||||
LOG.info("Contact added, reloading");
|
if (e instanceof PendingContactAddedEvent ||
|
||||||
loadContacts();
|
|
||||||
} else if (e instanceof ContactConnectedEvent) {
|
|
||||||
updateItem(((ContactConnectedEvent) e).getContactId(),
|
|
||||||
item -> new ContactListItem(item, true), false);
|
|
||||||
} else if (e instanceof ContactDisconnectedEvent) {
|
|
||||||
updateItem(((ContactDisconnectedEvent) e).getContactId(),
|
|
||||||
item -> new ContactListItem(item, false), false);
|
|
||||||
} else if (e instanceof ContactRemovedEvent) {
|
|
||||||
LOG.info("Contact removed, removing item");
|
|
||||||
removeItem(((ContactRemovedEvent) e).getContactId());
|
|
||||||
} else if (e instanceof ConversationMessageReceivedEvent) {
|
|
||||||
LOG.info("Conversation message received, updating item");
|
|
||||||
ConversationMessageReceivedEvent<?> p =
|
|
||||||
(ConversationMessageReceivedEvent<?>) e;
|
|
||||||
ConversationMessageHeader h = p.getMessageHeader();
|
|
||||||
updateItem(p.getContactId(), item -> new ContactListItem(item, h),
|
|
||||||
true);
|
|
||||||
} else if (e instanceof PendingContactAddedEvent ||
|
|
||||||
e instanceof PendingContactRemovedEvent) {
|
e instanceof PendingContactRemovedEvent) {
|
||||||
checkForPendingContacts();
|
checkForPendingContacts();
|
||||||
} else if (e instanceof AvatarUpdatedEvent) {
|
|
||||||
AvatarUpdatedEvent a = (AvatarUpdatedEvent) e;
|
|
||||||
updateItem(a.getContactId(), item -> new ContactListItem(item,
|
|
||||||
a.getAttachmentHeader()), false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveData<LiveResult<List<ContactListItem>>> getContactListItems() {
|
|
||||||
return contactListItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
LiveData<Boolean> getHasPendingContacts() {
|
LiveData<Boolean> getHasPendingContacts() {
|
||||||
return hasPendingContacts;
|
return hasPendingContacts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateItem(ContactId c,
|
|
||||||
Function<ContactListItem, ContactListItem> replacer, boolean sort) {
|
|
||||||
List<ContactListItem> list = updateListItems(contactListItems,
|
|
||||||
itemToTest -> itemToTest.getContact().getId().equals(c),
|
|
||||||
replacer);
|
|
||||||
if (list == null) return;
|
|
||||||
if (sort) Collections.sort(list);
|
|
||||||
contactListItems.setValue(new LiveResult<>(list));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeItem(ContactId c) {
|
|
||||||
List<ContactListItem> list = removeListItems(contactListItems,
|
|
||||||
itemToTest -> itemToTest.getContact().getId().equals(c));
|
|
||||||
if (list == null) return;
|
|
||||||
contactListItems.setValue(new LiveResult<>(list));
|
|
||||||
}
|
|
||||||
|
|
||||||
void checkForPendingContacts() {
|
void checkForPendingContacts() {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package org.briarproject.briar.android.contact;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||||
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
|
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||||
|
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
|
import org.briarproject.bramble.api.db.TransactionManager;
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
||||||
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
|
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
||||||
|
import org.briarproject.briar.android.viewmodel.LiveResult;
|
||||||
|
import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent;
|
||||||
|
import org.briarproject.briar.api.client.MessageTracker;
|
||||||
|
import org.briarproject.briar.api.conversation.ConversationManager;
|
||||||
|
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
||||||
|
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
|
||||||
|
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||||
|
import org.briarproject.briar.api.identity.AuthorManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.arch.core.util.Function;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class ContactsViewModel extends DbViewModel implements EventListener {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(ContactsViewModel.class.getName());
|
||||||
|
|
||||||
|
protected final ContactManager contactManager;
|
||||||
|
private final AuthorManager authorManager;
|
||||||
|
private final ConversationManager conversationManager;
|
||||||
|
private final ConnectionRegistry connectionRegistry;
|
||||||
|
private final EventBus eventBus;
|
||||||
|
|
||||||
|
private final MutableLiveData<LiveResult<List<ContactListItem>>>
|
||||||
|
contactListItems = new MutableLiveData<>();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ContactsViewModel(Application application,
|
||||||
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
|
LifecycleManager lifecycleManager, TransactionManager db,
|
||||||
|
AndroidExecutor androidExecutor, ContactManager contactManager,
|
||||||
|
AuthorManager authorManager,
|
||||||
|
ConversationManager conversationManager,
|
||||||
|
ConnectionRegistry connectionRegistry, EventBus eventBus) {
|
||||||
|
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
|
||||||
|
this.contactManager = contactManager;
|
||||||
|
this.authorManager = authorManager;
|
||||||
|
this.conversationManager = conversationManager;
|
||||||
|
this.connectionRegistry = connectionRegistry;
|
||||||
|
this.eventBus = eventBus;
|
||||||
|
this.eventBus.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCleared() {
|
||||||
|
super.onCleared();
|
||||||
|
eventBus.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadContacts() {
|
||||||
|
loadList(this::loadContacts, contactListItems::setValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ContactListItem> loadContacts(Transaction txn)
|
||||||
|
throws DbException {
|
||||||
|
long start = now();
|
||||||
|
List<ContactListItem> contacts = new ArrayList<>();
|
||||||
|
for (Contact c : contactManager.getContacts(txn)) {
|
||||||
|
ContactId id = c.getId();
|
||||||
|
if (!displayContact(id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AuthorInfo authorInfo = authorManager.getAuthorInfo(txn, c);
|
||||||
|
MessageTracker.GroupCount count =
|
||||||
|
conversationManager.getGroupCount(txn, id);
|
||||||
|
boolean connected = connectionRegistry.isConnected(c.getId());
|
||||||
|
contacts.add(new ContactListItem(c, authorInfo, connected, count));
|
||||||
|
}
|
||||||
|
Collections.sort(contacts);
|
||||||
|
logDuration(LOG, "Full load", start);
|
||||||
|
return contacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this method to display only a subset of contacts.
|
||||||
|
*/
|
||||||
|
protected boolean displayContact(ContactId contactId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if (e instanceof ContactAddedEvent) {
|
||||||
|
LOG.info("Contact added, reloading");
|
||||||
|
loadContacts();
|
||||||
|
} else if (e instanceof ContactConnectedEvent) {
|
||||||
|
updateItem(((ContactConnectedEvent) e).getContactId(),
|
||||||
|
item -> new ContactListItem(item, true), false);
|
||||||
|
} else if (e instanceof ContactDisconnectedEvent) {
|
||||||
|
updateItem(((ContactDisconnectedEvent) e).getContactId(),
|
||||||
|
item -> new ContactListItem(item, false), false);
|
||||||
|
} else if (e instanceof ContactRemovedEvent) {
|
||||||
|
LOG.info("Contact removed, removing item");
|
||||||
|
removeItem(((ContactRemovedEvent) e).getContactId());
|
||||||
|
} else if (e instanceof ConversationMessageReceivedEvent) {
|
||||||
|
LOG.info("Conversation message received, updating item");
|
||||||
|
ConversationMessageReceivedEvent<?> p =
|
||||||
|
(ConversationMessageReceivedEvent<?>) e;
|
||||||
|
ConversationMessageHeader h = p.getMessageHeader();
|
||||||
|
updateItem(p.getContactId(), item -> new ContactListItem(item, h),
|
||||||
|
true);
|
||||||
|
} else if (e instanceof AvatarUpdatedEvent) {
|
||||||
|
AvatarUpdatedEvent a = (AvatarUpdatedEvent) e;
|
||||||
|
updateItem(a.getContactId(), item -> new ContactListItem(item,
|
||||||
|
a.getAttachmentHeader()), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<LiveResult<List<ContactListItem>>> getContactListItems() {
|
||||||
|
return contactListItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateItem(ContactId c,
|
||||||
|
Function<ContactListItem, ContactListItem> replacer, boolean sort) {
|
||||||
|
List<ContactListItem> list = updateListItems(contactListItems,
|
||||||
|
itemToTest -> itemToTest.getContact().getId().equals(c),
|
||||||
|
replacer);
|
||||||
|
if (list == null) return;
|
||||||
|
if (sort) Collections.sort(list);
|
||||||
|
contactListItems.setValue(new LiveResult<>(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeItem(ContactId c) {
|
||||||
|
List<ContactListItem> list = removeListItems(contactListItems,
|
||||||
|
itemToTest -> itemToTest.getContact().getId().equals(c));
|
||||||
|
if (list == null) return;
|
||||||
|
contactListItems.setValue(new LiveResult<>(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package org.briarproject.briar.android.contact;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.briar.R;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public class LegacyContactListAdapter extends
|
|
||||||
BaseContactListAdapter<ContactListItem, ContactListItemViewHolder> {
|
|
||||||
|
|
||||||
public LegacyContactListAdapter(Context context,
|
|
||||||
OnContactClickListener<ContactListItem> listener) {
|
|
||||||
super(context, ContactListItem.class, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContactListItemViewHolder onCreateViewHolder(ViewGroup viewGroup,
|
|
||||||
int i) {
|
|
||||||
View v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
|
||||||
R.layout.list_item_contact, viewGroup, false);
|
|
||||||
|
|
||||||
return new ContactListItemViewHolder(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean areContentsTheSame(ContactListItem c1, ContactListItem c2) {
|
|
||||||
// check for all properties that influence visual
|
|
||||||
// representation of contact
|
|
||||||
if (c1.isEmpty() != c2.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (c1.getUnreadCount() != c2.getUnreadCount()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (c1.getTimestamp() != c2.getTimestamp()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return c1.isConnected() == c2.isConnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(ContactListItem c1, ContactListItem c2) {
|
|
||||||
return Long.compare(c2.getTimestamp(), c1.getTimestamp());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.briarproject.briar.android.contact;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
public interface OnContactClickListener<I> {
|
||||||
|
|
||||||
|
void onItemClick(View view, I item);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -124,7 +124,7 @@ public class AddContactViewModel extends DbViewModel {
|
|||||||
return addContactResult;
|
return addContactResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updatePendingContact(String name, PendingContact p) {
|
public void updatePendingContact(String name, PendingContact p) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
contactManager.removePendingContact(p.getId());
|
contactManager.removePendingContact(p.getId());
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ import org.briarproject.briar.android.fragment.BaseFragment;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.app.AlertDialog.Builder;
|
import androidx.appcompat.app.AlertDialog.Builder;
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.lifecycle.ViewModelProviders;
|
||||||
|
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
@@ -48,7 +48,6 @@ import static org.briarproject.briar.android.util.UiUtils.getDialogIcon;
|
|||||||
public class NicknameFragment extends BaseFragment {
|
public class NicknameFragment extends BaseFragment {
|
||||||
|
|
||||||
private static final String TAG = NicknameFragment.class.getName();
|
private static final String TAG = NicknameFragment.class.getName();
|
||||||
private static final String SAVED_LINK = "savedLink";
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
@@ -68,20 +67,6 @@ public class NicknameFragment extends BaseFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void injectFragment(ActivityComponent component) {
|
public void injectFragment(ActivityComponent component) {
|
||||||
component.inject(this);
|
component.inject(this);
|
||||||
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
|
|
||||||
.get(AddContactViewModel.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
// When the activity (and the ViewModel) get destroyed,
|
|
||||||
// the link will not be available anymore and needs to be restored.
|
|
||||||
// TODO migrate to SavedStateViewModelFactory (once we can use it)
|
|
||||||
String savedLink = savedInstanceState.getString(SAVED_LINK);
|
|
||||||
if (savedLink != null) viewModel.setRemoteHandshakeLink(savedLink);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -89,9 +74,14 @@ public class NicknameFragment extends BaseFragment {
|
|||||||
public View onCreateView(LayoutInflater inflater,
|
public View onCreateView(LayoutInflater inflater,
|
||||||
@Nullable ViewGroup container,
|
@Nullable ViewGroup container,
|
||||||
@Nullable Bundle savedInstanceState) {
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
if (getActivity() == null || getContext() == null) return null;
|
||||||
|
|
||||||
View v = inflater.inflate(R.layout.fragment_nickname,
|
View v = inflater.inflate(R.layout.fragment_nickname,
|
||||||
container, false);
|
container, false);
|
||||||
|
|
||||||
|
viewModel = ViewModelProviders.of(getActivity(), viewModelFactory)
|
||||||
|
.get(AddContactViewModel.class);
|
||||||
|
|
||||||
contactNameLayout = v.findViewById(R.id.contactNameLayout);
|
contactNameLayout = v.findViewById(R.id.contactNameLayout);
|
||||||
contactNameInput = v.findViewById(R.id.contactNameInput);
|
contactNameInput = v.findViewById(R.id.contactNameInput);
|
||||||
|
|
||||||
@@ -103,12 +93,6 @@ public class NicknameFragment extends BaseFragment {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putString(SAVED_LINK, viewModel.getRemoteHandshakeLink());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String getNicknameOrNull() {
|
private String getNicknameOrNull() {
|
||||||
Editable text = contactNameInput.getText();
|
Editable text = contactNameInput.getText();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.contact.ContactId;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter;
|
import org.briarproject.briar.android.contact.BaseContactListAdapter;
|
||||||
import org.briarproject.briar.android.contact.ContactItemViewHolder;
|
import org.briarproject.briar.android.contact.ContactItemViewHolder;
|
||||||
|
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
|
||||||
import org.briarproject.briar.android.contact.ContactItemViewHolder;
|
import org.briarproject.briar.android.contact.ContactItemViewHolder;
|
||||||
|
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.view.BriarRecyclerView;
|
import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
|
||||||
import org.briarproject.briar.android.contact.ContactItemViewHolder;
|
import org.briarproject.briar.android.contact.ContactItemViewHolder;
|
||||||
|
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ContactSelectorAdapter extends
|
class ContactSelectorAdapter extends
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import android.view.MenuItem;
|
|||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package org.briarproject.briar.android.contactselection;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|||||||
@@ -5,63 +5,42 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
import org.briarproject.briar.android.contact.ContactListAdapter;
|
||||||
import org.briarproject.briar.android.contact.ContactListItem;
|
import org.briarproject.briar.android.contact.ContactListItem;
|
||||||
import org.briarproject.briar.android.contact.LegacyContactListAdapter;
|
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.view.BriarRecyclerView;
|
import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
|
||||||
import org.briarproject.briar.api.conversation.ConversationManager;
|
|
||||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
|
||||||
import org.briarproject.briar.api.identity.AuthorManager;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.UiThread;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
|
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
|
||||||
|
|
||||||
@UiThread
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class ContactChooserFragment extends BaseFragment {
|
public class ContactChooserFragment extends BaseFragment
|
||||||
|
implements OnContactClickListener<ContactListItem> {
|
||||||
|
|
||||||
public static final String TAG = ContactChooserFragment.class.getName();
|
private static final String TAG = ContactChooserFragment.class.getName();
|
||||||
private static final Logger LOG = Logger.getLogger(TAG);
|
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
|
|
||||||
|
private ContactListViewModel viewModel;
|
||||||
|
private final ContactListAdapter adapter = new ContactListAdapter(this);
|
||||||
private BriarRecyclerView list;
|
private BriarRecyclerView list;
|
||||||
private LegacyContactListAdapter adapter;
|
|
||||||
private ContactId contactId;
|
private ContactId contactId;
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
static ContactChooserFragment newInstance(ContactId id) {
|
||||||
private volatile Contact c1;
|
|
||||||
@Inject
|
|
||||||
volatile ContactManager contactManager;
|
|
||||||
@Inject
|
|
||||||
volatile AuthorManager authorManager;
|
|
||||||
@Inject
|
|
||||||
volatile ConversationManager conversationManager;
|
|
||||||
@Inject
|
|
||||||
volatile ConnectionRegistry connectionRegistry;
|
|
||||||
|
|
||||||
public static ContactChooserFragment newInstance(ContactId id) {
|
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
|
|
||||||
ContactChooserFragment fragment = new ContactChooserFragment();
|
ContactChooserFragment fragment = new ContactChooserFragment();
|
||||||
@@ -73,6 +52,8 @@ public class ContactChooserFragment extends BaseFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void injectFragment(ActivityComponent component) {
|
public void injectFragment(ActivityComponent component) {
|
||||||
component.inject(this);
|
component.inject(this);
|
||||||
|
viewModel = new ViewModelProvider(this, viewModelFactory)
|
||||||
|
.get(ContactListViewModel.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,21 +63,20 @@ public class ContactChooserFragment extends BaseFragment {
|
|||||||
|
|
||||||
View contentView = inflater.inflate(R.layout.list, container, false);
|
View contentView = inflater.inflate(R.layout.list, container, false);
|
||||||
|
|
||||||
OnContactClickListener<ContactListItem> onContactClickListener =
|
|
||||||
(view, item) -> {
|
|
||||||
if (c1 == null) throw new IllegalStateException();
|
|
||||||
Contact c2 = item.getContact();
|
|
||||||
showMessageScreen(c1, c2);
|
|
||||||
};
|
|
||||||
adapter = new LegacyContactListAdapter(requireActivity(),
|
|
||||||
onContactClickListener);
|
|
||||||
|
|
||||||
list = contentView.findViewById(R.id.list);
|
list = contentView.findViewById(R.id.list);
|
||||||
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
list.setAdapter(adapter);
|
list.setAdapter(adapter);
|
||||||
list.setEmptyText(R.string.no_contacts);
|
list.setEmptyText(R.string.no_contacts);
|
||||||
|
|
||||||
contactId = new ContactId(requireArguments().getInt(CONTACT_ID));
|
contactId = new ContactId(requireArguments().getInt(CONTACT_ID));
|
||||||
|
viewModel.setContactId(contactId);
|
||||||
|
|
||||||
|
viewModel.loadContacts();
|
||||||
|
|
||||||
|
viewModel.getContactListItems().observe(getViewLifecycleOwner(),
|
||||||
|
result -> result.onError(this::handleException)
|
||||||
|
.onSuccess(adapter::submitList)
|
||||||
|
);
|
||||||
|
|
||||||
return contentView;
|
return contentView;
|
||||||
}
|
}
|
||||||
@@ -104,14 +84,13 @@ public class ContactChooserFragment extends BaseFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
loadContacts();
|
list.startPeriodicUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
adapter.clear();
|
list.stopPeriodicUpdate();
|
||||||
list.showProgressBar();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -119,43 +98,15 @@ public class ContactChooserFragment extends BaseFragment {
|
|||||||
return TAG;
|
return TAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadContacts() {
|
private void showMessageScreen(Contact other) {
|
||||||
listener.runOnDbThread(() -> {
|
|
||||||
try {
|
|
||||||
List<ContactListItem> contacts = new ArrayList<>();
|
|
||||||
for (Contact c : contactManager.getContacts()) {
|
|
||||||
if (c.getId().equals(contactId)) {
|
|
||||||
c1 = c;
|
|
||||||
} else {
|
|
||||||
AuthorInfo authorInfo = authorManager.getAuthorInfo(c);
|
|
||||||
ContactId id = c.getId();
|
|
||||||
GroupCount count =
|
|
||||||
conversationManager.getGroupCount(id);
|
|
||||||
boolean connected =
|
|
||||||
connectionRegistry.isConnected(c.getId());
|
|
||||||
contacts.add(new ContactListItem(c, authorInfo,
|
|
||||||
connected, count));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
displayContacts(contacts);
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayContacts(List<ContactListItem> contacts) {
|
|
||||||
runOnUiThreadUnlessDestroyed(() -> {
|
|
||||||
if (contacts.isEmpty()) list.showData();
|
|
||||||
else adapter.addAll(contacts);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showMessageScreen(Contact c1, Contact c2) {
|
|
||||||
IntroductionMessageFragment messageFragment =
|
IntroductionMessageFragment messageFragment =
|
||||||
IntroductionMessageFragment
|
IntroductionMessageFragment.newInstance(contactId.getInt(),
|
||||||
.newInstance(c1.getId().getInt(), c2.getId().getInt());
|
other.getId().getInt());
|
||||||
showNextFragment(messageFragment);
|
showNextFragment(messageFragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(View view, ContactListItem item) {
|
||||||
|
showMessageScreen(item.getContact());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package org.briarproject.briar.android.introduction;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
|
import org.briarproject.bramble.api.db.TransactionManager;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
|
import org.briarproject.briar.android.contact.ContactsViewModel;
|
||||||
|
import org.briarproject.briar.api.conversation.ConversationManager;
|
||||||
|
import org.briarproject.briar.api.identity.AuthorManager;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class ContactListViewModel extends ContactsViewModel {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private ContactId contactId;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ContactListViewModel(Application application,
|
||||||
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
|
LifecycleManager lifecycleManager, TransactionManager db,
|
||||||
|
AndroidExecutor androidExecutor, ContactManager contactManager,
|
||||||
|
AuthorManager authorManager,
|
||||||
|
ConversationManager conversationManager,
|
||||||
|
ConnectionRegistry connectionRegistry, EventBus eventBus) {
|
||||||
|
super(application, dbExecutor, lifecycleManager, db, androidExecutor,
|
||||||
|
contactManager, authorManager, conversationManager,
|
||||||
|
connectionRegistry, eventBus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setContactId(ContactId contactId) {
|
||||||
|
this.contactId = contactId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean displayContact(ContactId contactId) {
|
||||||
|
return !requireNonNull(this.contactId).equals(contactId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.briarproject.briar.android.introduction;
|
||||||
|
|
||||||
|
import org.briarproject.briar.android.viewmodel.ViewModelKey;
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel;
|
||||||
|
import dagger.Binds;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.multibindings.IntoMap;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public abstract class IntroductionModule {
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@ViewModelKey(ContactListViewModel.class)
|
||||||
|
abstract ViewModel bindContactListViewModel(
|
||||||
|
ContactListViewModel contactListViewModel);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.logging.Formatter;
|
import java.util.logging.Formatter;
|
||||||
@@ -18,16 +17,6 @@ import static java.util.Locale.US;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class BriefLogFormatter extends Formatter {
|
public class BriefLogFormatter extends Formatter {
|
||||||
|
|
||||||
public static String formatLog(Formatter formatter,
|
|
||||||
Collection<LogRecord> logRecords) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (LogRecord record : logRecords) {
|
|
||||||
String formatted = formatter.format(record);
|
|
||||||
sb.append(formatted).append('\n');
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
private final Object lock = new Object();
|
||||||
private final DateFormat dateFormat; // Locking: lock
|
private final DateFormat dateFormat; // Locking: lock
|
||||||
private final Date date; // Locking: lock
|
private final Date date; // Locking: lock
|
||||||
|
|||||||
@@ -21,10 +21,6 @@ public class CachingLogHandler extends Handler {
|
|||||||
// Locking: lock
|
// Locking: lock
|
||||||
private final Queue<LogRecord> recent = new LinkedList<>();
|
private final Queue<LogRecord> recent = new LinkedList<>();
|
||||||
|
|
||||||
// package-private constructor
|
|
||||||
CachingLogHandler() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void publish(LogRecord record) {
|
public void publish(LogRecord record) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.briarproject.briar.android.logging;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface LogDecrypter {
|
|
||||||
/**
|
|
||||||
* Returns decrypted log records from {@link AndroidUtils#getLogcatFile}
|
|
||||||
* or null if there was an error reading the logs.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
String decryptLogs(@Nullable byte[] logKey);
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package org.briarproject.briar.android.logging;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Scanner;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class LogDecrypterImpl implements LogDecrypter {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(LogDecrypterImpl.class.getName());
|
|
||||||
|
|
||||||
private final DevConfig devConfig;
|
|
||||||
private final StreamReaderFactory streamReaderFactory;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
LogDecrypterImpl(DevConfig devConfig,
|
|
||||||
StreamReaderFactory streamReaderFactory) {
|
|
||||||
this.devConfig = devConfig;
|
|
||||||
this.streamReaderFactory = streamReaderFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String decryptLogs(@Nullable byte[] logKey) {
|
|
||||||
if (logKey == null) return null;
|
|
||||||
SecretKey key = new SecretKey(logKey);
|
|
||||||
File logFile = devConfig.getLogcatFile();
|
|
||||||
try (InputStream in = new FileInputStream(logFile)) {
|
|
||||||
InputStream reader =
|
|
||||||
streamReaderFactory.createLogStreamReader(in, key);
|
|
||||||
Scanner s = new Scanner(reader);
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
while (s.hasNextLine()) sb.append(s.nextLine()).append("\n");
|
|
||||||
s.close();
|
|
||||||
return sb.toString();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
logFile.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.briarproject.briar.android.logging;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface LogEncrypter {
|
|
||||||
/**
|
|
||||||
* Writes encrypted log records to {@link AndroidUtils#getLogcatFile}
|
|
||||||
* and returns the encryption key if everything went fine.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
byte[] encryptLogs();
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package org.briarproject.briar.android.logging;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.util.logging.Formatter;
|
|
||||||
import java.util.logging.LogRecord;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class LogEncrypterImpl implements LogEncrypter {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(LogEncrypterImpl.class.getName());
|
|
||||||
|
|
||||||
private final DevConfig devConfig;
|
|
||||||
private final CachingLogHandler logHandler;
|
|
||||||
private final CryptoComponent crypto;
|
|
||||||
private final StreamWriterFactory streamWriterFactory;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
LogEncrypterImpl(DevConfig devConfig,
|
|
||||||
CachingLogHandler logHandler,
|
|
||||||
CryptoComponent crypto,
|
|
||||||
StreamWriterFactory streamWriterFactory) {
|
|
||||||
this.devConfig = devConfig;
|
|
||||||
this.logHandler = logHandler;
|
|
||||||
this.crypto = crypto;
|
|
||||||
this.streamWriterFactory = streamWriterFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public byte[] encryptLogs() {
|
|
||||||
SecretKey logKey = crypto.generateSecretKey();
|
|
||||||
File logFile = devConfig.getLogcatFile();
|
|
||||||
try (OutputStream out = new FileOutputStream(logFile)) {
|
|
||||||
StreamWriter streamWriter =
|
|
||||||
streamWriterFactory.createLogStreamWriter(out, logKey);
|
|
||||||
Writer writer =
|
|
||||||
new OutputStreamWriter(streamWriter.getOutputStream());
|
|
||||||
writeLogString(writer);
|
|
||||||
writer.close();
|
|
||||||
return logKey.getBytes();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeLogString(Writer writer) throws IOException {
|
|
||||||
Formatter formatter = new BriefLogFormatter();
|
|
||||||
for (LogRecord record : logHandler.getRecentLogRecords()) {
|
|
||||||
String formatted = formatter.format(record);
|
|
||||||
writer.append(formatted).append('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package org.briarproject.briar.android.logging;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class LoggingModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
CachingLogHandler provideCachingLogHandler() {
|
|
||||||
return new CachingLogHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
LogEncrypter provideLogEncrypter(LogEncrypterImpl logEncrypter) {
|
|
||||||
return logEncrypter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
LogDecrypter provideLogDecrypter(LogDecrypterImpl logDecrypter) {
|
|
||||||
return logDecrypter;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -68,7 +68,6 @@ import static android.view.View.VISIBLE;
|
|||||||
import static androidx.core.view.GravityCompat.START;
|
import static androidx.core.view.GravityCompat.START;
|
||||||
import static androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
|
import static androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
|
||||||
import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
|
import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
|
||||||
import static androidx.lifecycle.Lifecycle.State.STARTED;
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||||
@@ -298,12 +297,6 @@ public class NavDrawerActivity extends BriarActivity implements
|
|||||||
finish();
|
finish();
|
||||||
} else if (fm.getBackStackEntryCount() == 0
|
} else if (fm.getBackStackEntryCount() == 0
|
||||||
&& fm.findFragmentByTag(ContactListFragment.TAG) == null) {
|
&& fm.findFragmentByTag(ContactListFragment.TAG) == null) {
|
||||||
// don't start fragments in the wrong part of lifecycle (#1904)
|
|
||||||
if (!getLifecycle().getCurrentState().isAtLeast(STARTED)) {
|
|
||||||
LOG.warning("Tried to start contacts fragment in state " +
|
|
||||||
getLifecycle().getCurrentState().name());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* This makes sure that the first fragment (ContactListFragment) the
|
* This makes sure that the first fragment (ContactListFragment) the
|
||||||
* user sees is the same as the last fragment the user sees before
|
* user sees is the same as the last fragment the user sees before
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||||
import org.briarproject.briar.android.contactselection.BaseContactSelectorFragment;
|
import org.briarproject.briar.android.contactselection.BaseContactSelectorFragment;
|
||||||
import org.briarproject.briar.android.contactselection.ContactSelectorController;
|
import org.briarproject.briar.android.contactselection.ContactSelectorController;
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.view.ViewGroup;
|
|||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||||
import org.briarproject.briar.android.contactselection.BaseContactSelectorAdapter;
|
import org.briarproject.briar.android.contactselection.BaseContactSelectorAdapter;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import android.widget.ImageView;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||||
import org.briarproject.briar.android.contactselection.BaseSelectableContactHolder;
|
import org.briarproject.briar.android.contactselection.BaseSelectableContactHolder;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|||||||
@@ -1,40 +1,29 @@
|
|||||||
package org.briarproject.briar.android.reporting;
|
package org.briarproject.briar.android.reporting;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.content.Context;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.logging.LogEncrypter;
|
|
||||||
|
|
||||||
import java.lang.Thread.UncaughtExceptionHandler;
|
import java.lang.Thread.UncaughtExceptionHandler;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static org.briarproject.briar.android.util.UiUtils.startDevReportActivity;
|
import static org.briarproject.briar.android.util.UiUtils.startDevReportActivity;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class BriarExceptionHandler implements UncaughtExceptionHandler {
|
public class BriarExceptionHandler implements UncaughtExceptionHandler {
|
||||||
|
|
||||||
private final Application app;
|
private final Context ctx;
|
||||||
private final LogEncrypter logEncrypter;
|
|
||||||
private final long appStartTime;
|
private final long appStartTime;
|
||||||
|
|
||||||
@Inject
|
public BriarExceptionHandler(Context ctx) {
|
||||||
BriarExceptionHandler(Application app, LogEncrypter logEncrypter) {
|
this.ctx = ctx;
|
||||||
this.app = app;
|
this.appStartTime = System.currentTimeMillis();
|
||||||
this.logEncrypter = logEncrypter;
|
|
||||||
appStartTime = System.currentTimeMillis();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void uncaughtException(Thread t, Throwable e) {
|
public void uncaughtException(Thread t, Throwable e) {
|
||||||
// encrypt logs to disk before handing over to new process
|
|
||||||
// the intent has limited space, so we can't reliably store them there.
|
|
||||||
byte[] logKey = logEncrypter.encryptLogs();
|
|
||||||
|
|
||||||
// activity runs in its own process, so we can kill the old one
|
// activity runs in its own process, so we can kill the old one
|
||||||
startDevReportActivity(app.getApplicationContext(),
|
startDevReportActivity(ctx, CrashReportActivity.class, e, appStartTime);
|
||||||
CrashReportActivity.class, e, appStartTime, logKey);
|
|
||||||
Process.killProcess(Process.myPid());
|
Process.killProcess(Process.myPid());
|
||||||
System.exit(10);
|
System.exit(10);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import org.briarproject.bramble.api.Pair;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.BuildConfig;
|
import org.briarproject.briar.BuildConfig;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
|
import org.briarproject.briar.android.logging.BriefLogFormatter;
|
||||||
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
|
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
|
||||||
import org.briarproject.briar.android.reporting.ReportData.ReportItem;
|
import org.briarproject.briar.android.reporting.ReportData.ReportItem;
|
||||||
import org.briarproject.briar.android.reporting.ReportData.SingleReportInfo;
|
import org.briarproject.briar.android.reporting.ReportData.SingleReportInfo;
|
||||||
@@ -39,6 +41,8 @@ import java.net.InetAddress;
|
|||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.logging.Formatter;
|
||||||
|
import java.util.logging.LogRecord;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
@@ -70,8 +74,8 @@ class BriarReportCollector {
|
|||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReportData collectReportData(@Nullable Throwable t, long appStartTime,
|
public ReportData collectReportData(@Nullable Throwable t,
|
||||||
String logs) {
|
long appStartTime) {
|
||||||
ReportData reportData = new ReportData()
|
ReportData reportData = new ReportData()
|
||||||
.add(getBasicInfo(t))
|
.add(getBasicInfo(t))
|
||||||
.add(getDeviceInfo());
|
.add(getDeviceInfo());
|
||||||
@@ -82,7 +86,7 @@ class BriarReportCollector {
|
|||||||
.add(getStorage())
|
.add(getStorage())
|
||||||
.add(getConnectivity())
|
.add(getConnectivity())
|
||||||
.add(getBuildConfig())
|
.add(getBuildConfig())
|
||||||
.add(getLogcat(logs))
|
.add(getLogcat())
|
||||||
.add(getDeviceFeatures());
|
.add(getDeviceFeatures());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,8 +313,15 @@ class BriarReportCollector {
|
|||||||
buildConfig);
|
buildConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReportItem getLogcat(String logs) {
|
private ReportItem getLogcat() {
|
||||||
return new ReportItem("Logcat", R.string.dev_report_logcat, logs);
|
BriarApplication app = (BriarApplication) ctx.getApplicationContext();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
Formatter formatter = new BriefLogFormatter();
|
||||||
|
for (LogRecord record : app.getRecentLogRecords()) {
|
||||||
|
sb.append(formatter.format(record)).append('\n');
|
||||||
|
}
|
||||||
|
return new ReportItem("Logcat", R.string.dev_report_logcat,
|
||||||
|
sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReportItem getDeviceFeatures() {
|
private ReportItem getDeviceFeatures() {
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ public class CrashReportActivity extends BaseActivity
|
|||||||
|
|
||||||
public static final String EXTRA_THROWABLE = "throwable";
|
public static final String EXTRA_THROWABLE = "throwable";
|
||||||
public static final String EXTRA_APP_START_TIME = "appStartTime";
|
public static final String EXTRA_APP_START_TIME = "appStartTime";
|
||||||
public static final String EXTRA_APP_LOGCAT = "logcat";
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
@@ -57,8 +56,7 @@ public class CrashReportActivity extends BaseActivity
|
|||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
Throwable t = (Throwable) intent.getSerializableExtra(EXTRA_THROWABLE);
|
Throwable t = (Throwable) intent.getSerializableExtra(EXTRA_THROWABLE);
|
||||||
long appStartTime = intent.getLongExtra(EXTRA_APP_START_TIME, -1);
|
long appStartTime = intent.getLongExtra(EXTRA_APP_START_TIME, -1);
|
||||||
byte[] logKey = intent.getByteArrayExtra(EXTRA_APP_LOGCAT);
|
viewModel.init(t, appStartTime);
|
||||||
viewModel.init(t, appStartTime, logKey);
|
|
||||||
viewModel.getShowReport().observeEvent(this, show -> {
|
viewModel.getShowReport().observeEvent(this, show -> {
|
||||||
if (show) displayFragment(true);
|
if (show) displayFragment(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,8 +15,4 @@ public abstract class DevReportModule {
|
|||||||
@ViewModelKey(ReportViewModel.class)
|
@ViewModelKey(ReportViewModel.class)
|
||||||
abstract ViewModel bindReportViewModel(ReportViewModel reportViewModel);
|
abstract ViewModel bindReportViewModel(ReportViewModel reportViewModel);
|
||||||
|
|
||||||
@Binds
|
|
||||||
abstract Thread.UncaughtExceptionHandler bindUncaughtExceptionHandler(
|
|
||||||
BriarExceptionHandler handler);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ import org.briarproject.bramble.api.plugin.TorConstants;
|
|||||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
import org.briarproject.bramble.util.AndroidUtils;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.logging.BriefLogFormatter;
|
|
||||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
|
||||||
import org.briarproject.briar.android.logging.LogDecrypter;
|
|
||||||
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
|
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||||
@@ -22,7 +19,6 @@ import org.json.JSONException;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.logging.Formatter;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -40,16 +36,13 @@ import static java.util.logging.Logger.getLogger;
|
|||||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
import static org.briarproject.briar.android.logging.BriefLogFormatter.formatLog;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ReportViewModel extends AndroidViewModel {
|
public class ReportViewModel extends AndroidViewModel {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(ReportViewModel.class.getName());
|
getLogger(ReportViewModel.class.getName());
|
||||||
|
|
||||||
private final CachingLogHandler logHandler;
|
|
||||||
private final LogDecrypter logDecrypter;
|
|
||||||
private final BriarReportCollector collector;
|
private final BriarReportCollector collector;
|
||||||
private final DevReporter reporter;
|
private final DevReporter reporter;
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
@@ -65,39 +58,18 @@ class ReportViewModel extends AndroidViewModel {
|
|||||||
private boolean isFeedback;
|
private boolean isFeedback;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ReportViewModel(@NonNull Application application,
|
public ReportViewModel(@NonNull Application application,
|
||||||
CachingLogHandler logHandler,
|
DevReporter reporter, PluginManager pluginManager) {
|
||||||
LogDecrypter logDecrypter,
|
|
||||||
DevReporter reporter,
|
|
||||||
PluginManager pluginManager) {
|
|
||||||
super(application);
|
super(application);
|
||||||
collector = new BriarReportCollector(application);
|
this.collector = new BriarReportCollector(application);
|
||||||
this.logHandler = logHandler;
|
|
||||||
this.logDecrypter = logDecrypter;
|
|
||||||
this.reporter = reporter;
|
this.reporter = reporter;
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(@Nullable Throwable t, long appStartTime,
|
void init(@Nullable Throwable t, long appStartTime) {
|
||||||
@Nullable byte[] logKey) {
|
|
||||||
isFeedback = t == null;
|
isFeedback = t == null;
|
||||||
if (reportData.getValue() == null) new SingleShotAndroidExecutor(() -> {
|
if (reportData.getValue() == null) new SingleShotAndroidExecutor(() -> {
|
||||||
String decryptedLogs;
|
ReportData data = collector.collectReportData(t, appStartTime);
|
||||||
if (isFeedback) {
|
|
||||||
Formatter formatter = new BriefLogFormatter();
|
|
||||||
decryptedLogs =
|
|
||||||
formatLog(formatter, logHandler.getRecentLogRecords());
|
|
||||||
} else {
|
|
||||||
decryptedLogs = logDecrypter.decryptLogs(logKey);
|
|
||||||
if (decryptedLogs == null) {
|
|
||||||
// error decrypting logs, get logs from this process
|
|
||||||
Formatter formatter = new BriefLogFormatter();
|
|
||||||
decryptedLogs = formatLog(formatter,
|
|
||||||
logHandler.getRecentLogRecords());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ReportData data =
|
|
||||||
collector.collectReportData(t, appStartTime, decryptedLogs);
|
|
||||||
reportData.postValue(data);
|
reportData.postValue(data);
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
@@ -138,8 +110,8 @@ class ReportViewModel extends AndroidViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The content of the report that will be loaded after
|
* The content of the report
|
||||||
* {@link #init(Throwable, long, byte[])} was called.
|
* that will be loaded after {@link #init(Throwable, long)} was called.
|
||||||
*/
|
*/
|
||||||
LiveData<ReportData> getReportData() {
|
LiveData<ReportData> getReportData() {
|
||||||
return reportData;
|
return reportData;
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import javax.inject.Inject;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.text.TextUtilsCompat;
|
||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.Preference.OnPreferenceChangeListener;
|
import androidx.preference.Preference.OnPreferenceChangeListener;
|
||||||
@@ -69,6 +70,7 @@ import static android.provider.Settings.EXTRA_APP_PACKAGE;
|
|||||||
import static android.provider.Settings.EXTRA_CHANNEL_ID;
|
import static android.provider.Settings.EXTRA_CHANNEL_ID;
|
||||||
import static android.provider.Settings.System.DEFAULT_NOTIFICATION_URI;
|
import static android.provider.Settings.System.DEFAULT_NOTIFICATION_URI;
|
||||||
import static android.widget.Toast.LENGTH_SHORT;
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
|
import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
@@ -294,20 +296,13 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
Locale locale = Localizer.getLocaleFromTag(tag);
|
Locale locale = Localizer.getLocaleFromTag(tag);
|
||||||
if (locale == null)
|
if (locale == null)
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
// Check if the locale is supported on this device
|
// Exclude RTL locales on API < 17, they won't be laid out correctly
|
||||||
if (!Localizer.isLocaleSupported(locale)) {
|
if (SDK_INT < 17 && !isLeftToRight(locale)) {
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Skipping unsupported locale " + tag);
|
LOG.info("Skipping RTL locale " + tag);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String nativeName;
|
String nativeName = locale.getDisplayName(locale);
|
||||||
// Unknown languages won't be translated to their native name.
|
|
||||||
if (locale.getLanguage().equals("ast")) {
|
|
||||||
nativeName = "Asturianu";
|
|
||||||
} else if (locale.getLanguage().equals("oc")) {
|
|
||||||
nativeName = "Occitan";
|
|
||||||
} else {
|
|
||||||
nativeName = locale.getDisplayName(locale);
|
|
||||||
// Fallback to English if the name is unknown in both native and
|
// Fallback to English if the name is unknown in both native and
|
||||||
// current locale.
|
// current locale.
|
||||||
if (nativeName.equals(tag)) {
|
if (nativeName.equals(tag)) {
|
||||||
@@ -315,7 +310,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
if (!tmp.isEmpty() && !tmp.equals(nativeName))
|
if (!tmp.isEmpty() && !tmp.equals(nativeName))
|
||||||
nativeName = tmp;
|
nativeName = tmp;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Prefix with LRM marker to prevent any RTL direction
|
// Prefix with LRM marker to prevent any RTL direction
|
||||||
entries.add("\u200E" + nativeName.substring(0, 1).toUpperCase()
|
entries.add("\u200E" + nativeName.substring(0, 1).toUpperCase()
|
||||||
+ nativeName.substring(1));
|
+ nativeName.substring(1));
|
||||||
@@ -325,6 +319,13 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
language.setEntryValues(entryValues.toArray(new CharSequence[0]));
|
language.setEntryValues(entryValues.toArray(new CharSequence[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isLeftToRight(Locale locale) {
|
||||||
|
// TextUtilsCompat returns the wrong direction for Hebrew on some phones
|
||||||
|
String language = locale.getLanguage();
|
||||||
|
if (language.equals("iw") || language.equals("he")) return false;
|
||||||
|
int direction = TextUtilsCompat.getLayoutDirectionFromLocale(locale);
|
||||||
|
return direction == LAYOUT_DIRECTION_LTR;
|
||||||
|
}
|
||||||
|
|
||||||
private void setTorNetworkSummary(int torNetworkSetting) {
|
private void setTorNetworkSummary(int torNetworkSetting) {
|
||||||
if (torNetworkSetting != PREF_TOR_NETWORK_AUTOMATIC) {
|
if (torNetworkSetting != PREF_TOR_NETWORK_AUTOMATIC) {
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ import static java.util.concurrent.TimeUnit.DAYS;
|
|||||||
import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
|
import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
|
||||||
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
|
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
|
||||||
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
||||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_LOGCAT;
|
|
||||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME;
|
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME;
|
||||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_THROWABLE;
|
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_THROWABLE;
|
||||||
|
|
||||||
@@ -358,17 +357,16 @@ public class UiUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void triggerFeedback(Context ctx) {
|
public static void triggerFeedback(Context ctx) {
|
||||||
startDevReportActivity(ctx, FeedbackActivity.class, null, null, null);
|
startDevReportActivity(ctx, FeedbackActivity.class, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void startDevReportActivity(Context ctx,
|
public static void startDevReportActivity(Context ctx,
|
||||||
Class<? extends FragmentActivity> activity, @Nullable Throwable t,
|
Class<? extends FragmentActivity> activity, @Nullable Throwable t,
|
||||||
@Nullable Long appStartTime, @Nullable byte[] logKey) {
|
@Nullable Long appStartTime) {
|
||||||
final Intent dialogIntent = new Intent(ctx, activity);
|
final Intent dialogIntent = new Intent(ctx, activity);
|
||||||
dialogIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
dialogIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||||
dialogIntent.putExtra(EXTRA_THROWABLE, t);
|
dialogIntent.putExtra(EXTRA_THROWABLE, t);
|
||||||
dialogIntent.putExtra(EXTRA_APP_START_TIME, appStartTime);
|
dialogIntent.putExtra(EXTRA_APP_START_TIME, appStartTime);
|
||||||
dialogIntent.putExtra(EXTRA_APP_LOGCAT, logKey);
|
|
||||||
ctx.startActivity(dialogIntent);
|
ctx.startActivity(dialogIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,26 @@ public class BriarRecyclerView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
emptyObserver = new RecyclerView.AdapterDataObserver() {
|
emptyObserver = new RecyclerView.AdapterDataObserver() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged() {
|
||||||
|
super.onChanged();
|
||||||
|
showData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemRangeChanged(int positionStart, int itemCount) {
|
||||||
|
super.onItemRangeChanged(positionStart, itemCount);
|
||||||
|
if (itemCount > 0) showData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemRangeMoved(int fromPosition, int toPosition,
|
||||||
|
int itemCount) {
|
||||||
|
super.onItemRangeMoved(fromPosition, toPosition, itemCount);
|
||||||
|
if (itemCount > 0) showData();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemRangeInserted(int positionStart, int itemCount) {
|
public void onItemRangeInserted(int positionStart, int itemCount) {
|
||||||
super.onItemRangeInserted(positionStart, itemCount);
|
super.onItemRangeInserted(positionStart, itemCount);
|
||||||
|
|||||||
@@ -93,7 +93,4 @@ public interface AndroidNotificationManager {
|
|||||||
void blockAllBlogPostNotifications();
|
void blockAllBlogPostNotifications();
|
||||||
|
|
||||||
void unblockAllBlogPostNotifications();
|
void unblockAllBlogPostNotifications();
|
||||||
|
|
||||||
void restartNotifications(boolean locked, boolean mayAlertAgain);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,6 +112,7 @@
|
|||||||
android:textColor="@color/briar_primary"
|
android:textColor="@color/briar_primary"
|
||||||
android:textIsSelectable="true"
|
android:textIsSelectable="true"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
|
android:typeface="monospace"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/yourLinkIcon"
|
app:layout_constraintTop_toBottomOf="@+id/yourLinkIcon"
|
||||||
|
|||||||
@@ -139,10 +139,10 @@
|
|||||||
<Space
|
<Space
|
||||||
android:id="@+id/space"
|
android:id="@+id/space"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
app:layout_constrainedHeight="true"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/addButton"
|
app:layout_constraintBottom_toTopOf="@+id/addButton"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHeight_default="wrap"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/contactNameLayout" />
|
app:layout_constraintTop_toBottomOf="@+id/contactNameLayout" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
package org.briarproject.briar.android.logging;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
|
||||||
import org.junit.ClassRule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.rules.TemporaryFolder;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.LogRecord;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.FINE;
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.SEVERE;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
|
||||||
import static org.briarproject.briar.android.logging.BriefLogFormatter.formatLog;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
public class LogEncryptionDecryptionTest extends BrambleMockTestCase {
|
|
||||||
|
|
||||||
@ClassRule
|
|
||||||
public static TemporaryFolder folder = new TemporaryFolder();
|
|
||||||
|
|
||||||
private final SecureRandom random;
|
|
||||||
private final CachingLogHandler cachingLogHandler;
|
|
||||||
private final LogEncrypter logEncrypter;
|
|
||||||
private final LogDecrypter logDecrypter;
|
|
||||||
private final BriefLogFormatter logFormatter = new BriefLogFormatter();
|
|
||||||
|
|
||||||
public LogEncryptionDecryptionTest() throws IOException {
|
|
||||||
LoggingComponent loggingComponent = DaggerLoggingComponent.builder()
|
|
||||||
.loggingTestModule(new LoggingTestModule(folder.newFile()))
|
|
||||||
.build();
|
|
||||||
random = loggingComponent.random();
|
|
||||||
logEncrypter = loggingComponent.logEncrypter();
|
|
||||||
logDecrypter = loggingComponent.logDecrypter();
|
|
||||||
cachingLogHandler = loggingComponent.cachingLogHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncryptedMatchesDecrypted() {
|
|
||||||
ArrayList<LogRecord> logRecords =
|
|
||||||
new ArrayList<>(random.nextInt(99) + 1);
|
|
||||||
for (int i = 0; i < logRecords.size(); i++) {
|
|
||||||
LogRecord logRecord = getRandomLogRecord();
|
|
||||||
cachingLogHandler.publish(logRecord);
|
|
||||||
logRecords.add(logRecord);
|
|
||||||
}
|
|
||||||
byte[] logKey = logEncrypter.encryptLogs();
|
|
||||||
assertEquals(formatLog(logFormatter, logRecords),
|
|
||||||
logDecrypter.decryptLogs(logKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
private LogRecord getRandomLogRecord() {
|
|
||||||
Level[] levels = {SEVERE, WARNING, INFO, FINE};
|
|
||||||
Level level = levels[random.nextInt(levels.length)];
|
|
||||||
LogRecord logRecord =
|
|
||||||
new LogRecord(level, getRandomString(random.nextInt(128) + 1));
|
|
||||||
logRecord.setLoggerName(getRandomString(random.nextInt(23) + 1));
|
|
||||||
return logRecord;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package org.briarproject.briar.android.logging;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.BrambleCoreModule;
|
|
||||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Component;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Component(modules = {
|
|
||||||
BrambleCoreModule.class,
|
|
||||||
TestSecureRandomModule.class,
|
|
||||||
LoggingModule.class,
|
|
||||||
LoggingTestModule.class,
|
|
||||||
})
|
|
||||||
public interface LoggingComponent {
|
|
||||||
|
|
||||||
SecureRandom random();
|
|
||||||
|
|
||||||
CachingLogHandler cachingLogHandler();
|
|
||||||
|
|
||||||
LogEncrypter logEncrypter();
|
|
||||||
|
|
||||||
LogDecrypter logDecrypter();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
package org.briarproject.briar.android.logging;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
class LoggingTestModule {
|
|
||||||
|
|
||||||
private final File logFile;
|
|
||||||
|
|
||||||
LoggingTestModule(File logFile) {
|
|
||||||
this.logFile = logFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
DevConfig provideDevConfig() {
|
|
||||||
@NotNullByDefault
|
|
||||||
DevConfig devConfig = new DevConfig() {
|
|
||||||
@Override
|
|
||||||
public PublicKey getDevPublicKey() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDevOnionAddress() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getReportDir() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getLogcatFile() {
|
|
||||||
return logFile;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return devConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user