From 4638783e67085b209a72eb7d5f2a29213df9845b Mon Sep 17 00:00:00 2001 From: Hamish <133548095+Hamster45105@users.noreply.github.com> Date: Sat, 16 Nov 2024 12:11:22 +1100 Subject: [PATCH] Initial MkDocs code --- .github/workflows/deploy-wiki.yml | 32 +++++++ README.md | 2 +- .../android/en-US/full_description.txt | 2 +- .../metadata/android/ru/full_description.txt | 2 +- lib/pages/settings.dart | 2 +- lib/providers/settings_provider.dart | 1 + mkdocs.yml | 73 ++++++++++++++++ wiki/app_tracking.md | 79 ++++++++++++++++++ wiki/assets/logo.png | Bin 0 -> 39835 bytes wiki/index.md | 71 ++++++++++++++++ wiki/overrides/partials/copyright.html | 14 ++++ wiki/settings.md | 51 +++++++++++ wiki/sources.md | 72 ++++++++++++++++ wiki/stylesheets/custom.css | 39 +++++++++ wiki/ui_overview.md | 43 ++++++++++ 15 files changed, 479 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/deploy-wiki.yml create mode 100644 mkdocs.yml create mode 100644 wiki/app_tracking.md create mode 100644 wiki/assets/logo.png create mode 100644 wiki/index.md create mode 100644 wiki/overrides/partials/copyright.html create mode 100644 wiki/settings.md create mode 100644 wiki/sources.md create mode 100644 wiki/stylesheets/custom.css create mode 100644 wiki/ui_overview.md diff --git a/.github/workflows/deploy-wiki.yml b/.github/workflows/deploy-wiki.yml new file mode 100644 index 00000000..fa074f7e --- /dev/null +++ b/.github/workflows/deploy-wiki.yml @@ -0,0 +1,32 @@ +name: Deploy MkDocs +on: + push: + branches: + - main + paths: + - 'mkdocs.yml' + - 'wiki/**' + - '.github/workflows/deploy-wiki.yml' +permissions: + contents: write +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Configure Git Credentials + run: | + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV + - uses: actions/cache@v4 + with: + key: mkdocs-material-${{ env.cache_id }} + path: .cache + restore-keys: | + mkdocs-material + - run: pip install mkdocs-material + - run: mkdocs gh-deploy --force diff --git a/README.md b/README.md index f1f7efeb..1c72ef19 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Get Android app updates straight from the source. Obtainium allows you to install and update apps directly from their releases pages, and receive notifications when new releases are made available. More info: -- [Obtainium Wiki](https://github.com/ImranR98/Obtainium/wiki) +- [Obtainium Wiki](https://imranr98.github.io/Obtainium) - [AppVerifier](https://github.com/soupslurpr/AppVerifier) - App verification tool (recommended, integrates with Obtainium) - [apps.obtainium.imranr.dev](https://apps.obtainium.imranr.dev/) - Crowdsourced app configurations ([repository](https://github.com/ImranR98/apps.obtainium.imranr.dev)) - [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0) - Original motivation for this app diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt index 0bd67ad5..74bb7cd7 100644 --- a/fastlane/metadata/android/en-US/full_description.txt +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -1,5 +1,5 @@

Obtainium allows you to install and update Apps directly from their releases pages, and receive notifications when new releases are made available.

-

Read the Wiki

+

Read the Wiki

Currently supported App sources:

diff --git a/fastlane/metadata/android/ru/full_description.txt b/fastlane/metadata/android/ru/full_description.txt index bab594b6..dc0f00e8 100644 --- a/fastlane/metadata/android/ru/full_description.txt +++ b/fastlane/metadata/android/ru/full_description.txt @@ -1,5 +1,5 @@

Obtainium позволяет вам устанавливать и обновлять приложения прямо с их объявлений о выпусках и получать уведомления о новых выпусках.

-

Для деталей читайте Вики

+

Для деталей читайте Вики

Поддерживаемые источники приложений:

diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 19abd3a5..6c055eb6 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -886,7 +886,7 @@ class _SettingsPageState extends State { ), IconButton( onPressed: () { - launchUrlString('${settingsProvider.sourceUrl}/wiki', + launchUrlString(settingsProvider.wikiUrl, mode: LaunchMode.externalApplication); }, icon: const Icon(Icons.help_outline_rounded), diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index acc8b1e2..4204f1d7 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -31,6 +31,7 @@ class SettingsProvider with ChangeNotifier { bool justStarted = true; String sourceUrl = 'https://github.com/ImranR98/Obtainium'; + String wikiUrl = 'https://imranr98.github.io/Obtainium'; // Not done in constructor as we want to be able to await it Future initializeSettings() async { diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..d522221b --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,73 @@ +site_name: Obtainium Wiki +repo_url: https://github.com/ImranR98/Obtainium +docs_dir: wiki +site_description: Help page for Obtainium +site_author: ImranR98 +edit_uri: edit/main/wiki/ + +nav: + - Home: index.md + - UI Overview: ui_overview.md + - How Apps are Tracked: app_tracking.md + - App Sources: sources.md + - Settings: settings.md + +theme: + name: material + logo: assets/logo.png + custom_dir: wiki/overrides + favicon: https://raw.githubusercontent.com/ImranR98/Obtainium/main/assets/graphics/icon_small.png + + features: + - navigation.instant + - navigation.instant.progress + - content.action.edit + - search.suggest + - search.highlight + - search.share + - content.tooltips + + icon: + repo: fontawesome/brands/github + annotation: material/arrow-right-circle + + palette: + - primary: deep purple + - accent: green + +markdown_extensions: + - admonition + - pymdownx.details + - attr_list + - md_in_html + - attr_list + - pymdownx.snippets + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - pymdownx.tabbed: + alternate_style: true + +extra: + social: + - icon: simple/github + link: https://github.com/ImranR98/ + name: GitHub + - icon: simple/mastodon + link: https://freeradical.zone/@imran + name: Mastodon + - icon: simple/kofi + link: https://ko-fi.com/imranr + name: Ko-fi + - icon: material/heart + link: https://github.com/sponsors/ImranR98 + name: GitHub Sponsors + - icon: material/web + link: https://imranr.dev/ + name: Website + +plugins: + - search + +extra_css: + - stylesheets/custom.css diff --git a/wiki/app_tracking.md b/wiki/app_tracking.md new file mode 100644 index 00000000..9d6ebcd7 --- /dev/null +++ b/wiki/app_tracking.md @@ -0,0 +1,79 @@ +--- +title: How Apps are Tracked +description: How Apps are Tracked in Obtainium +--- + +# How Apps are Tracked + +## Basics + +When you add an app URL to Obtainium, you must pick an app [source](sources.md). Sources define how app info and APK files will be extracted from the URL you enter. Most of the time, Obtainium will automatically select the appropriate source to use - when this is not possible, an "Override Source" dropdown will be presented. + +At minimum, an app source must provide the following data for its apps: + +- The app version (or a 'pseudo-version' - an identifier that changes for each new version of the app) +- At least one APK download URL that corresponds to the version that was provided + +App sources may also provide other info - these enable extra features or UI benefits. For example: + +- The app author's name +- The app's package ID +- The release date of the latest version +- Info for previous versions or variants of the app + +In an ideal world, each app source would provide all required info in a straightforward way - with a single app per given URL with all required info provided in a standard format. However this is often not the case - there are many different ways app releases are handled even by the same source, so it isn't possible to have a fixed set of steps to handle them all. For this reason, you are presented with various additional options when adding an app, and these can be used to modify the way app info will be extracted. While the defaults work for most apps, you may want to understand these options to deal with edge cases - more info in the [App Sources](sources.md) section below. + +Note: Many filter settings in Obtainium (including many source-specific optional filters) make use of [regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) - you should be familiar with these. + +## Version Detection + +When Obtainium is tracking an app that is currently installed, it grabs the version of the app from Android and compares it to the version string provided by the source. It then compares the two to decide whether an update is available or whether the install status of the app has changed. This comparison can only be made if the two versions follow the same format, which may not always be the case. For example, you could have any of these cases among others: + +1. [Obtainium](https://github.com/ImranR98/Obtainium/releases/tag/v0.14.21-beta) from GitHub: + - Android-reported app version: `0.14.21` + - Source-reported version: `v0.14.21-beta` +2. [Cheogram](https://git.singpolyma.net/cheogram-android/refs/2.12.8-2) from a SourceHut instance: + - Android-reported app version: `2.12.8-2+free` + - Source-reported version: `2.12.8-2` +3. [Tor](https://www.torproject.org/download/) from the Tor website: + - Android-reported app version: `102.2.1-Release (12.5.6)` + - Source-reported version: none (no version string is provided by this HTML source so a URL hash is used instead as a 'pseudo-version') +4. [Quotable](https://github.com/Lijukay/Qwotable/releases/tag/v10) from GitHub: + - Android-reported app version: `1` + - Source-reported version: `v10` + +Obtainium stores a list of "standard" formats which it uses to make this comparison (like `x.y.z` or `x.y`). If both versions being compared conform to the same format, the comparison will be made. If not, version detection will be disabled for that app. In some cases, Obtainium will strip off extra parts from the source string if doing so would result in a standard version (like how `v` and `-beta` are removed from Obtainium's `v0.14.21-beta`), then it can make the comparison. We never try to strip parts off the "real" OS-provided version. + +This piece of code defines how the various "standard" formats are generated: https://github.com/ImranR98/Obtainium/blob/main/lib/providers/apps_provider.dart#L64 + +It's always possible to expand that code to add support for more formats, but this requires careful consideration. For example if Android reports that an installed app's version is `1.2` but the source says the latest available version of that app is `1.2-4`, should we strip off the `-4` and say the two are the same (meaning there is no update available)? This may be fine in some contexts (where the `-4` is not actually indicative of a change in the app itself) but not in other contexts. So it wouldn't be a good idea to support that specific case. + +Version detection being turned off should not usually have a significant impact on day-to-day use. If version detection is disabled for an app, you may occasionally run into inconsistencies between the real version of the app installed on your system and the version shown in the Obtainium UI. This should only happen in two cases: + +1. If an app's version changes due to actions taken outside of Obtainium (for example if it gets updated by Google Play) +2. If an attempt by Obtainium to [silently update](#background-updates) the app in the background fails + +In such cases, Obtainium would not be able to detect that the app's real OS version has changed and so it would not update its internal records accordingly - you would need to manually correct the inconsistency. + +See also: https://github.com/ImranR98/Obtainium/issues/946#issuecomment-1741745587 + +## Background Updates + +Obtainium checks for app updates in the background on a regular basis. You can control the frequency of these update tasks on the settings page. + +After a background update checking task is completed, any available updates are divided into 2 categories: + +1. Updates that can be applied in the background +2. Updates that cannot be applied in the background + +For an update to be automatically installed in the background (AKA a silent update), certain criteria should be met: + +- The OS must be Android 12 or higher +- The app being installed must target a [recent Android API level](https://developer.android.com/reference/android/content/pm/PackageInstaller.SessionParams#setRequireUserAction(int)) +- The currently installed version of the app must have been installed by Obtainium +- You must have background updates enabled in Obtainium (both universally and for this app in particular - this is the default) +- If there are multiple APKs available for the update, the additional options for that app must be configured such that Obtainium can filter these down to one APK + +Each available update is downloaded and installed if possible, and the user is then notified either of the update's availability or that it was installed in the background. + +Note that due to technical limitations, background updates can only be installed on an asynchronous, best-effort basis. So if a background update fails to install, you will not be notified of the error. diff --git a/wiki/assets/logo.png b/wiki/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d91b89497acff44c8c4c9940b112d8ab2fb64e0f GIT binary patch literal 39835 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�o8j$_472!QS!hy* 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 Uyxuzc2?c-dE(GT${E;*N2l`2|=Kufz literal 0 HcmV?d00001 diff --git a/wiki/index.md b/wiki/index.md new file mode 100644 index 00000000..1d40c244 --- /dev/null +++ b/wiki/index.md @@ -0,0 +1,71 @@ +# ![Obtainium Icon](https://raw.githubusercontent.com/ImranR98/Obtainium/main/assets/graphics/icon_small.png) Obtainium Wiki + +--- + +[![Ceasefire Now](https://badge.techforpalestine.org/default)](https://techforpalestine.org/learn-more) + +Get Android app updates straight from the source. + +Obtainium's core goal is to automate the process of downloading and installing Android app updates directly from their "source" websites (sites where app files are available for direct download). This process needs automation since users may not be willing or able to rely on an app store to update a given app, but Android apps (unlike PC apps) usually assume they will be updated externally by an app store and therefore don't include a built-in self-update function. + +While this concept is simple, the wide variety of supported sources, user preferences, and APK naming, versioning, and distribution methods make things more complicated. This Wiki explains the various app sources and settings available in Obtainium. It is not comprehensive and may not be fully up to date. + +More info + +- [AppVerifier](https://github.com/soupslurpr/AppVerifier) - App verification tool (recommended, integrates with Obtainium) +- [apps.obtainium.imranr.dev](https://apps.obtainium.imranr.dev/) - Crowdsourced app configurations +- [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0) - Original motivation for this app + +Currently supported App sources: + +- Open Source - General: + - [GitHub](https://github.com/) + - [GitLab](https://gitlab.com/) + - [Forgejo](https://forgejo.org/) ([Codeberg](https://codeberg.org/)) + - [F-Droid](https://f-droid.org/) + - Third Party F-Droid Repos + - [IzzyOnDroid](https://android.izzysoft.de/) + - [SourceHut](https://git.sr.ht/) +- Other - General: + - [APKPure](https://apkpure.net/) + - [Aptoide](https://aptoide.com/) + - [Uptodown](https://uptodown.com/) + - [Huawei AppGallery](https://appgallery.huawei.com/) + - [Tencent App Store](https://sj.qq.com/) + - Jenkins Jobs + - [APKMirror](https://apkmirror.com/) (Track-Only) +- Open Source - App-Specific: + - [VLC](https://videolan.org/) +- Other - App-Specific: + - [WhatsApp](https://whatsapp.com) + - [Telegram App](https://telegram.org) + - [Neutron Code](https://neutroncode.com) +- Direct APK Link +- "HTML" (Fallback): Any other URL that returns an HTML page with links to APK files + +## Finding App Configurations + +You can find crowdsourced app configurations at [apps.obtainium.imranr.dev](https://apps.obtainium.imranr.dev). + +If you can't find the configuration for an app you want, feel free to leave a request on the [discussions page](https://github.com/ImranR98/apps.obtainium.imranr.dev/discussions/new?category=app-requests). + +Or, contribute some configurations to the website by creating a PR at [this repo](https://github.com/ImranR98/apps.obtainium.imranr.dev). + +## Installation + +[Get it on GitHub](https://github.com/ImranR98/Obtainium/releases) +[Get it on IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/dev.imranr.obtainium) +[Get it on F-Droid](https://f-droid.org/packages/dev.imranr.obtainium.fdroid/) + +Verification info: + +- Package ID: `dev.imranr.obtainium` +- SHA-256 Hash of Signing Certificate: `B3:53:60:1F:6A:1D:5F:D6:60:3A:E2:F5:0B:E8:0C:F3:01:36:7B:86:B6:AB:8B:1F:66:24:3D:A9:6C:D5:73:62` + - Note: The above signature is also valid for the F-Droid flavour of Obtainium, thanks to [reproducible builds](https://f-droid.org/docs/Reproducible_Builds/). +- [PGP Public Key](https://keyserver.ubuntu.com/pks/lookup?search=contact%40imranr.dev&fingerprint=on&op=index) (to verify APK hashes) + +## Limitations +- For some sources, data is gathered using Web scraping and can easily break due to changes in website design. In such cases, more reliable methods may be unavailable. diff --git a/wiki/overrides/partials/copyright.html b/wiki/overrides/partials/copyright.html new file mode 100644 index 00000000..03dc7e4b --- /dev/null +++ b/wiki/overrides/partials/copyright.html @@ -0,0 +1,14 @@ + diff --git a/wiki/settings.md b/wiki/settings.md new file mode 100644 index 00000000..6dd5257c --- /dev/null +++ b/wiki/settings.md @@ -0,0 +1,51 @@ +--- +title: Settings +description: Settings explained +--- + +# Settings + +Explainations of the options in Obtainium Settings + +## Source Specific + +GitHub puts a cap on the number of API requests you can make in a given period of time. Since Obtainium uses the GitHub API to grab release info, you may run into a "rate limit" error if you have more than a few dozen GitHub apps. You can get around this by getting a Personal Access Token. + +GitLab releases sometimes contain APKs that are attached in non-standard ways, such that Obtainium cannot get to them easily. The GitLab API provides a far more reliable way to extract APKs but it cannot be used without an API key. While this shouldn't be an issue for most GitLab repos, you can add your own Personal Access Token in Obtainium's settings for more reliable APK extraction in edge cases where it does turn out to be a problem. + +### Setting Up Personal Access Tokens + +=== ":simple-github: GitHub" + To avoid API rate limits when tracking GitHub apps: + + 1. Login to [GitHub](https://github.com). + + 2. Go to the [Fine-grained tokens](https://github.com/settings/tokens?type=beta) section in developer settings. + + 3. Select **Generate new token**. + + 4. Give your token name and set an expiry date. + + 5. Scroll to the bottom and select **Generate token**. + + 6. Copy the token and paste it into the Obtainium settings. Make sure to copy your token now as you will not be able to see it again. + +=== ":simple-gitlab: GitLab" + For more reliable APK extraction from GitLab releases: + + 1. Login to [GitLab](https://github.com). + + 2. Go to the [personal access tokens](https://gitlab.com/-/user_settings/personal_access_tokens) section in settings. + + 3. Select **Add new token**. + + 4. Give your token name and set an expiration date. + + 5. Tick the `read_api` box. + + 6. Scroll to the bottom and select **Create personal access token**. + + 7. Copy the token and paste it into the Obtainium settings. Make sure to copy your token now as you will not be able to see it again. + + !!! info "When is this needed?" + See [this explanation](https://github.com/ImranR98/Obtainium/issues/3#issuecomment-1234695412) about non-standard APK attachments in GitLab releases diff --git a/wiki/sources.md b/wiki/sources.md new file mode 100644 index 00000000..9b7a4067 --- /dev/null +++ b/wiki/sources.md @@ -0,0 +1,72 @@ +--- +title: App Sources +description: Information specific to certain sources +--- + +# App Sources + +The way that an app is added, checked for updates, and installs will vary depending on source and the settings configured for that app by the user. + +The following options are available for all apps regardless of source: + +- Track-Only: Enabling this toggle allows you to receive update notifications for apps without attempting to actually download their APKs. This is useful to track updates for apps where no APKs are available, or where they cannot easily be extracted by Obtainium. Some sources are exclusively track-only - for example [ApkMirror](#apkmirror). Note that [version detection](app_tracking.md/#version-detection) will not work for any apps added as track-only. +- Version Detection: See the section on [version detection](app_tracking.md/#version-detection). +- Filter APKs by Regular Expression: When more than one APK is available for an app release, the user is prompted to pick one manually. Aside from being inconvenient, this means the app will not be updated silently in the background even when doing so would have otherwise been possible. This option lets the user specify a regular expression that can be used to filter out APK that don't match it (ideally down to one option). +- Attempt to filter APKs by CPU architecture if possible: This toggle will tell Obtainium to automatically filter out any APKs that don't appear to be compatible with the user's device. The logic used to do this is very simple and is based on the APK filename, so it is not always reliable and the user may still need to do their own filtering in many cases. +- App Name: This allows the user to specify their own name for the app instead of using the one Obtainium extracts. +- Exempt from background updates: This toggle will prevent the app from being updated silently in the background even if background updates are enabled and this app would otherwise have fulfilled all criteria to be updated in the background. +- Skip update notifications: This toggle tells Obtainium to avoid notifying the user that this app has an update available or that it was updated in the background. + +Aside from those, each app has additional source-specific options. Most of those are self-explanatory and may be updated frequently, so they are not covered in this Wiki. However, some sources do have more unique behaviours that need more explanation, and these are covered below. + +## GitHub + +GitHub puts a cap on the number of API requests you can make in a given period of time. Since Obtainium uses the GitHub API to grab release info, you may run into a "rate limit" error if you have more than a few dozen GitHub apps. You can get around this by following [these instructions](settings.md/#__tabbed_1_1). + +GitHub also allows developers to host multiple releases of their app. This usually means older versions of the same app, but may also include pre-releases, variants, etc. - so Obtainium provides various filters that let you navigate this and grab the exact releases you are interested in. + +## GitLab + +GitLab releases sometimes contain APKs that are attached in non-standard ways, such that Obtainium cannot get to them easily. To fix this, follow [these instructions](settings.md/#__tabbed_1_2). + +Just like GitHub, GitLab also allows developers to host older releases of the same app, so additional options are provided as appropriate. + +## F-Droid Third Party Repo + +Unlike with most other sources, F-Droid repos contain info about multiple apps all under the same URL. This means that, in addition to the repo URL, you must provide the name or ID of the app you want separately. If you're not sure what apps are available in a given repo, you can use the "Search Source" button on the [Import/Export page](ui_overview.md/#importexport-page) to find out. + +Note that Obtainium cannot automatically tell that a given URL points to a third-party F-Droid repo. This means that by default, adding a third-party F-Droid repo to Obtainium will result in the incorrect use of the [HTML source](#html) (a fallback source Obtainium uses for any URL it does not recognize). You must manually select "F-Droid Third Party Repo" from the "Override Source" dropdown to get around this. + +## APKMirror + +APKMirror is a "Track-Only" source. This means that while you can add an APKMirror URL to Obtainium for update notifications, you will not be able to actually download and install the app. This is because the maintainers of APKMirror do not allow it (see [issue #44](https://github.com/ImranR98/Obtainium/issues/44)). + +## HTML + +The 'HTML' source is a fallback option for any app sources that are not explicitly supported by Obtainium. Because of its flexibility, it is also a way of supporting niche, less popular sources without bloating the app. + +The HTML Source works in this way: + +1. It sends a request to the source URL provided by the user, and parses the response as HTML. It then looks for links on the page. +2. It filters out certain links. By default, this is any link that doesn't end in `.apk` but you can use "Custom APK Link Filter" to specify your own filter. +3. It sorts the remaining links. This sorting is alphanumeric sorting on the whole link, but you can choose to sort by only the last segment of the link. This last segment is usually the filename but may not be if you used your own filter in step 2. +4. It applies yet another optional user-defined filter on all remaining links. The difference between this filter and the one from step 2 is that this one is a more general filter that all Sources have - it is inherited from the parent `AppSource` class (the APK filter option described in [App Sources](#app-sources)). It's probably easier to use this in some situations rather than having a single more complicated regular expression in step 2 (see [this comment](https://github.com/ImranR98/Obtainium/issues/954#issuecomment-1745977857) for an example). It is also useful when used in combination with the intermediate link option. +5. Of the remaining links, it picks the first one (or the last one if you enabled the reverse option). +6. Now that we have the final APK link, we need a unique release ID to go with it so that when the ID changes, we know the app has an update available. For other Sources, the unique release ID is the app version, but for the HTML Source, it might not be possible to extract a version string. So by default, this is just a hash of the link. + - However, links often have version strings embedded within them. Obtainium can't know how to extract these on its own (different websites would have different ways of doing it), so the user can choose to specify a regular expression that can be applied to the link in order to extract the version - this is what the "Version Extraction" field is for. + - But often it can be difficult to come up with a regular expression that accurately matches the version while excluding extra characters. For this, we have the "Match Group" option that lets the user specify which group in the regular expression we should use as a version. + - The version extraction feature isn't really necessary - using link hashes is easier and more reliable. Some users might just want it since having the real version looks nicer/more accurate and it allows Obtainium, in most cases, to use [version detection](app_tracking.md/#version-detection). + +As for the "Intermediate Link" filter, if this is used, the HTML Source works as follows (see [issue #820](https://github.com/ImranR98/Obtainium/issues/820) for a situation where this is useful): + +1. It looks for links on an HTML page. +2. It filters out any links that don't match the intermediate link filter. +3. It grabs the first remaining link (no reverse option here), and then feeds that link as the input for the normal HTML Source process described previously. + +## Notes for Various Other Sources + +- HTML: Note that the HTML source includes default request headers that should be appropriate for most sites. In some cases (for example [SourceForge](https://sourceforge.net/), you may need to delete them (and possibly specify your own). +- Codeberg: This source is almost identical to GitHub in its additional options. +- F-Droid: Any app from F-Droid is likely to be [signed](https://developer.android.com/studio/publish/app-signing) with a different key than releases of the same app from other sources. This means that trying to update from an F-Droid release of a given app to a release from another source (for example GitHub) is likely to fail. +- Any Source that does not have a specific host associated with it (like [third-party F-Droid repos](#f-droid-third-party-repo), Jenkins instances, and SourceHut instances) will not be automatically recognized by Obtainium. You must manually pick the right source from the "Override Source" dropdown. +- Some sources (like APKPure) may provide [XAPK files](https://apkpure.com/xapk.html) instead of APK files. Obtainium's XAPK support is incomplete and may not work reliably. \ No newline at end of file diff --git a/wiki/stylesheets/custom.css b/wiki/stylesheets/custom.css new file mode 100644 index 00000000..acbad8ec --- /dev/null +++ b/wiki/stylesheets/custom.css @@ -0,0 +1,39 @@ +/* Download badges styling */ +.md-content img[alt*="Get it on"] { + height: 75px !important; + margin: 10px; + vertical-align: middle; + transition: transform 0.2s ease; +} + +.md-content img[alt*="Get it on"]:hover { + transform: scale(1.05); +} + +/* Copyright section styling */ + +.md-copyright { + padding: 1rem 0; + } + +.md-copyright__inner { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.md-copyright__separator { + color: var(--md-primary-fg-color--light); +} + +.md-copyright__link { + color: var(--md-primary-fg-color); + text-decoration: none; + transition: color 125ms; +} + +.md-copyright__link:hover { + color: var(--md-accent-fg-color); + text-decoration: underline; +} diff --git a/wiki/ui_overview.md b/wiki/ui_overview.md new file mode 100644 index 00000000..78993aef --- /dev/null +++ b/wiki/ui_overview.md @@ -0,0 +1,43 @@ +--- +title: UI Overview +description: Overview of the Obtainium UI +--- + +# UI Overview + +The tab bar at the bottom of the screen is the main way to navigate around Obtainium. + +## Apps Page + +This is the main screen; it contains a list of apps being tracked by Obtainium, and provides the basic info for each app. + +On this page, you can use the buttons at the bottom of the screen to perform various operations (like delete, update, tag, etc.) on multiple apps at once. You can do this after selecting one or more apps, either using a long-press to enter multi-select mode or using the "select all" button. + +You can also filter for specific apps by pressing the filter button. This lets you view only the apps that fulfill specific criteria (like installed/not-installed, latest/outdated, source site, etc.). + +## Add App Page + +This page lets you add an app by its source URL. + +As you enter the source URL, Obtainium will automatically detect what [source logic](app_tracking.md/#basics) it should use, and will present the relevant options. If a URL does not correspond to any supported source, the "[HTML](sources.md/#html)" source will be selected as a default. This source is a general fallback that may work in some cases. If you think Obtainium has made the wrong choice, you can manually specify the source type to use. + +For most sources, you don't need to be exact with the URL you enter; for example, any GitHub URL that contains the base repo URL would be accepted by the GitHub source (for example, `https://github.com/ImranR98/Obtainium/releases/latest` would automatically be trimmed down to `https://github.com/ImranR98/Obtainium`). However, your URL must be more precise in two situations: +1. When using the HTML source + - For example, the Tor Android APK is at `https://www.torproject.org/download/` so entering only `https://www.torproject.org/` would not work. +2. When manually picking a source (overriding Obtainium's HTML fallback choice) + +This page also lets you search for apps across sources that support this feature (the [Import/Export](#importexport-page) page also provides a separate search tool). Note that just because an app shows up in the search results does not mean it will be added successfully - it still needs to fulfill all other criteria. + +Finally, this page lists all supported sources, along with additional info such as whether a source is searchable. + +## Import/Export Page + +This page lets you export your Obtainium data so that it can be imported later. You can also enable automatic exports that happen whenever any data changes. + +This page also provides various other ways of importing large numbers of apps, including through search. + +The search tool on this page also lets you search sources that require you to specify the source host/domain, such as [third-party F-Droid repos](sources.md/#f-droid-third-party-repo). + +## Settings Page + +This page provides various UI and behaviour settings, including the option to enable more functionality for specific sources by adding required credentials. \ No newline at end of file