Compare commits

...

11 Commits

Author SHA1 Message Date
Michael Rogers
078534889e Bumped version number for beta release. 2017-08-04 15:16:51 +01:00
Torsten Grote
e92713006a Fix string in Spanish translation 2017-08-04 10:57:43 -03:00
akwizgran
18f43f3bc1 Merge branch '871-rss-feeds-lost' into 'master'
Fix bug where RSS feeds got lost when a fetching error occured

Closes #871

See merge request !583
2017-08-04 13:52:26 +00:00
akwizgran
a4118b40e1 Merge branch 'debug-build-alongside-beta' into 'master'
Make debug builds installable alongside official beta build

See merge request !582
2017-08-02 16:54:25 +00:00
Torsten Grote
de29fbc324 Fix bug where RSS feeds got lost when a fetching error occured 2017-08-01 15:32:51 -03:00
Torsten Grote
3197dcf9b5 Merge branch 'checked-camera-exceptions' into 'master'
Throw checked exceptions for camera errors

See merge request !580
2017-08-01 16:54:45 +00:00
akwizgran
35aad409fd Merge branch '994-notification-sound-delay' into 'master'
Always play a notification sound, if at least 2sec after last one

Closes #994

See merge request !581
2017-08-01 16:20:35 +00:00
Torsten Grote
08ce6a7331 Change app name for debug builds 2017-08-01 13:08:12 -03:00
Torsten Grote
33a0099065 Make debug builds installable alongside official beta build 2017-08-01 12:57:11 -03:00
Torsten Grote
34d20fafda Always play a notification sound, if at least 2sec after last one
This is the same behavior as Signal.
We might want to adjust the delay later on.

This is also introduces a new BriarNotificationBuilder as a first step
to clean up the Notification Manager code.
2017-08-01 12:47:11 -03:00
akwizgran
0d6983b4ef Throw checked exceptions for camera errors. 2017-08-01 15:56:20 +01:00
11 changed files with 382 additions and 155 deletions

View File

@@ -12,8 +12,8 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
versionCode 14
versionName "0.14"
versionCode 1609
versionName "0.16.9"
consumerProguardFiles 'proguard-rules.txt'
}

View File

