commit fd327955566004c40f9cbe4a5f0956acbb0ed342 equwal <13551856+equwal@users.noreply.github.com> 2026-09-26 14:36:07 -0700 Add a More apps section with links to the other apps The menu of the list has a More apps item. It opens a screen with the other sites and apps of the author. A tap on an entry opens its page in the browser. The Google Play build (-PplayStore=true) does not show the item. It does not show the Ko-fi item either. Add a More projects section to the README.
README.md | 24 +++++++++ app/src/main/AndroidManifest.xml | 5 ++ app/src/main/java/com/equwal/sbm/MainActivity.kt | 6 +++ app/src/main/java/com/equwal/sbm/MoreApps.kt | 34 ++++++++++++ .../main/java/com/equwal/sbm/MoreAppsActivity.kt | 62 ++++++++++++++++++++++ app/src/main/res/layout/activity_more_apps.xml | 21 ++++++++ app/src/main/res/layout/item_more_app.xml | 23 ++++++++ app/src/main/res/menu/main.xml | 4 ++ app/src/main/res/values/strings.xml | 30 +++++++++++ app/src/test/java/com/equwal/sbm/MoreAppsTest.kt | 24 +++++++++ 10 files changed, 233 insertions(+)
diff --git a/README.md b/README.md index a30bdc5..15963ba 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,30 @@ outside Google Play: ./gradlew bundleRelease -PplayStore=true # Google Play ./gradlew assembleRelease # F-Droid and GitHub +## More projects + +- [SubRead](https://subread.space/): read along with an audiobook, in the browser. + Also [for Android](https://github.com/equwal/subread-android/releases/latest), + [for YouTube](https://github.com/equwal/subread-extension/releases/latest) + and [for KOReader](https://github.com/equwal/subread.koplugin). +- [SubRead Overlay](https://github.com/equwal/subread-overlay/releases/latest): subtitle lines over any Android media player. +- [SubRead Dictionary](https://github.com/equwal/subread-dictionary/releases/latest): a pop-up dictionary for Android that reads Yomitan dictionaries. +- [SubRead Anki](https://github.com/equwal/subread-anki): one tap makes an Anki card from any Android app. +- [Subrep](https://github.com/equwal/subrep-android/releases/latest): live captions of the sound of your phone. +- [Book Simulator](https://booksimulator.com/): a reading room for Aozora Bunko and Project Gutenberg books. +- [honjimaku.com](https://honjimaku.com/): subtitles for Japanese audiobooks. +- [sbm Sync](https://sbmsync.com/): your bookmarks, the same on every device, + with [sbm](https://github.com/equwal/sbm) for dmenu, + [sbm for Android](https://github.com/equwal/sbm-android/releases/latest) + and the [sbm add-on](https://github.com/equwal/sbm-extension/releases/latest) for Firefox and Chrome. +- [Rebind](https://github.com/equwal/rebind/releases): remap the hardware buttons of e-ink readers and Android, + with [Ink Recents](https://github.com/equwal/ink-recents/releases/latest), + [Ink Dim](https://github.com/equwal/ink-dim/releases/latest) + and [Ink Update](https://github.com/equwal/ink-update/releases/latest). +- [dickt.store](https://dickt.store/): language-learning tools, flashcards and web toys. +- [hentaibun.online](https://hentaibun.online/): learn kanbun and kobun. +- [Recently Written](https://recentlywritten.com/): the blog, and a list of [all projects](https://recentlywritten.com/projects.html). + ## License AGPL-3.0. If sbm is useful to you, you can support it on diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ce21cad..5616ee2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -36,5 +36,10 @@ <data android:mimeType="text/plain" /> </intent-filter> </activity> + + <activity + android:name=".MoreAppsActivity" + android:exported="false" + android:label="@string/more_apps" /> </application> </manifest> diff --git a/app/src/main/java/com/equwal/sbm/MainActivity.kt b/app/src/main/java/com/equwal/sbm/MainActivity.kt index 7b76588..909a1c0 100644 --- a/app/src/main/java/com/equwal/sbm/MainActivity.kt +++ b/app/src/main/java/com/equwal/sbm/MainActivity.kt @@ -306,6 +306,8 @@ class MainActivity : Activity() { menu.findItem(R.id.preview_on).isChecked = previewOn // Google Play does not allow links to payments outside Google Play. menu.findItem(R.id.donate).isVisible = BuildConfig.DONATE + // The Google Play build has no More apps either. + menu.findItem(R.id.more_apps).isVisible = BuildConfig.DONATE return true } @@ -338,6 +340,10 @@ class MainActivity : Activity() { open(KOFI) true } + R.id.more_apps -> { + startActivity(Intent(this, MoreAppsActivity::class.java)) + true + } R.id.open_file -> { val pick = Intent(Intent.ACTION_OPEN_DOCUMENT).addCategory(Intent.CATEGORY_OPENABLE).setType("*/*") startActivityForResult(pick, CHOOSE) diff --git a/app/src/main/java/com/equwal/sbm/MoreApps.kt b/app/src/main/java/com/equwal/sbm/MoreApps.kt new file mode 100644 index 0000000..b656a9b --- /dev/null +++ b/app/src/main/java/com/equwal/sbm/MoreApps.kt @@ -0,0 +1,34 @@ +package com.equwal.sbm + +/** + * One link of the "More apps" section: a name and one line about it, as string resources, + * and the page that a tap opens. + */ +class MoreApp(val name: Int, val line: Int, val url: String) + +/** + * The other sites and apps of the same author, in the order of the catalog: the sites, + * then the Android apps, then the list of all projects. sbm for Android is not in it. + */ +val MORE_APPS = listOf( + MoreApp(R.string.more_subread, R.string.more_subread_line, "https://subread.space/"), + MoreApp(R.string.more_book_simulator, R.string.more_book_simulator_line, "https://booksimulator.com/"), + MoreApp(R.string.more_honjimaku, R.string.more_honjimaku_line, "https://honjimaku.com/"), + MoreApp(R.string.more_sbm_sync, R.string.more_sbm_sync_line, "https://sbmsync.com/"), + MoreApp(R.string.more_subread_android, R.string.more_subread_android_line, + "https://github.com/equwal/subread-android/releases/latest"), + MoreApp(R.string.more_subread_overlay, R.string.more_subread_overlay_line, + "https://github.com/equwal/subread-overlay/releases/latest"), + MoreApp(R.string.more_subread_dictionary, R.string.more_subread_dictionary_line, + "https://github.com/equwal/subread-dictionary/releases/latest"), + MoreApp(R.string.more_subread_anki, R.string.more_subread_anki_line, "https://github.com/equwal/subread-anki"), + MoreApp(R.string.more_subrep, R.string.more_subrep_line, + "https://github.com/equwal/subrep-android/releases/latest"), + MoreApp(R.string.more_rebind, R.string.more_rebind_line, "https://github.com/equwal/rebind/releases"), + MoreApp(R.string.more_ink_recents, R.string.more_ink_recents_line, + "https://github.com/equwal/ink-recents/releases/latest"), + MoreApp(R.string.more_ink_dim, R.string.more_ink_dim_line, "https://github.com/equwal/ink-dim/releases/latest"), + MoreApp(R.string.more_ink_update, R.string.more_ink_update_line, + "https://github.com/equwal/ink-update/releases/latest"), + MoreApp(R.string.more_all_projects, R.string.more_all_projects_line, "https://recentlywritten.com/projects.html"), +) diff --git a/app/src/main/java/com/equwal/sbm/MoreAppsActivity.kt b/app/src/main/java/com/equwal/sbm/MoreAppsActivity.kt new file mode 100644 index 0000000..2b2d67f --- /dev/null +++ b/app/src/main/java/com/equwal/sbm/MoreAppsActivity.kt @@ -0,0 +1,62 @@ +package com.equwal.sbm + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.view.WindowInsets +import android.widget.BaseAdapter +import android.widget.ListView +import android.widget.TextView +import android.widget.Toast +import android.widget.Toolbar + +/** The other sites and apps of the same author, from the menu of the list. A tap opens the page in a browser. */ +class MoreAppsActivity : Activity() { + override fun onCreate(state: Bundle?) { + super.onCreate(state) + setContentView(R.layout.activity_more_apps) + val toolbar = findViewById<Toolbar>(R.id.toolbar) + setActionBar(toolbar) + + // The same as the list screen: the toolbar colour goes under the + // status bar, and the rest of the screen stays clear of the bars. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + findViewById<View>(R.id.root).setOnApplyWindowInsetsListener { v, insets -> + val bars = insets.getInsets(WindowInsets.Type.systemBars()) + toolbar.setPadding(0, bars.top, 0, 0) + v.setPadding(bars.left, 0, bars.right, bars.bottom) + insets + } + } + + val list = findViewById<ListView>(R.id.list) + list.adapter = Rows() + list.setOnItemClickListener { _, _, position, _ -> open(MORE_APPS[position].url) } + } + + private fun open(url: String) { + try { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)).addCategory(Intent.CATEGORY_BROWSABLE)) + } catch (e: ActivityNotFoundException) { + Toast.makeText(this, getString(R.string.no_app, url), Toast.LENGTH_LONG).show() + } + } + + private inner class Rows : BaseAdapter() { + override fun getCount() = MORE_APPS.size + override fun getItem(position: Int) = MORE_APPS[position] + override fun getItemId(position: Int) = position.toLong() + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val view = convertView ?: layoutInflater.inflate(R.layout.item_more_app, parent, false) + view.findViewById<TextView>(R.id.title).setText(MORE_APPS[position].name) + view.findViewById<TextView>(R.id.detail).setText(MORE_APPS[position].line) + return view + } + } +} diff --git a/app/src/main/res/layout/activity_more_apps.xml b/app/src/main/res/layout/activity_more_apps.xml new file mode 100644 index 0000000..39639fb --- /dev/null +++ b/app/src/main/res/layout/activity_more_apps.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/root" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?android:attr/colorPrimary" + android:minHeight="?android:attr/actionBarSize" + android:theme="@android:style/ThemeOverlay.Material.Dark.ActionBar" + android:title="@string/more_apps" /> + + <ListView + android:id="@+id/list" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/app/src/main/res/layout/item_more_app.xml b/app/src/main/res/layout/item_more_app.xml new file mode 100644 index 0000000..030a7fa --- /dev/null +++ b/app/src/main/res/layout/item_more_app.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- One entry of More apps. It is the same as a row of the list, but the + line under the name is not cut. --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingHorizontal="16dp" + android:paddingVertical="10dp"> + + <TextView + android:id="@+id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + <TextView + android:id="@+id/detail" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:attr/textColorSecondary" /> +</LinearLayout> diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml index e8e11cc..fce940b 100644 --- a/app/src/main/res/menu/main.xml +++ b/app/src/main/res/menu/main.xml @@ -34,4 +34,8 @@ android:id="@+id/donate" android:showAsAction="never" android:title="@string/donate" /> + <item + android:id="@+id/more_apps" + android:showAsAction="never" + android:title="@string/more_apps" /> </menu> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6db0a41..d856486 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -53,4 +53,34 @@ <string name="bad_server">%1$s is not a server address.</string> <string name="no_version">the server gave no version.</string> <string name="file_busy">the file changes all the time. Try again later.</string> + + <string name="more_apps">More apps</string> + <string name="more_subread">SubRead</string> + <string name="more_subread_line">Read along with an audiobook.</string> + <string name="more_book_simulator">Book Simulator</string> + <string name="more_book_simulator_line">A reading room for Aozora Bunko and Project Gutenberg books.</string> + <string name="more_honjimaku">honjimaku.com</string> + <string name="more_honjimaku_line">Subtitles for Japanese audiobooks.</string> + <string name="more_sbm_sync">sbm Sync</string> + <string name="more_sbm_sync_line">Your bookmarks, the same on every device.</string> + <string name="more_subread_android">SubRead for Android</string> + <string name="more_subread_android_line">Times an audiobook against its ebook on the device.</string> + <string name="more_subread_overlay">SubRead Overlay</string> + <string name="more_subread_overlay_line">Subtitle lines over any Android media player.</string> + <string name="more_subread_dictionary">SubRead Dictionary</string> + <string name="more_subread_dictionary_line">Pop-up dictionary that reads Yomitan dictionaries.</string> + <string name="more_subread_anki">SubRead Anki</string> + <string name="more_subread_anki_line">One tap makes an Anki card from any app.</string> + <string name="more_subrep">Subrep</string> + <string name="more_subrep_line">Live captions of the sound of your phone.</string> + <string name="more_rebind">Rebind</string> + <string name="more_rebind_line">Remap the hardware buttons of e-ink readers and Android.</string> + <string name="more_ink_recents">Ink Recents</string> + <string name="more_ink_recents_line">A recent-apps switcher for e-ink.</string> + <string name="more_ink_dim">Ink Dim</string> + <string name="more_ink_dim_line">Frontlight below the lowest system level.</string> + <string name="more_ink_update">Ink Update</string> + <string name="more_ink_update_line">Tells you when Rebind and its extensions update.</string> + <string name="more_all_projects">All projects</string> + <string name="more_all_projects_line">Everything, with source code.</string> </resources> diff --git a/app/src/test/java/com/equwal/sbm/MoreAppsTest.kt b/app/src/test/java/com/equwal/sbm/MoreAppsTest.kt new file mode 100644 index 0000000..9f285fe --- /dev/null +++ b/app/src/test/java/com/equwal/sbm/MoreAppsTest.kt @@ -0,0 +1,24 @@ +package com.equwal.sbm + +import net.jqwik.api.Example + +class MoreAppsTest { + @Example + fun `the list leaves out this app`() { + check(MORE_APPS.none { "equwal/sbm-android" in it.url }) + } + + @Example + fun `each link opens an https page`() { + for (app in MORE_APPS) check(app.url.startsWith("https://")) { app.url } + } + + @Example + fun `the list keeps the order of the catalog`() { + val sites = listOf("https://subread.space/", "https://booksimulator.com/", "https://honjimaku.com/", "https://sbmsync.com/") + check(MORE_APPS.take(4).map { it.url } == sites) + check(MORE_APPS.last().url == "https://recentlywritten.com/projects.html") + // The catalog has 15 entries. This app is the one that is not in the list. + check(MORE_APPS.size == 14) + } +}