Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
FreshRSS-Android
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
30
Issues
30
List
Boards
Labels
Service Desk
Milestones
Merge Requests
2
Merge Requests
2
Requirements
Requirements
List
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Operations
Operations
Environments
Packages & Registries
Packages & Registries
Package Registry
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issue
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Christophe Henry
FreshRSS-Android
Commits
8579f4f8
Commit
8579f4f8
authored
Mar 25, 2019
by
Christophe Henry
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'share-template' into 'develop'
Adds template system for share intent See merge request
!6
parents
aa31f92a
d51a77b8
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
159 additions
and
78 deletions
+159
-78
app/build.gradle
app/build.gradle
+1
-0
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/SubscriptionArticlesDetailFragment.kt
...ubscriptionarticles/SubscriptionArticlesDetailFragment.kt
+11
-22
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/shareintent/ShareIntent.kt
...omponents/subscriptionarticles/shareintent/ShareIntent.kt
+32
-0
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/shareintent/chunkfilters/ChunkFilters.kt
...criptionarticles/shareintent/chunkfilters/ChunkFilters.kt
+58
-0
app/src/main/java/fr/chenry/android/freshrss/components/subscriptions/MainSubscriptionFragment.kt
...hrss/components/subscriptions/MainSubscriptionFragment.kt
+1
-1
app/src/main/java/fr/chenry/android/freshrss/store/database/models/Article.kt
.../chenry/android/freshrss/store/database/models/Article.kt
+1
-1
app/src/main/java/fr/chenry/android/freshrss/store/viewmodels/SubscriptionArticleVM.kt
...ndroid/freshrss/store/viewmodels/SubscriptionArticleVM.kt
+15
-6
app/src/test/java/fr/chenry/android/freshrss/components/subscriptionarticles/ShareIntentTest.kt
...eshrss/components/subscriptionarticles/ShareIntentTest.kt
+40
-0
app/src/test/java/fr/chenry/android/freshrss/store/api/models/UnreadCountTest.kt
...enry/android/freshrss/store/api/models/UnreadCountTest.kt
+0
-48
No files found.
app/build.gradle
View file @
8579f4f8
...
...
@@ -126,6 +126,7 @@ dependencies {
implementation
"joda-time:joda-time:2.10.1"
implementation
"com.squareup.picasso:picasso:2.71828"
implementation
"io.github.luizgrp.sectionedrecyclerviewadapter:sectionedrecyclerviewadapter:2.0.0"
implementation
"com.x5dev:chunk-templates:3.4.0"
// Tests
testImplementation
"junit:junit:4.12"
...
...
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/SubscriptionArticlesDetailFragment.kt
View file @
8579f4f8
...
...
@@ -11,19 +11,16 @@ import androidx.core.view.MenuItemCompat
import
androidx.fragment.app.Fragment
import
androidx.lifecycle.*
import
fr.chenry.android.freshrss.R
import
fr.chenry.android.freshrss.components.subscriptionarticles.shareintent.ShareIntent
import
fr.chenry.android.freshrss.store.Store
import
fr.chenry.android.freshrss.store.database.models.Article
import
fr.chenry.android.freshrss.store.database.models.ReadStatus
import
fr.chenry.android.freshrss.store.database.models.*
import
fr.chenry.android.freshrss.store.database.models.ReadStatus.UNREAD
import
fr.chenry.android.freshrss.store.viewmodels.SubscriptionArticleVM
import
fr.chenry.android.freshrss.store.viewmodels.SubscriptionArticleVMF
import
fr.chenry.android.freshrss.utils.capitalizeFull
import
fr.chenry.android.freshrss.utils.e
import
kotlinx.android.synthetic.main.fragment_subscription_article_detail.*
import
nl.komponents.kovenant.ui.alwaysUi
import
nl.komponents.kovenant.ui.failUi
import
java.util.regex.Pattern
import
kotlin.text.RegexOption.IGNORE_CASE
class
SubscriptionArticlesDetailFragment
:
Fragment
()
{
private
lateinit
var
articleId
:
String
...
...
@@ -34,6 +31,7 @@ class SubscriptionArticlesDetailFragment: Fragment() {
}
private
var
isFetching
=
MutableLiveData
<
Boolean
>().
apply
{
value
=
false
}
private
val
article
:
Article
get
()
=
model
.
liveData
.
value
!!
private
val
subscription
:
Subscription
get
()
=
model
.
subscription
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
...
...
@@ -82,7 +80,7 @@ class SubscriptionArticlesDetailFragment: Fragment() {
private
fun
setupShareAction
(
menu
:
Menu
)
{
menu
.
findItem
(
R
.
id
.
action_share
)
.
let
{
MenuItemCompat
.
getActionProvider
(
it
)
as
ShareActionProvider
}
.
setShareIntent
(
ShareIntent
())
.
setShareIntent
(
ShareIntent
(
getTemplateAttributes
()
))
}
private
fun
setupOpenInBrowser
(
menu
:
Menu
)
{
...
...
@@ -143,20 +141,11 @@ class SubscriptionArticlesDetailFragment: Fragment() {
return
true
}
inner
class
ShareIntent
:
Intent
(
Intent
.
ACTION_SEND
)
{
private
val
stripAuthorRegex
=
"\\s*[^\\p{L}]*\\s*${Pattern.quote(article.author)}\\s*\\W*\\s*"
.
toRegex
(
IGNORE_CASE
)
private
val
articleTitle
get
()
=
stripAuthorRegex
.
replace
(
article
.
title
,
""
).
capitalize
()
private
val
articleAuthor
get
()
=
if
(
article
.
author
.
isBlank
())
""
else
"— ${article.author}"
.
capitalizeFull
()
private
val
articleShareString
get
()
=
"""
$articleTitle $articleAuthor
${article.href}
"""
.
trimIndent
().
trim
()
init
{
type
=
"text/plain"
putExtra
(
Intent
.
EXTRA_TEXT
,
articleShareString
)
}
}
private
fun
getTemplateAttributes
()
=
mapOf
(
"subscription"
to
subscription
.
title
,
"author"
to
article
.
author
,
"title"
to
article
.
title
,
"content"
to
article
.
content
,
"href"
to
article
.
href
)
}
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/shareintent/ShareIntent.kt
0 → 100644
View file @
8579f4f8
package
fr.chenry.android.freshrss.components.subscriptionarticles.shareintent
import
android.content.Intent
import
com.x5.template.Theme
import
fr.chenry.android.freshrss.components.subscriptionarticles.shareintent.chunkfilters.SentenceCapFilter
import
fr.chenry.android.freshrss.components.subscriptionarticles.shareintent.chunkfilters.StripFragmentFilter
class
ShareIntent
(
attributes
:
Map
<
String
,
String
>):
Intent
(
Intent
.
ACTION_SEND
)
{
init
{
type
=
"text/plain"
putExtra
(
Intent
.
EXTRA_TEXT
,
format
(
attributes
))
}
companion
object
{
private
val
defaultTemplate
=
"""
|{${'$'}title|strip_fragment(subscription)|sentence_cap} — {${'$'}subscription|capitalize}
|{${'$'}href}
"""
.
trimMargin
(
"|"
)
private
val
template
=
Theme
().
let
{
it
.
registerFilter
(
SentenceCapFilter
())
it
.
registerFilter
(
StripFragmentFilter
())
it
.
makeChunk
().
apply
{
append
(
defaultTemplate
)}
}
fun
format
(
attributes
:
Map
<
String
,
String
>)
=
template
.
apply
{
attributes
.
forEach
{
t
,
u
->
set
(
t
,
u
)}
}.
toString
()
}
}
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/shareintent/chunkfilters/ChunkFilters.kt
0 → 100644
View file @
8579f4f8
package
fr.chenry.android.freshrss.components.subscriptionarticles.shareintent.chunkfilters
import
com.x5.template.Chunk
import
com.x5.template.filters.FilterArgs
import
com.x5.template.filters.ObjectFilter
import
java.util.regex.Pattern
import
kotlin.text.RegexOption.IGNORE_CASE
// Will match any separator token, i.e, any word not containing at least a letter or a number
const
val
sepToken
=
"[\\S&&[^\\p{L}]&&[^\\d]]"
class
SentenceCapFilter
:
ObjectFilter
()
{
override
fun
getFilterName
()
=
"sentence_cap"
override
fun
transformObject
(
chunk
:
Chunk
?,
`object`
:
Any
?,
args
:
FilterArgs
?)
=
`object`
.
toString
().
let
{
var
result
=
it
it
.
split
(
"\\s*$sepToken+\\s*"
.
toRegex
())
.
map
{
t
->
when
{
t
.
isBlank
()
->
t
t
.
length
==
1
->
t
[
0
].
toString
().
toUpperCase
()
else
->
"${t[0].toString().toUpperCase()}${t.substring(1).toLowerCase()}"
}
}
.
forEach
{
t
->
result
=
result
.
replace
(
t
,
t
,
ignoreCase
=
true
)}
result
}
}
class
StripFragmentFilter
:
ObjectFilter
()
{
override
fun
getFilterName
()
=
"strip_fragment"
override
fun
transformObject
(
chunk
:
Chunk
?,
`object`
:
Any
?,
args
:
FilterArgs
?)
=
stripFragment
(
`object`
.
toString
(),
args
?.
getFilterArgs
(
chunk
)
?.
getOrNull
(
0
)
?:
""
)
companion
object
{
/**
* Will remove the author from the title and any separator character with it.
* For instance:
* >>>> stripFragment("My title - Author", "Author")
* "My title"
* >>>> stripFragment("Author | My title", "Author"))
* "My title"
* >>>> stripFragment("Series | Author | My title", "Author")
* "Series | My title"
*/
fun
stripFragment
(
title
:
String
,
author
:
String
):
String
{
return
when
{
// Case where author is in the middle of the title, for instance: "Video series #1 | Author | Title"
title
.
contains
(
"\\s*$sepToken+\\s*${Pattern.quote(author)}\\s*$sepToken+\\s*"
.
toRegex
())
->
"\\s*$sepToken*\\s*${Pattern.quote(author)}"
.
toRegex
(
IGNORE_CASE
).
replace
(
title
,
""
)
else
->
"\\s*$sepToken*\\s*${Pattern.quote(author)}\\s*$sepToken*\\s*"
.
toRegex
(
IGNORE_CASE
).
replace
(
title
,
""
)
}
}
}
}
app/src/main/java/fr/chenry/android/freshrss/components/subscriptions/MainSubscriptionFragment.kt
View file @
8579f4f8
...
...
@@ -66,4 +66,4 @@ class MainSubscriptionFragment: Fragment(), BottomNavigationView.OnNavigationIte
Bundle
().
apply
{
putParcelable
(
SubscriptionsFragment
.
argumentKey
,
subscriptionSection
)}
)
}
}
\ No newline at end of file
}
app/src/main/java/fr/chenry/android/freshrss/store/database/models/Article.kt
View file @
8579f4f8
...
...
@@ -59,7 +59,7 @@ enum class ReadStatus: Parcelable {
@Dao
interface
ArticlesDAO
{
@Insert
(
onConflict
=
OnConflictStrategy
.
FAIL
)
@Insert
(
onConflict
=
OnConflictStrategy
.
ABORT
)
fun
insert
(
article
:
Article
)
@Insert
(
onConflict
=
OnConflictStrategy
.
REPLACE
)
...
...
app/src/main/java/fr/chenry/android/freshrss/store/viewmodels/SubscriptionArticleVM.kt
View file @
8579f4f8
package
fr.chenry.android.freshrss.store.viewmodels
import
androidx.lifecycle.*
import
fr.chenry.android.freshrss.FreshRSSApplication
import
fr.chenry.android.freshrss.store.database.FreshRSSDabatabase
import
fr.chenry.android.freshrss.store.database.models.*
class
SubscriptionArticleVM
(
private
val
articleId
:
ItemId
):
ViewModel
()
{
private
val
flowable
by
lazy
{
FreshRSSDabatabase
.
instance
.
getArticleById
(
articleId
)
}
private
val
source
:
LiveData
<
Articles
>
by
lazy
{
flowable
.
toLiveData
()
}
val
liveData
:
LiveData
<
Article
>
by
lazy
{
MutableLiveData
<
Article
>().
apply
{
value
=
flowable
.
blockingFirst
().
first
()
class
SubscriptionArticleVM
(
articleId
:
ItemId
):
ViewModel
()
{
val
liveData
:
LiveData
<
Article
>
val
subscription
:
Subscription
private
val
source
:
LiveData
<
Articles
>
init
{
val
flowable
=
FreshRSSDabatabase
.
instance
.
getArticleById
(
articleId
)
val
article
=
flowable
.
blockingFirst
().
first
()
source
=
flowable
.
toLiveData
()
liveData
=
MutableLiveData
<
Article
>().
apply
{
value
=
article
source
.
observeForever
{
value
=
it
.
first
()
}
}
subscription
=
FreshRSSApplication
.
database
.
getSubcriptionsById
(
article
.
streamId
).
blockingFirst
().
first
()
}
}
...
...
app/src/test/java/fr/chenry/android/freshrss/components/subscriptionarticles/ShareIntentTest.kt
0 → 100644
View file @
8579f4f8
package
fr.chenry.android.freshrss.components.subscriptionarticles
import
fr.chenry.android.freshrss.components.subscriptionarticles.shareintent.ShareIntent
import
fr.chenry.android.freshrss.components.subscriptionarticles.shareintent.chunkfilters.StripFragmentFilter
import
org.junit.Assert.assertEquals
import
org.junit.Test
class
ShareIntentTest
{
private
val
attributes
get
()
=
mapOf
(
"subscription"
to
"The subscription"
,
"author"
to
"Humble Me"
,
"title"
to
"My Title - My Series #1 - THIS IS AWESOME!"
,
"content"
to
"Lorem ipsum"
,
"href"
to
"http://example.com"
)
@Test
fun
stripAuthorTest
()
{
assertEquals
(
"My title"
,
StripFragmentFilter
.
stripFragment
(
"My title - Author"
,
"Author"
))
assertEquals
(
"My title"
,
StripFragmentFilter
.
stripFragment
(
"Author | My title"
,
"Author"
))
assertEquals
(
"My title"
,
StripFragmentFilter
.
stripFragment
(
"Author | My title"
,
"author"
))
assertEquals
(
"My title"
,
StripFragmentFilter
.
stripFragment
(
"author & My title"
,
"Author"
))
assertEquals
(
"Video series #1 | Title"
,
StripFragmentFilter
.
stripFragment
(
"Video series #1 | Author | Title"
,
"Author"
)
)
}
@Test
fun
format
()
{
assertEquals
(
ShareIntent
.
format
(
attributes
),
"""
|My title - My series #1 - This is awesome! — The Subscription
|http://example.com
"""
.
trimMargin
(
"|"
)
)
}
}
app/src/test/java/fr/chenry/android/freshrss/store/api/models/UnreadCountTest.kt
deleted
100644 → 0
View file @
aa31f92a
package
fr.chenry.android.freshrss.store.api.models
import
fr.chenry.android.freshrss.utils.JACKSON_OBJECT_MAPPER
import
org.joda.time.*
import
org.junit.*
import
org.junit.Assert.assertEquals
class
UnreadCountTest
{
@Before
fun
setUp
()
{
DateTimeUtils
.
setCurrentMillisFixed
(
System
.
currentTimeMillis
())
}
@After
fun
tearDown
()
{
DateTimeUtils
.
setCurrentMillisSystem
()
}
@Test
fun
testDeserialize
()
{
val
value
=
JACKSON_OBJECT_MAPPER
.
readValue
(
"""
{
"id": "feed/3",
"count": 0,
"newestItemTimestampUsec": "1552432206000000"
}
"""
.
trimIndent
(),
UnreadCount
::
class
.
java
)
assertEquals
(
UnreadCount
(
"feed/3"
,
0
,
LocalDateTime
(
"1552432206000000"
.
toLong
().
div
(
1000
),
DateTimeZone
.
UTC
)),
value
)
}
@Test
fun
testDeserializeWithNullTimestamp
()
{
val
value
=
JACKSON_OBJECT_MAPPER
.
readValue
(
"""
{
"id": "feed/3",
"count": 0
}
"""
.
trimIndent
(),
UnreadCount
::
class
.
java
)
assertEquals
(
UnreadCount
(
"feed/3"
,
0
,
LocalDateTime
.
now
()),
value
)
}
}
\ 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