Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
briar
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
681
Issues
681
List
Boards
Labels
Service Desk
Milestones
Merge Requests
16
Merge Requests
16
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
briar
briar
Commits
4796902b
Verified
Commit
4796902b
authored
Nov 23, 2018
by
Torsten Grote
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[android] store attachments and actually attach them to sent messages
parent
cdf4f3a2
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
207 additions
and
62 deletions
+207
-62
briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentController.java
...ject/briar/android/conversation/AttachmentController.java
+21
-2
briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java
...ject/briar/android/conversation/ConversationActivity.java
+9
-55
briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java
...ect/briar/android/conversation/ConversationViewModel.java
+155
-1
briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
...ain/java/org/briarproject/briar/android/util/UiUtils.java
+18
-0
briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingManager.java
...rg/briarproject/briar/api/messaging/MessagingManager.java
+2
-2
briar-core/src/main/java/org/briarproject/briar/messaging/MessagingManagerImpl.java
...rg/briarproject/briar/messaging/MessagingManagerImpl.java
+2
-2
No files found.
briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentController.java
View file @
4796902b
...
...
@@ -90,10 +90,16 @@ class AttachmentController {
return
attachments
;
}
/**
* Creates {@link AttachmentItem}s from the passed headers and Attachments.
* Note: This marks the {@link Attachment}'s {@link InputStream}
* and closes the streams.
*/
List
<
AttachmentItem
>
getAttachmentItems
(
List
<
Pair
<
AttachmentHeader
,
Attachment
>>
attachments
)
{
List
<
AttachmentItem
>
items
=
new
ArrayList
<>(
attachments
.
size
());
for
(
Pair
<
AttachmentHeader
,
Attachment
>
a
:
attachments
)
{
a
.
getSecond
().
getStream
().
mark
(
Integer
.
MAX_VALUE
);
AttachmentItem
item
=
getAttachmentItem
(
a
.
getFirst
(),
a
.
getSecond
());
items
.
add
(
item
);
...
...
@@ -101,12 +107,18 @@ class AttachmentController {
return
items
;
}
private
AttachmentItem
getAttachmentItem
(
AttachmentHeader
h
,
Attachment
a
)
{
/**
* Creates an {@link AttachmentItem} from the {@link Attachment}'s
* {@link InputStream}.
* Note: Requires a resettable InputStream
* with the beginning already marked.
* The stream will be closed when this method returns.
*/
AttachmentItem
getAttachmentItem
(
AttachmentHeader
h
,
Attachment
a
)
{
MessageId
messageId
=
h
.
getMessageId
();
Size
size
=
new
Size
();
InputStream
is
=
a
.
getStream
();
is
.
mark
(
Integer
.
MAX_VALUE
);
try
{
// use exif to get size
if
(
h
.
getContentType
().
equals
(
"image/jpeg"
))
{
...
...
@@ -178,6 +190,13 @@ class AttachmentController {
options
.
outMimeType
);
}
static
String
getContentTypeFromBitmap
(
InputStream
is
)
{
BitmapFactory
.
Options
options
=
new
BitmapFactory
.
Options
();
options
.
inJustDecodeBounds
=
true
;
BitmapFactory
.
decodeStream
(
is
,
null
,
options
);
return
options
.
outMimeType
;
}
private
Size
getThumbnailSize
(
int
width
,
int
height
,
String
mimeType
)
{
float
widthPercentage
=
maxWidth
/
(
float
)
width
;
float
heightPercentage
=
maxHeight
/
(
float
)
height
;
...
...
briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java
View file @
4796902b
...
...
@@ -29,7 +29,6 @@ import android.widget.ImageView;
import
android.widget.TextView
;
import
android.widget.Toast
;
import
org.briarproject.bramble.api.FormatException
;
import
org.briarproject.bramble.api.Pair
;
import
org.briarproject.bramble.api.contact.ContactId
;
import
org.briarproject.bramble.api.contact.ContactManager
;
...
...
@@ -49,7 +48,6 @@ import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import
org.briarproject.bramble.api.settings.Settings
;
import
org.briarproject.bramble.api.settings.SettingsManager
;
import
org.briarproject.bramble.api.sync.GroupId
;
import
org.briarproject.bramble.api.sync.Message
;
import
org.briarproject.bramble.api.sync.MessageId
;
import
org.briarproject.bramble.api.sync.event.MessagesAckedEvent
;
import
org.briarproject.bramble.api.sync.event.MessagesSentEvent
;
...
...
@@ -83,7 +81,6 @@ import org.briarproject.briar.api.introduction.IntroductionManager;
import
org.briarproject.briar.api.messaging.Attachment
;
import
org.briarproject.briar.api.messaging.AttachmentHeader
;
import
org.briarproject.briar.api.messaging.MessagingManager
;
import
org.briarproject.briar.api.messaging.PrivateMessage
;
import
org.briarproject.briar.api.messaging.PrivateMessageFactory
;
import
org.briarproject.briar.api.messaging.PrivateMessageHeader
;
import
org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager
;
...
...
@@ -196,8 +193,6 @@ public class ConversationActivity extends BriarActivity
ViewModelProvider
.
Factory
viewModelFactory
;
private
volatile
ContactId
contactId
;
@Nullable
private
volatile
GroupId
messagingGroupId
;
private
final
Observer
<
String
>
contactNameObserver
=
name
->
{
requireNonNull
(
name
);
...
...
@@ -245,6 +240,8 @@ public class ConversationActivity extends BriarActivity
requireNonNull
(
deleted
);
if
(
deleted
)
finish
();
});
viewModel
.
getAddedPrivateMessage
()
.
observe
(
this
,
this
::
onAddedPrivateMessage
);
setTransitionName
(
toolbarAvatar
,
getAvatarTransitionName
(
contactId
));
setTransitionName
(
toolbarStatus
,
getBulbTransitionName
(
contactId
));
...
...
@@ -600,16 +597,11 @@ public class ConversationActivity extends BriarActivity
@Override
public
void
onSendClick
(
@Nullable
String
text
,
List
<
Uri
>
imageUris
)
{
if
(!
imageUris
.
isEmpty
())
{
Toast
.
makeText
(
this
,
"Not yet implemented."
,
LENGTH_LONG
).
show
();
textInputView
.
clearText
();
return
;
}
if
(
isNullOrEmpty
(
text
))
throw
new
AssertionError
();
if
(
isNullOrEmpty
(
text
)
&&
imageUris
.
isEmpty
())
throw
new
AssertionError
();
long
timestamp
=
System
.
currentTimeMillis
();
timestamp
=
Math
.
max
(
timestamp
,
getMinTimestampForNewMessage
());
if
(
messagingGroupId
==
null
)
loadGroupId
(
text
,
timestamp
);
else
createMessage
(
text
,
timestamp
);
viewModel
.
sendMessage
(
text
,
imageUris
,
timestamp
);
textInputView
.
clearText
();
}
...
...
@@ -619,48 +611,10 @@ public class ConversationActivity extends BriarActivity
return
item
==
null
?
0
:
item
.
getTime
()
+
1
;
}
private
void
loadGroupId
(
String
text
,
long
timestamp
)
{
runOnDbThread
(()
->
{
try
{
messagingGroupId
=
messagingManager
.
getConversationId
(
contactId
);
createMessage
(
text
,
timestamp
);
}
catch
(
DbException
e
)
{
logException
(
LOG
,
WARNING
,
e
);
}
});
}
private
void
createMessage
(
String
text
,
long
timestamp
)
{
cryptoExecutor
.
execute
(()
->
{
try
{
//noinspection ConstantConditions init in loadGroupId()
storeMessage
(
privateMessageFactory
.
createPrivateMessage
(
messagingGroupId
,
timestamp
,
text
,
emptyList
()),
text
);
}
catch
(
FormatException
e
)
{
throw
new
RuntimeException
(
e
);
}
});
}
private
void
storeMessage
(
PrivateMessage
m
,
String
text
)
{
runOnDbThread
(()
->
{
try
{
long
start
=
now
();
messagingManager
.
addLocalMessage
(
m
);
logDuration
(
LOG
,
"Storing message"
,
start
);
Message
message
=
m
.
getMessage
();
PrivateMessageHeader
h
=
new
PrivateMessageHeader
(
message
.
getId
(),
message
.
getGroupId
(),
message
.
getTimestamp
(),
true
,
false
,
false
,
false
,
true
,
emptyList
());
textCache
.
put
(
message
.
getId
(),
text
);
addConversationItem
(
h
.
accept
(
visitor
));
}
catch
(
DbException
e
)
{
logException
(
LOG
,
WARNING
,
e
);
}
});
private
void
onAddedPrivateMessage
(
@Nullable
PrivateMessageHeader
h
)
{
if
(
h
==
null
)
return
;
addConversationItem
(
h
.
accept
(
visitor
));
viewModel
.
onAddedPrivateMessageSeen
();
}
private
void
askToRemoveContact
()
{
...
...
briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java
View file @
4796902b
...
...
@@ -5,19 +5,37 @@ import android.arch.lifecycle.AndroidViewModel;
import
android.arch.lifecycle.LiveData
;
import
android.arch.lifecycle.MutableLiveData
;
import
android.arch.lifecycle.Transformations
;
import
android.content.ContentResolver
;
import
android.net.Uri
;
import
android.support.annotation.Nullable
;
import
android.support.annotation.UiThread
;
import
org.briarproject.bramble.api.FormatException
;
import
org.briarproject.bramble.api.Pair
;
import
org.briarproject.bramble.api.contact.Contact
;
import
org.briarproject.bramble.api.contact.ContactId
;
import
org.briarproject.bramble.api.contact.ContactManager
;
import
org.briarproject.bramble.api.crypto.CryptoExecutor
;
import
org.briarproject.bramble.api.db.DatabaseExecutor
;
import
org.briarproject.bramble.api.db.DbException
;
import
org.briarproject.bramble.api.db.NoSuchContactException
;
import
org.briarproject.bramble.api.identity.AuthorId
;
import
org.briarproject.bramble.api.nullsafety.NotNullByDefault
;
import
org.briarproject.bramble.api.sync.GroupId
;
import
org.briarproject.bramble.api.sync.Message
;
import
org.briarproject.briar.android.util.UiUtils
;
import
org.briarproject.briar.api.messaging.Attachment
;
import
org.briarproject.briar.api.messaging.AttachmentHeader
;
import
org.briarproject.briar.api.messaging.MessagingManager
;
import
org.briarproject.briar.api.messaging.PrivateMessage
;
import
org.briarproject.briar.api.messaging.PrivateMessageFactory
;
import
org.briarproject.briar.api.messaging.PrivateMessageHeader
;
import
java.io.BufferedInputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.concurrent.Executor
;
import
java.util.logging.Logger
;
...
...
@@ -29,6 +47,8 @@ import static java.util.logging.Logger.getLogger;
import
static
org
.
briarproject
.
bramble
.
util
.
LogUtils
.
logDuration
;
import
static
org
.
briarproject
.
bramble
.
util
.
LogUtils
.
logException
;
import
static
org
.
briarproject
.
bramble
.
util
.
LogUtils
.
now
;
import
static
org
.
briarproject
.
briar
.
android
.
conversation
.
AttachmentController
.
getContentTypeFromBitmap
;
import
static
org
.
briarproject
.
briar
.
android
.
util
.
UiUtils
.
observeForeverOnce
;
@NotNullByDefault
public
class
ConversationViewModel
extends
AndroidViewModel
{
...
...
@@ -38,7 +58,11 @@ public class ConversationViewModel extends AndroidViewModel {
@DatabaseExecutor
private
final
Executor
dbExecutor
;
@CryptoExecutor
private
final
Executor
cryptoExecutor
;
private
final
MessagingManager
messagingManager
;
private
final
ContactManager
contactManager
;
private
final
PrivateMessageFactory
privateMessageFactory
;
private
final
AttachmentController
attachmentController
;
@Nullable
...
...
@@ -50,14 +74,24 @@ public class ConversationViewModel extends AndroidViewModel {
Transformations
.
map
(
contact
,
UiUtils:
:
getContactDisplayName
);
private
final
MutableLiveData
<
Boolean
>
contactDeleted
=
new
MutableLiveData
<>();
private
final
MutableLiveData
<
GroupId
>
messagingGroupId
=
new
MutableLiveData
<>();
private
final
MutableLiveData
<
PrivateMessageHeader
>
addedHeader
=
new
MutableLiveData
<>();
@Inject
ConversationViewModel
(
Application
application
,
@DatabaseExecutor
Executor
dbExecutor
,
ContactManager
contactManager
,
MessagingManager
messagingManager
)
{
@CryptoExecutor
Executor
cryptoExecutor
,
MessagingManager
messagingManager
,
ContactManager
contactManager
,
PrivateMessageFactory
privateMessageFactory
)
{
super
(
application
);
this
.
dbExecutor
=
dbExecutor
;
this
.
cryptoExecutor
=
cryptoExecutor
;
this
.
messagingManager
=
messagingManager
;
this
.
contactManager
=
contactManager
;
this
.
privateMessageFactory
=
privateMessageFactory
;
this
.
attachmentController
=
new
AttachmentController
(
messagingManager
,
application
.
getResources
());
contactDeleted
.
setValue
(
false
);
...
...
@@ -100,6 +134,122 @@ public class ConversationViewModel extends AndroidViewModel {
});
}
void
sendMessage
(
@Nullable
String
text
,
List
<
Uri
>
uris
,
long
timestamp
)
{
if
(
messagingGroupId
.
getValue
()
==
null
)
loadGroupId
();
observeForeverOnce
(
messagingGroupId
,
groupId
->
{
if
(
groupId
==
null
)
return
;
// calls through to creating and storing the message
// (wouldn't Kotlin's co-routines be nice here?)
storeAttachments
(
groupId
,
text
,
uris
,
timestamp
);
});
}
private
void
loadGroupId
()
{
if
(
contactId
==
null
)
throw
new
IllegalStateException
();
dbExecutor
.
execute
(()
->
{
try
{
messagingGroupId
.
postValue
(
messagingManager
.
getConversationId
(
contactId
));
}
catch
(
DbException
e
)
{
logException
(
LOG
,
WARNING
,
e
);
}
});
}
private
void
storeAttachments
(
GroupId
groupId
,
@Nullable
String
text
,
List
<
Uri
>
uris
,
long
timestamp
)
{
dbExecutor
.
execute
(()
->
{
long
start
=
now
();
List
<
AttachmentHeader
>
attachments
=
new
ArrayList
<>();
List
<
AttachmentItem
>
items
=
new
ArrayList
<>();
for
(
Uri
uri
:
uris
)
{
Pair
<
AttachmentHeader
,
AttachmentItem
>
pair
=
createAttachmentHeader
(
groupId
,
uri
,
timestamp
);
if
(
pair
==
null
)
continue
;
attachments
.
add
(
pair
.
getFirst
());
items
.
add
(
pair
.
getSecond
());
}
logDuration
(
LOG
,
"Storing attachments"
,
start
);
createMessage
(
groupId
,
text
,
attachments
,
items
,
timestamp
);
});
}
@Nullable
@DatabaseExecutor
private
Pair
<
AttachmentHeader
,
AttachmentItem
>
createAttachmentHeader
(
GroupId
groupId
,
Uri
uri
,
long
timestamp
)
{
InputStream
is
=
null
;
try
{
ContentResolver
contentResolver
=
getApplication
().
getContentResolver
();
is
=
contentResolver
.
openInputStream
(
uri
);
if
(
is
==
null
)
throw
new
IOException
();
is
=
new
BufferedInputStream
(
is
);
// adds support for reset()
is
.
mark
(
Integer
.
MAX_VALUE
);
String
contentType
=
contentResolver
.
getType
(
uri
);
if
(
contentType
==
null
)
contentType
=
getContentTypeFromBitmap
(
is
);
AttachmentHeader
h
=
messagingManager
.
addLocalAttachment
(
groupId
,
timestamp
,
contentType
,
is
);
AttachmentItem
item
=
attachmentController
.
getAttachmentItem
(
h
,
new
Attachment
(
is
));
return
new
Pair
<>(
h
,
item
);
}
catch
(
DbException
|
IOException
e
)
{
logException
(
LOG
,
WARNING
,
e
);
return
null
;
}
finally
{
if
(
is
!=
null
)
{
try
{
is
.
close
();
}
catch
(
IOException
e
)
{
logException
(
LOG
,
WARNING
,
e
);
}
}
}
}
private
void
createMessage
(
GroupId
groupId
,
@Nullable
String
text
,
List
<
AttachmentHeader
>
attachments
,
List
<
AttachmentItem
>
aItems
,
long
timestamp
)
{
cryptoExecutor
.
execute
(()
->
{
try
{
// TODO remove when text can be null in the backend
String
msgText
=
text
==
null
?
"null"
:
text
;
PrivateMessage
pm
=
privateMessageFactory
.
createPrivateMessage
(
groupId
,
timestamp
,
msgText
,
attachments
);
attachmentController
.
put
(
pm
.
getMessage
().
getId
(),
aItems
);
storeMessage
(
pm
,
msgText
,
attachments
);
}
catch
(
FormatException
e
)
{
throw
new
RuntimeException
(
e
);
}
});
}
private
void
storeMessage
(
PrivateMessage
m
,
@Nullable
String
text
,
List
<
AttachmentHeader
>
attachments
)
{
dbExecutor
.
execute
(()
->
{
try
{
long
start
=
now
();
messagingManager
.
addLocalMessage
(
m
);
logDuration
(
LOG
,
"Storing message"
,
start
);
Message
message
=
m
.
getMessage
();
PrivateMessageHeader
h
=
new
PrivateMessageHeader
(
message
.
getId
(),
message
.
getGroupId
(),
message
.
getTimestamp
(),
true
,
true
,
false
,
false
,
text
!=
null
,
attachments
);
// TODO add text to cache when available here
addedHeader
.
postValue
(
h
);
}
catch
(
DbException
e
)
{
logException
(
LOG
,
WARNING
,
e
);
}
});
}
@UiThread
void
onAddedPrivateMessageSeen
()
{
addedHeader
.
setValue
(
null
);
}
AttachmentController
getAttachmentController
()
{
return
attachmentController
;
}
...
...
@@ -120,4 +270,8 @@ public class ConversationViewModel extends AndroidViewModel {
return
contactDeleted
;
}
LiveData
<
PrivateMessageHeader
>
getAddedPrivateMessage
()
{
return
addedHeader
;
}
}
briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
View file @
4796902b
...
...
@@ -354,4 +354,22 @@ public class UiUtils {
});
}
/**
* Same as {@link #observeOnce(LiveData, LifecycleOwner, Observer)},
* but without a {@link LifecycleOwner}.
*
* Warning: Do NOT call from objects that have a lifecycle.
*/
@MainThread
public
static
<
T
>
void
observeForeverOnce
(
LiveData
<
T
>
liveData
,
Observer
<
T
>
observer
)
{
liveData
.
observeForever
(
new
Observer
<
T
>()
{
@Override
public
void
onChanged
(
@Nullable
T
t
)
{
observer
.
onChanged
(
t
);
liveData
.
removeObserver
(
this
);
}
});
}
}
briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingManager.java
View file @
4796902b
...
...
@@ -8,7 +8,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import
org.briarproject.bramble.api.sync.MessageId
;
import
org.briarproject.briar.api.conversation.ConversationManager.ConversationClient
;
import
java.
nio.ByteBuffer
;
import
java.
io.InputStream
;
@NotNullByDefault
public
interface
MessagingManager
extends
ConversationClient
{
...
...
@@ -37,7 +37,7 @@ public interface MessagingManager extends ConversationClient {
* Stores a local attachment message.
*/
AttachmentHeader
addLocalAttachment
(
GroupId
groupId
,
long
timestamp
,
String
contentType
,
ByteBuffer
data
)
throws
DbException
;
String
contentType
,
InputStream
is
)
throws
DbException
;
/**
* Returns the ID of the contact with the given private conversation.
...
...
briar-core/src/main/java/org/briarproject/briar/messaging/MessagingManagerImpl.java
View file @
4796902b
...
...
@@ -32,7 +32,7 @@ import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import
org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent
;
import
org.briarproject.briar.client.ConversationClientImpl
;
import
java.
nio.ByteBuffer
;
import
java.
io.InputStream
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.Map
;
...
...
@@ -152,7 +152,7 @@ class MessagingManagerImpl extends ConversationClientImpl
@Override
public
AttachmentHeader
addLocalAttachment
(
GroupId
groupId
,
long
timestamp
,
String
contentType
,
ByteBuffer
data
)
{
String
contentType
,
InputStream
is
)
{
// TODO add real implementation
byte
[]
b
=
new
byte
[
MessageId
.
LENGTH
];
new
Random
().
nextBytes
(
b
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment