Skip to content

Commit f1a3a23

Browse files
committed
Changed so image enclosures are shown in the Reader
1 parent ccabb49 commit f1a3a23

File tree

9 files changed

+906
-12
lines changed

9 files changed

+906
-12
lines changed

app/schemas/com.nononsenseapps.feeder.db.room.AppDatabase/29.json

+737
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.nononsenseapps.feeder.db.room
2+
3+
import androidx.core.database.getStringOrNull
4+
import androidx.room.testing.MigrationTestHelper
5+
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
6+
import androidx.test.core.app.ApplicationProvider
7+
import androidx.test.ext.junit.runners.AndroidJUnit4
8+
import androidx.test.filters.LargeTest
9+
import androidx.test.platform.app.InstrumentationRegistry
10+
import com.nononsenseapps.feeder.FeederApplication
11+
import kotlin.test.assertNull
12+
import org.junit.Rule
13+
import org.junit.Test
14+
import org.junit.runner.RunWith
15+
import org.kodein.di.DI
16+
import org.kodein.di.DIAware
17+
import org.kodein.di.android.closestDI
18+
19+
@RunWith(AndroidJUnit4::class)
20+
@LargeTest
21+
class TestMigrationFrom28To29 : DIAware {
22+
private val dbName = "testDb"
23+
private val feederApplication: FeederApplication = ApplicationProvider.getApplicationContext()
24+
override val di: DI by closestDI(feederApplication)
25+
26+
@Rule
27+
@JvmField
28+
val testHelper: MigrationTestHelper = MigrationTestHelper(
29+
InstrumentationRegistry.getInstrumentation(),
30+
AppDatabase::class.java,
31+
emptyList(),
32+
FrameworkSQLiteOpenHelperFactory(),
33+
)
34+
35+
@Test
36+
fun migrate() {
37+
@Suppress("SimpleRedundantLet")
38+
testHelper.createDatabase(dbName, FROM_VERSION).let { oldDB ->
39+
oldDB.execSQL(
40+
"""
41+
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)
42+
VALUES(1, 'feed', 'http://url', '', '', 0, 0, 666, 0, '', 0, 0, 0, 0)
43+
""".trimIndent(),
44+
)
45+
oldDB.execSQL(
46+
"""
47+
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)
48+
VALUES(8, 'http://item1', 'title', 'ptitle', 'psnippet', 0, 1, 0, 0, 1, 0, 0, 0, 1)
49+
""".trimIndent(),
50+
)
51+
}
52+
val db = testHelper.runMigrationsAndValidate(
53+
dbName,
54+
TO_VERSION,
55+
true,
56+
MigrationFrom28To29(di),
57+
)
58+
59+
db.query(
60+
"""
61+
SELECT enclosure_type FROM feed_items
62+
""".trimIndent(),
63+
).use {
64+
assert(it.count == 1)
65+
assert(it.moveToFirst())
66+
assertNull(it.getStringOrNull(0))
67+
}
68+
}
69+
70+
companion object {
71+
private const val FROM_VERSION = 28
72+
private const val TO_VERSION = 29
73+
}
74+
}

app/src/main/java/com/nononsenseapps/feeder/archmodel/Repository.kt

+5
Original file line numberDiff line numberDiff line change
@@ -684,8 +684,12 @@ data class Enclosure(
684684
val present: Boolean = false,
685685
val link: String = "",
686686
val name: String = "",
687+
val type: String = "",
687688
)
688689

