Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
AloqaIM-Android
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
AloqaIM-Android
Commits
8a68ef9c
Commit
8a68ef9c
authored
Feb 02, 2018
by
Leonardo Aramaki
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add MessageParser helper class for parsing markdown and possibly others
parent
8fecd4e9
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
181 additions
and
9 deletions
+181
-9
AppModule.kt
.../main/java/chat/rocket/android/dagger/module/AppModule.kt
+36
-9
MessageParser.kt
...src/main/java/chat/rocket/android/helper/MessageParser.kt
+145
-0
No files found.
app/src/main/java/chat/rocket/android/dagger/module/AppModule.kt
View file @
8a68ef9c
...
...
@@ -4,6 +4,7 @@ import android.app.Application
import
android.arch.persistence.room.Room
import
android.content.Context
import
android.content.SharedPreferences
import
android.graphics.Color
import
chat.rocket.android.BuildConfig
import
chat.rocket.android.app.RocketChatDatabase
import
chat.rocket.android.app.utils.CustomImageFormatConfigurator
...
...
@@ -11,16 +12,11 @@ import chat.rocket.android.authentication.infraestructure.MemoryTokenRepository
import
chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository
import
chat.rocket.android.dagger.qualifier.ForFresco
import
chat.rocket.android.helper.FrescoAuthInterceptor
import
chat.rocket.android.helper.MessageParser
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.SharedPrefsLocalRepository
import
chat.rocket.android.server.domain.ChatRoomsRepository
import
chat.rocket.android.server.domain.CurrentServerRepository
import
chat.rocket.android.server.domain.MultiServerTokenRepository
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.android.server.infraestructure.MemoryChatRoomsRepository
import
chat.rocket.android.server.infraestructure.ServerDao
import
chat.rocket.android.server.infraestructure.SharedPreferencesSettingsRepository
import
chat.rocket.android.server.infraestructure.SharedPrefsCurrentServerRepository
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.infraestructure.*
import
chat.rocket.android.util.AppJsonAdapterFactory
import
chat.rocket.android.util.TimberLogger
import
chat.rocket.common.util.PlatformLogger
...
...
@@ -38,7 +34,11 @@ import kotlinx.coroutines.experimental.Job
import
okhttp3.Interceptor
import
okhttp3.OkHttpClient
import
okhttp3.logging.HttpLoggingInterceptor
import
ru.noties.markwon.SpannableConfiguration
import
ru.noties.markwon.il.AsyncDrawableLoader
import
ru.noties.markwon.spans.SpannableTheme
import
timber.log.Timber
import
java.util.concurrent.Executors
import
javax.inject.Singleton
@Module
...
...
@@ -196,4 +196,31 @@ class AppModule {
fun
provideMultiServerTokenRepository
(
repository
:
LocalRepository
,
moshi
:
Moshi
):
MultiServerTokenRepository
{
return
SharedPreferencesMultiServerTokenRepository
(
repository
,
moshi
)
}
@Provides
@Singleton
fun
provideMessageRepository
():
MessagesRepository
{
return
MemoryMessagesRepository
()
}
@Provides
@Singleton
fun
provideConfiguration
(
context
:
Application
,
client
:
OkHttpClient
):
SpannableConfiguration
{
return
SpannableConfiguration
.
builder
(
context
)
.
asyncDrawableLoader
(
AsyncDrawableLoader
.
builder
()
.
client
(
client
)
.
executorService
(
Executors
.
newCachedThreadPool
())
.
resources
(
context
.
resources
)
.
build
())
.
theme
(
SpannableTheme
.
builder
()
.
linkColor
(
Color
.
BLUE
)
.
build
())
.
build
()
}
@Provides
@Singleton
fun
provideMessageParser
(
context
:
Application
,
configuration
:
SpannableConfiguration
):
MessageParser
{
return
MessageParser
(
context
,
configuration
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/helper/MessageParser.kt
0 → 100644
View file @
8a68ef9c
package
chat.rocket.android.helper
import
android.app.Application
import
android.content.Context
import
android.graphics.Canvas
import
android.graphics.Color
import
android.graphics.Paint
import
android.graphics.Typeface
import
android.graphics.drawable.Drawable
import
android.text.Layout
import
android.text.Spanned
import
android.text.style.*
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.core.model.Url
import
org.commonmark.node.BlockQuote
import
ru.noties.markwon.Markwon
import
ru.noties.markwon.SpannableBuilder
import
ru.noties.markwon.SpannableConfiguration
import
ru.noties.markwon.renderer.SpannableMarkdownVisitor
import
javax.inject.Inject
class
MessageParser
@Inject
constructor
(
val
context
:
Application
,
private
val
configuration
:
SpannableConfiguration
)
{
private
val
parser
=
Markwon
.
createParser
()
fun
renderMarkdown
(
text
:
String
,
quote
:
MessageViewModel
?,
urls
:
List
<
Url
>):
CharSequence
{
val
builder
=
SpannableBuilder
()
var
content
:
String
=
text
for
(
url
in
urls
)
{
content
=
content
.
replace
(
url
.
url
,
"[${url.url}](${url.url})"
)
}
val
parentNode
=
parser
.
parse
(
toLenientMarkdown
(
content
))
parentNode
.
accept
(
SpannableMarkdownVisitor
(
configuration
,
builder
))
quote
?.
apply
{
var
quoteNode
=
parser
.
parse
(
"> $sender $time"
)
parentNode
.
appendChild
(
quoteNode
)
quoteNode
.
accept
(
QuoteMessageSenderVisitor
(
context
,
configuration
,
builder
,
sender
.
length
))
quoteNode
=
parser
.
parse
(
"> ${toLenientMarkdown(quote.getOriginalMessage())}"
)
quoteNode
.
accept
(
QuoteMessageBodyVisitor
(
context
,
configuration
,
builder
))
}
return
builder
.
text
()
}
/**
* Convert to a lenient markdown consistent with Rocket.Chat web markdown instead of the official specs.
*/
private
fun
toLenientMarkdown
(
text
:
String
):
String
{
return
text
.
trim
().
replace
(
"\\*(.+)\\*"
.
toRegex
())
{
"**${it.groupValues[1].trim()}**"
}
.
replace
(
"\\~(.+)\\~"
.
toRegex
())
{
"~~${it.groupValues[1].trim()}~~"
}
.
replace
(
"\\_(.+)\\_"
.
toRegex
())
{
"_${it.groupValues[1].trim()}_"
}
}
class
QuoteMessageSenderVisitor
(
private
val
context
:
Context
,
configuration
:
SpannableConfiguration
,
private
val
builder
:
SpannableBuilder
,
private
val
senderNameLength
:
Int
)
:
SpannableMarkdownVisitor
(
configuration
,
builder
)
{
override
fun
visit
(
blockQuote
:
BlockQuote
)
{
// mark current length
val
length
=
builder
.
length
()
// pass to super to apply markdown
super
.
visit
(
blockQuote
)
val
res
=
context
.
resources
val
timeOffsetStart
=
length
+
senderNameLength
+
1
builder
.
setSpan
(
QuoteMarginSpan
(
context
.
getDrawable
(
R
.
drawable
.
quote
),
10
),
length
,
builder
.
length
())
builder
.
setSpan
(
StyleSpan
(
Typeface
.
BOLD
),
length
,
length
+
senderNameLength
)
builder
.
setSpan
(
ForegroundColorSpan
(
Color
.
BLACK
),
length
,
builder
.
length
())
// set time spans
builder
.
setSpan
(
AbsoluteSizeSpan
(
res
.
getDimensionPixelSize
(
R
.
dimen
.
message_time_text_size
)),
timeOffsetStart
,
builder
.
length
())
builder
.
setSpan
(
ForegroundColorSpan
(
res
.
getColor
(
R
.
color
.
darkGray
)),
timeOffsetStart
,
builder
.
length
())
}
}
class
QuoteMessageBodyVisitor
(
private
val
context
:
Context
,
configuration
:
SpannableConfiguration
,
private
val
builder
:
SpannableBuilder
)
:
SpannableMarkdownVisitor
(
configuration
,
builder
)
{
override
fun
visit
(
blockQuote
:
BlockQuote
)
{
// mark current length
val
length
=
builder
.
length
()
// pass to super to apply markdown
super
.
visit
(
blockQuote
)
builder
.
setSpan
(
QuoteMarginSpan
(
context
.
getDrawable
(
R
.
drawable
.
quote
),
10
),
length
,
builder
.
length
())
}
}
class
QuoteMarginSpan
:
LeadingMarginSpan
,
LineHeightSpan
{
private
var
mDrawable
:
Drawable
?
=
null
private
var
mPad
:
Int
=
0
constructor
(
b
:
Drawable
)
{
mDrawable
=
b
}
constructor
(
b
:
Drawable
,
pad
:
Int
)
{
mDrawable
=
b
mPad
=
pad
}
override
fun
getLeadingMargin
(
first
:
Boolean
):
Int
{
return
mDrawable
!!
.
intrinsicWidth
+
mPad
}
override
fun
drawLeadingMargin
(
c
:
Canvas
,
p
:
Paint
,
x
:
Int
,
dir
:
Int
,
top
:
Int
,
baseline
:
Int
,
bottom
:
Int
,
text
:
CharSequence
,
start
:
Int
,
end
:
Int
,
first
:
Boolean
,
layout
:
Layout
)
{
val
st
=
(
text
as
Spanned
).
getSpanStart
(
this
)
val
ix
=
x
val
itop
=
layout
.
getLineTop
(
layout
.
getLineForOffset
(
st
))
val
dw
=
mDrawable
!!
.
intrinsicWidth
val
dh
=
mDrawable
!!
.
intrinsicHeight
// XXX What to do about Paint?
mDrawable
!!
.
setBounds
(
ix
,
itop
,
ix
+
dw
,
itop
+
layout
.
height
)
mDrawable
!!
.
draw
(
c
)
}
override
fun
chooseHeight
(
text
:
CharSequence
,
start
:
Int
,
end
:
Int
,
istartv
:
Int
,
v
:
Int
,
fm
:
Paint
.
FontMetricsInt
)
{
if
(
end
==
(
text
as
Spanned
).
getSpanEnd
(
this
))
{
val
ht
=
mDrawable
!!
.
intrinsicHeight
var
need
=
ht
-
(
v
+
fm
.
descent
-
fm
.
ascent
-
istartv
)
if
(
need
>
0
)
fm
.
descent
+=
need
need
=
ht
-
(
v
+
fm
.
bottom
-
fm
.
top
-
istartv
)
if
(
need
>
0
)
fm
.
bottom
+=
need
}
}
}
}
\ No newline at end of file
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