Compare commits

..

9 Commits

Author SHA1 Message Date
akwizgran
a03c65f944 Create up to 10k max length forum posts for load testing. 2025-05-02 15:04:11 +01:00
akwizgran
070a0181d9 Merge branch 'remove-forum-without-opening' into 'master'
Allow forums to be removed without opening them

See merge request briar/briar!1841
2025-05-01 08:24:38 +00:00
akwizgran
d83ae3a3b4 Use long click to open menu, clean up some cruft. 2025-04-30 15:25:14 +01:00
akwizgran
143f04bf1b Merge branch 'mark-db-clean-after-compacting' into 'master'
Mark DB as clean after compacting, keep foreground service until shutdown completes

See merge request briar/briar!1842
2025-04-30 10:30:48 +00:00
akwizgran
138fa6f39d Allow forums to be removed without opening them. 2025-04-29 10:17:40 +01:00
akwizgran
8e1371acf0 Keep foreground service until lifecycle shutdown completes.
This ensures our background threads keep running.
2025-04-29 10:17:09 +01:00
akwizgran
29f0b9d3c0 Mark DB as clean after compacting.
This ensures we compact the DB at the next startup if we didn't finish
compacting it at shutdown.
2025-04-29 10:17:09 +01:00
akwizgran
eb45ccfe9e Merge branch 'fix-problem-from-recent-fix-for-annotations-processor' into 'master'
Fix problem in AS after 8962fefd

See merge request briar/briar!1840
2025-04-29 08:49:15 +00:00
Sebastian Kürten
e98b5a9882 Fix problem in AS after 8962fefd
Commit 8962fefd introduced a problem while loading the project into
Android Studio. Apparently the fix from that commit did not handly
updated types of the more recent Gradle API. This update should fix it.
2025-04-03 08:30:16 +02:00
12 changed files with 91 additions and 38 deletions

View File

