Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #123

Merged
merged 2 commits into from
Nov 14, 2023
Merged

Fixes #123

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
737 changes: 737 additions & 0 deletions app/schemas/com.nononsenseapps.feeder.db.room.AppDatabase/29.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.nononsenseapps.feeder.db.room

import androidx.core.database.getStringOrNull
import androidx.room.testing.MigrationTestHelper
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.platform.app.InstrumentationRegistry
import com.nononsenseapps.feeder.FeederApplication
import kotlin.test.assertNull
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.kodein.di.DI
import org.kodein.di.DIAware
import org.kodein.di.android.closestDI

@RunWith(AndroidJUnit4::class)
@LargeTest
class TestMigrationFrom28To29 : DIAware {
private val dbName = "testDb"
private val feederApplication: FeederApplication = ApplicationProvider.getApplicationContext()
override val di: DI by closestDI(feederApplication)

@Rule
@JvmField
val testHelper: MigrationTestHelper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
AppDatabase::class.java,
emptyList(),
FrameworkSQLiteOpenHelperFactory(),
)

@Test
fun migrate() {
@Suppress("SimpleRedundantLet")
testHelper.createDatabase(dbName, FROM_VERSION).let { oldDB ->
oldDB.execSQL(
"""
INSERT INTO feeds(id, title, url, custom_title, tag, notify, last_sync, response_hash, fulltext_by_default, open_articles_with, alternate_id, currently_syncing, when_modified, site_fetched)
VALUES(1, 'feed', 'http://url', '', '', 0, 0, 666, 0, '', 0, 0, 0, 0)
""".trimIndent(),
)
oldDB.execSQL(
"""
INSERT INTO feed_items(id, guid, title, plain_title, plain_snippet, notified, feed_id, first_synced_time, primary_sort_time, pinned, bookmarked, fulltext_downloaded, read_time, unread)
VALUES(8, 'http://item1', 'title', 'ptitle', 'psnippet', 0, 1, 0, 0, 1, 0, 0, 0, 1)
""".trimIndent(),
)
}
val db = testHelper.runMigrationsAndValidate(
dbName,
TO_VERSION,
true,
MigrationFrom28To29(di),
)

db.query(
"""
SELECT enclosure_type FROM feed_items
""".trimIndent(),
).use {
assert(it.count == 1)
assert(it.moveToFirst())
assertNull(it.getStringOrNull(0))
}
}

companion object {
private const val FROM_VERSION = 28
private const val TO_VERSION = 29
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -684,8 +684,12 @@ data class Enclosure(
val present: Boolean = false,
val link: String = "",
val name: String = "",
val type: String = "",
)

val Enclosure.isImage: Boolean
get() = type.startsWith("image/")

@Immutable
data class Article(
val item: FeedItemWithFeed?,
Expand All @@ -699,6 +703,7 @@ data class Article(
present = true,
link = link,
name = item.enclosureFilename ?: "",
type = item.enclosureType ?: "",
)
} ?: Enclosure(
present = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const val COL_PLAINTITLE = "plain_title"
const val COL_PLAINSNIPPET = "plain_snippet"
const val COL_IMAGEURL = "image_url"
const val COL_ENCLOSURELINK = "enclosure_link"
const val COL_ENCLOSURE_TYPE = "enclosure_type"
const val COL_LINK = "link"
const val COL_AUTHOR = "author"
const val COL_PUBDATE = "pub_date"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ private const val LOG_TAG = "FEEDER_APPDB"
RemoteFeed::class,
SyncDevice::class,
],
version = 28,
version = 29,
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
Expand Down Expand Up @@ -115,12 +115,23 @@ fun getAllMigrations(di: DI) = arrayOf(
MigrationFrom25To26(di),
MigrationFrom26To27(di),
MigrationFrom27To28(di),
MigrationFrom28To29(di),
)

/*
* 6 represents legacy database
* 7 represents new Room database
*/
class MigrationFrom28To29(override val di: DI) : Migration(28, 29), DIAware {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"""
alter table feed_items add column enclosure_type text
""".trimIndent(),
)
}
}