@@ -78,15 +78,19 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
versionCode 1608
versionName "0.16.8"
versionCode 1609
versionName "0.16.9"
applicationId "org.briarproject.briar.beta"
resValue "string", "app_package", "org.briarproject.briar.beta"
resValue "string", "app_name", "Briar Beta"
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
}
buildTypes {
debug {
applicationIdSuffix ".debug"
resValue "string", "app_package", "org.briarproject.briar.beta.debug"
resValue "string", "app_name", "Briar Debug"
shrinkResources false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'

View File

@@ -5,11 +5,8 @@ import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.UiThread;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.support.v4.content.ContextCompat;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DatabaseExecutor;
@@ -25,12 +22,14 @@ import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.ConversationActivity;
import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
import org.briarproject.briar.android.util.BriarNotificationBuilder;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent;
@@ -48,6 +47,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
@@ -61,8 +61,6 @@ import static android.content.Context.NOTIFICATION_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
import static android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.contact.ConversationActivity.CONTACT_ID;
@@ -95,6 +93,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private static final String BLOG_URI =
"content://org.briarproject.briar/blog";
private static final long SOUND_DELAY = TimeUnit.SECONDS.toMillis(2);
private static final Logger LOG =
Logger.getLogger(AndroidNotificationManagerImpl.class.getName());
@@ -102,6 +102,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private final SettingsManager settingsManager;
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final Clock clock;
private final AtomicBoolean used = new AtomicBoolean(false);
// The following must only be accessed on the main UI thread
@@ -117,16 +118,18 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private boolean blockContacts = false, blockGroups = false;
private boolean blockForums = false, blockBlogs = false;
private boolean blockIntroductions = false;
private long lastSound = 0;
private volatile Settings settings = new Settings();
@Inject
AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor,
SettingsManager settingsManager, AndroidExecutor androidExecutor,
Application app) {
Application app, Clock clock) {
this.dbExecutor = dbExecutor;
this.settingsManager = settingsManager;
this.androidExecutor = androidExecutor;
this.clock = clock;
appContext = app.getApplicationContext();
}
@@ -288,22 +291,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (contactTotal == 0) {
clearContactNotification();
} else if (settings.getBoolean(PREF_NOTIFY_PRIVATE, true)) {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_private_message);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.private_message_notification_text, contactTotal,
contactTotal));
boolean sound = settings.getBoolean(PREF_NOTIFY_SOUND, true);
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (sound && !StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
b.setNumber(contactTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
playSound(b);
if (contactCounts.size() == 1) {
// Touching the notification shows the relevant conversation
Intent i = new Intent(appContext, ConversationActivity.class);
@@ -326,21 +326,27 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(PRIVATE_MESSAGE_NOTIFICATION_ID, b.build());
}
}
@UiThread
private void playSound(BriarNotificationBuilder b) {
boolean sound = settings.getBoolean(PREF_NOTIFY_SOUND, true);
if (!sound) return;
long currentTime = clock.currentTimeMillis();
if (currentTime - lastSound > SOUND_DELAY) {
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
lastSound = clock.currentTimeMillis();
}
}
@UiThread
private int getDefaults() {
int defaults = DEFAULT_LIGHTS;
@@ -387,21 +393,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (groupTotal == 0) {
clearGroupMessageNotification();
} else if (settings.getBoolean(PREF_NOTIFY_GROUP, true)) {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_private_group);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.group_message_notification_text, groupTotal,
groupTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
b.setNumber(groupTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
playSound(b);
if (groupCounts.size() == 1) {
// Touching the notification shows the relevant group
Intent i = new Intent(appContext, GroupActivity.class);
@@ -425,15 +429,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(GROUP_MESSAGE_NOTIFICATION_ID, b.build());
@@ -474,21 +469,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (forumTotal == 0) {
clearForumPostNotification();
} else if (settings.getBoolean(PREF_NOTIFY_FORUM, true)) {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_forum);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.forum_post_notification_text, forumTotal,
forumTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
b.setNumber(forumTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
playSound(b);
if (forumCounts.size() == 1) {
// Touching the notification shows the relevant forum
Intent i = new Intent(appContext, ForumActivity.class);
@@ -512,15 +505,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(FORUM_POST_NOTIFICATION_ID, b.build());
@@ -561,21 +545,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (blogTotal == 0) {
clearBlogPostNotification();
} else if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_blog);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.blog_post_notification_text, blogTotal,
blogTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
b.setNumber(blogTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
playSound(b);
// Touching the notification shows the combined blog feed
Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_BLOGS, true);
@@ -585,15 +567,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addParentStack(NavDrawerActivity.class);
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(BLOG_POST_NOTIFICATION_ID, b.build());
@@ -623,20 +597,17 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
@UiThread
private void updateIntroductionNotification() {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
BriarNotificationBuilder b = new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_introduction);
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.introduction_notification_text, introductionTotal,
introductionTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
playSound(b);
// Touching the notification shows the contact list
Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_CONTACTS, true);
@@ -646,15 +617,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addParentStack(NavDrawerActivity.class);
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build());

View File

@@ -0,0 +1,14 @@
package org.briarproject.briar.android.keyagreement;
import java.io.IOException;
class CameraException extends IOException {
CameraException(String message) {
super(message);
}
CameraException(Throwable cause) {
super(cause);
}
}

View File

@@ -84,16 +84,14 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
}
@UiThread
public void start() {
public void start() throws CameraException {
LOG.info("Opening camera");
try {
LOG.info("Opening camera");
camera = Camera.open();
if (camera == null)
throw new RuntimeException("No back-facing camera.");
} catch (RuntimeException e) {
LOG.log(WARNING, "Error opening camera", e);
return;
throw new CameraException(e);
}
if (camera == null) throw new CameraException("No back-facing camera");
setDisplayOrientation(0);
// Use barcode scene mode if it's available
Parameters params = camera.getParameters();
@@ -117,64 +115,81 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
}
@UiThread
public void stop() {
public void stop() throws CameraException {
if (camera == null) return;
stopPreview();
LOG.info("Releasing camera");
try {
LOG.info("Releasing camera");
camera.release();
} catch (RuntimeException e) {
LOG.log(WARNING, "Error releasing camera", e);
throw new CameraException(e);
}
camera = null;
}
@UiThread
private void startPreview(SurfaceHolder holder) {
private void startPreview(SurfaceHolder holder) throws CameraException {
LOG.info("Starting preview");
if (camera == null) throw new CameraException("Camera is null");
try {
if (camera == null) throw new IOException("Camera is null.");
camera.setPreviewDisplay(holder);
camera.startPreview();
previewStarted = true;
startConsumer();
} catch (IOException | RuntimeException e) {
LOG.log(WARNING, "Error starting camera preview", e);
throw new CameraException(e);
}
}
@UiThread
private void stopPreview() {
private void stopPreview() throws CameraException {
LOG.info("Stopping preview");
if (camera == null) throw new CameraException("Camera is null");
try {
if (camera == null) throw new RuntimeException("Camera is null.");
stopConsumer();
camera.stopPreview();
} catch (RuntimeException e) {
LOG.log(WARNING, "Error stopping camera preview", e);
throw new CameraException(e);
}
previewStarted = false;
}
@UiThread
private void startConsumer() {
if (camera == null) throw new RuntimeException("Camera is null");
if (autoFocus) camera.autoFocus(this);
private void startConsumer() throws CameraException {
if (camera == null) throw new CameraException("Camera is null");
if (autoFocus) {
try {
camera.autoFocus(this);
} catch (RuntimeException e) {
throw new CameraException(e);
}
}
previewConsumer.start(camera);
}
@UiThread
private void stopConsumer() {
if (camera == null) throw new RuntimeException("Camera is null");
if (autoFocus) camera.cancelAutoFocus();
private void stopConsumer() throws CameraException {
if (camera == null) throw new CameraException("Camera is null");
if (autoFocus) {
try {
camera.cancelAutoFocus();
} catch (RuntimeException e) {
throw new CameraException(e);
}
}
previewConsumer.stop();
}
@UiThread
private void setDisplayOrientation(int rotationDegrees) {
private void setDisplayOrientation(int rotationDegrees)
throws CameraException {
int orientation;
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(0, info);
try {
Camera.getCameraInfo(0, info);
} catch (RuntimeException e) {
throw new CameraException(e);
}
if (info.facing == CAMERA_FACING_FRONT) {
orientation = (info.orientation + rotationDegrees) % 360;
orientation = (360 - orientation) % 360;
@@ -183,54 +198,70 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
}
if (LOG.isLoggable(INFO))
LOG.info("Display orientation " + orientation + " degrees");
if (camera == null) throw new CameraException("Camera is null");
try {
if (camera == null) throw new RuntimeException("Camera is null");
camera.setDisplayOrientation(orientation);
} catch (RuntimeException e) {
LOG.log(WARNING, "Error setting display orientation", e);
throw new CameraException(e);
}
displayOrientation = orientation;
}
@UiThread
private Parameters setSceneMode(Camera camera, Parameters params) {
private Parameters setSceneMode(Camera camera, Parameters params)
throws CameraException {
List<String> sceneModes = params.getSupportedSceneModes();
if (sceneModes == null) return params;
if (LOG.isLoggable(INFO)) LOG.info("Scene modes: " + sceneModes);
if (sceneModes.contains(SCENE_MODE_BARCODE)) {
params.setSceneMode(SCENE_MODE_BARCODE);
camera.setParameters(params);
return camera.getParameters();
try {
camera.setParameters(params);
return camera.getParameters();
} catch (RuntimeException e) {
throw new CameraException(e);
}
}
return params;
}
@UiThread
private Parameters disableFlash(Camera camera, Parameters params) {
private Parameters disableFlash(Camera camera, Parameters params)
throws CameraException {
params.setFlashMode(FLASH_MODE_OFF);
camera.setParameters(params);
return camera.getParameters();
try {
camera.setParameters(params);
return camera.getParameters();
} catch (RuntimeException e) {
throw new CameraException(e);
}
}
@UiThread
private Parameters disableSceneMode(Camera camera, Parameters params) {
private Parameters disableSceneMode(Camera camera, Parameters params)
throws CameraException {
params.setSceneMode(SCENE_MODE_AUTO);
camera.setParameters(params);
return camera.getParameters();
try {
camera.setParameters(params);
return camera.getParameters();
} catch (RuntimeException e) {
throw new CameraException(e);
}
}
@UiThread
private Parameters setBestParameters(Camera camera, Parameters params) {
private Parameters setBestParameters(Camera camera, Parameters params)
throws CameraException {
setVideoStabilisation(params);
setFocusMode(params);
params.setFlashMode(FLASH_MODE_OFF);
setPreviewSize(params);
try {
camera.setParameters(params);
return camera.getParameters();
} catch (RuntimeException e) {
LOG.log(WARNING, "Error setting best camera parameters", e);
throw new CameraException(e);
}
return camera.getParameters();
}
@UiThread
@@ -299,9 +330,15 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
}
@UiThread
private void logCameraParameters() {
if (camera != null && LOG.isLoggable(INFO)) {
Parameters params = camera.getParameters();
private void logCameraParameters() throws CameraException {
if (camera == null) throw new AssertionError();
if (LOG.isLoggable(INFO)) {
Parameters params;
try {
params = camera.getParameters();
} catch (RuntimeException e) {
throw new CameraException(e);
}
if (Build.VERSION.SDK_INT >= 15) {
LOG.info("Video stabilisation enabled: "
+ params.getVideoStabilization());
@@ -319,13 +356,18 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
post(new Runnable() {
@Override
public void run() {
surfaceCreatedUi(holder);
try {
surfaceCreatedUi(holder);
} catch (CameraException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
@UiThread
private void surfaceCreatedUi(SurfaceHolder holder) {
private void surfaceCreatedUi(SurfaceHolder holder) throws CameraException {
LOG.info("Surface created");
if (surface != null && surface != holder.getSurface()) {
LOG.info("Releasing old surface");
@@ -342,13 +384,19 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
post(new Runnable() {
@Override
public void run() {
surfaceChangedUi(holder, w, h);
try {
surfaceChangedUi(holder, w, h);
} catch (CameraException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
@UiThread
private void surfaceChangedUi(SurfaceHolder holder, int w, int h) {
private void surfaceChangedUi(SurfaceHolder holder, int w, int h)
throws CameraException {
if (LOG.isLoggable(INFO)) LOG.info("Surface changed: " + w + "x" + h);
if (surface != null && surface != holder.getSurface()) {
LOG.info("Releasing old surface");
@@ -365,7 +413,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
camera.setParameters(params);
logCameraParameters();
} catch (RuntimeException e) {
LOG.log(WARNING, "Error setting preview size", e);
throw new CameraException(e);
}
startPreview(holder);
}

View File

@@ -56,6 +56,7 @@ import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_LONG;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -143,6 +144,12 @@ public class ShowQrCodeFragment extends BaseEventFragment
public void onStart() {
super.onStart();
try {
cameraView.start();
} catch (CameraException e) {
logCameraExceptionAndFinish(e);
}
// Listen for changes to the Bluetooth state
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_STATE_CHANGED);
@@ -162,7 +169,6 @@ public class ShowQrCodeFragment extends BaseEventFragment
} else {
startListening();
}
cameraView.start();
}
@Override
@@ -170,7 +176,19 @@ public class ShowQrCodeFragment extends BaseEventFragment
super.onStop();
stopListening();
if (receiver != null) getActivity().unregisterReceiver(receiver);
cameraView.stop();
try {
cameraView.stop();
} catch (CameraException e) {
logCameraExceptionAndFinish(e);
}
}
@UiThread
private void logCameraExceptionAndFinish(CameraException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
Toast.makeText(getActivity(), R.string.camera_error,
LENGTH_LONG).show();
finish();
}
@UiThread

View File

@@ -0,0 +1,38 @@
package org.briarproject.briar.android.util;
import android.content.Context;
import android.os.Build;
import android.support.annotation.ColorRes;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.NotificationCompat;
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
import static android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
public class BriarNotificationBuilder extends NotificationCompat.Builder {
public BriarNotificationBuilder(Context context) {
super(context);
setAutoCancel(true);
}
public BriarNotificationBuilder setColorRes(@ColorRes int res) {
setColor(ContextCompat.getColor(mContext, res));
return this;
}
public BriarNotificationBuilder setLockscreenVisibility(String category,
boolean show) {
if (Build.VERSION.SDK_INT >= 21) {
setCategory(category);
if (show)
setVisibility(VISIBILITY_PRIVATE);
else
setVisibility(VISIBILITY_SECRET);
}
return this;
}
}

View File

@@ -122,7 +122,7 @@
<string name="introduction_activity_title">Seleccionar contacto</string>
<string name="introduction_message_title">Presentar a dos contactos</string>
<string name="introduction_message_hint">Añade un mensaje (opcional)</string>
<string name="introduction_button">Presentarle a</string>
<string name="introduction_button">Presentar contactos</string>
<string name="introduction_sent">Tu presentación se ha mandado.</string>
<string name="introduction_error">Ocurrió un error realizando la presentación.</string>
<string name="introduction_response_error">Error al responder a la presentación</string>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string translatable="false" name="app_name">Briar Beta</string>
<!-- Setup -->
<string name="setup_title">Briar Setup</string>
@@ -121,6 +120,7 @@
<string name="contact_already_exists">Contact %s already exists</string>
<string name="contact_exchange_failed">Contact exchange failed</string>
<string name="qr_code_invalid">The QR code is invalid</string>
<string name="camera_error">Camera error</string>
<string name="connecting_to_device">Connecting to device\u2026</string>
<string name="authenticating_with_device">Authenticating with device\u2026</string>
<string name="connection_aborted_local">Connection aborted by us! This could mean that someone is trying to interfere with your connection</string>

View File

@@ -291,8 +291,12 @@ class FeedManagerImpl implements FeedManager, Client, EventListener,
* This method is called periodically from a background service.
* It fetches all available feeds and posts new entries to the respective
* blog.
*
* We can not do this within one database {@link Transaction},
* because fetching can take a long time
* and we can not block the database that long.
*/
private void fetchFeeds() {
void fetchFeeds() {
LOG.info("Updating RSS feeds...");
// Get current feeds
@@ -313,12 +317,15 @@ class FeedManagerImpl implements FeedManager, Client, EventListener,
} catch (FeedException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
newFeeds.add(feed);
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
newFeeds.add(feed);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
newFeeds.add(feed);
}
}

View File

@@ -0,0 +1,135 @@
package org.briarproject.briar.feed;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPostFactory;
import org.briarproject.briar.api.feed.Feed;
import org.jmock.Expectations;
import org.junit.Test;
import java.net.UnknownHostException;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.SocketFactory;
import okhttp3.Dns;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEEDS;
import static org.briarproject.briar.api.feed.FeedManager.CLIENT_ID;
public class FeedManagerImplTest extends BrambleMockTestCase {
private final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
private final Executor ioExecutor = new ImmediateExecutor();
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final ContactGroupFactory contactGroupFactory =
context.mock(ContactGroupFactory.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
private final BlogManager blogManager = context.mock(BlogManager.class);
private final BlogPostFactory blogPostFactory =
context.mock(BlogPostFactory.class);
private final FeedFactory feedFactory = context.mock(FeedFactory.class);
private final Clock clock = context.mock(Clock.class);
private final Dns noDnsLookups = context.mock(Dns.class);
private final GroupId localGroupId = new GroupId(getRandomId());
private final Group localGroup =
new Group(localGroupId, CLIENT_ID, getRandomBytes(42));
private final GroupId blogGroupId = new GroupId(getRandomId());
private final Group blogGroup =
new Group(blogGroupId, BlogManager.CLIENT_ID, getRandomBytes(42));
private final AuthorId authorId = new AuthorId(getRandomId());
private final LocalAuthor localAuthor =
new LocalAuthor(authorId, "author", getRandomBytes(2),
getRandomBytes(2), 0);
private final Blog blog = new Blog(blogGroup, localAuthor, true);
private final Feed feed =
new Feed("http://example.org", blog, localAuthor, 0);
private final BdfDictionary feedDict = new BdfDictionary();
private final FeedManagerImpl feedManager =
new FeedManagerImpl(scheduler, ioExecutor, db, contactGroupFactory,
clientHelper, blogManager, blogPostFactory, feedFactory,
SocketFactory.getDefault(), clock, noDnsLookups);
@Test
public void testEmptyFetchFeed() throws Exception {
BdfList feedList = new BdfList();
expectGetFeeds(feedList);
expectStoreFeed(feedList);
feedManager.fetchFeeds();
}
@Test
public void testFetchFeedIoException() throws Exception {
final BdfDictionary feedDict= new BdfDictionary();
BdfList feedList = BdfList.of(feedDict);
expectGetFeeds(feedList);
context.checking(new Expectations() {{
oneOf(noDnsLookups).lookup("example.org");
will(throwException(new UnknownHostException()));
}});
expectStoreFeed(feedList);
feedManager.fetchFeeds();
}
private void expectGetLocalGroup() {
context.checking(new Expectations() {{
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID);
will(returnValue(localGroup));
}});
}
private void expectGetFeeds(final BdfList feedList) throws Exception {
final Transaction txn = new Transaction(null, true);
final BdfDictionary feedsDict =
BdfDictionary.of(new BdfEntry(KEY_FEEDS, feedList));
expectGetLocalGroup();
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn, localGroupId);
will(returnValue(feedsDict));
if (feedList.size() == 1) {
oneOf(feedFactory).createFeed(feedDict);
will(returnValue(feed));
}
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
}
private void expectStoreFeed(final BdfList feedList) throws Exception {
final BdfDictionary feedDict =
BdfDictionary.of(new BdfEntry(KEY_FEEDS, feedList));
expectGetLocalGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).mergeGroupMetadata(localGroupId, feedDict);
if (feedList.size() == 1) {
oneOf(feedFactory).feedToBdfDictionary(feed);
will(returnValue(feedList.getDictionary(0)));
}
}});
}
}