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
5c8e414b
Commit
5c8e414b
authored
Feb 19, 2020
by
Christophe Henry
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix webview crash on Android Lollipop
parent
45146842
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
110 additions
and
55 deletions
+110
-55
app/build.gradle
app/build.gradle
+5
-2
app/src/androidTest/java/fr/chenry/android/freshrss/activities/MainActivityTest.kt
...fr/chenry/android/freshrss/activities/MainActivityTest.kt
+36
-0
app/src/main/java/fr/chenry/android/freshrss/RefresherService.kt
.../main/java/fr/chenry/android/freshrss/RefresherService.kt
+4
-0
app/src/main/java/fr/chenry/android/freshrss/activities/MainActivity.kt
...ava/fr/chenry/android/freshrss/activities/MainActivity.kt
+12
-4
app/src/main/java/fr/chenry/android/freshrss/components/navigationdrawer/AddSubscriptionDialog.kt
...hrss/components/navigationdrawer/AddSubscriptionDialog.kt
+24
-26
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/webviewutils/FRSSWebView.kt
...mponents/subscriptionarticles/webviewutils/FRSSWebView.kt
+7
-11
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/webviewutils/FRSSWebViewClient.kt
...ts/subscriptionarticles/webviewutils/FRSSWebViewClient.kt
+5
-1
app/src/main/java/fr/chenry/android/freshrss/store/Store.kt
app/src/main/java/fr/chenry/android/freshrss/store/Store.kt
+6
-2
app/src/main/java/fr/chenry/android/freshrss/store/api/Api.kt
...src/main/java/fr/chenry/android/freshrss/store/api/Api.kt
+1
-1
app/src/main/java/fr/chenry/android/freshrss/store/database/FreshRSSDabatabase.kt
...nry/android/freshrss/store/database/FreshRSSDabatabase.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
-2
app/src/main/java/fr/chenry/android/freshrss/store/viewmodels/SubscriptionArticleVM.kt
...ndroid/freshrss/store/viewmodels/SubscriptionArticleVM.kt
+2
-2
app/src/main/res/menu/activity_main_drawer.xml
app/src/main/res/menu/activity_main_drawer.xml
+6
-3
No files found.
app/build.gradle
View file @
5c8e414b
...
...
@@ -11,7 +11,7 @@ android {
compileSdkVersion
28
defaultConfig
{
applicationId
"fr.chenry.android.freshrss"
minSdkVersion
2
3
minSdkVersion
2
1
targetSdkVersion
28
versionCode
12
versionName
"1.2.2"
...
...
@@ -114,6 +114,7 @@ dependencies {
def
jsoup_version
=
'1.12.2'
def
acraVersion
=
'5.5.0'
def
autoservice_version
=
"1.0-rc6"
def
android_test
=
"1.2.0"
// Linter
ktlint
"com.github.shyiko:ktlint:0.31.0"
...
...
@@ -187,9 +188,11 @@ dependencies {
// Tests
testImplementation
"junit:junit:4.13"
androidTestImplementation
"androidx.test:runner:1.2.0"
androidTestImplementation
"androidx.test:rules:$android_test"
androidTestImplementation
"androidx.test:runner:$android_test"
androidTestImplementation
"androidx.test.ext:junit:1.1.1"
androidTestImplementation
"androidx.test.espresso:espresso-core:$espresso_version"
androidTestImplementation
"androidx.test.espresso:espresso-contrib:$espresso_version"
androidTestImplementation
"com.github.javafaker:javafaker:1.0.2"
// Debug
...
...
app/src/androidTest/java/fr/chenry/android/freshrss/activities/MainActivityTest.kt
0 → 100644
View file @
5c8e414b
package
fr.chenry.android.freshrss.activities
import
androidx.test.espresso.Espresso.onView
import
androidx.test.espresso.action.ViewActions.click
import
androidx.test.espresso.assertion.ViewAssertions.matches
import
androidx.test.espresso.contrib.DrawerActions
import
androidx.test.espresso.contrib.DrawerMatchers.isClosed
import
androidx.test.espresso.contrib.DrawerMatchers.isOpen
import
androidx.test.espresso.matcher.ViewMatchers.*
import
androidx.test.ext.junit.runners.AndroidJUnit4
import
androidx.test.filters.LargeTest
import
androidx.test.rule.ActivityTestRule
import
fr.chenry.android.freshrss.R
import
org.junit.Rule
import
org.junit.Test
import
org.junit.runner.RunWith
@RunWith
(
AndroidJUnit4
::
class
)
@LargeTest
class
MainActivityTest
{
@
get
:
Rule
val
activityRule
=
ActivityTestRule
(
MainActivity
::
class
.
java
)
@Test
fun
`check-drawer-is-closed-when-adding-a-new-subscription`
()
{
// Open drawer
onView
(
withId
(
R
.
id
.
activity_main_navigation_drawer
)).
perform
(
DrawerActions
.
open
())
onView
(
withId
(
R
.
id
.
activity_main_navigation_drawer
)).
check
(
matches
(
isOpen
()))
onView
(
withText
(
R
.
string
.
title_add_subscription
)).
perform
(
click
())
onView
(
withText
(
android
.
R
.
string
.
ok
)).
perform
(
click
())
onView
(
withId
(
R
.
id
.
activity_main_navigation_drawer
)).
check
(
matches
(
isClosed
()))
}
}
app/src/main/java/fr/chenry/android/freshrss/RefresherService.kt
View file @
5c8e414b
...
...
@@ -10,6 +10,7 @@ import androidx.core.app.NotificationCompat
import
fr.chenry.android.freshrss.R.drawable
import
fr.chenry.android.freshrss.R.string
import
fr.chenry.android.freshrss.store.Store
import
fr.chenry.android.freshrss.store.database.models.VoidAccount
import
fr.chenry.android.freshrss.utils.*
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.runBlocking
...
...
@@ -48,6 +49,9 @@ class RefresherService: Service() {
fun
refresh
(
manual
:
Boolean
=
true
):
Promise
<
Unit
,
Exception
>
{
if
(
Store
.
refreshingPromise
.
value
!=
null
)
return
Store
.
refreshingPromise
.
value
!!
// This case should not happen. Blocking only for testing
if
((
Store
.
account
.
value
?:
VoidAccount
)
==
VoidAccount
)
return
Promise
.
ofSuccess
(
Unit
)
if
(!
F
.
context
.
isConnectedToNetwork
())
{
if
(
manual
)
{
Toast
.
makeText
(
...
...
app/src/main/java/fr/chenry/android/freshrss/activities/MainActivity.kt
View file @
5c8e414b
package
fr.chenry.android.freshrss.activities
import
android.content.res.AssetManager
import
android.os.Bundle
import
android.view.MenuItem
import
android.widget.TextView
...
...
@@ -22,6 +23,7 @@ import kotlinx.android.synthetic.main.activity_main.*
class
MainActivity
:
AppCompatActivity
()
{
private
val
accountVM
by
viewModels
<
AccountVM
>()
private
val
navigation
:
NavController
by
lazy
{
Navigation
.
findNavController
(
this
,
R
.
id
.
main_activity_host_fragment
)
}
...
...
@@ -66,13 +68,19 @@ class MainActivity: AppCompatActivity() {
return
super
.
onOptionsItemSelected
(
item
)
}
fun
onAddSubscriptionClick
(
@Suppress
(
"UNUSED_PARAMETER"
)
menuItem
:
MenuItem
):
Boolean
=
// TODO: Remove this function when androidx.appcompat:appcompat:1.2.0 is released
// See https://stackoverflow.com/a/59961940
override
fun
getAssets
():
AssetManager
=
resources
.
assets
fun
onAddSubscriptionClick
(
@Suppress
(
"UNUSED_PARAMETER"
)
item
:
MenuItem
)
{
drawerLayout
.
closeDrawers
()
AddSubscriptionDialog
{
Store
.
postAddSubscription
(
it
).
fail
(
this
::
e
)}
.
show
(
main_activity_host_fragment
!!
.
childFragmentManager
,
AddSubscriptionDialog
::
class
.
java
.
canonicalName
)
.
let
{
true
}
}
fun
onSettingsItemClick
(
@Suppress
(
"UNUSED_PARAMETER"
)
menuItem
:
MenuItem
):
Boolean
=
navigation
.
navigate
(
MainSubscriptionFragmentDirections
.
mainSubscriptionsToSettings
())
.
let
{
true
}
fun
onSettingsItemClick
(
@Suppress
(
"UNUSED_PARAMETER"
)
item
:
MenuItem
)
=
navigation
.
navigate
(
MainSubscriptionFragmentDirections
.
mainSubscriptionsToSettings
())
override
fun
onSupportNavigateUp
():
Boolean
=
navigation
.
navigateUp
(
appBarConfiguration
)
||
super
.
onSupportNavigateUp
()
...
...
app/src/main/java/fr/chenry/android/freshrss/components/navigationdrawer/AddSubscriptionDialog.kt
View file @
5c8e414b
...
...
@@ -4,8 +4,7 @@ import android.app.Dialog
import
android.content.ClipboardManager
import
android.content.Context
import
android.os.Bundle
import
android.widget.EditText
import
android.widget.TextView
import
android.widget.*
import
androidx.appcompat.app.AlertDialog
import
androidx.fragment.app.DialogFragment
import
fr.chenry.android.freshrss.F
...
...
@@ -19,39 +18,38 @@ class AddSubscriptionDialog(private val callback: (String) -> Unit): DialogFragm
}
private
val
dialogView
by
lazy
{
requireActivity
().
layoutInflater
.
inflate
(
R
.
layout
.
fragment_add_subscription_dialog
,
null
)
requireActivity
().
layoutInflater
.
inflate
(
R
.
layout
.
fragment_add_subscription_dialog
,
LinearLayout
(
context
),
false
)
}
private
val
editText
get
()
=
dialogView
.
findViewById
<
EditText
>(
R
.
id
.
fragment_add_subscription_dialog_text_field
)
override
fun
onCreateDialog
(
savedInstanceState
:
Bundle
?):
Dialog
{
return
activity
!!
.
let
{
val
builder
=
AlertDialog
.
Builder
(
it
)
val
result
=
builder
.
setView
(
dialogView
)
.
setPositiveButton
(
android
.
R
.
string
.
ok
)
{
_
,
_
->
callback
(
tryProcessYoutubeUrl
(
editText
.
text
.
toString
()))
}
.
setNegativeButton
(
android
.
R
.
string
.
cancel
)
{
dialog
,
_
->
dialog
.
cancel
()}
.
create
()
override
fun
onCreateDialog
(
savedInstanceState
:
Bundle
?):
Dialog
=
requireActivity
().
let
{
val
result
=
AlertDialog
.
Builder
(
it
).
setView
(
dialogView
)
.
setPositiveButton
(
android
.
R
.
string
.
ok
)
{
_
,
_
->
callback
(
tryProcessYoutubeUrl
(
editText
.
text
.
toString
()))}
.
setNegativeButton
(
android
.
R
.
string
.
cancel
)
{
dialog
,
_
->
dialog
.
cancel
()}
.
create
()
editText
?.
let
{
self
->
if
(
clipboard
.
hasPrimaryClip
())
{
val
item
=
clipboard
.
primaryClip
!!
.
getItemAt
(
0
)
val
uris
=
when
{
item
.
text
!=
null
->
item
.
text
.
toString
().
extractURLs
()
item
.
uri
!=
null
->
listOf
(
item
.
uri
)
else
->
listOf
()
}
editText
?.
let
{
self
->
if
(
clipboard
.
hasPrimaryClip
())
{
val
item
=
clipboard
.
primaryClip
!!
.
getItemAt
(
0
)
val
uris
=
when
{
item
.
text
!=
null
->
item
.
text
.
toString
().
extractURLs
()
item
.
uri
!=
null
->
listOf
(
item
.
uri
)
else
->
listOf
()
}
if
(
uris
.
isNotEmpty
())
{
self
.
setText
(
uris
[
0
].
toString
(),
TextView
.
BufferType
.
EDITABLE
)
self
.
selectAll
()
}
if
(
uris
.
isNotEmpty
())
{
self
.
setText
(
uris
[
0
].
toString
(),
TextView
.
BufferType
.
EDITABLE
)
self
.
selectAll
()
}
}
result
}
result
}
companion
object
{
...
...
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/webviewutils/FRSSWebView.kt
View file @
5c8e414b
package
fr.chenry.android.freshrss.components.subscriptionarticles.webviewutils
import
android.content.Context
import
android.os.Build
import
android.util.AttributeSet
import
android.webkit.WebSettings.MENU_ITEM_NONE
import
android.webkit.WebView
import
androidx.fragment.app.Fragment
import
fr.chenry.android.freshrss.F
...
...
@@ -11,22 +9,19 @@ import fr.chenry.android.freshrss.R
import
fr.chenry.android.freshrss.store.database.models.Article
import
org.jsoup.Jsoup
// WebView crashing on Lollipop devices
// See https://stackoverflow.com/a/49024931
class
FRSSWebView
@JvmOverloads
constructor
(
context
:
Context
,
attrs
:
AttributeSet
?
=
null
,
defStyleAttr
:
Int
=
0
,
defStyleRes
:
Int
=
0
):
WebView
(
context
.
applicationContext
,
attrs
,
defStyleAttr
,
defStyleRes
)
{
):
WebView
(
context
,
attrs
,
defStyleAttr
,
defStyleRes
)
{
init
{
isFocusable
=
true
isFocusableInTouchMode
=
true
settings
.
javaScriptEnabled
=
false
settings
.
defaultTextEncodingName
=
"UTF-8"
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
N
)
{
settings
.
disabledActionModeMenuItems
=
MENU_ITEM_NONE
}
}
fun
load
(
fragment
:
Fragment
,
article
:
Article
)
{
...
...
@@ -61,13 +56,14 @@ class FRSSWebView @JvmOverloads constructor(
it
.
attr
(
"data-original-href"
,
linkToOriginalImage
)
if
(
type
.
isNotBlank
())
it
.
attr
(
"data-original-type"
,
type
)
val
newImage
=
it
.
clone
()
link
.
after
(
newImage
).
remove
()
newImage
.
after
(
"""
newImage
.
after
(
"""
|<a href="$linkToOriginalImage" class="link-to-original-image">
| ${F.getString(R.string.link_to_original_image)}
|</a>"""
.
trimMargin
())
|</a>"""
.
trimMargin
()
)
}
return
doc
.
html
()
...
...
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/webviewutils/FRSSWebViewClient.kt
View file @
5c8e414b
...
...
@@ -11,6 +11,8 @@ class FRSSWebViewClient(private val fragment: Fragment, private val sourceUrl: U
if
(
request
?.
url
?.
scheme
?.
startsWith
(
"mailto"
)
==
true
)
{
val
mt
=
MailTo
.
parse
(
request
.
url
.
toString
())
fragment
.
startActivity
(
MailIntent
(
mt
.
to
,
mt
.
subject
,
mt
.
body
,
mt
.
cc
))
return
true
}
else
if
(
request
?.
url
?.
scheme
?.
startsWith
(
"http"
)
==
true
)
{
if
(
request
.
url
.
isRelative
&&
sourceUrl
==
null
)
return
true
...
...
@@ -18,8 +20,10 @@ class FRSSWebViewClient(private val fragment: Fragment, private val sourceUrl: U
sourceUrl
!!
.
buildUpon
().
path
(
request
.
url
.
path
?:
""
).
build
()
else
request
.
url
fragment
.
startActivity
(
Intent
(
Intent
.
ACTION_VIEW
).
apply
{
data
=
url
})
return
true
}
return
tru
e
return
fals
e
}
}
app/src/main/java/fr/chenry/android/freshrss/store/Store.kt
View file @
5c8e414b
...
...
@@ -116,8 +116,11 @@ object Store {
}
}
fun
postAddSubscription
(
url
:
String
):
Promise
<
Unit
,
Exception
>
=
ensureToken
().
bind
{
fun
postAddSubscription
(
url
:
String
):
Promise
<
Unit
,
Exception
>
{
// This case should not happen. Blocking only for testing
if
((
account
.
value
?:
VoidAccount
)
==
VoidAccount
)
return
Promise
.
ofSuccess
(
Unit
)
return
ensureToken
().
bind
{
val
addSubscriptionPromise
=
api
.
postAddSubscription
(
url
)
addSubscriptionPromise
successUi
{
id
->
...
...
@@ -145,4 +148,5 @@ object Store {
addSubscriptionPromise
.
toSuccessVoid
()
}
}
}
app/src/main/java/fr/chenry/android/freshrss/store/api/Api.kt
View file @
5c8e414b
...
...
@@ -52,7 +52,7 @@ class Api(val account: Account) {
.
promise
()
}
fun
postReadStatus
(
itemId
:
ItemId
,
readStatus
:
ReadStatus
):
Promise
<
Unit
,
Exception
>
{
fun
postReadStatus
(
itemId
:
String
,
readStatus
:
ReadStatus
):
Promise
<
Unit
,
Exception
>
{
val
parameters
=
listOf
(
"i"
to
itemId
,
(
if
(
readStatus
==
ReadStatus
.
READ
)
"a"
else
"r"
)
to
"user/-/state/com.google/read"
...
...
app/src/main/java/fr/chenry/android/freshrss/store/database/FreshRSSDabatabase.kt
View file @
5c8e414b
...
...
@@ -28,7 +28,7 @@ abstract class FreshRSSDabatabase : RoomDatabase() {
// Articles
fun
getArticlesByStreamId
(
streamId
:
StreamId
)
=
getArticlesDAO
().
getByStreamId
(
streamId
)
fun
getArticleById
(
id
:
ItemId
)
=
getArticlesDAO
().
getById
(
id
)
fun
getArticleById
(
id
:
String
)
=
getArticlesDAO
().
getById
(
id
)
fun
getArticleByStreamIdAndUnread
(
streamId
:
StreamId
)
=
getArticlesDAO
().
getByStreamIdAndUnread
(
streamId
,
ReadStatus
.
UNREAD
.
name
)
...
...
app/src/main/java/fr/chenry/android/freshrss/store/database/models/Article.kt
View file @
5c8e414b
...
...
@@ -21,7 +21,6 @@ import io.reactivex.Flowable
import
kotlinx.android.parcel.Parcelize
import
org.joda.time.LocalDateTime
typealias
ItemId
=
String
typealias
Articles
=
List
<
Article
>
@Entity
(
tableName
=
"articles"
)
...
...
@@ -92,7 +91,7 @@ interface ArticlesDAO {
fun
getByStreamId
(
streamId
:
StreamId
):
LiveData
<
Articles
>
@Query
(
"SELECT * FROM articles WHERE id = :id"
)
fun
getById
(
id
:
ItemId
):
Flowable
<
Articles
>
fun
getById
(
id
:
String
):
Flowable
<
Articles
>
@Query
(
"SELECT * FROM articles WHERE streamId = :streamId AND readStatus = :readStatus"
)
fun
getByStreamIdAndUnread
(
streamId
:
StreamId
,
readStatus
:
String
=
ReadStatus
.
UNREAD
.
name
):
LiveData
<
Articles
>
...
...
app/src/main/java/fr/chenry/android/freshrss/store/viewmodels/SubscriptionArticleVM.kt
View file @
5c8e414b
...
...
@@ -4,7 +4,7 @@ import androidx.lifecycle.*
import
fr.chenry.android.freshrss.F
import
fr.chenry.android.freshrss.store.database.models.*
class
SubscriptionArticleVM
(
articleId
:
ItemId
)
:
ViewModel
()
{
class
SubscriptionArticleVM
(
articleId
:
String
)
:
ViewModel
()
{
val
liveData
:
LiveData
<
Article
>
val
subscription
:
Subscription
private
val
source
:
LiveData
<
Articles
>
...
...
@@ -23,7 +23,7 @@ class SubscriptionArticleVM(articleId: ItemId) : ViewModel() {
}
}
class
SubscriptionArticleVMF
(
private
val
articleId
:
ItemId
)
:
ViewModelProvider
.
NewInstanceFactory
()
{
class
SubscriptionArticleVMF
(
private
val
articleId
:
String
)
:
ViewModelProvider
.
NewInstanceFactory
()
{
override
fun
<
T
:
ViewModel
?
>
create
(
modelClass
:
Class
<
T
>):
T
{
@Suppress
(
"UNCHECKED_CAST"
)
return
SubscriptionArticleVM
(
articleId
)
as
T
...
...
app/src/main/res/menu/activity_main_drawer.xml
View file @
5c8e414b
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<menu
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
>
<item
android:title=
"@string/title_add_subscription"
android:id=
"@+id/activity_main_drawer_add_subscription"
android:icon=
"@drawable/ic_add_black_24dp"
android:onClick=
"onAddSubscriptionClick"
/>
android:onClick=
"onAddSubscriptionClick"
app:showAsAction=
"withText"
/>
<item
android:title=
"@string/application"
>
<menu>
...
...
@@ -12,7 +14,8 @@
android:title=
"@string/title_settings"
android:id=
"@+id/activity_main_drawer_settings"
android:icon=
"@drawable/ic_settings_black_24dp"
android:onClick=
"onSettingsItemClick"
/>
android:onClick=
"onSettingsItemClick"
app:showAsAction=
"withText"
/>
</menu>
</item>
...
...
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