Links to the repo are unpleasant to see:
* Link previews are too large & distracting
* Links without a preview give no context
I want something which both gives context, and is compact
`macos-clipboard-to-discord` converts an issue link:
https://github.com/ankidroid/Anki-Android/pull/16804
into:
* A link with text: "#16804: refactor/docs: `libs.versions.toml` changelogs/renames"
* Without a preview
* Redirecting to the correct PR
`./format-for-discord` performs this formatting: accepting a PR/Issue number: 16804
and outputting the above to stdout
This folder has not been updated once in 14 years, refers to a
data source that does not exists anymore, and would be used to provide
analytics we can probably get somewhere else.
I don't expect it's worth anyone time to try repairing it.
Closes#16695. Also simplify sligthly bug #16692.
Related to PR 16348 - the file is no longer needed as it was
for the special (now defunct) ChromeOS store. We stopped processing
it, but we were still uploading it for translation (and the file was
still up in crowdin so being downloaded...)
Note: this is a single commit migration so it clears the git history
for the build file. I think this is acceptable as most history is about dependency updates.
* fix: localizations not being added to values-iw
Some devices rely on hebrew translations being in the values-heb resource directory
and others rely on the translations being in values-iw. This change is a quick fix
which reuses the same algorithm for updating localization resources, while duplicating
hebrew translations to values-heb and values-iw.
* Update tools/localization/src/update.ts
---------
Co-authored-by: Mike Hardy <github@mikehardy.net>
* build(deps): bump org.jetbrains.kotlin:kotlin-reflect
Bumps [org.jetbrains.kotlin:kotlin-reflect](https://github.com/JetBrains/kotlin) from 1.9.22 to 1.9.23.
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.9.23/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.22...v1.9.23)
---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-reflect
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
* build(deps): bump commons-io:commons-io from 2.15.1 to 2.16.0
Bumps commons-io:commons-io from 2.15.1 to 2.16.0.
---
updated-dependencies:
- dependency-name: commons-io:commons-io
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
* chore(deps): bump com.google.protobuf:protobuf-kotlin-lite
Bumps com.google.protobuf:protobuf-kotlin-lite from 3.25.3 to 4.26.1.
---
updated-dependencies:
- dependency-name: com.google.protobuf:protobuf-kotlin-lite
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* build(deps): bump org.robolectric:robolectric from 4.11.1 to 4.12.1
Bumps [org.robolectric:robolectric](https://github.com/robolectric/robolectric) from 4.11.1 to 4.12.1
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.11.1...robolectric-4.12.1)
---
updated-dependencies:
- dependency-name: org.robolectric:robolectric
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
* test: add note on fragility of robolectric references w/o proper classloader
this came from upstream - test should work now but may break in future and
with this note it shouldn't be very surprising - they may opt to fix if it is
in their interest but in general it is working but not fully supported
* build: adopt android gradle plugin 8.3.2
* build(deps): bump commons-io:commons-io from 2.16.0 to 2.16.1
Bumps commons-io:commons-io from 2.16.0 to 2.16.1.
---
updated-dependencies:
- dependency-name: commons-io:commons-io
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
* build(deps): bump lint from 31.3.1 to 31.3.2
Bumps `lint` from 31.3.1 to 31.3.2.
Updates `com.android.tools.lint:lint-api` from 31.3.1 to 31.3.2
Updates `com.android.tools.lint:lint` from 31.3.1 to 31.3.2
Updates `com.android.tools.lint:lint-tests` from 31.3.1 to 31.3.2
---
updated-dependencies:
- dependency-name: com.android.tools.lint:lint-api
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: com.android.tools.lint:lint
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: com.android.tools.lint:lint-tests
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
* build(deps-dev): bump eslint-plugin-n in /tools/localization
Bumps [eslint-plugin-n](https://github.com/eslint-community/eslint-plugin-n) from 16.6.2 to 17.2.1.
- [Release notes](https://github.com/eslint-community/eslint-plugin-n/releases)
- [Changelog](https://github.com/eslint-community/eslint-plugin-n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint-community/eslint-plugin-n/compare/16.6.2...v17.2.1)
---
updated-dependencies:
- dependency-name: eslint-plugin-n
dependency-type: direct:development
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* build(deps): bump org.mockito.kotlin:mockito-kotlin from 5.2.1 to 5.3.1
Bumps [org.mockito.kotlin:mockito-kotlin](https://github.com/mockito/mockito-kotlin) from 5.2.1 to 5.3.1.
- [Release notes](https://github.com/mockito/mockito-kotlin/releases)
- [Commits](https://github.com/mockito/mockito-kotlin/compare/5.2.1...5.3.1)
---
updated-dependencies:
- dependency-name: org.mockito.kotlin:mockito-kotlin
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
* fix(test, ci): call gradle wrapper directly now that setup-gradle is used
This was a missing second half of the forward port from "gradle-build-action"
to "setup-gradle" - now that we call setup-gradle, for future gradle-related
tasks in the workflow you just call the wrapper directly
* build(deps): bump codecov/codecov-action from 3 to 4 (#14434)
* build(deps): bump codecov/codecov-action from 3 to 4
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v3...v4)
---
updated-dependencies:
- dependency-name: codecov/codecov-action
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* fix: CODECOV_TOKEN needed in env now with v4 of action
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mike Hardy <github@mikehardy.net>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
extract & use strings from `com.github.ByteHamster:SearchPreference`
which our translators wish to translate
The repo states:
> If you want to translate the texts shown by the library together
> with your app's other strings, you can just override the strings
> defined in lib/src/main/res/values/strings.xml in your own application
> by copying those lines to your app's strings.xml.
https://github.com/ByteHamster/SearchPreference?tab=readme-ov-file#translations
We license the strings as MIT in a separate file so we may contribute them upstream (20-search-preference.xml)
There is a bug in using `search_preference_clear_history`:
https://redirect.github.com/ByteHamster/SearchPreference/issues/33
which I am patching:
https://redirect.github.com/ByteHamster/SearchPreference/issues/34
`pref_search_no_results` already exists `searchpreference_no_results`
Issue 15453
* chore(i18n): switch sync translations workflow to ubuntu from macos
our i18n infrastructure used to rely on some macOS-specific behavior,
but now that it is all javascript I don't think the runner OS actually
matters
if it does not matter, then ubuntu is preferred as macos has limited
concurrency on free accounts
* build(deps, i18n): bump all javascript dependencies
they're all minors and/or patches so nothing special
closes out all the dependabot issues for javascript deps at once
* Init Project
* upload tranlation source files to crowdin
* build, download and extract i18n files
* convert update function to ts
* fix regex, string replacement
* fix for translation of markettitle
* index for running upload, download, update
* upload, download, update using workflows
* Update sync_translations.yml
fix env config
cd to project dir
* revert username and email in git
* fix anyError condition check
* fix env and update project id
* refactor, add comments, extract constant
fix: market title string upload
create temp dir
* added comments, find broken strings before commit
- added docs
- added return type for functions
- better message for uploading, download, building and downloading
- find broken strings before commit
* add prettier
* add eslint
* remove find broken strings
* wait for zip build on crowdin server
* check broken strings
* use only non positional strings in test
* test string format errors
* check errors for float
* test: remove typescript string format checker and associated jest test
it duplicates android lint at great cost in dev time and run time
* chore: consolidate credential prep workflow steps
* style: `yarn install` is equivalent to `yarn`, just call `yarn`
* perf: avoid shell glob and add whole res folder
the only stages that should be changed are translation ones, so
we can let git do the expansion internally, which will avoid
shell-globbing which can have perf issues and/or fail if it matches
too many files
* style(lint): result of `yarn format` auto-formatting run
* style(lint): buildProgress var never reassigned, use const
* build(deps): result of `yarn upgrade --latest`
* fix: use new crowdin personal access token
this was freshly generated and added to github org under same name
* fix: use official project id
* style: progress output more condensed
* dev: use `tsc --watch` for dev command
this gives us continuous incremental build
* style: use exact header as in current file to minimize diff
we may decide to update this later, but this is important during
i18n sync process change to make sure diff is minimal
* fix: ignore 12-dont-translate files
these do not need to be copied from the zip to the app res directories
* fix: marketing title is created fresh each time
appending without clearing it out means it will grow infinitely
* docs: update comment on language list to match updated python version
this was changed in python while new system was in development,
synchronizing change here
* docs: simple comments on upload process
* add readme
* docs(l10n): expand the readme, reformat, use conventional all-caps README
* fix: ingest PR review feedback, prepare for final merge
- use numerical separator
- update comment about language tags (2- vs 3-letter etc)
- typos and formatting
- mark package as non-publishable ("private")
---------
Co-authored-by: Mike Hardy <github@mikehardy.net>
* Update target API to 23
* Update to 2.1.66, and switch to new schema by default
+ Temporarily disable unused resource warnings
* Drop legacy syncing code
* Drop legacy import/export code
* Drop legacy tag handling
* Drop legacy config handling
* Drop legacy database check
* Stub out unused v1 scheduler code
Can't be fully removed, as we still need to be able to open a v1 collection
so the user can upgrade.
moveVersions test has been removed, as backend code is used for moving
outside of unit tests.
* Drop legacy deck handling
* Drop legacy search code
* Drop legacy notetype code
This breaks the card template editor, so I've temporarily disabled it
in the GUI. Getting it working again will require switching to the new
template rendering code in the new backend.
Also breaks an "empty cards" action in CardContentProvider,
as I was not sure what it was trying to accomplish.
* Drop legacy media code
This removes oakkitten's custom media checking code, as keeping it
would require keeping a bunch of the duplicated Kotlin logic. His comment
about cancellation is incorrect: the call can be cancelled with backend.
setWantsAbort() on a background thread. And since the app enforces filenames
are normalized when they're added anyway, I don't think avoiding automatic
normalization is worth the duplicated code.
* Drop legacy Collection/DB code
* Drop most references to defaultLegacySchema
* Remove legacy schema tests from CI
CI won't work yet, because backend version needs updating
* Fix import CSV call
* Remove some unused symbols from anki module
* Drop majority of old stats code
* Remove unused symbols from libanki
* Remove some broken Android tests, and fix one
checkIfStudyOptionsIsDisplayedOnTablet() is also consistently failing,
but was doing so before I started on these changes.
* Move config methods into col.config
* Simplify config API
- Use kotlinx.serialization so that arbitrary typed objects can be
(de)serialized
- There is a single generic get(), that always returns an optional
value, which will be null if the key is missing, is null, or is not the
correct type. There will always be collections that have invalid data,
so the calling code always needs to be prepared to substitute a reasonable
default in such cases.
- Expose the typed getBool() method from the backend.
* Remove unused legacy importer
* Drop jackson
* Models -> Notetypes
* Remove some more unused symbols
* Remove generic type aliases
* Rename Model -> Notetype
I had to leave ModelTest.kt's filename alone, as changing it to Notetype.kt
reorders the unit tests, and causes about 4 tests to start flaking.
* Get card template editor preview working again
* Enable undo on edits; fix test hang
* Use backend for bulk suspend/undo
By default, undoableOp() notifies all screens listening for opExecuted,
so the refreshing happens automatically, and the manual refresh code
at the end of the old routine is no longer required. It is possible to
bypass this when you want to manually update the UI, but this is probably
not worth attempting until the card browser is either switched over to
a recycler view, or reimplemented as a web component.
The LongArrays have been switched to simple lists, as Google's protobuf
code expects Iterable<Long>
* Use backend for marking/undo, and change deck/undo
* Migrate reviewer actions/undo to backend
* Migrate remainder of undo code to backend, apart from v2 undo
Couple of notes:
- The legacy v2 undo no longer returns the undone card, and instead
rebuilds the queue. In some circumstances (eg a learning card has become
due), this can result in a different card being shown after undoing.
This is not ideal, but v2 does not have long to live at this point.
- The reset/reposition tests were reusing the old card cache values,
which have to be reloaded after an operation.
- A test in AbstractSched had to be moved to ReviewerTest so it can have
access to an activity.
- UndoTest has been removed, as it mainly focused on removed code.
* GetCard() -> getNextCardAndRedraw()
* render_output -> renderOutput()
* Drop AsyncTask
Probably the hardest part of AnkiDroid's code base to follow; very
glad to see the end of it.
Closes#7108
* Make note addition undoable
* Make bulk tag update undoable
* Remove remaining explicit transaction handling
The backend automatically wraps backend actions in a transaction,
and removing the explicit calls will allow us to drop the redundant
mutex that rsdroid acquires.
* Remove remaining unused classes/methods/properties
* Remove FunctionalInterfaces
* Switch to backend answer comparison
* Remove unused locking code + legacy storage code
* Switch ContentProviderTest to new schema, and fix bug
* Remove legacy Deck Options settings
* fix: remove minSdkVersion < 23 code
* Use short time in snackbars
People complained about snackbars interrupting their reviews before
* fix: don't duplicate undo label
It were showing a message like `Undo Undo Add Note`
* Remove "Advanced statistics"
* Remove legacy_schema local property
* Leave zip validation up to the backend
The backend takes care of validating the zip file and ensuring files aren't
written outside of the media folder.
I've expanded the mime match to include the zip mime, on the assumption
that's what the mime type is being changed to on some devices. If that
proves to be insufficient, a much simpler approach would be to look
for 50 4B 03 04 at the start of the file to determine if it's a zip file
or not.
This leaves only Mani's add-on code using commons-compression, so you
could potentially switch to a simpler tgz-specific library in the future
if that is easier.
* Disable two flaky tests
They started to flaky reliably after the legacy deck options test
was removed.
* Improve logging of HTTP requests
* Remove ModelBrowser
Replaced by ManageNotetypes
* Remove legacy CardInfo
replaced by com.ichi2.anki.pages.CardInfo
* Change comments at AdvancedSettingsFragment
* test: check if prefs analytics list don't have extra elements
* ContextCompat.getColor -> getColor
that compat method is for implementations in API < 23
* Remove more unused files
* refactor: move SECONDS_PER_DAY to a separate util file
in order to be able to remove Stats.kt later
* refactor: remove Stats and OverviewStatsBuilder
* Access sched.card directly in tests
The helper was adding a 500ms sleep on every fetch, which slowed the
scheduler tests down considerably.
* Make v3 scheduler the default; drop support for v2
The v3 scheduler was originally released in 2021, and we've been waiting
for a stable AnkiDroid release to support it before we could switch users
over to it. Now that 2.16 is out, we can finally push v3 out across the
ecosystem.
While we could theoretically make v3 the default without removing v2,
here are the reasons why I think we're better off switching to v3 only:
- AnkiWeb's review interface will likely switch over to v3-only in the
coming weeks, and AnkiMobile will likely drop v2 around the same time
2.17 comes out.
- v2 and v3 differ in a few ways that makes maintaining the two separate
paths more complicated: things like the different undo paths, counts not
including the current card, and the way the v3 scheduler supports custom
scheduler js. 2.17 is a chance to clean up a lot of old cruft in the
code base, and the old scheduling code is part of that.
- v2 and v3 are compatible with each other, and don't require a full sync
to change, so it doesn't break syncing with old clients (though depending
on settings, due counts may differ, which needs explanation)
I had to disable a couple of tests for this that we'll probably want
to restore in some form:
- corruptVersion16CollectionShowsDatabaseError() needs an update, and
it may be time to rip out the old CollectionHelper colIsOpen(), getColSafe()
and so on, migrating code that uses it over to withCol() instead. The
'collection inaccessible' dialog also needs a rethink - perhaps the various
exceptions could be handled in launchCatchingTask instead.
- testUndoResetsCardCountsToCorrectValue() is failing because initLayout()
creates a gesture listener, which fails with an error about the looper
not being initialized. I am not sure what's going on there - one option
would be to move the test to androidTest.
* Drop v2 sched file; rename files
* Drop AbstractSched and legacy undo code/queue code
* Migrate BaseSched into Scheduler
* More unused symbol removal
* Remove DeckTreeNode
* Remove unused processChildren()
* Remove shouldDisplayCounts(), as counts always available
* Remove manual hashCode()/compareTo() implementations
* Drop AbstractDeckTreeNode
* Turn DeckNode into a wrapper for DeckTreeNode
* Drop TreeNode and simplify filtering
* Remove `New card position` global preference
Overridden by per-deck configuration
Closes#12319
* fix: remove chess.css from card_template_html
* Store current queue state in reviewer
Prerequisite for solving #12620
Also dropped answerButtons(), as it's always 4
* Use local HTTP server for serving flashcard content
Prerequisite for solving #12620
* Implement support for custom JS scheduling
Enables FSRS and closes#12620
* Remove more unused code
* Remove separate Deck(Config)V16 objects and more unused code
* Move some NotetypeJson methods into its file
* Finish tidying Decks.kt
* Drop redundant col property on Collection
* Remove some usages of CollectionGetter
* Rename col->getColUnsafe to encourage migration
* Remove Reviewer.sched
* Handle deck tree not initialized in empty collection
* refactor: move syncStatus() to Collection
to avoid direct calls to the backend
* fix: do not show error when updating the menu if there is no internet
Reproduction steps:
1. have the collection synced
2. disable the device internet connection
3. Restart the app
* Remove unused resources and re-enable lint
* Restore sync cancel strings
* Defer loading webpage until server has initialized
* Ignore translations without a base entry
https://github.com/ankidroid/Anki-Android/pull/14171#issuecomment-1660286342
* Remove the SortOrder deprecation
* Remove the apparently-unused Backup.kt
* Remove last remaining use of Collection's context field
* Stop passing Context to backend
The library loading is now AnkiDroid's responsibility, so the backend
does not require access to an Android context.
Also remove the unused context field from Collection.
* Use backend for rendering next time labels
* Use backend for rendering finished message
The custom study & unbury descriptions are slightly different, but
should suffice until AnkiDroid can start using the Svelte finished
screen.
* Run SchedulerTest without Robolectric
Halves the run time. To do this, I implemented a new JvmTest class
and copied some of the helpers in RobolectricTest into it.
* Remove unused Kotlin implementation of template parsing
* Move Util code dealing with resources out of libanki
* Migrate remaining libanki tests to raw JVM
+ Remove some commented-out tests
Total savings are a drop from about 3m40s to 2m40s, with the scheduler
test class dropping its runtime by about half.
* Lazy-initialize col in tests
* Restore 'experimental' string
* Add comments to update-localizations.py
* Restore AcraAnalyticsInteraction.kt
---------
Co-authored-by: Brayan Oliveira <69634269+brayandso@users.noreply.github.com>
- will abort (like before) if play store rejects us or play build fails
- will only commit/push build tag if play store works
- universal builds happen after play upload so universal doesn't upload
On my desktop, running `./tools/storage/set_scopde_storage.sh` led to
> Alienware% ./tools/storage/set_scoped_storage.sh
> First argument missing.
> \nPossible arguments:
> limited\t\tEquivalent to a fresh install. /AnkiDroid/ is inaccessible
> full\t\tEquivalent to an upgrade from targetSdkVersion 29 to 30. /AnkiDroid/ is accessible\n
Adding `-e` ensures the characters are printed as expected.
Otherwise, bash is not used. Instead, I get
> ./tools/storage/set_scoped_storage.sh: 5: Syntax error: "(" unexpected
because `function` is not basic shell I guess.
this is still before any builds leave the build machine and become
public, but is after builds succeed so should burn fewer version numbers
in the event builds fail, as just happened
First change is to have `<br/>` instead of `\n`. For the sake of the
consistency. (And also because I imagine \n would be ignored)
Second change is to add actual new line at the end of HTML new line. Text become
more readable.
A script to enable/disable scoped storage without the need to
uninstall/upgrade from `targetSdkVersion 29`
Makes testing of the disable/enable routine quicker
Issue #5304
* Update backend version to 2.1.56
Requires https://github.com/ankidroid/Anki-Android-Backend/pull/257
This includes a change that will likely fix the "permission denied"
errors users were getting when removing media manually/during sync.
2.1.56 revamped cloze deletion to include support for nested clozes,
and includes extra information in the generated HTML to identify unexpanded
clozes/extract their original content. This breaks the unit tests in this
repo that expect the cloze deletions to follow a particular format, like
what happened in https://github.com/ankidroid/Anki-Android/pull/12897/files,
but more thoroughly this time :-) To work around this I've just disabled
the tests in the new schema path - while you could potentially task someone
with updating them as a "good first issue", I don't think they're adding
much value in the new path, as the backend has its own unit tests for these
already, so they're effectively testing the same thing twice.
I noticed that the version number in build.gradle referred to a script
that was no longer relevant, so I've removed it.
* Update backend to 2.1.57
Some of Oakkitten's previous changes to the custom sync preferences have
had to be redone, as the backend now expects the base URL instead of
individual settings for collection and media sync.
* Move app_name constant to build.gradle
So it can be changed if necessary
* Add customSuffix and customName build args
In order to allow making parallel builds from a CLI command
* Make parallel with custom args instead of regex
Deprecate the old script and use gradle arguments instead
Since we're moving to scoped storage, uninstalling the app will very likely
delete user data.
In Debug/Dev, we want this to impact the dev's workflow as little as possible
So we change the applicationId. This relays the expectation that a 'stable'
anki should be using 'com.ichi2.anki' and an 'unstable/dev' AnkiDroid should be
'com.ichi2.anki.debug'. A developer should have two copies of the app, each with
their own storage location.
API:
As previously, define the API permissions/authority using the applicationId:
permission: 'com.ichi2.anki.debug.permission'
authority: 'com.ichi2.anki.debug.flashcards'
androidTest needs to access 'com.ichi2.anki.debug.flashcards' and have
'com.ichi2.anki.debug.permission'.
To resolve this, if the API is built in debug mode, the constants are modified:
(api/build.gradle)
* AUTHORITY = "com.ichi2.anki.debug.flashcards"
* READ_WRITE_PERMISSION = "com.ichi2.anki.debug.permission.READ_WRITE_DATABASE"
Implementation:
High level:
* Define variables in build.gradle
* Handle modifying them via parallel-package-name.sh
Low level:
* Use 'applicationIdSuffix' for this
* Modifies '<provider android:authorities>' in AndroidManifest.xml
* Required for installation on device
* Use 'applicationId'
* Modifies '<permission android:name' in AndroidManifest.xml
* Not required - cleanup on parallel-package-name.sh
* Modifies preferences_sync.xml
* via resValue in build.gradle
* Fixes crash
Source for both: https://developer.android.com/studio/build/build-variants#build-types
applicationIdSuffix:
https://developer.android.com/reference/tools/gradle-api/7.3/com/android/build/api/dsl/ApplicationVariantDimension#applicationIdSuffix()
@string/applicationId
* syntax ref: https://stackoverflow.com/questions/27954215/changing-resvalue-in-variant
manifestPlaceholders/${applicationId}
https://developer.android.com/studio/build/manage-manifests
Learning:
> By default, the build tools also provide your app's application ID in the ${applicationId} placeholder. The value always matches the final application ID for the current build, including changes by build variants. This is useful when you want to use a unique namespace for identifiers such as an intent action, even between your build variants.
https://developer.android.com/studio/build/manage-manifests