Compare commits

...
Author SHA1 Message Date
anasandClaude Opus 5 331e0544bc Archive the signed 0.4.0, and add the F-Droid recipe
The signing key is on this machine now, so 0.4.0 is signed and archived like
every release before it. The certificate is the same one:

  CN=Ganjoor for Android, O=anas-rashid, C=PK
  SHA-256 d3b5a144b600dd6d9ae8236f0a1bd630767e88d7eaa1573edaadbf8e651fe494

verified against 0.3.0's before archiving, which is the point of checking: a
different key would have forced every existing install to be removed first, and
releases/README.md promises that never happens.

The 0.3.0 row also gets its real commit; it still said HEAD, which stops meaning
anything the moment another release lands on top of it.

metadata/com.ganjoor.android.yml is the recipe F-Droid's build server uses,
kept with the source it describes rather than only in a fork of fdroiddata.
UpdateCheckMode is Tags, so each new tag is picked up without editing it again.
F-Droid signs with their own key, which is why the release build has to succeed
with no keystore present.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-07 19:17:08 +02:00
anasandClaude Opus 5 a725e66a9a Converge the two remotes after 0.4.0
Gitea and GitHub each made their own merge commit for the 0.4.0 release branch.
Both have the same tree — the same release, merged twice — so this reconciles
the history and leaves one main again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-07 19:02:36 +02:00
Anas Rashid 31279bc5da Merge pull request #6 from anas-rashid/release/0.4.0
Release 0.4.0
2026-10-07 19:01:30 +02:00
Anas Rashid 23efc2afa3 Merge pull request #5 from anas-rashid/test/dictionary-fits-beside
Test the rule that chooses the dictionary's container
2026-10-07 19:01:20 +02:00
anas 270c28c9c9 Merge pull request 'Release 0.4.0' (#7) from release/0.4.0 into main
Reviewed-on: #7
2026-10-07 16:53:58 +00:00
anasandClaude Opus 5 3486a89b56 Test the rule that chooses the dictionary's container
The guard added in #3 decides whether the dictionary opens beside the poem or as
a sheet over it, and until now nothing proved it. I tried four times to verify it
by hand and failed every time: reaching it needs a tap landing on the glyphs of
one Persian word inside a SelectionContainer, and adb's synthetic taps either
became a long press and opened the selection menu, or landed between words and
opened the couplet's actions instead. The verses are not exposed to uiautomator
either, so there is nothing to aim at.

The arithmetic behind the rule can be tested even though the gesture cannot, so
it moves into a pure function and gets four cases: a tablet keeps the panel, a
foldable open in portrait does not, a phone never gets it however wide the page
is measured, and the boundary sits exactly at the panel plus the minimum measure
(616dp passes, 615dp does not).

No behaviour changes — this is the same expression, named and covered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-07 18:43:01 +02:00
6 changed files with 96 additions and 2 deletions

No files matched your search

@@ -111,7 +111,7 @@ fun PoemScreen(
// foldable held open in portrait the page is already down to ~450dp, and a panel beside it
// left the verse a couple of characters a line. Where it does not fit, the sheet is the
// better answer: it covers the foot of the poem but leaves the lines whole.
val roomForPanel = wide && maxWidth - DictionaryPanelWidth >= MinPageMeasure
val roomForPanel = dictionaryFitsBeside(maxWidth, wide)
Load(
key = fullUrl,
@@ -39,6 +39,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.ganjoor.android.R
import com.ganjoor.android.data.Definition
@@ -64,6 +65,21 @@ val DictionaryPanelWidth = 216.dp
*/
val MinPageMeasure = 400.dp
/**
* Whether the dictionary earns a column of its own beside a page [pageWidth] wide.
*
* Not a question about the window but about the page: by the time a poem is open the columns have
* already taken their room, and on a book-style foldable held open in portrait what is left is
* about 450dp. A panel beside that leaves the verse a couple of characters a line, so below
* [MinPageMeasure] the bottom sheet is used instead — it covers the foot of the poem but leaves
* every line whole.
*
* A pure function so the rule can be tested: the gesture that reaches it cannot be driven
* reliably, but the arithmetic behind it can.
*/
internal fun dictionaryFitsBeside(pageWidth: Dp, wide: Boolean): Boolean =
wide && pageWidth - DictionaryPanelWidth >= MinPageMeasure
/** English prose inside an otherwise right-to-left sheet. */
@Composable
private fun LeftToRight(content: @Composable () -> Unit) {
@@ -0,0 +1,41 @@
package com.ganjoor.android
import androidx.compose.ui.unit.dp
import com.ganjoor.android.ui.dictionaryFitsBeside
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
/**
* The rule that decides whether the dictionary opens beside the poem or as a sheet over it.
*
* Driving the gesture that reaches it — a tap landing on the glyphs of one Persian word — is not
* something adb does reliably, so the arithmetic is tested directly instead.
*/
class DictionaryPanelFitTest {
@Test
fun `a tablet has room beside the poem`() {
// 1280dp window, less the poets rail and two list columns, leaves the page about 900dp.
assertTrue(dictionaryFitsBeside(900.dp, wide = true))
}
@Test
fun `a foldable open in portrait does not`() {
// 700dp window, less the rail and the newest column, leaves about 450dp — and 450 less
// the 216dp panel is 234dp, which is a cramped phone column, not a measure for a verse.
assertFalse(dictionaryFitsBeside(450.dp, wide = true))
}
@Test
fun `a phone never gets the panel, however the page is measured`() {
assertFalse(dictionaryFitsBeside(420.dp, wide = false))
assertFalse(dictionaryFitsBeside(2000.dp, wide = false))
}
@Test
fun `the boundary is the panel plus the minimum measure`() {
assertTrue(dictionaryFitsBeside(616.dp, wide = true)) // 400 + 216, exactly
assertFalse(dictionaryFitsBeside(615.dp, wide = true))
}
}
+35
View File
@@ -0,0 +1,35 @@
# F-Droid metadata for Ganjoor for Android.
#
# This file is not read by the app or the build; it is the recipe F-Droid's build
# server uses, kept here so it travels with the source it describes. To submit or
# update the listing, copy it into a fork of gitlab.com/fdroid/fdroiddata as
# metadata/com.ganjoor.android.yml and open a merge request.
#
# F-Droid builds from source and signs with their own key, which is why the
# release build must succeed with no keystore present — see app/build.gradle.kts.
Categories:
- Reading
License: MIT
AuthorName: Muhammad Anas Rashid
SourceCode: https://github.com/anas-rashid/ganjoorandroid
IssueTracker: https://github.com/anas-rashid/ganjoorandroid/issues
Changelog: https://github.com/anas-rashid/ganjoorandroid/releases
AutoName: گنجور
RepoType: git
Repo: https://github.com/anas-rashid/ganjoorandroid.git
Builds:
- versionName: 0.4.0
versionCode: 6
commit: v0.4.0
subdir: app
gradle:
- yes
AutoUpdateMode: Version
UpdateCheckMode: Tags
CurrentVersion: 0.4.0
CurrentVersionCode: 6
+3 -1
View File
@@ -12,7 +12,8 @@ from source and signs with its own key, so these are for direct installation onl
| 0.2.0 | 2 | `d92445f` | Share a poem, couplet or selection; look a word up from any app's selection menu; the optional AI assistant. |
| 0.2.1 | 3 | `ec05989` | Assistant answers in the page rather than over it, a visible chevron for each couplet's actions, and answers remembered so scrolling doesn't re-ask. |
| 0.2.2 | 4 | `d7568d1` | Credits Material Symbols for the glyphs, and states the terms around the optional assistant. |
| 0.3.0 | 5 | `HEAD` | Pin poets to the home screen: three views (pinned, Ganjoor's order, alphabetical), as cards or a list, with a download control on each row. |
| 0.3.0 | 5 | `5d12a1c` | Pin poets to the home screen: three views (pinned, Ganjoor's order, alphabetical), as cards or a list, with a download control on each row. |
| 0.4.0 | 6 | `270c28c` | Columns on tablets and unfolded foldables; the dictionary and reading settings beside the poem rather than over it; couplets in cards, two hemistichs to a line where there is room; skeletons while pages load; a seek bar on recitations. |
## Checksums
@@ -22,6 +23,7 @@ from source and signs with its own key, so these are for direct installation onl
5163554eb01845c131c31a9da7bdd748170bb9b5f82edaece754b4cf40b02b0c ganjoor-0.2.1.apk
e473269044710813d2cf4119de663fe180f887239df7bdb1ccee0239ef84fd3e ganjoor-0.2.2.apk
9c38d5cacf9e809c2189ba581f8aa6eb4072d4fa0316f00d66434461cf4b1459 ganjoor-0.3.0.apk
1c64cefa0fb2b51bf73386460ec1b88bda3e20832fa6926c2f7f1fb863ab9fc7 ganjoor-0.4.0.apk
```
Installing: `adb install -r releases/ganjoor-<version>.apk`. A phone holding a *debug* build has
Binary file not shown.