690+
val Enclosure.isImage: Boolean
691+
get() = type.startsWith("image/")
692+
689693
@Immutable
690694
data class Article(
691695
val item: FeedItemWithFeed?,
@@ -699,6 +703,7 @@ data class Article(
699703
present = true,
700704
link = link,
701705
name = item.enclosureFilename ?: "",
706+
type = item.enclosureType ?: "",
702707
)
703708
} ?: Enclosure(
704709
present = false,

app/src/main/java/com/nononsenseapps/feeder/db/Constants.kt

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const val COL_PLAINTITLE = "plain_title"
1818
const val COL_PLAINSNIPPET = "plain_snippet"
1919
const val COL_IMAGEURL = "image_url"
2020
const val COL_ENCLOSURELINK = "enclosure_link"
21+
const val COL_ENCLOSURE_TYPE = "enclosure_type"
2122
const val COL_LINK = "link"
2223
const val COL_AUTHOR = "author"
2324
const val COL_PUBDATE = "pub_date"

app/src/main/java/com/nononsenseapps/feeder/db/room/AppDatabase.kt

+12-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ private const val LOG_TAG = "FEEDER_APPDB"
5050
RemoteFeed::class,
5151
SyncDevice::class,
5252
],
53-
version = 28,
53+
version = 29,
5454
)
5555
@TypeConverters(Converters::class)
5656
abstract class AppDatabase : RoomDatabase() {
@@ -115,12 +115,23 @@ fun getAllMigrations(di: DI) = arrayOf(
115115
MigrationFrom25To26(di),
116116
MigrationFrom26To27(di),
117117
MigrationFrom27To28(di),
118+
MigrationFrom28To29(di),
118119
)
119120

120121
/*
121122
* 6 represents legacy database
122123
* 7 represents new Room database
123124
*/
125+
class MigrationFrom28To29(override val di: DI) : Migration(28, 29), DIAware {
126+
override fun migrate(database: SupportSQLiteDatabase) {
127+
database.execSQL(
128+
"""
129+
alter table feed_items add column enclosure_type text
130+
""".trimIndent(),
131+
)
132+
}
133+
}
134+
124135
class MigrationFrom27To28(override val di: DI) : Migration(27, 28), DIAware {
125136
override fun migrate(database: SupportSQLiteDatabase) {
126137
database.execSQL(

app/src/main/java/com/nononsenseapps/feeder/db/room/FeedItem.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import androidx.room.PrimaryKey
99
import com.nononsenseapps.feeder.db.COL_AUTHOR
1010
import com.nononsenseapps.feeder.db.COL_BOOKMARKED
1111
import com.nononsenseapps.feeder.db.COL_ENCLOSURELINK
12+
import com.nononsenseapps.feeder.db.COL_ENCLOSURE_TYPE
1213
import com.nononsenseapps.feeder.db.COL_FEEDID
1314
import com.nononsenseapps.feeder.db.COL_FIRSTSYNCEDTIME
1415
import com.nononsenseapps.feeder.db.COL_FULLTEXT_DOWNLOADED
@@ -69,6 +70,7 @@ data class FeedItem @Ignore constructor(
6970
@ColumnInfo(name = COL_PLAINSNIPPET) var plainSnippet: String = "",
7071
@ColumnInfo(name = COL_IMAGEURL) var imageUrl: String? = null,
7172
@ColumnInfo(name = COL_ENCLOSURELINK) var enclosureLink: String? = null,
73+
@ColumnInfo(name = COL_ENCLOSURE_TYPE) var enclosureType: String? = null,
7274
@ColumnInfo(name = COL_AUTHOR) var author: String? = null,
7375
@ColumnInfo(name = COL_PUBDATE, typeAffinity = ColumnInfo.TEXT) override var pubDate: ZonedDateTime? = null,
7476
@ColumnInfo(name = COL_LINK) override var link: String? = null,
@@ -125,7 +127,10 @@ data class FeedItem @Ignore constructor(
125127
this.plainSnippet = summary
126128

127129
this.imageUrl = absoluteImage
128-
this.enclosureLink = entry.attachments?.firstOrNull()?.url
130+
val firstEnclosure = entry.attachments?.firstOrNull()
131+
this.enclosureLink = firstEnclosure?.url
132+
this.enclosureType = firstEnclosure?.mime_type?.lowercase()
133+
129134
this.author = entry.author?.name ?: feed.author?.name
130135
this.link = entry.url
131136

app/src/main/java/com/nononsenseapps/feeder/db/room/FeedItemWithFeed.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.nononsenseapps.feeder.db.COL_AUTHOR
66
import com.nononsenseapps.feeder.db.COL_BOOKMARKED
77
import com.nononsenseapps.feeder.db.COL_CUSTOM_TITLE
88
import com.nononsenseapps.feeder.db.COL_ENCLOSURELINK
9+
import com.nononsenseapps.feeder.db.COL_ENCLOSURE_TYPE
910
import com.nononsenseapps.feeder.db.COL_FEEDCUSTOMTITLE
1011
import com.nononsenseapps.feeder.db.COL_FEEDID
1112
import com.nononsenseapps.feeder.db.COL_FEEDTITLE
@@ -33,7 +34,7 @@ import java.time.ZonedDateTime
3334

3435
const val feedItemColumnsWithFeed = """
3536
$FEED_ITEMS_TABLE_NAME.$COL_ID AS $COL_ID, $COL_GUID, $FEED_ITEMS_TABLE_NAME.$COL_TITLE AS $COL_TITLE,
36-
$COL_PLAINTITLE, $COL_PLAINSNIPPET, $FEED_ITEMS_TABLE_NAME.$COL_IMAGEURL, $COL_ENCLOSURELINK,
37+
$COL_PLAINTITLE, $COL_PLAINSNIPPET, $FEED_ITEMS_TABLE_NAME.$COL_IMAGEURL, $COL_ENCLOSURELINK, $COL_ENCLOSURE_TYPE,
3738
$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,
3839
$FEEDS_TABLE_NAME.$COL_TITLE AS $COL_FEEDTITLE,
3940
$FEEDS_TABLE_NAME.$COL_CUSTOM_TITLE AS $COL_FEEDCUSTOMTITLE,
@@ -51,6 +52,7 @@ data class FeedItemWithFeed @Ignore constructor(
5152
@ColumnInfo(name = COL_PLAINSNIPPET) var plainSnippet: String = "",
5253
@ColumnInfo(name = COL_IMAGEURL) var imageUrl: String? = null,
5354
@ColumnInfo(name = COL_ENCLOSURELINK) var enclosureLink: String? = null,
55+
@ColumnInfo(name = COL_ENCLOSURE_TYPE) var enclosureType: String? = null,
5456
var author: String? = null,
5557
@ColumnInfo(name = COL_PUBDATE) var pubDate: ZonedDateTime? = null,
5658
override var link: String? = null,

app/src/main/java/com/nononsenseapps/feeder/ui/compose/feedarticle/ReaderView.kt

+67-9
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import androidx.compose.foundation.focusGroup
88
import androidx.compose.foundation.indication
99
import androidx.compose.foundation.interaction.MutableInteractionSource
1010
import androidx.compose.foundation.layout.Arrangement
11+
import androidx.compose.foundation.layout.BoxWithConstraints
1112
import androidx.compose.foundation.layout.Column
1213
import androidx.compose.foundation.layout.PaddingValues
1314
import androidx.compose.foundation.layout.Spacer
15+
import androidx.compose.foundation.layout.aspectRatio
1416
import androidx.compose.foundation.layout.fillMaxWidth
1517
import androidx.compose.foundation.layout.height
1618
import androidx.compose.foundation.layout.width
@@ -21,25 +23,42 @@ import androidx.compose.foundation.lazy.rememberLazyListState
2123
import androidx.compose.foundation.text.selection.SelectionContainer
2224
import androidx.compose.material.ContentAlpha
2325
import androidx.compose.material.LocalContentAlpha
26+
import androidx.compose.material.icons.Icons
27+
import androidx.compose.material.icons.outlined.ErrorOutline
28+
import androidx.compose.material.icons.outlined.Terrain
2429
import androidx.compose.material3.MaterialTheme
2530
import androidx.compose.material3.Text
2631
import androidx.compose.runtime.Composable
2732
import androidx.compose.runtime.CompositionLocalProvider
33+
import androidx.compose.runtime.getValue
2834
import androidx.compose.runtime.remember
2935
import androidx.compose.ui.Alignment
3036
import androidx.compose.ui.Modifier
37+
import androidx.compose.ui.draw.clip
38+
import androidx.compose.ui.graphics.RectangleShape
39+
import androidx.compose.ui.layout.ContentScale
40+
import androidx.compose.ui.platform.LocalContext
3141
import androidx.compose.ui.res.stringResource
3242
import androidx.compose.ui.semantics.CustomAccessibilityAction
3343
import androidx.compose.ui.semantics.clearAndSetSemantics
3444
import androidx.compose.ui.semantics.contentDescription
3545
import androidx.compose.ui.semantics.customActions
3646
import androidx.compose.ui.semantics.semantics
3747
import androidx.compose.ui.unit.dp
48+
import coil.compose.AsyncImage
49+
import coil.request.ImageRequest
50+
import coil.size.Precision
51+
import coil.size.Scale
3852
import com.nononsenseapps.feeder.R
3953
import com.nononsenseapps.feeder.archmodel.Enclosure
54+
import com.nononsenseapps.feeder.archmodel.isImage
55+
import com.nononsenseapps.feeder.ui.compose.coil.rememberTintedVectorPainter
4056
import com.nononsenseapps.feeder.ui.compose.text.WithBidiDeterminedLayoutDirection
57+
import com.nononsenseapps.feeder.ui.compose.text.WithTooltipIfNotBlank
58+
import com.nononsenseapps.feeder.ui.compose.text.rememberMaxImageWidth
4159
import com.nononsenseapps.feeder.ui.compose.theme.LinkTextStyle
4260
import com.nononsenseapps.feeder.ui.compose.theme.LocalDimens
61+
import com.nononsenseapps.feeder.ui.compose.theme.hasImageAspectRatioInReader
4362
import com.nononsenseapps.feeder.ui.compose.utils.ProvideScaledText
4463
import com.nononsenseapps.feeder.ui.compose.utils.ScreenType
4564
import com.nononsenseapps.feeder.ui.compose.utils.focusableInNonTouchMode
@@ -158,15 +177,47 @@ fun ReaderView(
158177

159178
if (enclosure.present) {
160179
item {
161-
val openLabel = if (enclosure.name.isBlank()) {
162-
stringResource(R.string.open_enclosed_media)
180+
if (enclosure.isImage) {
181+
BoxWithConstraints(
182+
modifier = Modifier
183+
.clip(RectangleShape)
184+
.fillMaxWidth(),
185+
) {
186+
WithTooltipIfNotBlank(tooltip = enclosure.name) { innerModifier ->
187+
val imageWidth by rememberMaxImageWidth()
188+
AsyncImage(
189+
model = ImageRequest.Builder(LocalContext.current)
190+
.data(enclosure.link)
191+
.scale(Scale.FIT)
192+
.size(imageWidth)
193+
.precision(Precision.INEXACT)
194+
.build(),
195+
contentDescription = enclosure.name,
196+
placeholder = rememberTintedVectorPainter(
197+
Icons.Outlined.Terrain,
198+
),
199+
error = rememberTintedVectorPainter(Icons.Outlined.ErrorOutline),
200+
contentScale = if (dimens.hasImageAspectRatioInReader) {
201+
ContentScale.Fit
202+
} else {
203+
ContentScale.FillWidth
204+
},
205+
modifier = innerModifier
206+
.fillMaxWidth()
207+
.run {
208+
dimens.imageAspectRatioInReader?.let { ratio ->
209+
aspectRatio(ratio)
210+
} ?: this
211+
},
212+
)
213+
}
214+
}
163215
} else {
164-
stringResource(R.string.open_enclosed_media_file, enclosure.name)
165-
}
166-
Column(
167-
modifier = Modifier
168-
.width(dimens.maxReaderWidth),
169-
) {
216+
val openLabel = if (enclosure.name.isBlank()) {
217+
stringResource(R.string.open_enclosed_media)
218+
} else {
219+
stringResource(R.string.open_enclosed_media_file, enclosure.name)
220+
}
170221
ProvideScaledText(
171222
style = MaterialTheme.typography.bodyLarge.merge(
172223
LinkTextStyle(),
@@ -175,6 +226,7 @@ fun ReaderView(
175226
Text(
176227
text = openLabel,
177228
modifier = Modifier
229+
.width(dimens.maxReaderWidth)
178230
.clickable {
179231
onEnclosureClick()
180232
}
@@ -189,7 +241,11 @@ fun ReaderView(
189241
} catch (e: Exception) {
190242
// Observed nullpointer exception when setting customActions
191243
// No clue why it could be null
192-
Log.e("FeederReaderScreen", "Exception in semantics", e)
244+
Log.e(
245+
LOG_TAG,
246+
"Exception in semantics",
247+
e,
248+
)
193249
}
194250
},
195251
)
@@ -202,3 +258,5 @@ fun ReaderView(
202258
}
203259
}
204260
}
261+
262+
private const val LOG_TAG = "FEEDER_READER"

app/stdout

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
No issues found.

0 commit comments

Comments
 (0)