mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Merge branch '705-adapter-revisions' into 'master'
Fix race conditions when updating UI from events (again) This is my second attempt at fixing race conditions caused by updating the UI from events while background tasks are loading data from the DB. Unlike my first attempt, this one is pretty simple and doesn't require too much reasoning about possible races. The first commit fixes a few list loading bugs I found while working on this problem, and moves the lifecycle callbacks from resume/pause to start/stop, closing #609. The second commit contains the fix for #705, which works as follows: * Each BriarAdapter has a revision counter * Before making a change to the adapter that could be overwritten by a background task, increment the revision * Before starting a background task that could overwrite other changes, get the current revision * Before applying changes from a background task that could overwrite other changes, check whether the revision has changed * If the revision has changed, restart the background task * Otherwise apply the changes Closes #609. #705 remains open because the PagerAdapters for blogs need to be updated. See merge request !356
This commit is contained in:
@@ -12,7 +12,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||
import static android.view.inputmethod.InputMethodManager.SHOW_FORCED;
|
||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||
import static org.briarproject.android.TestingConstants.PREVENT_SCREENSHOTS;
|
||||
|
||||
@@ -62,18 +61,18 @@ public abstract class BaseActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
for (ActivityLifecycleController alc : lifecycleControllers) {
|
||||
alc.onActivityResume();
|
||||
alc.onActivityStart();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
for (ActivityLifecycleController alc : lifecycleControllers) {
|
||||
alc.onActivityPause();
|
||||
alc.onActivityStop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,10 +34,11 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
Logger.getLogger(BriarActivity.class.getName());
|
||||
|
||||
@Inject
|
||||
protected BriarController briarController;
|
||||
// TODO remove this when the deprecated method runOnDbThread is removed
|
||||
BriarController briarController;
|
||||
|
||||
@Deprecated
|
||||
@Inject
|
||||
protected DbController dbController;
|
||||
DbController dbController;
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int request, int result, Intent data) {
|
||||
@@ -49,8 +50,8 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
if (!briarController.hasEncryptionKey() && !isFinishing()) {
|
||||
Intent i = new Intent(this, PasswordActivity.class);
|
||||
i.setFlags(FLAG_ACTIVITY_NO_ANIMATION | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
|
||||
@@ -141,8 +141,8 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
updateTransports();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ import org.briarproject.api.blogs.BlogManager;
|
||||
import org.briarproject.api.blogs.BlogPostHeader;
|
||||
import org.briarproject.api.db.DatabaseExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.BlogPostAddedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
@@ -47,7 +45,7 @@ abstract class BaseControllerImpl extends DbControllerImpl
|
||||
private final Map<MessageId, BlogPostHeader> headerCache =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
protected volatile OnBlogPostAddedListener listener;
|
||||
private volatile OnBlogPostAddedListener listener;
|
||||
|
||||
BaseControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, EventBus eventBus,
|
||||
@@ -63,9 +61,7 @@ abstract class BaseControllerImpl extends DbControllerImpl
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onStart() {
|
||||
if (listener == null)
|
||||
throw new IllegalStateException(
|
||||
"OnBlogPostAddedListener needs to be attached");
|
||||
if (listener == null) throw new IllegalStateException();
|
||||
eventBus.addListener(this);
|
||||
}
|
||||
|
||||
@@ -75,26 +71,30 @@ abstract class BaseControllerImpl extends DbControllerImpl
|
||||
eventBus.removeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof BlogPostAddedEvent) {
|
||||
final BlogPostAddedEvent b = (BlogPostAddedEvent) e;
|
||||
LOG.info("New blog post added");
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onBlogPostAdded(b.getHeader(), b.isLocal());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnBlogPostAddedListener(OnBlogPostAddedListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
void onBlogPostAdded(final BlogPostHeader h, final boolean local) {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onBlogPostAdded(h, local);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void onBlogRemoved() {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onBlogRemoved();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void loadBlogPosts(final GroupId groupId,
|
||||
final ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
public class BlogControllerImpl extends BaseControllerImpl
|
||||
@@ -50,15 +51,15 @@ public class BlogControllerImpl extends BaseControllerImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResume() {
|
||||
super.onStart(); // TODO: Should be called when activity starts. #609
|
||||
public void onActivityStart() {
|
||||
super.onStart();
|
||||
notificationManager.blockNotification(groupId);
|
||||
notificationManager.clearBlogPostNotification(groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPause() {
|
||||
super.onStop(); // TODO: Should be called when activity stops. #609
|
||||
public void onActivityStop() {
|
||||
super.onStop();
|
||||
notificationManager.unblockNotification(groupId);
|
||||
}
|
||||
|
||||
@@ -75,20 +76,16 @@ public class BlogControllerImpl extends BaseControllerImpl
|
||||
public void eventOccurred(Event e) {
|
||||
if (groupId == null) throw new IllegalStateException();
|
||||
if (e instanceof BlogPostAddedEvent) {
|
||||
BlogPostAddedEvent s = (BlogPostAddedEvent) e;
|
||||
if (s.getGroupId().equals(groupId)) {
|
||||
super.eventOccurred(e);
|
||||
BlogPostAddedEvent b = (BlogPostAddedEvent) e;
|
||||
if (b.getGroupId().equals(groupId)) {
|
||||
LOG.info("Blog post added");
|
||||
onBlogPostAdded(b.getHeader(), b.isLocal());
|
||||
}
|
||||
} else if (e instanceof GroupRemovedEvent) {
|
||||
GroupRemovedEvent s = (GroupRemovedEvent) e;
|
||||
if (s.getGroup().getId().equals(groupId)) {
|
||||
GroupRemovedEvent g = (GroupRemovedEvent) e;
|
||||
if (g.getGroup().getId().equals(groupId)) {
|
||||
LOG.info("Blog removed");
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onBlogRemoved();
|
||||
}
|
||||
});
|
||||
onBlogRemoved();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,11 +112,15 @@ public class BlogControllerImpl extends BaseControllerImpl
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
LocalAuthor a = identityManager.getLocalAuthor();
|
||||
Blog b = blogManager.getBlog(groupId);
|
||||
boolean ours = a.getId().equals(b.getAuthor().getId());
|
||||
boolean removable = blogManager.canBeRemoved(groupId);
|
||||
BlogItem blog = new BlogItem(b, ours, removable);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading blog took " + duration + " ms");
|
||||
handler.onResult(blog);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
@@ -138,8 +139,12 @@ public class BlogControllerImpl extends BaseControllerImpl
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
Blog b = blogManager.getBlog(groupId);
|
||||
blogManager.removeBlog(b);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Removing blog took " + duration + " ms");
|
||||
handler.onResult(null);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
|
||||
@@ -123,18 +123,13 @@ public class BlogFragment extends BaseFragment implements
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
loadBlog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
loadBlogPosts(false);
|
||||
list.startPeriodicUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
list.stopPeriodicUpdate();
|
||||
}
|
||||
|
||||
@@ -215,9 +210,11 @@ public class BlogFragment extends BaseFragment implements
|
||||
adapter.add(post);
|
||||
if (local) {
|
||||
list.scrollToPosition(0);
|
||||
displaySnackbar(R.string.blogs_blog_post_created, false);
|
||||
displaySnackbar(R.string.blogs_blog_post_created,
|
||||
false);
|
||||
} else {
|
||||
displaySnackbar(R.string.blogs_blog_post_received, true);
|
||||
displaySnackbar(R.string.blogs_blog_post_received,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,11 +233,11 @@ public class BlogFragment extends BaseFragment implements
|
||||
listener) {
|
||||
@Override
|
||||
public void onResultUi(Collection<BlogPostItem> posts) {
|
||||
if (posts.size() > 0) {
|
||||
if (posts.isEmpty()) {
|
||||
list.showData();
|
||||
} else {
|
||||
adapter.addAll(posts);
|
||||
if (reload) list.scrollToPosition(0);
|
||||
} else {
|
||||
list.showData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.android.blogs;
|
||||
|
||||
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.android.controller.handler.ResultHandler;
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.db.DbException;
|
||||
|
||||
@@ -12,6 +11,6 @@ public interface FeedController extends BaseController {
|
||||
void loadBlogPosts(
|
||||
ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler);
|
||||
|
||||
void loadPersonalBlog(ResultHandler<Blog> resultHandler);
|
||||
void loadPersonalBlog(ResultExceptionHandler<Blog, DbException> handler);
|
||||
|
||||
}
|
||||
|
||||
@@ -2,14 +2,16 @@ package org.briarproject.android.blogs;
|
||||
|
||||
import org.briarproject.android.api.AndroidNotificationManager;
|
||||
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.android.controller.handler.ResultHandler;
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.blogs.BlogManager;
|
||||
import org.briarproject.api.db.DatabaseExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.NoSuchGroupException;
|
||||
import org.briarproject.api.db.NoSuchMessageException;
|
||||
import org.briarproject.api.event.BlogPostAddedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.GroupRemovedEvent;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
@@ -52,15 +54,28 @@ public class FeedControllerImpl extends BaseControllerImpl
|
||||
notificationManager.unblockAllBlogPostNotifications();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof BlogPostAddedEvent) {
|
||||
BlogPostAddedEvent b = (BlogPostAddedEvent) e;
|
||||
LOG.info("Blog post added");
|
||||
onBlogPostAdded(b.getHeader(), b.isLocal());
|
||||
} else if (e instanceof GroupRemovedEvent) {
|
||||
GroupRemovedEvent g = (GroupRemovedEvent) e;
|
||||
if (g.getGroup().getClientId().equals(blogManager.getClientId())) {
|
||||
LOG.info("Blog removed");
|
||||
onBlogRemoved();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadBlogPosts(
|
||||
final ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler) {
|
||||
LOG.info("Loading all blog posts...");
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// load blog posts
|
||||
long now = System.currentTimeMillis();
|
||||
Collection<BlogPostItem> posts = new ArrayList<>();
|
||||
for (Blog b : blogManager.getBlogs()) {
|
||||
@@ -85,24 +100,23 @@ public class FeedControllerImpl extends BaseControllerImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPersonalBlog(final ResultHandler<Blog> resultHandler) {
|
||||
LOG.info("Loading personal blog...");
|
||||
public void loadPersonalBlog(
|
||||
final ResultExceptionHandler<Blog, DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// load blog posts
|
||||
long now = System.currentTimeMillis();
|
||||
Author a = identityManager.getLocalAuthor();
|
||||
Blog b = blogManager.getPersonalBlog(a);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading pers. blog took " + duration + " ms");
|
||||
resultHandler.onResult(b);
|
||||
LOG.info("Loading blog took " + duration + " ms");
|
||||
handler.onResult(b);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
resultHandler.onResult(null);
|
||||
handler.onException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -20,7 +20,6 @@ import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.android.blogs.BaseController.OnBlogPostAddedListener;
|
||||
import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
|
||||
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||
import org.briarproject.android.controller.handler.UiResultHandler;
|
||||
import org.briarproject.android.fragment.BaseFragment;
|
||||
import org.briarproject.android.view.BriarRecyclerView;
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
@@ -28,6 +27,7 @@ import org.briarproject.api.blogs.BlogPostHeader;
|
||||
import org.briarproject.api.db.DbException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -42,6 +42,7 @@ public class FeedFragment extends BaseFragment implements
|
||||
OnBlogPostClickListener, OnBlogPostAddedListener {
|
||||
|
||||
public final static String TAG = FeedFragment.class.getName();
|
||||
private static final Logger LOG = Logger.getLogger(TAG);
|
||||
|
||||
@Inject
|
||||
FeedController feedController;
|
||||
@@ -99,40 +100,61 @@ public class FeedFragment extends BaseFragment implements
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
feedController.onStart();
|
||||
feedController.loadPersonalBlog(
|
||||
new UiResultHandler<Blog>(listener) {
|
||||
@Override
|
||||
public void onResultUi(Blog b) {
|
||||
personalBlog = b;
|
||||
}
|
||||
});
|
||||
feedController.loadBlogPosts(
|
||||
new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
|
||||
listener) {
|
||||
@Override
|
||||
public void onResultUi(Collection<BlogPostItem> posts) {
|
||||
if (posts.isEmpty()) {
|
||||
list.showData();
|
||||
} else {
|
||||
adapter.addAll(posts);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
// TODO
|
||||
}
|
||||
});
|
||||
list.startPeriodicUpdate();
|
||||
loadPersonalBlog();
|
||||
loadBlogPosts(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
feedController.onStop();
|
||||
adapter.clear();
|
||||
list.showProgressBar();
|
||||
list.stopPeriodicUpdate();
|
||||
// TODO save list position in database/preferences?
|
||||
}
|
||||
|
||||
private void loadPersonalBlog() {
|
||||
feedController.loadPersonalBlog(
|
||||
new UiResultExceptionHandler<Blog, DbException>(listener) {
|
||||
@Override
|
||||
public void onResultUi(Blog b) {
|
||||
personalBlog = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
// TODO: Decide how to handle errors in the UI
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadBlogPosts(final boolean clear) {
|
||||
final int revision = adapter.getRevision();
|
||||
feedController.loadBlogPosts(
|
||||
new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
|
||||
listener) {
|
||||
@Override
|
||||
public void onResultUi(Collection<BlogPostItem> posts) {
|
||||
if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
if (clear) adapter.setItems(posts);
|
||||
else adapter.addAll(posts);
|
||||
if (posts.isEmpty()) list.showData();
|
||||
} else {
|
||||
LOG.info("Concurrent update, reloading");
|
||||
loadBlogPosts(clear);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExceptionUi(DbException e) {
|
||||
// TODO: Decide how to handle errors in the UI
|
||||
}
|
||||
});
|
||||
list.startPeriodicUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.blogs_feed_actions, menu);
|
||||
@@ -178,6 +200,7 @@ public class FeedFragment extends BaseFragment implements
|
||||
listener) {
|
||||
@Override
|
||||
public void onResultUi(BlogPostItem post) {
|
||||
adapter.incrementRevision();
|
||||
adapter.add(post);
|
||||
if (local) {
|
||||
showSnackBar(R.string.blogs_blog_post_created);
|
||||
@@ -185,6 +208,7 @@ public class FeedFragment extends BaseFragment implements
|
||||
showSnackBar(R.string.blogs_blog_post_received);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
// TODO: Decide how to handle errors in the UI
|
||||
@@ -234,6 +258,6 @@ public class FeedFragment extends BaseFragment implements
|
||||
|
||||
@Override
|
||||
public void onBlogRemoved() {
|
||||
finish();
|
||||
loadBlogPosts(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,7 @@ public class RssFeedManageActivity extends BriarActivity
|
||||
|
||||
private BriarRecyclerView list;
|
||||
private RssFeedAdapter adapter;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
private volatile GroupId groupId = null;
|
||||
private GroupId groupId;
|
||||
|
||||
@Inject
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@@ -65,11 +63,18 @@ public class RssFeedManageActivity extends BriarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
loadFeeds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
adapter.clear();
|
||||
list.showProgressBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
@@ -120,27 +125,43 @@ public class RssFeedManageActivity extends BriarActivity
|
||||
}
|
||||
|
||||
private void loadFeeds() {
|
||||
final int revision = adapter.getRevision();
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
addFeeds(feedManager.getFeeds());
|
||||
displayFeeds(revision, feedManager.getFeeds());
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
list.setEmptyText(R.string.blogs_rss_feeds_manage_error);
|
||||
list.showData();
|
||||
onLoadError();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addFeeds(final List<Feed> feeds) {
|
||||
private void displayFeeds(final int revision, final List<Feed> feeds) {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (feeds.size() == 0) list.showData();
|
||||
else adapter.addAll(feeds);
|
||||
if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
if (feeds.isEmpty()) list.showData();
|
||||
else adapter.addAll(feeds);
|
||||
} else {
|
||||
LOG.info("Concurrent update, reloading");
|
||||
loadFeeds();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onLoadError() {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
list.setEmptyText(R.string.blogs_rss_feeds_manage_error);
|
||||
list.showData();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -149,6 +170,7 @@ public class RssFeedManageActivity extends BriarActivity
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adapter.incrementRevision();
|
||||
adapter.remove(feed);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -94,15 +94,15 @@ public class WriteBlogPostActivity extends BriarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
notificationManager.unblockNotification(groupId);
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
notificationManager.blockNotification(groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
notificationManager.blockNotification(groupId);
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
notificationManager.unblockNotification(groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,7 +13,6 @@ import org.briarproject.R;
|
||||
import org.briarproject.android.util.BriarAdapter;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import im.delight.android.identicons.IdenticonDrawable;
|
||||
@@ -90,17 +89,6 @@ public abstract class BaseContactListAdapter<VH extends BaseContactListAdapter.B
|
||||
return INVALID_POSITION; // Not found
|
||||
}
|
||||
|
||||
int findItemPosition(GroupId g) {
|
||||
int count = getItemCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
ContactListItem item = getItemAt(i);
|
||||
if (item != null && item.getGroupId().equals(g)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return INVALID_POSITION; // Not found
|
||||
}
|
||||
|
||||
public static class BaseContactHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public final ViewGroup layout;
|
||||
|
||||
@@ -170,8 +170,8 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
notificationManager.blockAllContactNotifications();
|
||||
notificationManager.clearAllContactNotifications();
|
||||
eventBus.addListener(this);
|
||||
@@ -180,8 +180,8 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
eventBus.removeListener(this);
|
||||
notificationManager.unblockAllContactNotifications();
|
||||
adapter.clear();
|
||||
@@ -190,6 +190,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
}
|
||||
|
||||
private void loadContacts() {
|
||||
final int revision = adapter.getRevision();
|
||||
listener.runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -213,10 +214,10 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
displayContacts(contacts);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Full load took " + duration + " ms");
|
||||
displayContacts(revision, contacts);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
@@ -225,12 +226,19 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
});
|
||||
}
|
||||
|
||||
private void displayContacts(final List<ContactListItem> contacts) {
|
||||
private void displayContacts(final int revision,
|
||||
final List<ContactListItem> contacts) {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (contacts.size() == 0) list.showData();
|
||||
else adapter.addAll(contacts);
|
||||
if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
if (contacts.isEmpty()) list.showData();
|
||||
else adapter.addAll(contacts);
|
||||
} else {
|
||||
LOG.info("Concurrent update, reloading");
|
||||
loadContacts();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -238,40 +246,45 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof ContactStatusChangedEvent) {
|
||||
LOG.info("Contact Status changed, reloading");
|
||||
// is also broadcast when contact was added
|
||||
loadContacts();
|
||||
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
|
||||
if (c.isActive()) {
|
||||
LOG.info("Contact activated, reloading");
|
||||
loadContacts();
|
||||
} else {
|
||||
LOG.info("Contact deactivated, removing item");
|
||||
removeItem(c.getContactId());
|
||||
}
|
||||
} else if (e instanceof ContactConnectedEvent) {
|
||||
setConnected(((ContactConnectedEvent) e).getContactId(), true);
|
||||
} else if (e instanceof ContactDisconnectedEvent) {
|
||||
setConnected(((ContactDisconnectedEvent) e).getContactId(), false);
|
||||
} else if (e instanceof ContactRemovedEvent) {
|
||||
LOG.info("Contact removed");
|
||||
LOG.info("Contact removed, removing item");
|
||||
removeItem(((ContactRemovedEvent) e).getContactId());
|
||||
} else if (e instanceof PrivateMessageReceivedEvent) {
|
||||
LOG.info("Message received, update contact");
|
||||
LOG.info("Private message received, updating item");
|
||||
PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e;
|
||||
PrivateMessageHeader h = p.getMessageHeader();
|
||||
updateItem(p.getGroupId(), ConversationItem.from(h));
|
||||
updateItem(p.getContactId(), ConversationItem.from(h));
|
||||
} else if (e instanceof IntroductionRequestReceivedEvent) {
|
||||
LOG.info("Introduction Request received, update contact");
|
||||
LOG.info("Introduction request received, updating item");
|
||||
IntroductionRequestReceivedEvent m =
|
||||
(IntroductionRequestReceivedEvent) e;
|
||||
IntroductionRequest ir = m.getIntroductionRequest();
|
||||
updateItem(m.getContactId(), ConversationItem.from(ir));
|
||||
} else if (e instanceof IntroductionResponseReceivedEvent) {
|
||||
LOG.info("Introduction Response received, update contact");
|
||||
LOG.info("Introduction response received, updating item");
|
||||
IntroductionResponseReceivedEvent m =
|
||||
(IntroductionResponseReceivedEvent) e;
|
||||
IntroductionResponse ir = m.getIntroductionResponse();
|
||||
updateItem(m.getContactId(), ConversationItem.from(ir));
|
||||
} else if (e instanceof InvitationRequestReceivedEvent) {
|
||||
LOG.info("Invitation Request received, update contact");
|
||||
LOG.info("Invitation request received, updating item");
|
||||
InvitationRequestReceivedEvent m = (InvitationRequestReceivedEvent) e;
|
||||
InvitationRequest ir = m.getRequest();
|
||||
updateItem(m.getContactId(), ConversationItem.from(ir));
|
||||
} else if (e instanceof InvitationResponseReceivedEvent) {
|
||||
LOG.info("Invitation Response received, update contact");
|
||||
LOG.info("Invitation response received, updating item");
|
||||
InvitationResponseReceivedEvent m =
|
||||
(InvitationResponseReceivedEvent) e;
|
||||
InvitationResponse ir = m.getResponse();
|
||||
@@ -283,6 +296,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adapter.incrementRevision();
|
||||
int position = adapter.findItemPosition(c);
|
||||
ContactListItem item = adapter.getItemAt(position);
|
||||
if (item != null) {
|
||||
@@ -293,24 +307,11 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
});
|
||||
}
|
||||
|
||||
private void updateItem(final GroupId g, final ConversationItem m) {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int position = adapter.findItemPosition(g);
|
||||
ContactListItem item = adapter.getItemAt(position);
|
||||
if (item != null) {
|
||||
item.addMessage(m);
|
||||
adapter.updateItemAt(position, item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeItem(final ContactId c) {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adapter.incrementRevision();
|
||||
int position = adapter.findItemPosition(c);
|
||||
ContactListItem item = adapter.getItemAt(position);
|
||||
if (item != null) adapter.remove(item);
|
||||
@@ -322,6 +323,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adapter.incrementRevision();
|
||||
int position = adapter.findItemPosition(c);
|
||||
ContactListItem item = adapter.getItemAt(position);
|
||||
if (item != null) {
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.briarproject.android.view.TextInputView;
|
||||
import org.briarproject.android.view.TextInputView.TextInputListener;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.blogs.BlogSharingManager;
|
||||
import org.briarproject.api.clients.BaseMessageHeader;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
@@ -42,7 +41,6 @@ import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.CryptoExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.NoSuchContactException;
|
||||
import org.briarproject.api.db.NoSuchMessageException;
|
||||
import org.briarproject.api.event.ContactConnectedEvent;
|
||||
import org.briarproject.api.event.ContactDisconnectedEvent;
|
||||
import org.briarproject.api.event.ContactRemovedEvent;
|
||||
@@ -150,7 +148,6 @@ public class ConversationActivity extends BriarActivity
|
||||
private volatile ContactId contactId = null;
|
||||
private volatile String contactName = null;
|
||||
private volatile byte[] contactIdenticonKey = null;
|
||||
private volatile boolean connected = false;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
@@ -214,18 +211,19 @@ public class ConversationActivity extends BriarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
eventBus.addListener(this);
|
||||
notificationManager.blockNotification(groupId);
|
||||
notificationManager.clearPrivateMessageNotification(groupId);
|
||||
loadData();
|
||||
loadContactDetails();
|
||||
loadMessages();
|
||||
list.startPeriodicUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
eventBus.removeListener(this);
|
||||
notificationManager.unblockNotification(groupId);
|
||||
list.stopPeriodicUpdate();
|
||||
@@ -277,7 +275,7 @@ public class ConversationActivity extends BriarActivity
|
||||
finish();
|
||||
}
|
||||
|
||||
private void loadData() {
|
||||
private void loadContactDetails() {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -291,13 +289,10 @@ public class ConversationActivity extends BriarActivity
|
||||
contactIdenticonKey =
|
||||
contact.getAuthor().getId().getBytes();
|
||||
}
|
||||
connected = connectionRegistry.isConnected(contactId);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading contact took " + duration + " ms");
|
||||
displayContactDetails();
|
||||
// Load the messages here to make sure we have a contactId
|
||||
loadMessages();
|
||||
} catch (NoSuchContactException e) {
|
||||
finishOnUiThread();
|
||||
} catch (DbException e) {
|
||||
@@ -316,7 +311,7 @@ public class ConversationActivity extends BriarActivity
|
||||
new IdenticonDrawable(contactIdenticonKey));
|
||||
toolbarTitle.setText(contactName);
|
||||
|
||||
if (connected) {
|
||||
if (connectionRegistry.isConnected(contactId)) {
|
||||
toolbarStatus.setImageDrawable(ContextCompat
|
||||
.getDrawable(ConversationActivity.this,
|
||||
R.drawable.contact_online));
|
||||
@@ -335,6 +330,7 @@ public class ConversationActivity extends BriarActivity
|
||||
}
|
||||
|
||||
private void loadMessages() {
|
||||
final int revision = adapter.getRevision();
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -359,8 +355,9 @@ public class ConversationActivity extends BriarActivity
|
||||
invitations.addAll(blogInvitations);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading headers took " + duration + " ms");
|
||||
displayMessages(headers, introductions, invitations);
|
||||
LOG.info("Loading messages took " + duration + " ms");
|
||||
displayMessages(revision, headers, introductions,
|
||||
invitations);
|
||||
} catch (NoSuchContactException e) {
|
||||
finishOnUiThread();
|
||||
} catch (DbException e) {
|
||||
@@ -371,59 +368,66 @@ public class ConversationActivity extends BriarActivity
|
||||
});
|
||||
}
|
||||
|
||||
private void displayMessages(final Collection<PrivateMessageHeader> headers,
|
||||
private void displayMessages(final int revision,
|
||||
final Collection<PrivateMessageHeader> headers,
|
||||
final Collection<IntroductionMessage> introductions,
|
||||
final Collection<InvitationMessage> invitations) {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
textInputView.setSendButtonEnabled(true);
|
||||
if (headers.isEmpty() && introductions.isEmpty() &&
|
||||
invitations.isEmpty()) {
|
||||
// we have no messages,
|
||||
// so let the list know to hide progress bar
|
||||
list.showData();
|
||||
} else {
|
||||
List<ConversationItem> items = new ArrayList<>();
|
||||
for (PrivateMessageHeader h : headers) {
|
||||
ConversationMessageItem item = ConversationItem.from(h);
|
||||
byte[] body = bodyCache.get(h.getId());
|
||||
if (body == null) loadMessageBody(h.getId());
|
||||
else item.setBody(body);
|
||||
items.add(item);
|
||||
}
|
||||
for (IntroductionMessage m : introductions) {
|
||||
ConversationItem item;
|
||||
if (m instanceof IntroductionRequest) {
|
||||
item = ConversationItem
|
||||
.from((IntroductionRequest) m);
|
||||
} else {
|
||||
item = ConversationItem
|
||||
.from(ConversationActivity.this,
|
||||
contactName,
|
||||
(IntroductionResponse) m);
|
||||
}
|
||||
items.add(item);
|
||||
}
|
||||
for (InvitationMessage i : invitations) {
|
||||
if (i instanceof InvitationRequest) {
|
||||
InvitationRequest r = (InvitationRequest) i;
|
||||
items.add(ConversationItem.from(r));
|
||||
} else if (i instanceof InvitationResponse) {
|
||||
InvitationResponse r = (InvitationResponse) i;
|
||||
items.add(ConversationItem
|
||||
.from(ConversationActivity.this,
|
||||
contactName, r));
|
||||
}
|
||||
}
|
||||
adapter.addAll(items);
|
||||
if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
textInputView.setSendButtonEnabled(true);
|
||||
List<ConversationItem> items = createItems(headers,
|
||||
introductions, invitations);
|
||||
if (items.isEmpty()) list.showData();
|
||||
else adapter.addAll(items);
|
||||
// Scroll to the bottom
|
||||
list.scrollToPosition(adapter.getItemCount() - 1);
|
||||
} else {
|
||||
LOG.info("Concurrent update, reloading");
|
||||
loadMessages();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<ConversationItem> createItems(
|
||||
Collection<PrivateMessageHeader> headers,
|
||||
Collection<IntroductionMessage> introductions,
|
||||
Collection<InvitationMessage> invitations) {
|
||||
int size = headers.size() + introductions.size() + invitations.size();
|
||||
List<ConversationItem> items = new ArrayList<>(size);
|
||||
for (PrivateMessageHeader h : headers) {
|
||||
ConversationMessageItem item = ConversationItem.from(h);
|
||||
byte[] body = bodyCache.get(h.getId());
|
||||
if (body == null) loadMessageBody(h.getId());
|
||||
else item.setBody(body);
|
||||
items.add(item);
|
||||
}
|
||||
for (IntroductionMessage im : introductions) {
|
||||
if (im instanceof IntroductionRequest) {
|
||||
IntroductionRequest ir = (IntroductionRequest) im;
|
||||
items.add(ConversationItem.from(ir));
|
||||
} else {
|
||||
IntroductionResponse ir = (IntroductionResponse) im;
|
||||
items.add(ConversationItem.from(ConversationActivity.this,
|
||||
contactName, ir));
|
||||
}
|
||||
}
|
||||
for (InvitationMessage im : invitations) {
|
||||
if (im instanceof InvitationRequest) {
|
||||
InvitationRequest ir = (InvitationRequest) im;
|
||||
items.add(ConversationItem.from(ir));
|
||||
} else if (im instanceof InvitationResponse) {
|
||||
InvitationResponse ir = (InvitationResponse) im;
|
||||
items.add(ConversationItem.from(ConversationActivity.this,
|
||||
contactName, ir));
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private void loadMessageBody(final MessageId m) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
@@ -435,8 +439,6 @@ public class ConversationActivity extends BriarActivity
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading body took " + duration + " ms");
|
||||
displayMessageBody(m, body);
|
||||
} catch (NoSuchMessageException e) {
|
||||
// The item will be removed when we get the event
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
@@ -469,6 +471,7 @@ public class ConversationActivity extends BriarActivity
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adapter.incrementRevision();
|
||||
adapter.add(item);
|
||||
// Scroll to the bottom
|
||||
list.scrollToPosition(adapter.getItemCount() - 1);
|
||||
@@ -496,9 +499,10 @@ public class ConversationActivity extends BriarActivity
|
||||
public void run() {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
for (Map.Entry<MessageId, GroupId> e : unread.entrySet())
|
||||
messagingManager
|
||||
.setReadFlag(e.getValue(), e.getKey(), true);
|
||||
for (Map.Entry<MessageId, GroupId> e : unread.entrySet()) {
|
||||
messagingManager.setReadFlag(e.getValue(), e.getKey(),
|
||||
true);
|
||||
}
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Marking read took " + duration + " ms");
|
||||
@@ -525,7 +529,6 @@ public class ConversationActivity extends BriarActivity
|
||||
PrivateMessageHeader h = p.getMessageHeader();
|
||||
addConversationItem(ConversationItem.from(h));
|
||||
loadMessageBody(h.getId());
|
||||
markMessageReadIfNew(h);
|
||||
}
|
||||
} else if (e instanceof MessagesSentEvent) {
|
||||
MessagesSentEvent m = (MessagesSentEvent) e;
|
||||
@@ -543,14 +546,12 @@ public class ConversationActivity extends BriarActivity
|
||||
ContactConnectedEvent c = (ContactConnectedEvent) e;
|
||||
if (c.getContactId().equals(contactId)) {
|
||||
LOG.info("Contact connected");
|
||||
connected = true;
|
||||
displayContactDetails();
|
||||
}
|
||||
} else if (e instanceof ContactDisconnectedEvent) {
|
||||
ContactDisconnectedEvent c = (ContactDisconnectedEvent) e;
|
||||
if (c.getContactId().equals(contactId)) {
|
||||
LOG.info("Contact disconnected");
|
||||
connected = false;
|
||||
displayContactDetails();
|
||||
}
|
||||
} else if (e instanceof IntroductionRequestReceivedEvent) {
|
||||
@@ -561,7 +562,6 @@ public class ConversationActivity extends BriarActivity
|
||||
IntroductionRequest ir = event.getIntroductionRequest();
|
||||
ConversationItem item = new ConversationIntroductionInItem(ir);
|
||||
addConversationItem(item);
|
||||
markMessageReadIfNew(ir);
|
||||
}
|
||||
} else if (e instanceof IntroductionResponseReceivedEvent) {
|
||||
IntroductionResponseReceivedEvent event =
|
||||
@@ -572,7 +572,6 @@ public class ConversationActivity extends BriarActivity
|
||||
ConversationItem item =
|
||||
ConversationItem.from(this, contactName, ir);
|
||||
addConversationItem(item);
|
||||
markMessageReadIfNew(ir);
|
||||
}
|
||||
} else if (e instanceof InvitationRequestReceivedEvent) {
|
||||
InvitationRequestReceivedEvent event =
|
||||
@@ -582,7 +581,6 @@ public class ConversationActivity extends BriarActivity
|
||||
InvitationRequest ir = event.getRequest();
|
||||
ConversationItem item = ConversationItem.from(ir);
|
||||
addConversationItem(item);
|
||||
markMessageReadIfNew(ir);
|
||||
}
|
||||
} else if (e instanceof InvitationResponseReceivedEvent) {
|
||||
InvitationResponseReceivedEvent event =
|
||||
@@ -593,51 +591,16 @@ public class ConversationActivity extends BriarActivity
|
||||
ConversationItem item =
|
||||
ConversationItem.from(this, contactName, ir);
|
||||
addConversationItem(item);
|
||||
markMessageReadIfNew(ir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void markMessageReadIfNew(final BaseMessageHeader h) {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ConversationItem item = adapter.getLastItem();
|
||||
if (item != null) {
|
||||
// Mark the message read if it's the newest message
|
||||
long lastMsgTime = item.getTime();
|
||||
long newMsgTime = h.getTimestamp();
|
||||
if (newMsgTime > lastMsgTime)
|
||||
markNewMessageRead(h.getGroupId(), h.getId());
|
||||
else loadMessages();
|
||||
} else {
|
||||
// mark the message as read as well if it is the first one
|
||||
markNewMessageRead(h.getGroupId(), h.getId());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void markNewMessageRead(final GroupId g, final MessageId m) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
messagingManager.setReadFlag(g, m, true);
|
||||
loadMessages();
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void markMessages(final Collection<MessageId> messageIds,
|
||||
final boolean sent, final boolean seen) {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adapter.incrementRevision();
|
||||
Set<MessageId> messages = new HashSet<>(messageIds);
|
||||
SparseArray<OutgoingItem> list = adapter.getOutgoingMessages();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@@ -654,7 +617,6 @@ public class ConversationActivity extends BriarActivity
|
||||
|
||||
@Override
|
||||
public void onSendClick(String text) {
|
||||
markMessagesRead();
|
||||
if (text.equals("")) return;
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
||||
@@ -857,13 +819,11 @@ public class ConversationActivity extends BriarActivity
|
||||
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
||||
try {
|
||||
if (accept) {
|
||||
introductionManager
|
||||
.acceptIntroduction(contactId, sessionId,
|
||||
timestamp);
|
||||
introductionManager.acceptIntroduction(contactId,
|
||||
sessionId, timestamp);
|
||||
} else {
|
||||
introductionManager
|
||||
.declineIntroduction(contactId, sessionId,
|
||||
timestamp);
|
||||
introductionManager.declineIntroduction(contactId,
|
||||
sessionId, timestamp);
|
||||
}
|
||||
loadMessages();
|
||||
} catch (DbException | FormatException e) {
|
||||
|
||||
@@ -6,9 +6,9 @@ public interface ActivityLifecycleController {
|
||||
|
||||
void onActivityCreate(Activity activity);
|
||||
|
||||
void onActivityResume();
|
||||
void onActivityStart();
|
||||
|
||||
void onActivityPause();
|
||||
void onActivityStop();
|
||||
|
||||
void onActivityDestroy();
|
||||
}
|
||||
|
||||
@@ -19,18 +19,18 @@ public class BriarControllerImpl implements BriarController {
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BriarControllerImpl.class.getName());
|
||||
|
||||
@Inject
|
||||
BriarServiceConnection serviceConnection;
|
||||
@Inject
|
||||
DatabaseConfig databaseConfig;
|
||||
@Inject
|
||||
Activity activity;
|
||||
private final BriarServiceConnection serviceConnection;
|
||||
private final DatabaseConfig databaseConfig;
|
||||
private final Activity activity;
|
||||
|
||||
private boolean bound = false;
|
||||
|
||||
@Inject
|
||||
public BriarControllerImpl() {
|
||||
|
||||
BriarControllerImpl(BriarServiceConnection serviceConnection,
|
||||
DatabaseConfig databaseConfig, Activity activity) {
|
||||
this.serviceConnection = serviceConnection;
|
||||
this.databaseConfig = databaseConfig;
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,13 +40,11 @@ public class BriarControllerImpl implements BriarController {
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onActivityResume() {
|
||||
public void onActivityStart() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onActivityPause() {
|
||||
public void onActivityStop() {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -57,12 +57,12 @@ public class NavDrawerControllerImpl extends DbControllerImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResume() {
|
||||
public void onActivityStart() {
|
||||
eventBus.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPause() {
|
||||
public void onActivityStop() {
|
||||
eventBus.removeListener(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.android.forum;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.LayoutRes;
|
||||
@@ -98,9 +99,8 @@ public class ForumActivity extends
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
ActivityOptionsCompat options =
|
||||
makeCustomAnimation(this, android.R.anim.slide_in_left,
|
||||
android.R.anim.slide_out_right);
|
||||
ActivityOptionsCompat options = makeCustomAnimation(this,
|
||||
android.R.anim.slide_in_left, android.R.anim.slide_out_right);
|
||||
// Handle presses on the action bar items
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_forum_compose_post:
|
||||
@@ -110,9 +110,8 @@ public class ForumActivity extends
|
||||
Intent i2 = new Intent(this, ShareForumActivity.class);
|
||||
i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i2.putExtra(GROUP_ID, groupId.getBytes());
|
||||
ActivityCompat
|
||||
.startActivityForResult(this, i2, REQUEST_FORUM_SHARED,
|
||||
options.toBundle());
|
||||
ActivityCompat.startActivityForResult(this, i2,
|
||||
REQUEST_FORUM_SHARED, options.toBundle());
|
||||
return true;
|
||||
case R.id.action_forum_sharing_status:
|
||||
Intent i3 = new Intent(this, SharingStatusForumActivity.class);
|
||||
@@ -146,17 +145,14 @@ public class ForumActivity extends
|
||||
}
|
||||
|
||||
private void showUnsubscribeDialog() {
|
||||
DialogInterface.OnClickListener okListener =
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog,
|
||||
int which) {
|
||||
deleteNamedGroup();
|
||||
}
|
||||
};
|
||||
AlertDialog.Builder builder =
|
||||
new AlertDialog.Builder(ForumActivity.this,
|
||||
R.style.BriarDialogTheme);
|
||||
OnClickListener okListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
deleteNamedGroup();
|
||||
}
|
||||
};
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(
|
||||
ForumActivity.this, R.style.BriarDialogTheme);
|
||||
builder.setTitle(getString(R.string.dialog_title_leave_forum));
|
||||
builder.setMessage(getString(R.string.dialog_message_leave_forum));
|
||||
builder.setNegativeButton(R.string.dialog_button_leave, okListener);
|
||||
@@ -166,19 +162,15 @@ public class ForumActivity extends
|
||||
|
||||
private void deleteNamedGroup() {
|
||||
forumController.deleteNamedGroup(
|
||||
new UiResultExceptionHandler<Void, DbException>(
|
||||
ForumActivity.this) {
|
||||
new UiResultExceptionHandler<Void, DbException>(this) {
|
||||
@Override
|
||||
public void onResultUi(Void v) {
|
||||
Toast.makeText(ForumActivity.this,
|
||||
R.string.forum_left_toast,
|
||||
LENGTH_SHORT)
|
||||
.show();
|
||||
R.string.forum_left_toast, LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExceptionUi(
|
||||
DbException exception) {
|
||||
public void onExceptionUi(DbException exception) {
|
||||
// TODO proper error handling
|
||||
finish();
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class ForumControllerImpl
|
||||
extends ThreadListControllerImpl<Forum, ForumItem, ForumPostHeader, ForumPost>
|
||||
public class ForumControllerImpl extends
|
||||
ThreadListControllerImpl<Forum, ForumItem, ForumPostHeader, ForumPost>
|
||||
implements ForumController {
|
||||
|
||||
private static final Logger LOG =
|
||||
@@ -49,8 +49,8 @@ public class ForumControllerImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResume() {
|
||||
super.onActivityResume();
|
||||
public void onActivityStart() {
|
||||
super.onActivityStart();
|
||||
notificationManager.clearForumPostNotification(getGroupId());
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class ForumControllerImpl
|
||||
super.eventOccurred(e);
|
||||
|
||||
if (e instanceof ForumPostReceivedEvent) {
|
||||
final ForumPostReceivedEvent pe = (ForumPostReceivedEvent) e;
|
||||
ForumPostReceivedEvent pe = (ForumPostReceivedEvent) e;
|
||||
if (pe.getGroupId().equals(getGroupId())) {
|
||||
LOG.info("Forum post received, adding...");
|
||||
final ForumPostHeader fph = pe.getForumPostHeader();
|
||||
@@ -102,9 +102,8 @@ public class ForumControllerImpl
|
||||
@Override
|
||||
protected ForumPost createLocalMessage(String body, long timestamp,
|
||||
@Nullable MessageId parentId, LocalAuthor author) {
|
||||
return forumManager
|
||||
.createLocalPost(getGroupId(), body, timestamp, parentId,
|
||||
author);
|
||||
return forumManager.createLocalPost(getGroupId(), body, timestamp,
|
||||
parentId, author);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -111,9 +111,8 @@ public class ForumListFragment extends BaseEventFragment implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
notificationManager.blockAllForumPostNotifications();
|
||||
notificationManager.clearAllForumPostNotifications();
|
||||
loadForums();
|
||||
@@ -122,9 +121,8 @@ public class ForumListFragment extends BaseEventFragment implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
notificationManager.unblockAllForumPostNotifications();
|
||||
adapter.clear();
|
||||
list.showProgressBar();
|
||||
@@ -152,11 +150,11 @@ public class ForumListFragment extends BaseEventFragment implements
|
||||
}
|
||||
|
||||
private void loadForums() {
|
||||
final int revision = adapter.getRevision();
|
||||
listener.runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// load forums
|
||||
long now = System.currentTimeMillis();
|
||||
Collection<ForumListItem> forums = new ArrayList<>();
|
||||
for (Forum f : forumManager.getForums()) {
|
||||
@@ -168,10 +166,10 @@ public class ForumListFragment extends BaseEventFragment implements
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
displayForums(forums);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Full load took " + duration + " ms");
|
||||
displayForums(revision, forums);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
@@ -180,12 +178,19 @@ public class ForumListFragment extends BaseEventFragment implements
|
||||
});
|
||||
}
|
||||
|
||||
private void displayForums(final Collection<ForumListItem> forums) {
|
||||
private void displayForums(final int revision,
|
||||
final Collection<ForumListItem> forums) {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (forums.size() > 0) adapter.addAll(forums);
|
||||
else list.showData();
|
||||
if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
if (forums.isEmpty()) list.showData();
|
||||
else adapter.addAll(forums);
|
||||
} else {
|
||||
LOG.info("Concurrent update, reloading");
|
||||
loadForums();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -245,9 +250,10 @@ public class ForumListFragment extends BaseEventFragment implements
|
||||
}
|
||||
} else if (e instanceof ForumPostReceivedEvent) {
|
||||
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
|
||||
LOG.info("Forum post added, updating...");
|
||||
LOG.info("Forum post added, updating item");
|
||||
updateItem(f.getGroupId(), f.getForumPostHeader());
|
||||
} else if (e instanceof ForumInvitationReceivedEvent) {
|
||||
LOG.info("Forum invitation received, reloading available forums");
|
||||
loadAvailableForums();
|
||||
}
|
||||
}
|
||||
@@ -256,6 +262,7 @@ public class ForumListFragment extends BaseEventFragment implements
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adapter.incrementRevision();
|
||||
int position = adapter.findItemPosition(g);
|
||||
ForumListItem item = adapter.getItemAt(position);
|
||||
if (item != null) {
|
||||
@@ -270,6 +277,7 @@ public class ForumListFragment extends BaseEventFragment implements
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adapter.incrementRevision();
|
||||
int position = adapter.findItemPosition(g);
|
||||
ForumListItem item = adapter.getItemAt(position);
|
||||
if (item != null) adapter.remove(item);
|
||||
|
||||
@@ -12,14 +12,14 @@ public abstract class BaseEventFragment extends BaseFragment implements
|
||||
protected volatile EventBus eventBus;
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
eventBus.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
eventBus.removeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ public abstract class BaseFragment extends Fragment
|
||||
|
||||
public interface BaseFragmentListener extends DestroyableContext {
|
||||
|
||||
@Deprecated
|
||||
void runOnDbThread(Runnable runnable);
|
||||
|
||||
@UiThread
|
||||
|
||||
@@ -155,14 +155,14 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
eventBus.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
eventBus.removeListener(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
package org.briarproject.android.introduction;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.view.View;
|
||||
|
||||
import org.briarproject.android.contact.ContactListAdapter;
|
||||
import org.briarproject.android.contact.ContactListItem;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
|
||||
public class ContactChooserAdapter extends ContactListAdapter {
|
||||
@UiThread
|
||||
class ContactChooserAdapter extends ContactListAdapter {
|
||||
|
||||
private AuthorId localAuthorId;
|
||||
|
||||
public ContactChooserAdapter(Context context,
|
||||
OnItemClickListener listener) {
|
||||
|
||||
ContactChooserAdapter(Context context, OnItemClickListener listener) {
|
||||
super(context, listener);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class ContactChooserAdapter extends ContactListAdapter {
|
||||
*
|
||||
* @param authorId The ID of the local Author
|
||||
*/
|
||||
public void setLocalAuthor(AuthorId authorId) {
|
||||
void setLocalAuthor(AuthorId authorId) {
|
||||
localAuthorId = authorId;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@@ -45,18 +45,18 @@ public class ContactChooserFragment extends BaseFragment {
|
||||
private IntroductionActivity introductionActivity;
|
||||
private BriarRecyclerView list;
|
||||
private ContactChooserAdapter adapter;
|
||||
private int contactId;
|
||||
private ContactId contactId;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
protected volatile Contact c1;
|
||||
volatile Contact c1;
|
||||
@Inject
|
||||
protected volatile ContactManager contactManager;
|
||||
volatile ContactManager contactManager;
|
||||
@Inject
|
||||
protected volatile IdentityManager identityManager;
|
||||
volatile IdentityManager identityManager;
|
||||
@Inject
|
||||
protected volatile ConversationManager conversationManager;
|
||||
volatile ConversationManager conversationManager;
|
||||
@Inject
|
||||
protected volatile ConnectionRegistry connectionRegistry;
|
||||
volatile ConnectionRegistry connectionRegistry;
|
||||
|
||||
public static ContactChooserFragment newInstance() {
|
||||
|
||||
@@ -87,9 +87,7 @@ public class ContactChooserFragment extends BaseFragment {
|
||||
new ContactListAdapter.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(View view, ContactListItem item) {
|
||||
if (c1 == null) {
|
||||
throw new RuntimeException("c1 not accountExists");
|
||||
}
|
||||
if (c1 == null) throw new IllegalStateException();
|
||||
Contact c2 = item.getContact();
|
||||
if (!c1.getLocalAuthorId()
|
||||
.equals(c2.getLocalAuthorId())) {
|
||||
@@ -113,15 +111,14 @@ public class ContactChooserFragment extends BaseFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
loadContacts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
adapter.clear();
|
||||
list.showProgressBar();
|
||||
}
|
||||
@@ -145,7 +142,7 @@ public class ContactChooserFragment extends BaseFragment {
|
||||
AuthorId localAuthorId =
|
||||
identityManager.getLocalAuthor().getId();
|
||||
for (Contact c : contactManager.getActiveContacts()) {
|
||||
if (c.getId().getInt() == contactId) {
|
||||
if (c.getId().equals(contactId)) {
|
||||
c1 = c;
|
||||
} else {
|
||||
ContactId id = c.getId();
|
||||
@@ -176,7 +173,7 @@ public class ContactChooserFragment extends BaseFragment {
|
||||
@Override
|
||||
public void run() {
|
||||
adapter.setLocalAuthor(localAuthorId);
|
||||
if (contacts.size() == 0) list.showData();
|
||||
if (contacts.isEmpty()) list.showData();
|
||||
else adapter.addAll(contacts);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.android.BriarActivity;
|
||||
import org.briarproject.android.fragment.BaseFragment;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
|
||||
// TODO extend the BriarFragmentActivity ?
|
||||
public class IntroductionActivity extends BriarActivity implements
|
||||
@@ -21,16 +22,16 @@ public class IntroductionActivity extends BriarActivity implements
|
||||
|
||||
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
||||
|
||||
private int contactId;
|
||||
private ContactId contactId;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = getIntent();
|
||||
contactId = intent.getIntExtra(CONTACT_ID, -1);
|
||||
if (contactId == -1)
|
||||
throw new IllegalArgumentException("Wrong ContactId");
|
||||
int id = intent.getIntExtra(CONTACT_ID, -1);
|
||||
if (id == -1) throw new IllegalStateException("No ContactId");
|
||||
contactId = new ContactId(id);
|
||||
|
||||
setContentView(R.layout.activity_fragment_container);
|
||||
|
||||
@@ -75,7 +76,7 @@ public class IntroductionActivity extends BriarActivity implements
|
||||
}
|
||||
}
|
||||
|
||||
int getContactId() {
|
||||
ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,13 @@ public class AddContactActivity extends BriarActivity
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(AddContactActivity.class.getName());
|
||||
|
||||
@Inject protected CryptoComponent crypto;
|
||||
@Inject protected InvitationTaskFactory invitationTaskFactory;
|
||||
@Inject protected ReferenceManager referenceManager;
|
||||
@Inject
|
||||
CryptoComponent crypto;
|
||||
@Inject
|
||||
InvitationTaskFactory invitationTaskFactory;
|
||||
@Inject
|
||||
ReferenceManager referenceManager;
|
||||
|
||||
private AddContactView view = null;
|
||||
private InvitationTask task = null;
|
||||
private long taskHandle = -1;
|
||||
@@ -52,7 +56,8 @@ public class AddContactActivity extends BriarActivity
|
||||
private String contactName = null;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject protected volatile IdentityManager identityManager;
|
||||
@Inject
|
||||
volatile IdentityManager identityManager;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
@@ -150,8 +155,8 @@ public class AddContactActivity extends BriarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
view.populate();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,15 +40,13 @@ public class KeyAgreementActivity extends BriarFragmentActivity implements
|
||||
private static final int STEP_QR = 2;
|
||||
|
||||
@Inject
|
||||
protected EventBus eventBus;
|
||||
|
||||
private Toolbar toolbar;
|
||||
EventBus eventBus;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject
|
||||
protected volatile ContactExchangeTask contactExchangeTask;
|
||||
volatile ContactExchangeTask contactExchangeTask;
|
||||
@Inject
|
||||
protected volatile IdentityManager identityManager;
|
||||
volatile IdentityManager identityManager;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
@@ -61,7 +59,7 @@ public class KeyAgreementActivity extends BriarFragmentActivity implements
|
||||
super.onCreate(state);
|
||||
setContentView(R.layout.activity_plain);
|
||||
|
||||
toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
@@ -83,14 +81,14 @@ public class KeyAgreementActivity extends BriarFragmentActivity implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
eventBus.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
eventBus.removeListener(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -162,25 +162,16 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
||||
} else {
|
||||
startListening();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
openCamera();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
releaseCamera();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
stopListening();
|
||||
if (receiver != null) getActivity().unregisterReceiver(receiver);
|
||||
releaseCamera();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
|
||||
@@ -132,16 +132,16 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
getPreferenceScreen().getSharedPreferences()
|
||||
.registerOnSharedPreferenceChangeListener(this);
|
||||
showPanicApp(PanicResponder.getTriggerPackageName(getActivity()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
getPreferenceScreen().getSharedPreferences()
|
||||
.unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class GroupControllerImpl
|
||||
extends ThreadListControllerImpl<PrivateGroup, GroupMessageItem, GroupMessageHeader, GroupMessage>
|
||||
public class GroupControllerImpl extends
|
||||
ThreadListControllerImpl<PrivateGroup, GroupMessageItem, GroupMessageHeader, GroupMessage>
|
||||
implements GroupController {
|
||||
|
||||
private static final Logger LOG =
|
||||
@@ -48,8 +48,8 @@ public class GroupControllerImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResume() {
|
||||
super.onActivityResume();
|
||||
public void onActivityStart() {
|
||||
super.onActivityStart();
|
||||
// TODO: Add new notification manager methods for private groups
|
||||
}
|
||||
|
||||
@@ -101,9 +101,8 @@ public class GroupControllerImpl
|
||||
@Override
|
||||
protected GroupMessage createLocalMessage(String body, long timestamp,
|
||||
@Nullable MessageId parentId, LocalAuthor author) {
|
||||
return privateGroupManager
|
||||
.createLocalMessage(getGroupId(), body, timestamp, parentId,
|
||||
author);
|
||||
return privateGroupManager.createLocalMessage(getGroupId(), body,
|
||||
timestamp, parentId, author);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,56 +1,51 @@
|
||||
package org.briarproject.android.privategroup.list;
|
||||
|
||||
import org.briarproject.api.clients.MessageTracker.GroupCount;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.privategroup.GroupMessageHeader;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
// This class is not thread-safe
|
||||
@NotNullByDefault
|
||||
class GroupItem {
|
||||
|
||||
private final PrivateGroup privateGroup;
|
||||
private int messageCount;
|
||||
private long lastUpdate;
|
||||
private int unreadCount;
|
||||
private int messageCount, unreadCount;
|
||||
private long timestamp;
|
||||
private boolean dissolved;
|
||||
|
||||
GroupItem(@NotNull PrivateGroup privateGroup, int messageCount,
|
||||
long lastUpdate, int unreadCount, boolean dissolved) {
|
||||
|
||||
GroupItem(PrivateGroup privateGroup, GroupCount count, boolean dissolved) {
|
||||
this.privateGroup = privateGroup;
|
||||
this.messageCount = messageCount;
|
||||
this.lastUpdate = lastUpdate;
|
||||
this.unreadCount = unreadCount;
|
||||
this.messageCount = count.getMsgCount();
|
||||
this.unreadCount = count.getUnreadCount();
|
||||
this.timestamp = count.getLatestMsgTime();
|
||||
this.dissolved = dissolved;
|
||||
}
|
||||
|
||||
void addMessageHeader(GroupMessageHeader header) {
|
||||
messageCount++;
|
||||
if (header.getTimestamp() > lastUpdate) {
|
||||
lastUpdate = header.getTimestamp();
|
||||
if (header.getTimestamp() > timestamp) {
|
||||
timestamp = header.getTimestamp();
|
||||
}
|
||||
if (!header.isRead()) {
|
||||
unreadCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
PrivateGroup getPrivateGroup() {
|
||||
return privateGroup;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
GroupId getId() {
|
||||
return privateGroup.getId();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
Author getCreator() {
|
||||
return privateGroup.getAuthor();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
String getName() {
|
||||
return privateGroup.getName();
|
||||
}
|
||||
@@ -63,8 +58,8 @@ class GroupItem {
|
||||
return messageCount;
|
||||
}
|
||||
|
||||
long getLastUpdate() {
|
||||
return lastUpdate;
|
||||
long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
int getUnreadCount() {
|
||||
|
||||
@@ -38,7 +38,7 @@ class GroupListAdapter extends BriarAdapter<GroupItem, GroupViewHolder> {
|
||||
public int compare(GroupItem a, GroupItem b) {
|
||||
if (a == b) return 0;
|
||||
// The group with the latest message comes first
|
||||
long aTime = a.getLastUpdate(), bTime = b.getLastUpdate();
|
||||
long aTime = a.getTimestamp(), bTime = b.getTimestamp();
|
||||
if (aTime > bTime) return -1;
|
||||
if (aTime < bTime) return 1;
|
||||
// Break ties by group name
|
||||
@@ -50,7 +50,7 @@ class GroupListAdapter extends BriarAdapter<GroupItem, GroupViewHolder> {
|
||||
@Override
|
||||
public boolean areContentsTheSame(GroupItem a, GroupItem b) {
|
||||
return a.getMessageCount() == b.getMessageCount() &&
|
||||
a.getLastUpdate() == b.getLastUpdate() &&
|
||||
a.getTimestamp() == b.getTimestamp() &&
|
||||
a.getUnreadCount() == b.getUnreadCount() &&
|
||||
a.isDissolved() == b.isDissolved();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ public interface GroupListController extends DbController {
|
||||
ResultExceptionHandler<Void, DbException> result);
|
||||
|
||||
interface GroupListListener extends DestroyableContext {
|
||||
|
||||
@UiThread
|
||||
void onGroupMessageAdded(GroupMessageHeader header);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.api.clients.MessageTracker.GroupCount;
|
||||
import org.briarproject.api.db.DatabaseExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.NoSuchGroupException;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
@@ -16,6 +17,7 @@ import org.briarproject.api.event.GroupMessageAddedEvent;
|
||||
import org.briarproject.api.event.GroupRemovedEvent;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.privategroup.GroupMessageHeader;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
@@ -29,6 +31,7 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
public class GroupListControllerImpl extends DbControllerImpl
|
||||
@@ -81,59 +84,77 @@ public class GroupListControllerImpl extends DbControllerImpl
|
||||
@CallSuper
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof GroupMessageAddedEvent) {
|
||||
final GroupMessageAddedEvent m = (GroupMessageAddedEvent) e;
|
||||
LOG.info("New group message added");
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onGroupMessageAdded(m.getHeader());
|
||||
}
|
||||
});
|
||||
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
|
||||
LOG.info("Private group message added");
|
||||
onGroupMessageAdded(g.getHeader());
|
||||
} else if (e instanceof GroupAddedEvent) {
|
||||
final GroupAddedEvent gae = (GroupAddedEvent) e;
|
||||
ClientId id = gae.getGroup().getClientId();
|
||||
GroupAddedEvent g = (GroupAddedEvent) e;
|
||||
ClientId id = g.getGroup().getClientId();
|
||||
if (id.equals(groupManager.getClientId())) {
|
||||
LOG.info("Private group added");
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onGroupAdded(gae.getGroup().getId());
|
||||
}
|
||||
});
|
||||
onGroupAdded(g.getGroup().getId());
|
||||
}
|
||||
} else if (e instanceof GroupRemovedEvent) {
|
||||
final GroupRemovedEvent gre = (GroupRemovedEvent) e;
|
||||
ClientId id = gre.getGroup().getClientId();
|
||||
GroupRemovedEvent g = (GroupRemovedEvent) e;
|
||||
ClientId id = g.getGroup().getClientId();
|
||||
if (id.equals(groupManager.getClientId())) {
|
||||
LOG.info("Private group removed");
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onGroupRemoved(gre.getGroup().getId());
|
||||
}
|
||||
});
|
||||
onGroupRemoved(g.getGroup().getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onGroupMessageAdded(final GroupMessageHeader h) {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onGroupMessageAdded(h);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onGroupAdded(final GroupId g) {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onGroupAdded(g);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onGroupRemoved(final GroupId g) {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onGroupRemoved(g);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadGroups(
|
||||
final ResultExceptionHandler<Collection<GroupItem>, DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LOG.info("Loading groups from database...");
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
Collection<PrivateGroup> groups =
|
||||
groupManager.getPrivateGroups();
|
||||
List<GroupItem> items = new ArrayList<>(groups.size());
|
||||
for (PrivateGroup g : groups) {
|
||||
GroupCount c = groupManager.getGroupCount(g.getId());
|
||||
boolean dissolved = groupManager.isDissolved(g.getId());
|
||||
items.add(new GroupItem(g, c.getMsgCount(),
|
||||
c.getLatestMsgTime(), c.getUnreadCount(),
|
||||
dissolved));
|
||||
try {
|
||||
GroupId id = g.getId();
|
||||
GroupCount count = groupManager.getGroupCount(id);
|
||||
boolean dissolved = groupManager.isDissolved(id);
|
||||
items.add(new GroupItem(g, count, dissolved));
|
||||
} catch (NoSuchGroupException e) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading groups took " + duration + " ms");
|
||||
handler.onResult(items);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
@@ -150,9 +171,13 @@ public class GroupListControllerImpl extends DbControllerImpl
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LOG.info("Removing group from database...");
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
groupManager.removePrivateGroup(g);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Removing group took " + duration + " ms");
|
||||
handler.onResult(null);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.android.privategroup.list;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
@@ -16,7 +15,6 @@ import org.briarproject.R;
|
||||
import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||
import org.briarproject.android.fragment.BaseFragment;
|
||||
import org.briarproject.android.invitation.AddContactActivity;
|
||||
import org.briarproject.android.privategroup.list.GroupListController.GroupListListener;
|
||||
import org.briarproject.android.privategroup.list.GroupViewHolder.OnGroupRemoveClickListener;
|
||||
import org.briarproject.android.view.BriarRecyclerView;
|
||||
@@ -25,6 +23,7 @@ import org.briarproject.api.privategroup.GroupMessageHeader;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -32,6 +31,7 @@ public class GroupListFragment extends BaseFragment implements
|
||||
GroupListListener, OnGroupRemoveClickListener {
|
||||
|
||||
public final static String TAG = GroupListFragment.class.getName();
|
||||
private static final Logger LOG = Logger.getLogger(TAG);
|
||||
|
||||
public static GroupListFragment newInstance() {
|
||||
return new GroupListFragment();
|
||||
@@ -114,7 +114,6 @@ public class GroupListFragment extends BaseFragment implements
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
// TODO handle error
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -122,6 +121,7 @@ public class GroupListFragment extends BaseFragment implements
|
||||
@UiThread
|
||||
@Override
|
||||
public void onGroupMessageAdded(GroupMessageHeader header) {
|
||||
adapter.incrementRevision();
|
||||
int position = adapter.findItemPosition(header.getGroupId());
|
||||
GroupItem item = adapter.getItemAt(position);
|
||||
if (item != null) {
|
||||
@@ -139,6 +139,7 @@ public class GroupListFragment extends BaseFragment implements
|
||||
@UiThread
|
||||
@Override
|
||||
public void onGroupRemoved(GroupId groupId) {
|
||||
adapter.incrementRevision();
|
||||
adapter.removeItem(groupId);
|
||||
}
|
||||
|
||||
@@ -148,22 +149,25 @@ public class GroupListFragment extends BaseFragment implements
|
||||
}
|
||||
|
||||
private void loadGroups() {
|
||||
final int revision = adapter.getRevision();
|
||||
controller.loadGroups(
|
||||
new UiResultExceptionHandler<Collection<GroupItem>, DbException>(
|
||||
listener) {
|
||||
@Override
|
||||
public void onResultUi(Collection<GroupItem> result) {
|
||||
if (result.isEmpty()) {
|
||||
list.showData();
|
||||
public void onResultUi(Collection<GroupItem> groups) {
|
||||
if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
if (groups.isEmpty()) list.showData();
|
||||
else adapter.addAll(groups);
|
||||
} else {
|
||||
adapter.addAll(result);
|
||||
LOG.info("Concurrent update, reloading");
|
||||
loadGroups();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
// TODO handle this error
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class GroupViewHolder extends RecyclerView.ViewHolder {
|
||||
postCount.setTextColor(
|
||||
getColor(ctx, R.color.briar_text_secondary));
|
||||
|
||||
long lastUpdate = group.getLastUpdate();
|
||||
long lastUpdate = group.getTimestamp();
|
||||
date.setText(AndroidUtils.formatDate(ctx, lastUpdate));
|
||||
date.setVisibility(VISIBLE);
|
||||
avatar.setProblem(false);
|
||||
|
||||
@@ -163,8 +163,8 @@ public class DevReportActivity extends BaseCrashReportDialog
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
if (chevron.isSelected()) refresh();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.briarproject.api.sync.GroupId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -57,11 +56,11 @@ public class ContactSelectorFragment extends BaseFragment implements
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject
|
||||
protected volatile ContactManager contactManager;
|
||||
volatile ContactManager contactManager;
|
||||
@Inject
|
||||
protected volatile IdentityManager identityManager;
|
||||
volatile IdentityManager identityManager;
|
||||
@Inject
|
||||
protected volatile ForumSharingManager forumSharingManager;
|
||||
volatile ForumSharingManager forumSharingManager;
|
||||
|
||||
private volatile GroupId groupId;
|
||||
|
||||
@@ -91,8 +90,9 @@ public class ContactSelectorFragment extends BaseFragment implements
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
Bundle args = getArguments();
|
||||
groupId = new GroupId(args.getByteArray(GROUP_ID));
|
||||
if (groupId == null) throw new IllegalStateException("No GroupId");
|
||||
byte[] b = args.getByteArray(GROUP_ID);
|
||||
if (b == null) throw new IllegalStateException("No GroupId");
|
||||
groupId = new GroupId(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,12 +125,16 @@ public class ContactSelectorFragment extends BaseFragment implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
loadContacts(selectedContacts);
|
||||
}
|
||||
|
||||
if (selectedContacts != null)
|
||||
loadContacts(Collections.unmodifiableCollection(selectedContacts));
|
||||
else loadContacts(null);
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
adapter.clear();
|
||||
list.showProgressBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -202,9 +206,8 @@ public class ContactSelectorFragment extends BaseFragment implements
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Load took " + duration + " ms");
|
||||
displayContacts(Collections.unmodifiableList(contacts));
|
||||
displayContacts(contacts);
|
||||
} catch (DbException e) {
|
||||
displayContacts(Collections.<ContactListItem>emptyList());
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
@@ -216,8 +219,8 @@ public class ContactSelectorFragment extends BaseFragment implements
|
||||
shareActivity.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!contacts.isEmpty()) adapter.addAll(contacts);
|
||||
else list.showData();
|
||||
if (contacts.isEmpty()) list.showData();
|
||||
else adapter.addAll(contacts);
|
||||
updateMenuItem();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.android.sharing;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -28,7 +29,7 @@ abstract class InvitationsActivity extends BriarActivity
|
||||
protected static final Logger LOG =
|
||||
Logger.getLogger(InvitationsActivity.class.getName());
|
||||
|
||||
private InvitationAdapter adapter;
|
||||
protected InvitationAdapter adapter;
|
||||
private BriarRecyclerView list;
|
||||
|
||||
@Inject
|
||||
@@ -42,7 +43,6 @@ abstract class InvitationsActivity extends BriarActivity
|
||||
|
||||
adapter = getAdapter(this, this);
|
||||
|
||||
|
||||
list = (BriarRecyclerView) findViewById(R.id.list);
|
||||
if (list != null) {
|
||||
list.setLayoutManager(new LinearLayoutManager(this));
|
||||
@@ -51,21 +51,22 @@ abstract class InvitationsActivity extends BriarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
eventBus.addListener(this);
|
||||
loadInvitations(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
eventBus.removeListener(this);
|
||||
adapter.clear();
|
||||
list.showProgressBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof ContactRemovedEvent) {
|
||||
LOG.info("Contact removed, reloading...");
|
||||
@@ -83,6 +84,7 @@ abstract class InvitationsActivity extends BriarActivity
|
||||
Toast.makeText(this, res, LENGTH_SHORT).show();
|
||||
|
||||
// remove item and finish if it was the last
|
||||
adapter.incrementRevision();
|
||||
adapter.remove(item);
|
||||
if (adapter.getItemCount() == 0) {
|
||||
supportFinishAfterTransition();
|
||||
@@ -101,7 +103,7 @@ abstract class InvitationsActivity extends BriarActivity
|
||||
|
||||
abstract protected int getDeclineRes();
|
||||
|
||||
protected void displayInvitations(
|
||||
protected void displayInvitations(final int revision,
|
||||
final Collection<InvitationItem> invitations, final boolean clear) {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
@@ -109,9 +111,13 @@ abstract class InvitationsActivity extends BriarActivity
|
||||
if (invitations.isEmpty()) {
|
||||
LOG.info("No more invitations available, finishing");
|
||||
finish();
|
||||
} else if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
if (clear) adapter.setItems(invitations);
|
||||
else adapter.addAll(invitations);
|
||||
} else {
|
||||
if (clear) adapter.clear();
|
||||
adapter.addAll(invitations);
|
||||
LOG.info("Concurrent update, reloading");
|
||||
loadInvitations(clear);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -29,9 +29,9 @@ public class InvitationsBlogActivity extends InvitationsActivity {
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject
|
||||
protected volatile BlogManager blogManager;
|
||||
volatile BlogManager blogManager;
|
||||
@Inject
|
||||
protected volatile BlogSharingManager blogSharingManager;
|
||||
volatile BlogSharingManager blogSharingManager;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
@@ -62,31 +62,35 @@ public class InvitationsBlogActivity extends InvitationsActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationAdapter getAdapter(Context ctx,
|
||||
AvailableForumClickListener listener) {
|
||||
return new BlogInvitationAdapter(ctx, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadInvitations(final boolean clear) {
|
||||
final int revision = adapter.getRevision();
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Collection<InvitationItem> invitations = new ArrayList<>();
|
||||
try {
|
||||
Collection<InvitationItem> invitations = new ArrayList<>();
|
||||
long now = System.currentTimeMillis();
|
||||
invitations.addAll(blogSharingManager.getInvitations());
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Load took " + duration + " ms");
|
||||
displayInvitations(revision, invitations, clear);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
displayInvitations(invitations, clear);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void respondToInvitation(final InvitationItem item,
|
||||
final boolean accept) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@@ -95,6 +99,7 @@ public class InvitationsBlogActivity extends InvitationsActivity {
|
||||
try {
|
||||
Blog b = (Blog) item.getShareable();
|
||||
for (Contact c : item.getNewSharers()) {
|
||||
// TODO: What happens if a contact has been removed?
|
||||
blogSharingManager.respondToInvitation(b, c, accept);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
@@ -105,10 +110,12 @@ public class InvitationsBlogActivity extends InvitationsActivity {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAcceptRes() {
|
||||
return R.string.blogs_sharing_joined_toast;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDeclineRes() {
|
||||
return R.string.blogs_sharing_declined_toast;
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ public class InvitationsForumActivity extends InvitationsActivity {
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject
|
||||
protected volatile ForumManager forumManager;
|
||||
volatile ForumManager forumManager;
|
||||
@Inject
|
||||
protected volatile ForumSharingManager forumSharingManager;
|
||||
volatile ForumSharingManager forumSharingManager;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
@@ -62,31 +62,35 @@ public class InvitationsForumActivity extends InvitationsActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationAdapter getAdapter(Context ctx,
|
||||
AvailableForumClickListener listener) {
|
||||
return new ForumInvitationAdapter(ctx, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadInvitations(final boolean clear) {
|
||||
final int revision = adapter.getRevision();
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Collection<InvitationItem> invitations = new ArrayList<>();
|
||||
try {
|
||||
Collection<InvitationItem> invitations = new ArrayList<>();
|
||||
long now = System.currentTimeMillis();
|
||||
invitations.addAll(forumSharingManager.getInvitations());
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Load took " + duration + " ms");
|
||||
displayInvitations(revision, invitations, clear);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
displayInvitations(invitations, clear);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void respondToInvitation(final InvitationItem item,
|
||||
final boolean accept) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@@ -95,6 +99,7 @@ public class InvitationsForumActivity extends InvitationsActivity {
|
||||
try {
|
||||
Forum f = (Forum) item.getShareable();
|
||||
for (Contact c : item.getNewSharers()) {
|
||||
// TODO: What happens if a contact has been removed?
|
||||
forumSharingManager.respondToInvitation(f, c, accept);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
@@ -105,10 +110,12 @@ public class InvitationsForumActivity extends InvitationsActivity {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAcceptRes() {
|
||||
return R.string.forum_joined_toast;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDeclineRes() {
|
||||
return R.string.forum_declined_toast;
|
||||
}
|
||||
|
||||
@@ -63,13 +63,21 @@ abstract class SharingStatusActivity extends BriarActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
loadSharedBy();
|
||||
loadSharedWith();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
sharedByAdapter.clear();
|
||||
sharedByList.showProgressBar();
|
||||
sharedWithAdapter.clear();
|
||||
sharedWithList.showProgressBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
// Handle presses on the action bar items
|
||||
@@ -97,11 +105,11 @@ abstract class SharingStatusActivity extends BriarActivity {
|
||||
}
|
||||
|
||||
private void loadSharedBy() {
|
||||
dbController.runOnDbThread(new Runnable() {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<ContactListItem> contactItems = new ArrayList<>();
|
||||
try {
|
||||
List<ContactListItem> contactItems = new ArrayList<>();
|
||||
for (Contact c : getSharedBy()) {
|
||||
LocalAuthor localAuthor = identityManager
|
||||
.getLocalAuthor(c.getLocalAuthorId());
|
||||
@@ -110,11 +118,11 @@ abstract class SharingStatusActivity extends BriarActivity {
|
||||
groupId, new GroupCount(0, 0, 0));
|
||||
contactItems.add(item);
|
||||
}
|
||||
displaySharedBy(contactItems);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
displaySharedBy(contactItems);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -123,21 +131,18 @@ abstract class SharingStatusActivity extends BriarActivity {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (contacts.isEmpty()) {
|
||||
sharedByList.showData();
|
||||
} else {
|
||||
sharedByAdapter.addAll(contacts);
|
||||
}
|
||||
if (contacts.isEmpty()) sharedByList.showData();
|
||||
else sharedByAdapter.addAll(contacts);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadSharedWith() {
|
||||
dbController.runOnDbThread(new Runnable() {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<ContactListItem> contactItems = new ArrayList<>();
|
||||
try {
|
||||
List<ContactListItem> contactItems = new ArrayList<>();
|
||||
for (Contact c : getSharedWith()) {
|
||||
LocalAuthor localAuthor = identityManager
|
||||
.getLocalAuthor(c.getLocalAuthorId());
|
||||
@@ -146,11 +151,11 @@ abstract class SharingStatusActivity extends BriarActivity {
|
||||
groupId, new GroupCount(0, 0, 0));
|
||||
contactItems.add(item);
|
||||
}
|
||||
displaySharedWith(contactItems);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
displaySharedWith(contactItems);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -159,11 +164,8 @@ abstract class SharingStatusActivity extends BriarActivity {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (contacts.isEmpty()) {
|
||||
sharedWithList.showData();
|
||||
} else {
|
||||
sharedWithAdapter.addAll(contacts);
|
||||
}
|
||||
if (contacts.isEmpty()) sharedWithList.showData();
|
||||
else sharedWithAdapter.addAll(contacts);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.support.annotation.UiThread;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
||||
import org.briarproject.android.util.VersionedAdapter;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -16,21 +17,23 @@ import java.util.Map;
|
||||
|
||||
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
||||
|
||||
@UiThread
|
||||
public abstract class ThreadItemAdapter<I extends ThreadItem>
|
||||
extends RecyclerView.Adapter<ThreadItemViewHolder<I>> {
|
||||
extends RecyclerView.Adapter<ThreadItemViewHolder<I>>
|
||||
implements VersionedAdapter {
|
||||
|
||||
static final int UNDEFINED = -1;
|
||||
|
||||
private final NestedTreeList<I> items = new NestedTreeList<>();
|
||||
private final Map<I, ValueAnimator> animatingItems = new HashMap<>();
|
||||
private final ThreadItemListener<I> listener;
|
||||
private final LinearLayoutManager layoutManager;
|
||||
|
||||
// highlight not dependant on time
|
||||
private I replyItem;
|
||||
// temporary highlight
|
||||
private I addedItem;
|
||||
|
||||
private final ThreadItemListener<I> listener;
|
||||
private final LinearLayoutManager layoutManager;
|
||||
private volatile int revision = 0;
|
||||
|
||||
public ThreadItemAdapter(ThreadItemListener<I> listener,
|
||||
LinearLayoutManager layoutManager) {
|
||||
@@ -290,6 +293,17 @@ public abstract class ThreadItemAdapter<I extends ThreadItem>
|
||||
animatingItems.remove(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void incrementRevision() {
|
||||
revision++;
|
||||
}
|
||||
|
||||
protected interface ThreadItemListener<I> {
|
||||
|
||||
void onItemVisible(I item);
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static android.support.design.widget.Snackbar.make;
|
||||
import static android.view.View.GONE;
|
||||
@@ -42,6 +43,9 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
protected static final String KEY_INPUT_VISIBILITY = "inputVisibility";
|
||||
protected static final String KEY_REPLY_ID = "replyId";
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ThreadListActivity.class.getName());
|
||||
|
||||
protected A adapter;
|
||||
protected BriarRecyclerView list;
|
||||
protected TextInputView textInput;
|
||||
@@ -75,7 +79,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
|
||||
if (state != null) {
|
||||
byte[] replyIdBytes = state.getByteArray(KEY_REPLY_ID);
|
||||
if(replyIdBytes != null) replyId = new MessageId(replyIdBytes);
|
||||
if (replyIdBytes != null) replyId = new MessageId(replyIdBytes);
|
||||
}
|
||||
|
||||
loadItems();
|
||||
@@ -106,18 +110,24 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
protected abstract void onNamedGroupLoaded(G groupItem);
|
||||
|
||||
private void loadItems() {
|
||||
final int revision = adapter.getRevision();
|
||||
getController().loadItems(
|
||||
new UiResultExceptionHandler<Collection<I>, DbException>(
|
||||
this) {
|
||||
new UiResultExceptionHandler<Collection<I>, DbException>(this) {
|
||||
@Override
|
||||
public void onResultUi(Collection<I> items) {
|
||||
if (items.isEmpty()) {
|
||||
list.showData();
|
||||
if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
if (items.isEmpty()) {
|
||||
list.showData();
|
||||
} else {
|
||||
adapter.setItems(items);
|
||||
list.showData();
|
||||
if (replyId != null)
|
||||
adapter.setReplyItemById(replyId);
|
||||
}
|
||||
} else {
|
||||
adapter.setItems(items);
|
||||
list.showData();
|
||||
if (replyId != null)
|
||||
adapter.setReplyItemById(replyId);
|
||||
LOG.info("Concurrent update, reloading");
|
||||
loadItems();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,35 +141,33 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
list.startPeriodicUpdate();
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
list.stopPeriodicUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
textInput.setVisibility(
|
||||
savedInstanceState.getBoolean(KEY_INPUT_VISIBILITY) ?
|
||||
VISIBLE : GONE);
|
||||
boolean visible = savedInstanceState.getBoolean(KEY_INPUT_VISIBILITY);
|
||||
textInput.setVisibility(visible ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(KEY_INPUT_VISIBILITY,
|
||||
textInput.getVisibility() == VISIBLE);
|
||||
boolean visible = textInput.getVisibility() == VISIBLE;
|
||||
outState.putBoolean(KEY_INPUT_VISIBILITY, visible);
|
||||
ThreadItem replyItem = adapter.getReplyItem();
|
||||
if (replyItem != null) {
|
||||
outState.putByteArray(KEY_REPLY_ID,
|
||||
replyItem.getId().getBytes());
|
||||
outState.putByteArray(KEY_REPLY_ID, replyItem.getId().getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,6 +281,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
}
|
||||
|
||||
protected void addItem(final I item, boolean isLocal) {
|
||||
adapter.incrementRevision();
|
||||
adapter.add(item);
|
||||
if (isLocal && adapter.isVisible(item)) {
|
||||
displaySnackbarShort(getItemPostedString());
|
||||
|
||||
@@ -49,8 +49,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
private final EventBus eventBus;
|
||||
private final Clock clock;
|
||||
|
||||
private final Map<MessageId, String> bodyCache =
|
||||
new ConcurrentHashMap<>();
|
||||
private final Map<MessageId, String> bodyCache = new ConcurrentHashMap<>();
|
||||
|
||||
private volatile GroupId groupId;
|
||||
|
||||
@@ -82,14 +81,14 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onActivityResume() {
|
||||
public void onActivityStart() {
|
||||
notificationManager.blockNotification(getGroupId());
|
||||
eventBus.addListener(this);
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onActivityPause() {
|
||||
public void onActivityStop() {
|
||||
notificationManager.unblockNotification(getGroupId());
|
||||
eventBus.removeListener(this);
|
||||
}
|
||||
@@ -127,8 +126,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
G groupItem = loadNamedGroup();
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info(
|
||||
"Loading named group took " + duration + " ms");
|
||||
LOG.info("Loading group took " + duration + " ms");
|
||||
handler.onResult(groupItem);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
@@ -149,7 +147,6 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LOG.info("Loading items...");
|
||||
try {
|
||||
// Load headers
|
||||
long now = System.currentTimeMillis();
|
||||
@@ -193,8 +190,8 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LOG.info("Loading item...");
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
String body;
|
||||
if (!bodyCache.containsKey(header.getId())) {
|
||||
body = loadMessageBody(header.getId());
|
||||
@@ -202,6 +199,9 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
} else {
|
||||
body = bodyCache.get(header.getId());
|
||||
}
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading item took " + duration + " ms");
|
||||
I item = buildItem(header, body);
|
||||
handler.onResult(item);
|
||||
} catch (DbException e) {
|
||||
@@ -250,12 +250,16 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
LocalAuthor author = identityManager.getLocalAuthor();
|
||||
long timestamp = getLatestTimestamp();
|
||||
timestamp =
|
||||
Math.max(timestamp, clock.currentTimeMillis());
|
||||
createMessage(body, timestamp, parentId, author,
|
||||
handler);
|
||||
timestamp = Math.max(timestamp, clock.currentTimeMillis());
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Loading identity and timestamp took " +
|
||||
duration + " ms");
|
||||
}
|
||||
createMessage(body, timestamp, parentId, author, handler);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
@@ -274,8 +278,11 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
cryptoExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LOG.info("Creating message...");
|
||||
long now = System.currentTimeMillis();
|
||||
M msg = createLocalMessage(body, timestamp, parentId, author);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Creating message took " + duration + " ms");
|
||||
storePost(msg, body, handler);
|
||||
}
|
||||
});
|
||||
@@ -291,7 +298,6 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
LOG.info("Store message...");
|
||||
long now = System.currentTimeMillis();
|
||||
H header = addLocalMessage(msg);
|
||||
bodyCache.put(msg.getMessage().getId(), body);
|
||||
@@ -354,10 +360,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
}
|
||||
|
||||
private void checkGroupId() {
|
||||
if (groupId == null) {
|
||||
throw new IllegalStateException(
|
||||
"You must set the GroupId before the controller is started.");
|
||||
}
|
||||
if (groupId == null) throw new IllegalStateException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.util.SortedList;
|
||||
import android.support.v7.widget.RecyclerView.Adapter;
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
@@ -11,11 +12,13 @@ import java.util.Collection;
|
||||
import static android.support.v7.util.SortedList.INVALID_POSITION;
|
||||
|
||||
public abstract class BriarAdapter<T, V extends ViewHolder>
|
||||
extends Adapter<V> {
|
||||
extends Adapter<V> implements VersionedAdapter {
|
||||
|
||||
protected final Context ctx;
|
||||
protected final SortedList<T> items;
|
||||
|
||||
private volatile int revision = 0;
|
||||
|
||||
public BriarAdapter(Context ctx, Class<T> c) {
|
||||
this.ctx = ctx;
|
||||
this.items = new SortedList<>(c, new SortedList.Callback<T>() {
|
||||
@@ -75,6 +78,13 @@ public abstract class BriarAdapter<T, V extends ViewHolder>
|
||||
this.items.addAll(items);
|
||||
}
|
||||
|
||||
public void setItems(Collection<T> items) {
|
||||
this.items.beginBatchedUpdates();
|
||||
this.items.clear();
|
||||
this.items.addAll(items);
|
||||
this.items.endBatchedUpdates();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T getItemAt(int position) {
|
||||
if (position == INVALID_POSITION || position >= items.size()) {
|
||||
@@ -103,4 +113,14 @@ public abstract class BriarAdapter<T, V extends ViewHolder>
|
||||
return items.size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void incrementRevision() {
|
||||
revision++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.briarproject.android.util;
|
||||
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
public interface VersionedAdapter {
|
||||
|
||||
/**
|
||||
* Returns the adapter's revision counter. This method should be called on
|
||||
* any thread before starting an asynchronous load that could overwrite
|
||||
* other changes to the adapter, and called again on the UI thread before
|
||||
* applying the changes from the asynchronous load. If the revision has
|
||||
* changed between the two calls, the asynchronous load should be restarted
|
||||
* without applying its changes. Otherwise {@link #incrementRevision()}
|
||||
* should be called before applying the changes.
|
||||
*/
|
||||
int getRevision();
|
||||
|
||||
/**
|
||||
* Increments the adapter's revision counter. This method should be called
|
||||
* on the UI thread before applying any changes to the adapter that could
|
||||
* be overwritten by an asynchronous load.
|
||||
*/
|
||||
@UiThread
|
||||
void incrementRevision();
|
||||
|
||||
}
|
||||
@@ -1,17 +1,25 @@
|
||||
package org.briarproject.api.crypto;
|
||||
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
|
||||
/** Annotation for injecting the executor for long-running crypto tasks. */
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Annotation for injecting the executor for long-running crypto tasks. Also
|
||||
* used for annotating methods that should run on the crypto executor.
|
||||
* <p>
|
||||
* The contract of this executor is that tasks may be run concurrently, and
|
||||
* submitting a task will never block. Tasks must not run indefinitely. Tasks
|
||||
* submitted during shutdown are discarded.
|
||||
*/
|
||||
@Qualifier
|
||||
@Target({FIELD, METHOD, PARAMETER})
|
||||
@Retention(RUNTIME)
|
||||
public @interface CryptoExecutor {}
|
||||
public @interface CryptoExecutor {
|
||||
}
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
package org.briarproject.api.db;
|
||||
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Annotation for injecting the executor for database tasks.
|
||||
* Annotation for injecting the executor for database tasks. Also used for
|
||||
* annotating methods that should run on the database executor.
|
||||
* <p>
|
||||
* The contract of this executor is that tasks are executed in the order
|
||||
* they're submitted, tasks are not executed concurrently, and submitting a
|
||||
* task will never block.
|
||||
* The contract of this executor is that tasks are run in the order they're
|
||||
* submitted, tasks are not run concurrently, and submitting a task will never
|
||||
* block. Tasks must not run indefinitely. Tasks submitted during shutdown are
|
||||
* discarded.
|
||||
*/
|
||||
@Qualifier
|
||||
@Target({ FIELD, METHOD, PARAMETER })
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.api.event;
|
||||
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
@@ -9,11 +10,13 @@ import org.briarproject.api.sync.GroupId;
|
||||
public class PrivateMessageReceivedEvent extends Event {
|
||||
|
||||
private final PrivateMessageHeader messageHeader;
|
||||
private final ContactId contactId;
|
||||
private final GroupId groupId;
|
||||
|
||||
public PrivateMessageReceivedEvent(PrivateMessageHeader messageHeader,
|
||||
GroupId groupId) {
|
||||
ContactId contactId, GroupId groupId) {
|
||||
this.messageHeader = messageHeader;
|
||||
this.contactId = contactId;
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
@@ -21,6 +24,10 @@ public class PrivateMessageReceivedEvent extends Event {
|
||||
return messageHeader;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
public GroupId getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
package org.briarproject.api.lifecycle;
|
||||
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
|
||||
/** Annotation for injecting the executor used by long-lived IO tasks. */
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Annotation for injecting the executor for long-running IO tasks. Also used
|
||||
* for annotating methods that should run on the UI executor.
|
||||
* <p>
|
||||
* The contract of this executor is that tasks may be run concurrently, and
|
||||
* submitting a task will never block. Tasks may run indefinitely. Tasks
|
||||
* submitted during shutdown are discarded.
|
||||
*/
|
||||
@Qualifier
|
||||
@Target({ FIELD, METHOD, PARAMETER })
|
||||
@Target({FIELD, METHOD, PARAMETER})
|
||||
@Retention(RUNTIME)
|
||||
public @interface IoExecutor {}
|
||||
public @interface IoExecutor {
|
||||
}
|
||||
@@ -102,10 +102,11 @@ class MessagingManagerImpl extends ConversationClientImpl
|
||||
boolean local = meta.getBoolean("local");
|
||||
boolean read = meta.getBoolean(MSG_KEY_READ);
|
||||
PrivateMessageHeader header = new PrivateMessageHeader(
|
||||
m.getId(), m.getGroupId(), timestamp, contentType, local, read,
|
||||
m.getId(), groupId, timestamp, contentType, local, read,
|
||||
false, false);
|
||||
ContactId contactId = getContactId(txn, groupId);
|
||||
PrivateMessageReceivedEvent event = new PrivateMessageReceivedEvent(
|
||||
header, groupId);
|
||||
header, contactId, groupId);
|
||||
txn.attach(event);
|
||||
trackIncomingMessage(txn, m);
|
||||
|
||||
@@ -133,6 +134,17 @@ class MessagingManagerImpl extends ConversationClientImpl
|
||||
}
|
||||
}
|
||||
|
||||
private ContactId getContactId(Transaction txn, GroupId g)
|
||||
throws DbException {
|
||||
try {
|
||||
BdfDictionary meta =
|
||||
clientHelper.getGroupMetadataAsDictionary(txn, g);
|
||||
return new ContactId(meta.getLong("contactId").intValue());
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContactId getContactId(GroupId g) throws DbException {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user