class MigrationFrom27To28(override val di: DI) : Migration(27, 28), DIAware {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.room.PrimaryKey
import com.nononsenseapps.feeder.db.COL_AUTHOR
import com.nononsenseapps.feeder.db.COL_BOOKMARKED
import com.nononsenseapps.feeder.db.COL_ENCLOSURELINK
import com.nononsenseapps.feeder.db.COL_ENCLOSURE_TYPE
import com.nononsenseapps.feeder.db.COL_FEEDID
import com.nononsenseapps.feeder.db.COL_FIRSTSYNCEDTIME
import com.nononsenseapps.feeder.db.COL_FULLTEXT_DOWNLOADED
Expand Down Expand Up @@ -69,6 +70,7 @@ data class FeedItem @Ignore constructor(
@ColumnInfo(name = COL_PLAINSNIPPET) var plainSnippet: String = "",
@ColumnInfo(name = COL_IMAGEURL) var imageUrl: String? = null,
@ColumnInfo(name = COL_ENCLOSURELINK) var enclosureLink: String? = null,
@ColumnInfo(name = COL_ENCLOSURE_TYPE) var enclosureType: String? = null,
@ColumnInfo(name = COL_AUTHOR) var author: String? = null,
@ColumnInfo(name = COL_PUBDATE, typeAffinity = ColumnInfo.TEXT) override var pubDate: ZonedDateTime? = null,
@ColumnInfo(name = COL_LINK) override var link: String? = null,
Expand Down Expand Up @@ -125,7 +127,10 @@ data class FeedItem @Ignore constructor(
this.plainSnippet = summary

this.imageUrl = absoluteImage
this.enclosureLink = entry.attachments?.firstOrNull()?.url
val firstEnclosure = entry.attachments?.firstOrNull()
this.enclosureLink = firstEnclosure?.url
this.enclosureType = firstEnclosure?.mime_type?.lowercase()

this.author = entry.author?.name ?: feed.author?.name
this.link = entry.url

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.nononsenseapps.feeder.db.COL_AUTHOR
import com.nononsenseapps.feeder.db.COL_BOOKMARKED
import com.nononsenseapps.feeder.db.COL_CUSTOM_TITLE
import com.nononsenseapps.feeder.db.COL_ENCLOSURELINK
import com.nononsenseapps.feeder.db.COL_ENCLOSURE_TYPE
import com.nononsenseapps.feeder.db.COL_FEEDCUSTOMTITLE
import com.nononsenseapps.feeder.db.COL_FEEDID
import com.nononsenseapps.feeder.db.COL_FEEDTITLE
Expand Down Expand Up @@ -33,7 +34,7 @@ import java.time.ZonedDateTime

const val feedItemColumnsWithFeed = """
$FEED_ITEMS_TABLE_NAME.$COL_ID AS $COL_ID, $COL_GUID, $FEED_ITEMS_TABLE_NAME.$COL_TITLE AS $COL_TITLE,
$COL_PLAINTITLE, $COL_PLAINSNIPPET, $FEED_ITEMS_TABLE_NAME.$COL_IMAGEURL, $COL_ENCLOSURELINK,
$COL_PLAINTITLE, $COL_PLAINSNIPPET, $FEED_ITEMS_TABLE_NAME.$COL_IMAGEURL, $COL_ENCLOSURELINK, $COL_ENCLOSURE_TYPE,
$COL_AUTHOR, $COL_PUBDATE, $COL_LINK, $COL_READ_TIME, $FEEDS_TABLE_NAME.$COL_TAG AS $COL_TAG, $FEEDS_TABLE_NAME.$COL_ID AS $COL_FEEDID,
$FEEDS_TABLE_NAME.$COL_TITLE AS $COL_FEEDTITLE,
$FEEDS_TABLE_NAME.$COL_CUSTOM_TITLE AS $COL_FEEDCUSTOMTITLE,
Expand All @@ -51,6 +52,7 @@ data class FeedItemWithFeed @Ignore constructor(
@ColumnInfo(name = COL_PLAINSNIPPET) var plainSnippet: String = "",
@ColumnInfo(name = COL_IMAGEURL) var imageUrl: String? = null,
@ColumnInfo(name = COL_ENCLOSURELINK) var enclosureLink: String? = null,
@ColumnInfo(name = COL_ENCLOSURE_TYPE) var enclosureType: String? = null,
var author: String? = null,
@ColumnInfo(name = COL_PUBDATE) var pubDate: ZonedDateTime? = null,
override var link: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import androidx.compose.foundation.focusGroup
import androidx.compose.foundation.indication
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
Expand All @@ -21,25 +23,42 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ErrorOutline
import androidx.compose.material.icons.outlined.Terrain
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import coil.request.ImageRequest
import coil.size.Precision
import coil.size.Scale
import com.nononsenseapps.feeder.R
import com.nononsenseapps.feeder.archmodel.Enclosure
import com.nononsenseapps.feeder.archmodel.isImage
import com.nononsenseapps.feeder.ui.compose.coil.rememberTintedVectorPainter
import com.nononsenseapps.feeder.ui.compose.text.WithBidiDeterminedLayoutDirection
import com.nononsenseapps.feeder.ui.compose.text.WithTooltipIfNotBlank
import com.nononsenseapps.feeder.ui.compose.text.rememberMaxImageWidth
import com.nononsenseapps.feeder.ui.compose.theme.LinkTextStyle
import com.nononsenseapps.feeder.ui.compose.theme.LocalDimens
import com.nononsenseapps.feeder.ui.compose.theme.hasImageAspectRatioInReader
import com.nononsenseapps.feeder.ui.compose.utils.ProvideScaledText
import com.nononsenseapps.feeder.ui.compose.utils.ScreenType
import com.nononsenseapps.feeder.ui.compose.utils.focusableInNonTouchMode
Expand Down Expand Up @@ -158,15 +177,47 @@ fun ReaderView(

if (enclosure.present) {
item {
val openLabel = if (enclosure.name.isBlank()) {
stringResource(R.string.open_enclosed_media)
if (enclosure.isImage) {
BoxWithConstraints(
modifier = Modifier
.clip(RectangleShape)
.fillMaxWidth(),
) {
WithTooltipIfNotBlank(tooltip = enclosure.name) { innerModifier ->
val imageWidth by rememberMaxImageWidth()
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(enclosure.link)
.scale(Scale.FIT)
.size(imageWidth)
.precision(Precision.INEXACT)
.build(),
contentDescription = enclosure.name,
placeholder = rememberTintedVectorPainter(
Icons.Outlined.Terrain,
),
error = rememberTintedVectorPainter(Icons.Outlined.ErrorOutline),
contentScale = if (dimens.hasImageAspectRatioInReader) {
ContentScale.Fit
} else {
ContentScale.FillWidth
},
modifier = innerModifier
.fillMaxWidth()
.run {
dimens.imageAspectRatioInReader?.let { ratio ->
aspectRatio(ratio)
} ?: this
},
)
}
}
} else {
stringResource(R.string.open_enclosed_media_file, enclosure.name)
}
Column(
modifier = Modifier
.width(dimens.maxReaderWidth),
) {
val openLabel = if (enclosure.name.isBlank()) {
stringResource(R.string.open_enclosed_media)
} else {
stringResource(R.string.open_enclosed_media_file, enclosure.name)
}
ProvideScaledText(
style = MaterialTheme.typography.bodyLarge.merge(
LinkTextStyle(),
Expand All @@ -175,6 +226,7 @@ fun ReaderView(
Text(
text = openLabel,
modifier = Modifier
.width(dimens.maxReaderWidth)
.clickable {
onEnclosureClick()
}
Expand All @@ -189,7 +241,11 @@ fun ReaderView(
} catch (e: Exception) {
// Observed nullpointer exception when setting customActions
// No clue why it could be null
Log.e("FeederReaderScreen", "Exception in semantics", e)
Log.e(
LOG_TAG,
"Exception in semantics",
e,
)
}
},
)
Expand All @@ -202,3 +258,5 @@ fun ReaderView(
}
}
}

private const val LOG_TAG = "FEEDER_READER"
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ class AnnotatedParagraphStringBuilder {
return false
}

val endsWithNonBreakingSpace: Boolean
get() {
if (mLastTwoChars.isEmpty()) {
return false
}
mLastTwoChars.peekLatest()?.let { latest ->
if (latest.code == 160) {
return true
}
}

return false
}

fun pushVerbatimTtsAnnotation(verbatim: String) =
builder.pushTtsAnnotation(VerbatimTtsAnnotation(verbatim))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ class AnnotatedStringComposer : HtmlParser() {
strings

override fun emitParagraph(): Boolean {
if (builder.isEmpty()) {
// List items emit dots and non-breaking space. Don't newline after that
if (builder.isEmpty() || builder.endsWithNonBreakingSpace) {
// Nothing to emit, and nothing to reset
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class EagerComposer(
}

override fun emitParagraph(): Boolean {
if (builder.isEmpty()) {
// List items emit dots and non-breaking space. Don't newline after that
if (builder.isEmpty() || builder.endsWithNonBreakingSpace) {
// Nothing to emit, and nothing to reset
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ private fun HtmlComposer.appendTextChildren(
.forEach { listItem ->
withParagraph {
// no break space
append("• ")
append("•\u00A0")
appendTextChildren(
listItem.childNodes(),
baseUrl = baseUrl,
Expand All @@ -595,7 +595,7 @@ private fun HtmlComposer.appendTextChildren(
.forEachIndexed { i, listItem ->
withParagraph {
// no break space
append("${i + 1}. ")
append("${i + 1}.\u00A0")
appendTextChildren(
listItem.childNodes(),
baseUrl = baseUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class LazyListComposer(
) : HtmlComposer() {

override fun emitParagraph(): Boolean {
if (builder.isEmpty()) {
// List items emit dots and non-breaking space. Don't newline after that
if (builder.isEmpty() || builder.endsWithNonBreakingSpace) {
// Nothing to emit, and nothing to reset
return false
}
Expand Down
1 change: 1 addition & 0 deletions app/stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
No issues found.