@@ -89,11 +89,17 @@ class H2Database extends JdbcDatabase {
try { try {
c = createConnection(); c = createConnection();
closeAllConnections(); closeAllConnections();
setDirty(c, false); LOG.info("Compacting DB");
s = c.createStatement(); s = c.createStatement();
s.execute("SHUTDOWN COMPACT"); s.execute("SHUTDOWN COMPACT");
LOG.info("Finished compacting DB");
s.close(); s.close();
c.close(); c.close();
// Reopen the DB to mark it as clean after compacting
c = createConnection();
setDirty(c, false);
LOG.info("Marked DB as clean");
c.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s, LOG, WARNING); tryToClose(s, LOG, WARNING);
tryToClose(c, LOG, WARNING); tryToClose(c, LOG, WARNING);
@@ -126,6 +132,7 @@ class H2Database extends JdbcDatabase {
closeAllConnections(); closeAllConnections();
s = c.createStatement(); s = c.createStatement();
s.execute("SHUTDOWN COMPACT"); s.execute("SHUTDOWN COMPACT");
LOG.info("Finished compacting DB");
s.close(); s.close();
c.close(); c.close();
} catch (SQLException e) { } catch (SQLException e) {

View File

@@ -217,19 +217,14 @@ public class BriarService extends Service {
@Override @Override
public void onDestroy() { public void onDestroy() {
// Hold a wake lock during shutdown super.onDestroy();
wakeLockManager.runWakefully(() -> { LOG.info("Destroyed");
super.onDestroy(); // Stop the lifecycle, if not already stopped
LOG.info("Destroyed"); shutdown(false);
stopForeground(true); stopForeground(true);
if (receiver != null) { if (receiver != null) {
getApplicationContext().unregisterReceiver(receiver); getApplicationContext().unregisterReceiver(receiver);
} }
// Stop the services in a background thread
wakeLockManager.executeWakefully(() -> {
if (started) lifecycleManager.stopServices();
}, "LifecycleShutdown");
}, "LifecycleShutdown");
} }
@Override @Override
@@ -299,8 +294,8 @@ public class BriarService extends Service {
private void shutdownFromBackground() { private void shutdownFromBackground() {
// Hold a wake lock during shutdown // Hold a wake lock during shutdown
wakeLockManager.runWakefully(() -> { wakeLockManager.runWakefully(() -> {
// Stop the service // Begin lifecycle shutdown
stopSelf(); shutdown(true);
// Hide the UI // Hide the UI
hideUi(); hideUi();
// Wait for shutdown to complete, then exit // Wait for shutdown to complete, then exit
@@ -335,8 +330,18 @@ public class BriarService extends Service {
/** /**
* Starts the shutdown process. * Starts the shutdown process.
*/ */
public void shutdown() { public void shutdown(boolean stopAndroidService) {
stopSelf(); // This will call onDestroy() // Hold a wake lock during shutdown
wakeLockManager.runWakefully(() -> {
// Stop the lifecycle services in a background thread,
// then stop this Android service if needed
wakeLockManager.executeWakefully(() -> {
if (started) lifecycleManager.stopServices();
if (stopAndroidService) {
androidExecutor.runOnUiThread(() -> stopSelf());
}
}, "LifecycleShutdown");
}, "LifecycleShutdown");
} }
public class BriarBinder extends Binder { public class BriarBinder extends Binder {

View File

@@ -147,7 +147,7 @@ public class BriarControllerImpl implements BriarController {
service.waitForStartup(); service.waitForStartup();
// Shut down the service and wait for it to shut down // Shut down the service and wait for it to shut down
LOG.info("Shutting down service"); LOG.info("Shutting down service");
service.shutdown(); service.shutdown(true);
service.waitForShutdown(); service.waitForShutdown();
} catch (InterruptedException e) { } catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for service"); LOG.warning("Interrupted while waiting for service");

View File

@@ -13,15 +13,18 @@ import androidx.recyclerview.widget.ListAdapter;
@NotNullByDefault @NotNullByDefault
class ForumListAdapter extends ListAdapter<ForumListItem, ForumViewHolder> { class ForumListAdapter extends ListAdapter<ForumListItem, ForumViewHolder> {
ForumListAdapter() { private final ForumListViewModel viewModel;
ForumListAdapter(ForumListViewModel viewModel) {
super(new ForumListCallback()); super(new ForumListCallback());
this.viewModel = viewModel;
} }
@Override @Override
public ForumViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public ForumViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate( View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_item_forum, parent, false); R.layout.list_item_forum, parent, false);
return new ForumViewHolder(v); return new ForumViewHolder(v, viewModel);
} }
@Override @Override

View File

@@ -40,7 +40,7 @@ public class ForumListFragment extends BaseFragment implements
private ForumListViewModel viewModel; private ForumListViewModel viewModel;
private BriarRecyclerView list; private BriarRecyclerView list;
private Snackbar snackbar; private Snackbar snackbar;
private final ForumListAdapter adapter = new ForumListAdapter(); private ForumListAdapter adapter;
@Inject @Inject
ViewModelProvider.Factory viewModelFactory; ViewModelProvider.Factory viewModelFactory;
@@ -54,6 +54,7 @@ public class ForumListFragment extends BaseFragment implements
component.inject(this); component.inject(this);
viewModel = new ViewModelProvider(this, viewModelFactory) viewModel = new ViewModelProvider(this, viewModelFactory)
.get(ForumListViewModel.class); .get(ForumListViewModel.class);
adapter = new ForumListAdapter(viewModel);
} }
@Nullable @Nullable

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.forum; package org.briarproject.briar.android.forum;
import android.app.Application; import android.app.Application;
import android.widget.Toast;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
@@ -15,6 +16,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.event.GroupAddedEvent; import org.briarproject.bramble.api.sync.event.GroupAddedEvent;
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent; import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.R;
import org.briarproject.briar.android.viewmodel.DbViewModel; import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveResult; import org.briarproject.briar.android.viewmodel.LiveResult;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
@@ -40,6 +42,7 @@ import androidx.annotation.UiThread;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import static android.widget.Toast.LENGTH_SHORT;
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.logDuration;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@@ -180,4 +183,17 @@ class ForumListViewModel extends DbViewModel implements EventListener {
return numInvitations; return numInvitations;
} }
void deleteForum(GroupId groupId) {
runOnDbThread(() -> {
try {
Forum f = forumManager.getForum(groupId);
forumManager.removeForum(f);
androidExecutor.runOnUiThread(() -> Toast
.makeText(getApplication(), R.string.forum_left_toast,
LENGTH_SHORT).show());
} catch (DbException e) {
handleException(e);
}
});
}
} }

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.PopupMenu;
import android.widget.TextView; import android.widget.TextView;
import org.briarproject.briar.R; import org.briarproject.briar.R;
@@ -20,6 +21,7 @@ import static org.briarproject.briar.android.activity.BriarActivity.GROUP_NAME;
class ForumViewHolder extends RecyclerView.ViewHolder { class ForumViewHolder extends RecyclerView.ViewHolder {
private final ForumListViewModel viewModel;
private final Context ctx; private final Context ctx;
private final ViewGroup layout; private final ViewGroup layout;
private final TextAvatarView avatar; private final TextAvatarView avatar;
@@ -27,8 +29,9 @@ class ForumViewHolder extends RecyclerView.ViewHolder {
private final TextView postCount; private final TextView postCount;
private final TextView date; private final TextView date;
ForumViewHolder(View v) { ForumViewHolder(View v, ForumListViewModel viewModel) {
super(v); super(v);
this.viewModel = viewModel;
ctx = v.getContext(); ctx = v.getContext();
layout = (ViewGroup) v; layout = (ViewGroup) v;
avatar = v.findViewById(R.id.avatarView); avatar = v.findViewById(R.id.avatarView);
@@ -64,6 +67,21 @@ class ForumViewHolder extends RecyclerView.ViewHolder {
date.setVisibility(VISIBLE); date.setVisibility(VISIBLE);
} }
// Open popup menu on long click
layout.setOnLongClickListener(v -> {
PopupMenu pm = new PopupMenu(ctx, v);
pm.getMenuInflater().inflate(R.menu.forum_list_item_actions,
pm.getMenu());
pm.setOnMenuItemClickListener(it -> {
if (it.getItemId() == R.id.action_forum_delete) {
viewModel.deleteForum(item.getForum().getId());
}
return true;
});
pm.show();
return true;
});
// Open Forum on Click // Open Forum on Click
layout.setOnClickListener(v -> { layout.setOnClickListener(v -> {
Intent i = new Intent(ctx, ForumActivity.class); Intent i = new Intent(ctx, ForumActivity.class);

View File

@@ -174,7 +174,7 @@
android:id="@+id/seekBarForumMessages" android:id="@+id/seekBarForumMessages"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:max="50" android:max="10000"
android:paddingTop="5dp" android:paddingTop="5dp"
android:progress="20" android:progress="20"
app:layout_constraintEnd_toStartOf="@+id/TextViewForumMessagesSb" app:layout_constraintEnd_toStartOf="@+id/TextViewForumMessagesSb"

View File

@@ -11,7 +11,6 @@
android:layout_width="@dimen/listitem_picture_frame_size" android:layout_width="@dimen/listitem_picture_frame_size"
android:layout_height="@dimen/listitem_picture_frame_size" android:layout_height="@dimen/listitem_picture_frame_size"
android:layout_marginStart="@dimen/listitem_horizontal_margin" android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
app:layout_constraintBottom_toTopOf="@+id/divider" app:layout_constraintBottom_toTopOf="@+id/divider"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@@ -38,7 +37,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium" android:layout_marginTop="@dimen/margin_medium"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small" android:textSize="@dimen/text_size_small"
app:layout_constraintEnd_toStartOf="@+id/dateView" app:layout_constraintEnd_toStartOf="@+id/dateView"
@@ -51,7 +49,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/listitem_horizontal_margin" android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small" android:textSize="@dimen/text_size_small"
app:layout_constraintBaseline_toBaselineOf="@+id/postCountView" app:layout_constraintBaseline_toBaselineOf="@+id/postCountView"
@@ -63,7 +60,6 @@
style="@style/Divider.ThreadItem" style="@style/Divider.ThreadItem"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_marginStart="@dimen/margin_medium" android:layout_marginStart="@dimen/margin_medium"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_marginTop="@dimen/listitem_horizontal_margin" android:layout_marginTop="@dimen/listitem_horizontal_margin"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_forum_delete"
android:title="@string/forum_leave" />
</menu>

View File

@@ -470,20 +470,19 @@ public class TestDataCreatorImpl implements TestDataCreator {
private void createRandomForumPosts(Forum forum, List<Contact> contacts, private void createRandomForumPosts(Forum forum, List<Contact> contacts,
int numForumPosts) throws DbException { int numForumPosts) throws DbException {
List<ForumPost> posts = new ArrayList<>(); List<MessageId> postIds = new ArrayList<>();
for (int i = 0; i < numForumPosts; i++) { for (int i = 0; i < numForumPosts; i++) {
Contact contact = contacts.get(random.nextInt(contacts.size())); Contact contact = contacts.get(random.nextInt(contacts.size()));
LocalAuthor author = localAuthors.get(contact); LocalAuthor author = localAuthors.get(contact);
long timestamp = clock.currentTimeMillis() - (long) i * 60 * 1000; long timestamp = clock.currentTimeMillis() - (long) i * 60 * 1000;
String text = getRandomText(); String text = getRandomText();
MessageId parent = null; MessageId parent = null;
if (random.nextBoolean() && posts.size() > 0) { if (random.nextBoolean() && !postIds.isEmpty()) {
ForumPost parentPost = posts.get(random.nextInt(posts.size())); parent = postIds.get(random.nextInt(postIds.size()));
parent = parentPost.getMessage().getId();
} }
ForumPost post = forumManager.createLocalPost(forum.getId(), text, ForumPost post = forumManager.createLocalPost(forum.getId(), text,
timestamp, parent, author); timestamp, parent, author);
posts.add(post); postIds.add(post.getMessage().getId());
db.transaction(false, txn -> db.transaction(false, txn ->
db.receiveMessage(txn, contact.getId(), post.getMessage())); db.receiveMessage(txn, contact.getId(), post.getMessage()));
} }
@@ -573,7 +572,7 @@ public class TestDataCreatorImpl implements TestDataCreator {
} }
private String getRandomText() { private String getRandomText() {
int minLength = 3 + random.nextInt(500); int minLength = 30_000;
int maxWordLength = 15; int maxWordLength = 15;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
while (sb.length() < minLength) { while (sb.length() < minLength) {

View File

@@ -6,9 +6,9 @@ sourceSets.configureEach { sourceSet ->
idea { idea {
module { module {
sourceDirs += compileJava.options.generatedSourceOutputDirectory sourceDirs += compileJava.options.generatedSourceOutputDirectory.get().getAsFile()
generatedSourceDirs += compileJava.options.generatedSourceOutputDirectory generatedSourceDirs += compileJava.options.generatedSourceOutputDirectory.get().getAsFile()
testSourceDirs += compileTestJava.options.generatedSourceOutputDirectory testSourceDirs += compileTestJava.options.generatedSourceOutputDirectory.get().getAsFile()
generatedSourceDirs += compileTestJava.options.generatedSourceOutputDirectory generatedSourceDirs += compileTestJava.options.generatedSourceOutputDirectory.get().getAsFile()
} }
} }