diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
commit | d8bbc7858622b6d9c278469aab701ca0b609cddf (patch) | |
tree | eff41dc61d9f714852212739e6b3738b82a2af87 /mobile/android/docs | |
parent | Releasing progress-linux version 125.0.3-1~progress7.99u1. (diff) | |
download | firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/docs')
27 files changed, 1665 insertions, 21 deletions
diff --git a/mobile/android/docs/.gitignore b/mobile/android/docs/.gitignore new file mode 100644 index 0000000000..45c150536e --- /dev/null +++ b/mobile/android/docs/.gitignore @@ -0,0 +1,3 @@ +_site +.sass-cache +.jekyll-metadata diff --git a/mobile/android/docs/fenix.rst b/mobile/android/docs/fenix.rst index 46ed237613..56dd6eec8d 100644 --- a/mobile/android/docs/fenix.rst +++ b/mobile/android/docs/fenix.rst @@ -1,51 +1,94 @@ +.. _fenix-contributor-guide: + Building Firefox for Android ============================ +1. Cloning the repo +------------------- + First, you'll want to `set up your machine to build Firefox </setup>`_. -Follow the instructions there, choosing "GeckoView/Firefox for Android" as -the bootstrap option. +Follow the instructions there, choosing "GeckoView/Firefox for Android" or "GeckoView/Firefox for Android Artifact Mode" as +the bootstrap option. Please refer to the "Bootstrap" section below to understand better those options. Once you're set up and have a GeckoView build from the above, please continue with the following steps. -1. Clone the repository and initial setup ------------------------------------------ +2. Bootstrap +------------ + +If you intend to work mainly on GeckoView, you can find more information `here <geckoview/contributor/for-gecko-engineers.html>`_. + +Bootstrap configures everything for GeckoView and Fenix (Firefox for Android) development. .. code-block:: shell - git clone https://github.com/mozilla-mobile/firefox-android - cd firefox-android/fenix - echo dependencySubstitutions.geckoviewTopsrcdir=/path/to/mozilla-central > local.properties + ./mach bootstrap + +You should then choose one the following options: + +A- You will not change any GeckoView code, or only Java and JS code on GeckoView: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Choose: ``3. GeckoView/Firefox for Android Artifact Mode`` + +Artifact mode downloads pre-built C++ components rather than building them locally, trading bandwidth for time. +(more on Artifact mode) + +B- You intend to change GeckoView code: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Choose: ``4. GeckoView/Firefox for Android`` -replace `/path/to/mozilla-central` with the location of your mozilla-central/mozilla-unified source tree. +This will build GeckoView from scratch, and take more time than the option above. -2. Build --------- +Once ``./mach bootstrap`` is complete, it will automatically write the configuration into a new ``mozconfig`` file. +If you already have a ``mozconfig``, mach will instead output a new configuration that you should append to your existing file. + +3. Build GeckoView +------------------ + +You can now build GeckoView, using .. code-block:: shell - export JAVA_HOME=$HOME/.mozbuild/jdk/jdk-17.0.6+10 - export ANDROID_HOME=$HOME/.mozbuild/android-sdk-<os_name> - ./gradlew clean app:assembleDebug + ./mach build + +.. _build_fenix: + +4. Build Fenix or other Android projects Using Android Studio +------------------------------------------------------------- -`<os_name>` is either `linux`, `macosx` or `windows` depending on the OS you're building from. +1. **You will only work on one of those projects: Fenix, Focus, Android Components** +Open your project's folder on Android Studio. You can find it under: ``[your mozilla-central path]/mobile/android`` -For more details, check out the `more complete documentation <https://github.com/mozilla-mobile/firefox-android/tree/main/fenix>`_. +After ``./mach build`` completed successfully, you will need to use `File/Sync Project with Gradle files`. -3. Run ------- +2. **You will work on GeckoView only, or GeckoView in integration with the other projects** -From the gecko working directory: +Open the root folder (meaning the ``mozilla-central`` folder you just checked out) on Android Studio. +From there, you should be able to choose the project you want to build. + +.. image:: img/android-studio-build-toolbar.png + :alt: Screenshot Android Studio's toolbar, showing the projects that can be built + +After ``./mach build`` completed successfully, you will need to do a full Gradle Sync. + +3. Run Fenix or other Android projects using command line +--------------------------------------------------------- + +From the root mozilla-central directory, you can run an emulator with the following command: .. code-block:: shell ./mach android-emulator -From the firefox-android working directory: +From the `mobile/android/fenix` working directory, build, install and launch Fenix with: .. code-block:: shell - ./gradlew :app:installFenixDebug - "$ANDROID_HOME/platform-tools/adb" shell am start -n org.mozilla.fenix.debug/org.mozilla.fenix.debug.App + export JAVA_HOME=$HOME/.mozbuild/jdk/jdk-<latest-version> + export ANDROID_HOME=$HOME/.mozbuild/android-sdk-<os_name> + ./gradlew :app:installFenixDebug + "$ANDROID_HOME/platform-tools/adb" shell am start -n org.mozilla.fenix.debug/org.mozilla.fenix.debug.App diff --git a/mobile/android/docs/img/android-studio-build-toolbar.png b/mobile/android/docs/img/android-studio-build-toolbar.png Binary files differnew file mode 100644 index 0000000000..f21f9d69f4 --- /dev/null +++ b/mobile/android/docs/img/android-studio-build-toolbar.png diff --git a/mobile/android/docs/shared/android/CONTRIBUTING.md b/mobile/android/docs/shared/android/CONTRIBUTING.md new file mode 100644 index 0000000000..2d40b61137 --- /dev/null +++ b/mobile/android/docs/shared/android/CONTRIBUTING.md @@ -0,0 +1,90 @@ +# Contributing to Mozilla Mobile's Android projects + +Thank you for taking the time to contribute to one of Mozilla's Android +projects! π₯ π¦ <3 π€! π π For a full list of projects, see +[the README](../../../README.md). + +Before contributing, please review our [Community Participation Guidelines]. + +We welcome all types of contributions, including, but not limited to: +* [Filing issues](#filing-issues) +* [Translating our apps](#translating-our-apps) +* [User experience design](#user-experience-design) +* [Writing documentation](#writing-documentation) +* [Testing the app](#testing-the-app) +* [Writing code](./CONTRIBUTING_code.md) + +Have another idea for how you can contribute? Let us know! + +### Communication +For each our projects, we communicate with the project's: +- Issues tracker +- Mailing list +- [Matrix Fenix](https://chat.mozilla.org/#/room/#fenix:mozilla.org) channel. *We're available Monday-Friday +during GMT and PST working hours.* + +**Be sure to join them** and don't be afraid to make postings of your +own! A link to each project-specific communication channel can be found in +the project's README. + +## Filing issues + +We usually track our project's issues on GitHub (example: [focus-android][fa issues]). +To file a bug: +* Find your project's issues: these will be linked from the project's README +or you can click directly into the issues from the project's home page +* Create a new issue! + +To ensure we can understand, reproduce, and effectively triage your issues, +**please fill out the provided template** when filing an issue! When issues +are filed in a consistent style and include the information we need to +make decisions, we're more likely to take action on these issues than if +we don't understand them. + +*Be sure to include any extra information you find valuable!* For example, posting +[your user agent](https://duckduckgo.com/?q=my+user+agent) can help us debug problems. + +## Translating our apps + +* Get in touch with the [existing localization + team](https://wiki.mozilla.org/L10n:Teams). +* Localization happens on + [Pontoon](https://pontoon.mozilla.org/projects/) (example: [focus-android][fa pontoon]). + +## User experience design + +Get in touch with one of our designers (@brampitoyo or @aminalhazwani) +or other core team members to get involved! With a limited number of engineers, we +can't implement every design so we want to make sure any design work you do will +make it into our products. + +## Writing documentation + +### Product support documentation +Each Mozilla product provides in-app support. This documentation can be found on +[support.mozilla.org][sumo]. If you wish to contribute, please contact a core team +member to get started. + +### Developer/repository documentation +See an issue with our documentation, either in a project's repository or this +`shared-docs` repository? Please file an issue or create a pull request +to fix it yourself! + +## Testing the app + +* Download one of our products and look for issues. Some products have pre-release +Beta builds: check out their READMEs to download them! +* File [new issues](#filing-issues) for all bugs you encounter. Make sure that an +issue isn't filed already (via the search). +* Make sure that your bug report follows the provided template +* Try to reproduce issues reported by others and add additional or missing +information to the bug reports. + +## Writing code + +See [CONTRIBUTING_code.md](./CONTRIBUTING_code.md). + +[Community Participation Guidelines]: https://www.mozilla.org/en-US/about/governance/policies/participation/ +[fa issues]: https://github.com/mozilla-mobile/focus-android/issues +[fa pontoon]: https://pontoon.mozilla.org/projects/focus-for-android/ +[sumo]: https://support.mozilla.org/en-US/ diff --git a/mobile/android/docs/shared/android/CONTRIBUTING_code.md b/mobile/android/docs/shared/android/CONTRIBUTING_code.md new file mode 100644 index 0000000000..73de91d21c --- /dev/null +++ b/mobile/android/docs/shared/android/CONTRIBUTING_code.md @@ -0,0 +1,121 @@ +# Contributing code to Mozilla's Android projects +Thank you for taking the time to contribute to one of Mozilla's Android +projects! π₯ π¦ β€οΈ π€! π π For a full list of projects, see +[the README](../../../README.md). + +Before contributing, please review our [Community Participation Guidelines]. + +If you run into trouble at any point, ask for help! Check out our +[preferred communication channels.](./CONTRIBUTING.md#communication) + +Contents: +- [Beginner's guides](#beginners-guides) +- [Configuring Android Studio](#configuring-android-studio) +- [Finding issues to work on](#finding-issues-to-work-on) +- [Creating a Pull Request](#creating-a-pull-request) +- [Merging](#merging) +- [Writing tests](#writing-tests) + +## Beginner's guides +Unfamiliar with the technology we use? No problem! We were once new to this +too! Here are few guides we've compiled to help you get started: +- [Git guide](../git_guide.md#android) +- [Android guide](android_guide.md) + - [Accessibility guide](accessibility_guide.md) +- [Kotlin guide](kotlin_guide.md) +- [Writing custom lint rules guide](writing_lint_rules.md) +- [Taskcluster guide](taskcluster_guide.md) +- [MockK guide](https://notwoods.github.io/mockk-guidebook/) + +If these are confusing or if you have questions, please let us know! + +## Configuring Android Studio +We don't allow wildcard imports (ex: `import kotlinx.coroutines.*`) so we recommend preventing Android Studio from auto-importing or optimizing with them. This can be done via the `Preferences > Editor > Kotlin > Code Style > Use single name import` option. + +## Finding issues to work on +**New to Mozilla's mobile projects?** See issues labeled `good first issue` in your project's +issues tracker (example: [focus-android][fa good first]). These are designed to be +easier to implement so you can focus on learning our pull request workflow. *Please only +fix one of these.* + +**Looking for more challenging issues?** See issues labeled `help wanted` (example: +[focus-android][fa help]). These are issues that are ready to be implemented without +additional product or UX discussion. + +**When you find an issue you'd like to work on,** *comment on the issue* saying that +you'd like to work on it. This ensures it is still available for you to work on. + +**If you want to work on a new feature**, *always file an issue first* and wait +for our team to discuss it. We want to ensure all teams (product, ux, engineering) +have an opportunity to provide feedback. **Pull requests for unsolicited features +are unlikely to get merged.** + +## Creating a Pull Request +Our team follows [the GitHub pull request workflow][gh workflow]: fork, branch, commit, +pull request, automated tests, review, merge. If you're new to GitHub, check out [the official +guides][gh guides] for more information. + +An example commit message summary looks like, `For #5: Upgrade gradle to v1.3.0`. + +Please follow these guidelines for your pull requests: + +- All Pull Requests should address an issue. If your pull request doesn't have an +issue, file it! + - GitHub search defaults to issues, not PRs, so ensuring there is an issue for your PR + means it'll be easier to find +- The commit message summary should briefly describe what code changed in the commit, *not +the issue you're fixing.* + - We encourage you to use the commit message body to elaborate what changed and why +- Include the issue number in your commit messages. This links your PR to the issue it's +intended to fix. + - If your PR closes an issue, include `Closes #...` in one of your commit messages. This + will automatically close the linked issue ([more info][auto close]). + - If your PR has to go through a longer process, for example QA verification, use the + `For #...` syntax to allow the linked issue to be closed at a later, more appropriate time. +- Prefer "micro commits". + - A micro commit is a small commit that generally changes one thing. + A single Pull Request may comprise of multiple incremental micro commits. + - A series of micro commits should tell a story. For example, if your goal is to add a new + icon to the toolbar, you can make a commit to add the icon asset and then make a commit to + use the icon in the code. + - Commits should generally not undo the work of previous commits in the same PR. + - If you're not comfortable making micro commits, it's okay to begin contributing without + them. +- Add a reviewer to ensure someone sees, and reviews, your pull request so it can be merged +- If the tests fail, please try to fix them! Keeping the tests passing ensures our code isn't +broken and the code is unlikely to get merged without passing tests. If you run into trouble, +ask for help! +- If there are UI changes, include a screenshot so UX can also do a visual review +- When in doubt, look at the closed PRs in the repository to follow as an example or ask +us online! + +If your code is not approved, address the suggested comments, push your changes, and re-request +review from your reviewer again. + +## Merging +After your code has been approved and the tests pass, your code will be merged into master +by the core team. When merging, we use GitHub's "Rebase and merge": +- We keep a linear git history for readability +- We prefer incremental commits to remain in the history + - It's easier to read, helps with bisection, and matches repo state during review. + +## Writing tests +To learn more about how our tests are structured, see [testing.md](testing.md). + +## Stale PRs +If changes are requested in your patch and there is no indication of further activity within 2 weeks, +we may close your PR in order to keep our repo in a manageable state. This does not mean we are not +interested in your patch! Once the requested changes are made, feel free to re-open the PR. + +Conversely, if your PR is not receiving attention from the organization, here are a few strategies +for escalating it: +- Make sure your PR has the `needs-review` label. +- Tag specific reviewers. Look for people that recently touched the file, that are active in other patches, or the component triage owner for the associated bug in Bugzilla. +- Reach out to us on [Matrix](https://matrix.to/#/#fenix:mozilla.org). + +[Community Participation Guidelines]: https://www.mozilla.org/en-US/about/governance/policies/participation/ +[fa good first]: https://github.com/mozilla-mobile/focus-android/labels/good%20first%20issue +[fa help]: https://github.com/mozilla-mobile/focus-android/labels/help%20wanted +[gh workflow]: https://guides.github.com/introduction/flow/ +[gh guides]: https://guides.github.com/ +[auto close]: https://help.github.com/articles/closing-issues-using-keywords/ diff --git a/mobile/android/docs/shared/android/accessibility_guide.md b/mobile/android/docs/shared/android/accessibility_guide.md new file mode 100644 index 0000000000..48f72159e5 --- /dev/null +++ b/mobile/android/docs/shared/android/accessibility_guide.md @@ -0,0 +1,84 @@ +# Android Accessibility Guide +Accessibility (or "a11y") is about making apps usable by everyone. Android developers need to pay particular attention to users with visual impairment, auditory impairment, and restricted motor skills. This guide is intended to get you started with making your Android app more accessible. + +To identify how well the accessibility of your app is working, try it as a user! For example, try closing your eyes while using the TalkBack screen reader. You can also use [the Accessibility Scanner][scanner] for an automated analysis. + +If you have questions, please ask! + +## Introductory resources +The [official Android accessibility overview](https://developer.android.com/guide/topics/ui/accessibility/apps) is a great overview to common issues users may face and how you should address them as an Android developer. A good supplement, for visual impairment, is [this official Android Developers video](https://www.youtube.com/watch?v=1by5J7c5Vz4). This [presentation on accessibility](https://www.youtube.com/watch?v=UOr3mgqJU0A&feature=youtu.be&list=PLnVy79PaFHMUqqvwbjyKJZv1N8rzHOCBi) and [this podcast](https://androidbackstage.blogspot.com/2014/10/episode-14-accessibility.html) are also recommended by our developers. + +Some general tips: +- Look through the accessibility preferences in the Developer Options: there are many features, such as visual simulations, to help you. +- If it is impossible to provide an intuitive, accessible user experience for your current UI, this is a design smell: a UI that is accessible tends to be the best experience for all users. + - For example, a dialog with one option, like "Remove", that is dismissed by clicking outside the dialog, will be unintuitive to screen reader users. However, this dialog may also be unintuitive to users who are unfamiliar with common software paradigms (i.e. tap outside to dismiss) too. A better dialog would be one with a "dismiss" option in addition to "remove" +- Avoid creating separate code paths and user experiences to address accessibility issues: in our experience, accessibility is not often tested so it's easy to break the experience if you forget to update one code path + +## Screen reader tips +**TalkBack** is the default Android system screen reader: it's usually installed by default but you may need to install it from the Play Store. When testing, don't forget your headphones! + +**VoiceView** is the default system screen reader on Amazon products: it is installed by default. + +Some development tips: +- Don't try to override TalkBack's behavior outside of what is suggested in the docs: users expect the system to work consistently so overriding that behavior can be unintuitive. + - Generally let the system handle focus changes: it's unintuitive when it jumps around. +- Overriding the accessibility APIs in code is a code smell: generally, you can make the changes you need using the methods recommended in the docs, which provides a more maintainable and intuitive experience to users. Also note the code APIs are unintuitive and not well documented. + +### Cut out redundant steps with `android:importantForAccessibility="no"` +Unintuitively, setting some views to not be important for accessibility improves accessibility. +For example, take this layout that represents a menu item that consists of an image and text. +``` +<LinearLayout> + <ImageView/> + <TextView/> +</LinearLayout> +``` +When a user swipes right past this composite view, the cursor will first land on the LinearLayout, and then on the TextView: +<img alt="Animation of cursor landing menu items and their text children" src="https://github.com/mozilla-mobile/firefox-android/raw/main/docs/shared/android/images/double-nav.gif" width="240"> + +This is both inefficient and confusing for the user, since they will encounter each item twice. To streamline this experience, set `android:importantForAccessibility="no"` on the `TextView`. This will tell TalkBack not to bother with that node: +<img alt="Animation of cursor only landing on menu items" src="https://github.com/mozilla-mobile/firefox-android/raw/main/docs/shared/android/images/single-nav.gif" width="240"> + +## Automated testing +You can regularly use [the Accessibility Scanner][scanner] to get a cursory overview of how the accessibility of your app is doing. + +When adding specialized accessibility logic, it is encouraged to add as much testing as possible since these code paths are not exercised in typical use and the chance of regression is high. Below is an example Mockito and Robolectric test that validates accessibility events that are resulted from a [loading progress update](https://github.com/mozilla-mobile/android-components/pull/2526). + +```kotlin +@Test +fun `displayProgress will send accessibility events`() { + val toolbar = BrowserToolbar(context) + val root = mock(ViewParent::class.java) + Shadows.shadowOf(toolbar).setMyParent(root) + `when`(root.requestSendAccessibilityEvent(any(), any())).thenReturn(false) + + Shadows.shadowOf(context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager).setEnabled(true) + + toolbar.displayProgress(10) + toolbar.displayProgress(50) + toolbar.displayProgress(100) + + val captor = ArgumentCaptor.forClass(AccessibilityEvent::class.java) + + verify(root, times(4)).requestSendAccessibilityEvent(any(), captor.capture()) + + assertEquals(AccessibilityEvent.TYPE_ANNOUNCEMENT, captor.allValues[0].eventType) + assertEquals(context.getString(R.string.mozac_browser_toolbar_progress_loading), captor.allValues[0].text[0]) + + assertEquals(AccessibilityEvent.TYPE_VIEW_SCROLLED, captor.allValues[1].eventType) + assertEquals(10, captor.allValues[1].scrollY) + assertEquals(100, captor.allValues[1].maxScrollY) + + assertEquals(AccessibilityEvent.TYPE_VIEW_SCROLLED, captor.allValues[2].eventType) + assertEquals(50, captor.allValues[2].scrollY) + assertEquals(100, captor.allValues[2].maxScrollY) + + assertEquals(AccessibilityEvent.TYPE_VIEW_SCROLLED, captor.allValues[3].eventType) + assertEquals(100, captor.allValues[3].scrollY) + assertEquals(100, captor.allValues[3].maxScrollY) +} +``` + +In addition the [official documentation](https://developer.android.com/training/accessibility/testing#automated) seems like a good starting point to learn more about accessibility validation in tests. We should look into integrating this into our CI. + +[scanner]: https://support.google.com/accessibility/android/answer/6376570?hl=en diff --git a/mobile/android/docs/shared/android/android_guide.md b/mobile/android/docs/shared/android/android_guide.md new file mode 100644 index 0000000000..abbf242606 --- /dev/null +++ b/mobile/android/docs/shared/android/android_guide.md @@ -0,0 +1,48 @@ +# Android guide +This guide is designed to help you get started on Android and introduce you to some intermediate topics. + +- [Getting started](#getting-started) +- [Staying informed](#staying-informed) +- [Intermediate topics](#intermediate-topics) + +## Getting started +Assuming you already have some coding experience, Google's official [Developing Android Apps udacity course][udacity course] comes highly recommended by our team. After completing your first few lessons, you can try to working on our code base, continuing the course as necessary. + +Once you have a little experience, the beginner's guides in the [official Android developer docs][devdocs] teach popular topics pretty well and are a good reference. However, they aren't very good at telling you where to go next (which is why we recommended the course). Note: the documentation on less popular topics are often missing information. + +There are also many good Android tips and conference talks on YouTube. + +You may wish to see [our guide for properly configuring Java](configure_java.md) to properly set up command line tools. + +## Staying informed +If you like staying up-to-date on the latest Android topics, members of our team recommend: +- Reading blogs + - [Official Android developer blog](https://android-developers.googleblog.com/) (also has a newsletter) +- Subscribing to weekly newsletters + - [Android weekly](https://androidweekly.net/) + - [Jetpack Compose Newsletter by Commonsware](https://jetc.dev/) +- Listening to podcasts + - [Fragmented](http://fragmentedpodcast.com/) + - [Android Developers Backstage](https://androidbackstage.blogspot.com/) +- Visiting conferences (or watching their recordings) + - Google IO + - Android Developer Summit + - Droidcon +- Visiting local meetups + +## Intermediate topics +Comfortable with the basics? Here are a few of our favorite resources to get started on intermediate topics. +- [Android style tips](http://blog.danlew.net/2014/11/19/styles-on-android/) (circa 2014) +- [Our Accessibility guide](accessibility_guide.md) +- [WorkManager FAQ](workmanager_faq.md) + +### Navigating the Android source code +Oftentimes, it's easier to solve a problem when you understand how your code is interacting with the Android framework: you can do this by reading the Android source code! You can do plain-text searches on the Android source code at http://androidxref.com/ (choosing the latest API level and searching in the "frameworks" is usually sufficient). + +You can also navigate the Android source code in Android Studio, after downloading the sources: +- Click a framework method and use "go to definition" +- Step into framework methods via the debugger +- Set breakpoints in framework files to stop whenever the framework calls that method + +[udacity course]: https://www.udacity.com/course/new-android-fundamentals--ud851 +[devdocs]: https://developer.android.com/docs/ diff --git a/mobile/android/docs/shared/android/automation.md b/mobile/android/docs/shared/android/automation.md new file mode 100644 index 0000000000..be40b88ad7 --- /dev/null +++ b/mobile/android/docs/shared/android/automation.md @@ -0,0 +1,121 @@ +# Automation and continuous integration systems + +Our Android projects share a similar automation model: +- Taskcluster is our primary CI: we use it for as much automation as +possible, including our core builds and releases, because it's a +flexible first party service we can rely on. +- GitHub Actions is secondary: it runs some regular tasks or a subset of tests for pull requests. + +## Taskcluster + +[Taskcluster][tc] is a generic task execution framework developed and used by Mozilla to automate continuous integration and release processes. + +We currently use Taskcluster to: +* Build, test and run code tools for all master commits and pull requests +(including contributor PRs). See [Code quality tools](#code-quality-tools) +for additional information. +* Build (unsigned) release builds whenever a release is created on GitHub +* Automatically import new translations from the L10N repository. + +To see a Taskcluster configuration for your project, see +`<project-root>/.taskcluster.yml` (here's an +[example in focus-android][tc yml]). + +Taskcluster runs using docker: see our [docker guide](docker_guide.md) for +more information. + +## GitHub Actions + +[GitHub Actions][gha] is GitHub's own CI/CD service. + +We currently use GitHub Actions to: +* Build, test and run code tools for pull requests (including contributor PRs). +* Automatically update dependencies & synchronize localization files +* Automatically adding additional labels to new issues + +To see the GitHub Actions configuration for your project, see `<project-root>/.github/workflows` (here's an [example in Fenix][gha-fenix]). + +You can find the workflows and builds logs under the "Actions" tab of each project. + +# Code quality tools + +For Kotlin only projects, run these tools: +* Android lint +* [detekt](https://github.com/arturbosch/detekt) +* [ktlint](https://github.com/shyiko/ktlint) + +For projects that also include Java, add these tools: +* checkstyle +* findbugs +* PMD + +To see what tools are currently run in automation for your project, see +the project's `<project-root>/.taskcluster.yml` +(here's an [example in focus-android][tc yml tools]). + +# Third-party services + +## Codecov + +Codecov evaluates the code coverage reports generated by our build and tracks the coverage over time. It warns when merging pull requests would lead to dropping the coverage below a specified threshold. + +* https://codecov.io/gh +* [focus-android code coverage over time](https://codecov.io/gh/mozilla-mobile/focus-android/branch/master) + +# Automation tasks + +## Pull request pipeline + +![](https://i.imgur.com/SJ9zSs8.png) + +## Release pipeline + +Eventually we want to automate the whole release process pipeline in order to have "one click" releases without much workload for the engineering team. + +### β
Active: Release builds + +Whenever a release is created on GitHub, we build releases versions of the project on taskcluster (example: [focus-android configuration](https://github.com/mozilla-mobile/focus-android/blob/master/.taskcluster.yml#L91)). + +### β
Active: Signing + +Signing requires access to Mozilla's signing certificates. In addition to that a signing task needs to be able to validate the origin of the APK ("Can I trust the task that built the APK?") to avoid signing random tasks. + +* [Chain of Trust](http://scriptworker.readthedocs.io/en/latest/chain_of_trust.html) +* [pushapkscript](https://github.com/mozilla-releng/pushapkscript) + +### β
Active: Upload to archive.mozilla.org + +Every release is published on archive.mozilla.org. Nightly builds go to https://archive.mozilla.org/pub/fenix/nightly/ and https://archive.mozilla.org/pub/focus/nightly/. +Release builds are uploaded to https://archive.mozilla.org/pub/fenix/releases/ and https://archive.mozilla.org/pub/focus/releases/. + +* [beetmover script](https://github.com/mozilla-releng/scriptworker-scripts/tree/master/beetmoverscript/) + +### β
Active: Publish + +Finally we want to publish a release build on Google Play + +Like other scriptworker instances, we can use the existing instance. The current implementation is fennec-specific. We have to add support there too. pushapk_scriptworker mainly delegates checks and uploads to mozapkpublisher. That tool needs Focus support. Working on mozapkpublisher doesn't require any Taskcluster knowledge. + +* [pushapkscript](https://github.com/mozilla-releng/scriptworker-scripts/tree/master/pushapkscript/) +* [mozapkpublisher](https://github.com/mozilla-releng/mozapkpublisher) + +## Nightly (Alpha) pipeline + +TODO: explain the current process + +## Maintenance tasks + +### β
Active: String import + +Every day at 12:00 UTC we run a script that imports the latest translations from the L10N repository and creates a pull request for the import if there are new strings. Eventually this import can be merged automatically. For now we want to review those changes manually to validate that the task is running as intended. + +We hope to improve this process soon. + +* [Task script](https://github.com/mozilla-mobile/focus-android/blob/master/tools/taskcluster/import_strings_and_create_pull_request.sh) +* [Hook and task definition](https://tools.taskcluster.net/hooks/project-focus/strings-import) + +[tc yml]: https://github.com/mozilla-mobile/focus-android/blob/master/.taskcluster.yml +[tc yml tools]: https://github.com/mozilla-mobile/focus-android/blob/38f79e25493ab08b8322cd4c059891f37fbf500f/.taskcluster.yml#L39 +[tc]: https://docs.taskcluster.net/docs +[gha]: https://github.com/features/actions +[gha-fenix]: https://github.com/mozilla-mobile/fenix/tree/main/.github/workflows diff --git a/mobile/android/docs/shared/android/configure_java.md b/mobile/android/docs/shared/android/configure_java.md new file mode 100644 index 0000000000..00dc288ae3 --- /dev/null +++ b/mobile/android/docs/shared/android/configure_java.md @@ -0,0 +1,44 @@ +# Configure Java for Android +If you are building Android via the command line (which includes our recommended pre-push hooks), you will need to configure Java. If not, you can just use Android Studio and do not need to read further. + +Notes: +- this guide is written using macOS and will need to be updated to additionally support Linux and Windows. +- if you find a better way to do configure Java, let us know! + +## Background +At the time of writing, Android development seems to work best when using Java 17. By default `brew` and other package managers will install more recent versions that may throw errors during development. + +## Methods +### Method #1: configure Java 17 from Android Studio +Do the following: +* Download and install Android Studio +* Add the following line to your `~/.zshrc` or equivalent shell startup file: `export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"` + * Update the path if you installed Android Studio to a non-standard location + +That's it! To verify correctness, open a new shell, navigate to a directory with Android source code, and type `./gradlew tasks`. + +This works because macOS comes with a `/usr/bin/java` stub which will defer the location of the JDK to the value in the `JAVA_HOME` environment variable. + +### Method #2: install Java 17 from Homebrew +You can install Java from [Homebrew]([url](https://brew.sh/)) using the command below. + +```bash +brew install openjdk@17 +``` + +### Method #3: install from website +See https://docs.oracle.com/en/java/javase/17/install/installation-jdk-macos.html + +## Troubleshooting +### Get Java version +To see what version of Java you have installed, run: +```sh +java -version +``` + +If you have Java 17 configured, you'll see output like: +``` +openjdk 17.0.7 2023-04-18 +OpenJDK Runtime Environment Homebrew (build 17.0.7+0) +OpenJDK 64-Bit Server VM Homebrew (build 17.0.7+0, mixed mode, sharing) +``` diff --git a/mobile/android/docs/shared/android/device_testing.md b/mobile/android/docs/shared/android/device_testing.md new file mode 100644 index 0000000000..f2e89b1a9b --- /dev/null +++ b/mobile/android/docs/shared/android/device_testing.md @@ -0,0 +1,74 @@ +# Physical Device Testing + +We rely on [Firebase Test Lab][firebase] for automated on-device testing. + +[firebase]: https://firebase.google.com/docs/test-lab/ + +Fenix UI tests use virtual devices by default. +These are run automatically on all Pull Requests and merges into the `main` branch. +Virtual devicesare quick to spin up, there is basically no upper limit of devices that can spawn on the cloud infrastructure and they usually produce the same result as running the test on a physical device. + +However it is possible to run the tests on physical devices as well. +This requires some changes to the CI configuration. + +> **NOTE**: Do not land a Pull Request that switches CI from virtual to physical devices! +> Add the `pr:do-not-land` label and call out that the PR is only there for testing! + +By default the Fenix CI runs tests using virtual devices on `x86`. +That's faster when the host is also a `x86(_64)` system, but most physical devices use the Arm platform. +So first we need to instruct it to run tests on Arm. + +Which platform to test on is defined in [`taskcluster/ci/ui-test/kind.yml`](https://github.com/mozilla-mobile/fenix/blob/58e12b18e6e9f4f67c059fe9c9bf9f02579a55db/taskcluster/ci/ui-test/kind.yml#L65). +Find the line where it downloads the `target.apk` produced in a previous step and change it from `x86` to `arm64-v8a`: + +```patch + run: + commands: +- - [wget, {artifact-reference: '<signing/public/build/target.x86.apk>'}, '-O', app.apk] ++ - [wget, {artifact-reference: '<signing/public/build/target.arm64-v8a.apk>'}, '-O', app.apk] +``` + +Then look for the line where it invokes the `ui-test.sh` and tell it to use `arm64-v8a` again: + +```patch + run: + commands: +- - [automation/taskcluster/androidTest/ui-test.sh, x86, app.apk, android-test.apk, '-1'] ++ - [automation/taskcluster/androidTest/ui-test.sh, arm64-v8a, app.apk, android-test.apk, '-1'] +``` + +With the old CI configuration it will look for Firebase parameters in `automation/taskcluster/androidTest/flank-x86.yml`. +Now that we switched the architecture it will pick up [`automation/taskcluster/androidTest/flank-arm64-v8a.yml`](https://github.com/mozilla-mobile/fenix/blob/58e12b18e6e9f4f67c059fe9c9bf9f02579a55db/automation/taskcluster/androidTest/flank-arm64-v8a.yml) instead. + +In that file we can now pick the device we want to run on: + +```patch + device: +- - model: Pixel2 ++ - model: dreamlte + version: 28 +``` + +You can get a [list of available devices](https://firebase.google.com/docs/test-lab/android/available-testing-devices) by running `gcloud` locally: + +``` +gcloud firebase test android models list +``` + +The value from the `MODEL_ID` column is what you use for the `model` parameter in `flank-arm64-v8a.yml`. +`dreamlte` translates to a Samsung Galaxy S8, which is available on Android API version 28. + +If you only want to run a subset of tests define the `test-targets`: + +```yaml +test-targets: + - class org.mozilla.fenix.ui.BookmarksTest +``` + +Specify an exact test class as above to run tests from just that class. + +And that's all the configuration necessary. +Save your changes, commit them, then push up your code and create a pull request. +Once the decision task on your PR finishes you will find a `ui-test-x86-debug` job (yes, `x86`, we didn't rename the job). +Its log file will have details on the test run and contain links to the test run summary. +Follow them to get more details, including the logcat output and a video of the test run. diff --git a/mobile/android/docs/shared/android/docker_guide.md b/mobile/android/docs/shared/android/docker_guide.md new file mode 100644 index 0000000000..3bc40f44ae --- /dev/null +++ b/mobile/android/docs/shared/android/docker_guide.md @@ -0,0 +1,103 @@ +# mozilla-mobile Docker Guide +TaskCluster runs our automation tasks, such as compilation and unit tests, in a [Docker container](https://en.wikipedia.org/wiki/Docker_(software)). We maintain our own Docker images. A complete up-to-date listing is available [on Docker Hub](https://hub.docker.com/r/mozillamobile/): +- [`mozillamobile/focus-android`](https://hub.docker.com/r/mozillamobile/focus-android/) +- [`mozillamobile/firefox-tv`](https://hub.docker.com/r/mozillamobile/firefox-tv/) +- [`mozillamobile/android-components`](https://hub.docker.com/r/mozillamobile/android-components/) +- [`mozillamobile/fenix`](https://hub.docker.com/r/mozillamobile/fenix/) + + These images contain all the tools (e.g. Android SDK, Python runtime) needed to run our tasks. These images are built from a Dockerfile located in our repository (e.g. [focus-android Dockerfile](https://github.com/mozilla-mobile/focus-android/blob/master/tools/docker/Dockerfile)). Taskcluster downloads this image from [Docker Hub](https://hub.docker.com/) whenever it needs to run one of our tasks. + +If you want to build or use an image locally then you need to install the Community Edition of Docker on your machine: +https://www.docker.com/community-edition + +## Updating the Docker image +If our toolchain changes or if we require new tools for running tasks then it might be needed to update the image taskcluster uses. This sample will use the `mozillamobile/focus-android` docker image as an example: + +1. Install Docker Community Edition on your machine + +2. Modify [Dockerfile](https://github.com/mozilla-mobile/focus-android/blob/master/tools/docker/Dockerfile). See [Dockerfile reference](https://docs.docker.com/engine/reference/builder/) for a list of commands that can be used to build an image. + +3. Build the Dockerfile locally and tag it _mozillamobile/focus-android_: + +```bash +docker build -t mozillamobile/focus-android . +``` + +4. Run the image locally and test that everything is working as you intended to: + +```bash +docker run -it mozillamobile/focus-android /bin/bash +``` + +5. Commit Dockerfile and open a PR for it. + +6. Push the image to Docker Hub so that taskcluster can find it: + +```bash +docker push mozillamobile/focus-android. +``` + +Running this command requires that you have an account, are added to the "mozillamobile" organization, and have logged in via the command line: `docker login`. Ask :sebastian if you need to be added. + +Once the docker image is pushed, make sure your `taskcluster.yml` is referencing the new docker image with the correct tag number under `payload`. + +Note: If there is a failure while building the docker image, this often comes from lack of memory. Try increasing the memory and swap space on GUI and/or `-m 5g` build argument. + +## Debugging errors +If you receive an error on TaskCluster that you can't reproduce locally, you may need to use Docker locally to reproduce the TaskCluster build environment. + +tldr version to use docker: +```sh +# Install docker from https://www.docker.com/community-edition + +# Pull image +docker pull mozillamobile/focus-android + +# Start image and run bash +docker run -it mozillamobile/focus-android /bin/bash + +# You now have a shell in the image with a checkout of the project (old) +# and all SDK tools installed. Update the checkout and run the failing command. +# This is an ubuntu image. Use "apt-get" to install whatever helps you (e.g. vim) +``` + +## Cheat sheet +A list of docker commands that can be helpful: + +```sh +# See all locally available images +docker images + +# Run and image and execute bash in it: +docker run -it <image> /bin/bash + +# Show all containers +docker ps -a + +# Start a stopped/exited container +docker start <containerId> + +# Join (running) container +docker attach <containerId> + +# Copy file from container to host +docker cp <containerId>:/file/path/within/container /host/path/target + +# Build image from Dockerfile with tag +docker build -t <tag> . + +# Push image to Docker hub +docker push <tag> + +# Pull image from Docker hub +docker pull <tag> +``` + +## Help. I'm running out of disk space +Over time Docker is using a lot of disk space for all the images and containers you have started. At least on MacOS even removing images and containers doesn't reclaim disk space. The only thing that seems to help is to remove this gigantic Docker.qcow2 file. Note that doing that will remove all your local images and containers. You will need to re-pull or re-build images you need. + +```bash +rm /Users/<your username>/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/Docker.qcow2 +``` + +More about this in [this Docker bug report](https://github.com/docker/for-mac/issues/371). diff --git a/mobile/android/docs/shared/android/fretboard.md b/mobile/android/docs/shared/android/fretboard.md new file mode 100644 index 0000000000..575f17e87b --- /dev/null +++ b/mobile/android/docs/shared/android/fretboard.md @@ -0,0 +1,143 @@ +# Fretboard +This getting started guide is designed to introduce you to adding A/B experiments to your Android apps using the Fretboard Android Component as we use it at Mozilla. This document summarizes and supplements the official Fretboard documentation. + +- [What is Fretboard](#what-is-fretboard) +- [Integrating Fretboard Into A Project](#integrating-fretboard-into-a-project) +- [Setting Up An Experiment Source](#experiment-source) +- [Synchronizing Experiments from the Source](#synchronizing-experiments-from-the-source) +- [Experiment Filters](#experiment-filters) +- [Experiment Overrides](#experiment-overrides) +- [Experiment Schema for Kinto](#experiment-schema-for-kinto) +- [Fretboard README](https://github.com/mozilla-mobile/android-components/blob/master/components/service/fretboard/README.md) +- [Fretboard Blog Announcement](https://mozilla-mobile.github.io/android-components/2018/08/10/fretboard.html) + +## What is Fretboard +Fretboard is an open source A/B testing framework. It was designed to replace KeepSafe's Switchboard library and server, which was used in the older Firefox for Android, codenamed "Fennec". + +## Integrating Fretboard Into a Project + +### Add Fretboard to Build.Gradle +First add Fretboard to your build.gradle file: + +`implementation "org.mozilla.components:fretboard:{latest-version}"` + +You can [find the latest version here](https://github.com/mozilla-mobile/android-components/releases/latest). + +### Create a Fretboard Instance +Learn how to set up your Fretboard instance in the [Fretboard documentation](https://github.com/mozilla-mobile/android-components/blob/master/components/service/fretboard/README.md#creating-fretboard-instance). + +## Setting Up An Experiment Source +You will need to choose an experiment source. Fretboard currently supports two choices for experiment sources. It can either read from a [Kinto](https://github.com/Kinto/kinto) server or from a JSON file on local disk. Using Kinto has the benefit of offering buckets, so you can split your users into an A and a B population. + +## Synchronizing Experiments from the Source +There are two calls used for synchronizing experiments. One, Fretboard.loadExperiments() which reads current experiment values from a file on disk and a second call, Fretboard.updateExperiments() that goes out to the ExperimentSource. Both calls should not be made from Android main/UI thread. Any one of the multithreading methods on Android should be used to perform the work asynchronously from a background thread: Kotlin Coroutines, RxJava, AsyncTasks, Executors, etc. + +You may want to call loadExperiments() before calling updateExperiments() so archived values from past runs are loaded quickly from disk before waiting for experiment values to be updated from the network. + +You can learn about these calls from the [Fretboard documentation](https://github.com/mozilla-mobile/android-components/tree/master/components/service/fretboard#fetching-experiments-from-disk). + +## Experiment Filters +In addition to separating users into buckets, you may decide to limit your experiment to a user population with specific characteristics, such as a particular app version, app ID, country, language, or device manufacturer. This is done using experiment filters, which is described in the [Fretboard documentation](https://github.com/mozilla-mobile/android-components/tree/master/components/service/fretboard#filters) + +You may even create your own filters by defining a custom ValuesProvider. + +## Experiment Overrides +Fretboard Overrides offer a means of locally changing which experiments are enabled for the current device. You can set an override and the server configuration will no longer choose which experiments are set. These values are stored locally in Android shared properties, so they will be wiped when a user clears app data. + +There are methods to both set and clear overrides using either blocking or non-blocking calls. If you clear an override, then that device will resume receiving experiments from the ExperimentSource. Using a blocking call will ensure that the override is set before the next statement gets executed, but it is forbidden and will cause an error to use blocking calls from Android's main/UI thread. + +Learn more about overrides in the [Fretboard documentation](https://github.com/mozilla-mobile/android-components/tree/master/components/service/fretboard#setting-override-values). + +## Experiment Schema For Kinto +Below is an example JSON schema for Kinto, which can be used to provide the default filters for Fretboard experiments. You will have to change this slightly if you define your own filters or use more buckets than 0 through 100. + +```json + { + "type": "object", + "required": [ + "name", + "match", + "buckets" + ], + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "match": { + "type": "object", + "title": "Matching", + "properties": { + "lang": { + "type": "string", + "description": "Language, pulled from the default locale (e.g. eng)" + }, + "appId": { + "type": "string", + "title": "Android App ID", + "description": "^org.mozilla.fennec|org.mozilla.firefox_beta|org.mozilla.firefox$" + }, + "device": { + "type": "string", + "description": "Android device name" + }, + "country": { + "type": "string", + "description": "Country, pulled from the default locale (e.g. USA)" + }, + "regions": { + "type": "array", + "items": { + "type": "string", + "title": "Regions", + "default": "", + "minLength": 0, + "description": "Similar to a GeoIP lookup" + }, + "title": "Regions", + "default": [], + "description": "Compared with GeoIP lookup.", + "uniqueItems": true + }, + "version": { + "type": "string", + "title": "Android App Version", + "description": "A regexp on the Android app version number (e.g. 47.0a1, 46.0)" + }, + "userAgent": { + "type": "string", + "description": "Browser User-Agent regexp. i.e: Firefox/46.0" + }, + "manufacturer": { + "type": "string", + "description": "Android device manufacturer" + } + } + }, + "buckets": { + "type": "object", + "title": "Buckets", + "required": [ + "min", + "max" + ], + "properties": { + "max": { + "type": "string", + "pattern": "^0|100|[1-9][0-9]?$", + "minLength": 1 + }, + "min": { + "type": "string", + "pattern": "^0|100|[1-9][0-9]?$", + "minLength": 1 + } + } + }, + "description": { + "type": "string", + "title": "Description" + } + } + } + ``` diff --git a/mobile/android/docs/shared/android/images/double-nav.gif b/mobile/android/docs/shared/android/images/double-nav.gif Binary files differnew file mode 100644 index 0000000000..d2adf7da6d --- /dev/null +++ b/mobile/android/docs/shared/android/images/double-nav.gif diff --git a/mobile/android/docs/shared/android/images/single-nav.gif b/mobile/android/docs/shared/android/images/single-nav.gif Binary files differnew file mode 100644 index 0000000000..40f8558330 --- /dev/null +++ b/mobile/android/docs/shared/android/images/single-nav.gif diff --git a/mobile/android/docs/shared/android/kotlin_guide.md b/mobile/android/docs/shared/android/kotlin_guide.md new file mode 100644 index 0000000000..8b6231a69c --- /dev/null +++ b/mobile/android/docs/shared/android/kotlin_guide.md @@ -0,0 +1,57 @@ +# Kotlin guide +This guide is designed to help you get started with [Kotlin], the language we write most of our code in, and to introduce you to some intermediate topics. +- [Getting Started](#getting-started) +- [Staying Informed](#staying-informed) +- [Intermediate Topics](#intermediate-topics) + +## Getting Started +There are multiple ways to get started with Kotlin, depending on how you like to learn. If you like: +- Learning while coding, try the [Kotlin Koans] online or in the IDE. Kotlin Koans are a series of exercises to get you familiar with the Kotlin syntax. Each exercise is created as a failing unit test and your job is to make it pass. + - If you do it in the IDE, we recommend using [IntelliJ Community Edition] with the [Kotlin Edu plugin]: educational plugins may not work in Android Studio yet, but the Kotlin Edu plugin has a tighter integration than using the Koans on their own. + - If you want to learn on the go, there's a good [Kotlin Koans Android app]. +- Reading documentation, the official Kotlin docs are good: start at [Basic Syntax] and work your way through the pages in the side bar +- Reading books, try [Kotlin in Action]. If you're an SF employee, we have this on the bookshelf, as well as [Kotlin Programming: The Big Nerd Ranch Guide]. +- Comparing Swift & Kotlin: [Swift is like Kotlin](http://nilhcem.com/swift-is-like-kotlin/) This site provides a ton of examples of how Swift code looks in Kotlin. If you're comfortable with Swift (or moving from iOS development) this is a great resource for you! + +Once you've gained some basic familiarity with the language, you can gain more experience by: +- Watching videos and conference talks on YouTube. +- [Convert Java to Kotlin with the auto-converter][convert]and change the resulting code into idiomatic Kotlin +- Doing coding exercises on a site like [exercism.io](https://exercism.io/) +- Working on Mozilla code ;) + +You should also take a look at the official style guides: +- [Jetbrains](https://kotlinlang.org/docs/reference/coding-conventions.html) +- [Android](https://android.github.io/kotlin-guides/) + +## Staying Informed +If you like staying up-to-date on the latest Kotlin topics, members of our team recommend: +- Reading blogs + - [Official Kotlin blog](https://blog.jetbrains.com/kotlin) (also has a newsletter) +- Subscribing to weekly newsletters + - [Kotlin weekly](http://kotlinweekly.net/) +- Visiting conferences (or watching their recordings) + - [KotlinConf](https://kotlinconf.com/) +- Visiting local meetups + +## Intermediate Topics +You can see what the Kotlin compiler is doing under the hood (e.g. is this lambda performant? What does it keep a reference to?) by using "Show Kotlin Bytecode" and then "Decompile" back to Java. + +Writing Domain Specific Languages (DSLs) is a neat feature in Kotlin: there's a chapter near the end of Kotlin in Action if you want to read about it. + +### Coroutines +The official documentation on coroutines is really good: +- [kotlinx.coroutines by example](https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md) +- [UI programming with coroutines](https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md) (read the first guide first) + +If you'd prefer to get started with videos, [this KotlinConf 2017 talk](https://www.youtube.com/watch?v=_hfBv0a09Jc) is a great place to get started. + +[Kotlin]: https://kotlinlang.org/ +[Basic Syntax]: https://kotlinlang.org/docs/reference/basic-syntax.html +[Kotlin Koans]: https://kotlinlang.org/docs/tutorials/koans.html +[course]: https://www.udacity.com/course/kotlin-for-android-developers--ud888 +[Kotlin in Action]: https://www.manning.com/books/kotlin-in-action +[convert]: https://www.jetbrains.com/help/idea/converting-a-java-file-to-kotlin-file.html +[IntelliJ Community Edition]: https://www.jetbrains.com/idea/download/ +[Kotlin Edu plugin]: https://www.jetbrains.com/education/kotlin-edu/ +[Kotlin Programming: The Big Nerd Ranch Guide]: https://www.bignerdranch.com/books/kotlin-programming/ +[Kotlin Koans Android app]: https://play.google.com/store/apps/details?id=me.vickychijwani.kotlinkoans diff --git a/mobile/android/docs/shared/android/sprint_process.md b/mobile/android/docs/shared/android/sprint_process.md new file mode 100644 index 0000000000..1aba592ee1 --- /dev/null +++ b/mobile/android/docs/shared/android/sprint_process.md @@ -0,0 +1,62 @@ +# APT sprint process +Projects created by the Android Product Team typically ship a major release every six weeks and ship a minor release twice between major releases (every two weeks). For example, if v2.0 is released on week 0, v2.1 will be released on week 2, v2.2 will be released on week 4, and v3.0 will be released on week 6. Emergency bug fixes can be released more frequently. Projects will typically link to their release schedule (example: [focus-android](https://wiki.mozilla.org/Mobile/Focus/Android/Train_Schedule)). + +Development is broken down into two week-long chunks of time called sprints: we track the work for these sprints in GitHub milestones (example: [focus-android][milestone example]). We have several planning meetings to identify what should go into each release and thus into each sprint. + +## Sprint planning meetings +### Roadmap (monthly) +The product team plans and prioritizes the project roadmap: the general project plan over the next few months. These meetings will: +- At a high level, decide which features go into which releases +- Schedule team work weeks +- Modify the release schedule (if necessary) + +Updates from this meeting will be emailed to the full team. + +### Triage (weekly) +The full team will go through newly filed issues to: +- Assign priority labels (P1, P2, ...) to bugs without priority labels or the `addressed` label +- Ensure all P1 labels are assigned to a milestone +- Ensure all P2 labels are assigned to a milestone + +Each project should link to its own triage queries (example: [focus-android](https://github.com/mozilla-mobile/focus-android/issues?q=is%3Aissue+is%3Aopen+-label%3AP1+-label%3AP2+-label%3AP3+-label%3AP4+-label%3AP5+-label%3Aaddressed+-label%3Ablocked+sort%3Aupdated-desc+no%3Amilestone)). + +For more information on priority labels, [see below](#categorizing-issues). + +### Weekly bug planning (weekly) +The full team meets to manage bugs. This meeting time-slot alternates between two separate agendas: + +#### Sprint planning +Held before a sprint begins, in this meeting we: +- Decide what goes into the current sprint by promoting P2 issues to P1 +- Decide what goes into the next sprint by adding issues to the upcoming milestone and setting them as P2 + +#### Backlog grooming +Held mid-sprint, in this meeting we: +- Handle triage overflow +- Add to the contributor bug lists (e.g. `help wanted`) +- Check in on current sprint progress +- Look over the upcoming sprint + +## Categorizing issues +We have several conventions for categorizing our issues. + +### Labels +We label issues with their priority: these priorities are based on the [Bugzilla triage process][triage priority]. +* `P1`: issues for the current 2-week sprint +* `P2`: issues for the 6-week milestone release +* `P3`: Backlog +* `P5`: Will not fix but will accept a patch + +Other labels: +* `addressed`: label for excluding items from triage. Should be used for [meta] items. + +### Issue Prefixes +In the issue title, we may prefix the name to categorize the issues. + +* `[meta]`: larger issues that need to be broken down, into a `[breakdown]` issue and issues for its smaller parts. This should include a checklist of all the issues (including the `[breakdown]` issue). + + These do NOT get a P* label, but should be in a milestone. +* `[breakdown]`: issue to track the work of breaking down a larger bug + +[triage priority]: https://wiki.mozilla.org/Bugmasters/Process/Triage#Weekly_or_More_Frequently_.28depending_on_the_component.29 +[milestone example]: https://github.com/mozilla-mobile/focus-android/milestone/30?closed=1 diff --git a/mobile/android/docs/shared/android/taskcluster_guide.md b/mobile/android/docs/shared/android/taskcluster_guide.md new file mode 100644 index 0000000000..5c02b10fd7 --- /dev/null +++ b/mobile/android/docs/shared/android/taskcluster_guide.md @@ -0,0 +1,119 @@ +# Taskcluster guide + +This guide is designed to help you get started with [Taskcluster](https://firefox-ci-tc.services.mozilla.com/), the Continuous Integration system we use, and to introduce you to some intermediate topics. +- [Getting Started](#getting-started) +- [FAQ and HOWTOs](#faq-and-howtos) + +## Getting Started +Are you totally new to Taskcluster or Taskgraph? Check out this [3-minute-video](https://johanlorenzo.github.io/blog/2019/10/24/taskgraph-is-now-deployed-to-the-biggest-mozilla-mobile-projects.html). If you want an overview of Taskgraph, you can read the blog post attached to it. + +If you have any questions regarding Taskcluster or Taskgraph, feel free to join #releaseduty-mobile on Mozilla's Slack instance. + +## FAQ and HOWTOs + +### How do I test some changes I make under the `taskcluster/` folder? + +The easy way: create a PR and you will get results in minutes. If you want to test your changes locally, have a look at the [README of Taskgraph](https://hg.mozilla.org/ci/taskgraph/file/tip/README.rst). + +### How do I see test results? + +* On PRs: the status badge will let you know whether it is green, running, or broken. +* On the master branch: Treeherder is a great tool to have an overview. Here are the links for: + * [fenix](https://treeherder.mozilla.org/#/jobs?repo=fenix) + * [android-components](https://treeherder.mozilla.org/#/jobs?repo=android-components) + * [reference-browser](https://treeherder.mozilla.org/#/jobs?repo=reference-browser) +* More generally: the status badge attached to each commit on Github will point you to each task run. For instance, here is a link to one of Fenix's release branch: https://github.com/mozilla-mobile/fenix/commits/releases/v5.0 + +### Why is my PR taking much longer than usual? What is this toolchain task? + +Major mobile projects now cache external dependencies in a Taskcluster artifact. We implemented this solution because some maven repositories couldn't keep up with the load we required from them. This also puts us one step closer to having reproducible builds. +The cache gets rebuilt if [any of these files](https://github.com/mozilla-mobile/fenix/blob/7974a5c77c82915ce64faa6c403c0feadd7d6580/taskcluster/ci/toolchain/android.yml#L43-L47) change. Otherwise, build tasks just reuse the right cache: taskgraph is smart enough to know which existing one to use, we call this kind of tasks `toolchain`. +Sadly, there is no way to ask gradle to just download dependencies - we have to **compile everything**. This is why such task can be really long. We will address the slowness of fenix in https://github.com/mozilla-mobile/fenix/issues/10910. + +### How do I find the latest nightly? + +If you are interested in just getting the latest APKs, use these links: + * [fenix](https://firefox-ci-tc.services.mozilla.com/tasks/index/mobile.v2.fenix.nightly.latest) (choose the architecture you are interested in) + * [android-components](https://firefox-ci-tc.services.mozilla.com/tasks/index/mobile.v2.android-components.nightly.latest) (choose the component you are interested in) + * [reference-browser](https://firefox-ci-tc.services.mozilla.com/tasks/index/mobile.v2.reference-browser.nightly.latest) (choose the architecture you are interested in) + +If you want to understand why something is wrong with nightly: + 1. click on one of the aforementioned Treeherder links + 1. type `nightly` in the search bar at the top right corner and press `Enter` => this will filter out any non-nightly job + 1. check if there are any non-green jobs. If none are displayed, do not hesitate to load more results by clicking on one of the `get next` buttons. + +### How do I rerun jobs? + +* On pull requests: just close and reopen the PR, the full task graph will be retriggered. (You cannot rerun individual tasks while [bug 1641282](https://bugzilla.mozilla.org/show_bug.cgi?id=1641282) is not fixed) +* Otherwise: on the taskcluster UI, when looking at a task, go to the bottom right corner and select "Rerun". If it doesn't work, please contact the Release Engineering team (#releaseduty-mobile on Slack) + +If you hesitate between reruns and retriggers, choose `rerun` first, then `retrigger` if the former failed. + +### How do I run a staging nightly/beta/release on PRs? + +It is easy! Just create a new file called `try_task_config.json` at the root of the repository and it will change what graph taskgraph generates. + +β οΈ Do not forget to remove this file before merging your patch. + +#### Nightly + +```json +{ + "parameters": { + "optimize_target_tasks": true, + "target_tasks_method": "nightly" + }, + "version": 2 +} +``` + +will generate a nightly graph on your PR. + +#### Beta + +```json +{ + "parameters": { + "optimize_target_tasks": true, + "release_type": "beta", + "target_tasks_method": "release" + }, + "version": 2 +} +``` + +#### Release + +```json +{ + "parameters": { + "optimize_target_tasks": true, + "release_type": "release", + "target_tasks_method": "release" + }, + "version": 2 +} +``` + + +#### More generally +You can know what `target_tasks_method` to provide by looking at the `@_target_task()` sections in `target_tasks.py`. E.g: [target_tasks.py in Fenix](https://github.com/mozilla-mobile/fenix/blob/824dedb19588a9052b03ad162155c62ecd08e316/taskcluster/fenix_taskgraph/target_tasks.py#L29). + +### Why don't PRs open by external contributors get Tasckluster jobs? + +Long story short: It's because of a limitation of the Taskcluster security model. More details in this [Bugzilla bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1534764). +2 workarounds: + a. A person with write access on the repo opens a 2nd PRs with the same patch. This will trigger TC as usual. + b. If the repository has bors enabled, first make sure the PR doesn't do anything suspicious, then comment `bors try` on the PR. [Bors](https://bors.tech/) will run Taskcluster jobs on an integration branch. + + +### How do I get secrets/tokens baked into an APK? + +We use Taskcluster secrets to store secrets and tokens that will be injected into APKs. See [this pull request](https://github.com/mozilla-mobile/fenix/pull/7772) as an example. Steps are: + +1. Implement a similar code based on the PR mentioned just before. +1. Tell a member of the Release Engineering team you would like to add a new secret. +1. Securely send the secret by following [one of these methods](https://mana.mozilla.org/wiki/display/SVCOPS/Sharing+a+secret+with+a+coworker) +1. For Releng: + 1. Go to https://firefox-ci-tc.services.mozilla.com/secrets/?search=fenix + 1. Edit existing secrets to include the new key and the new (unencrypted) value. Key is the [first value of this tuple](https://github.com/mozilla-mobile/fenix/pull/7772/files#diff-99b25bd5c02b53ff6a6260f416305b52533dfd3f70e0c2627a561f375cab3339R50). Value is the secret it self. You usually want to edit the `nightly`, `beta`, and `release` secrets. `nightly-simulation` should be edited too but with dummy values. diff --git a/mobile/android/docs/shared/android/testing.md b/mobile/android/docs/shared/android/testing.md new file mode 100644 index 0000000000..74d147a2f6 --- /dev/null +++ b/mobile/android/docs/shared/android/testing.md @@ -0,0 +1,155 @@ +# Testing +*This document is intended to explain which testing styles and technologies the Android teams at Mozilla use and why. It should be accessible to developers new to testing on Android.* + +*Presently this document describes testing on Firefox for Fire TV: it should grow to include other applications.* + +The primary problem we try to solve with automated testing is preventing unintended changes, which often lead to bugs or poor user experiences. This problem is most common -- thus we benefit the most from thorough testing -- when developers are modifying complex or unfamiliar code. + +## Quick primer on automated testing +Automated tests are typically broken up into the following categories: +- End-to-End tests + - Black box tests that act like users: they click the screen and assert what is displayed, typically following common user scenarios + - Notable for being **the only test of the full system.** Tend to **run slowly**, **be fragile**, and **be difficult to debug** +- Integration tests + - Tests comprising of multiple parts of the system: they can modify and assert internal state + - Compromise between end-to-end and unit tests. Tend to **run fairly quickly** since, unlike UI tests, they don't need to wait for UI changes +- Unit tests + - Tiny tests that test one single bit of functionality; they typically don't overlap + - When written with non-overlapping assertions across tests, notable for **clearly identifying a single piece of broken functionality in a system.** Tend to **run quickly**. + +Due to trade-offs of speed, correctness, fragility, and ability to debug, it's recommended to have a significant number of unit tests, a small number of integration tests, and an even smaller number of end-to-end tests. For more details, research the Test Pyramid. + +## Automated testing on Android +Android tests are broken into two major categories: +- On-device testing + - Runs on Android hardware or emulator + - Typically used for **end-to-end and integration tests** + - Accurate for real world users but slow and fragile due to coordinating two devices, low power hardware + - Located in a sourceset like, `<proj>/app/src/androidTest` +- JVM testing + - Runs on your development machine + - Typically used for **unit tests** + - Runs very quickly + - Located in a sourceset like, `<proj>/app/src/test` + +In all categories, by default tests are declared using the JUnit APIs (see below). + +Note: Google [is converging the test APIs][Frictionless Android Testing] for on-device and off-device testing so eventually you can write once and run anywhere. + +### On-device testing +Users run your code on Android devices so testing on these devices is **the most accurate form of testing**. However, between copying code and execution times, running tests on Android devices or emulators is **significantly slower** than running code on your development machine's JVM, especially when the test suites get large. As such, **on-device testing should generally only be used when:** +- Running end-to-end tests or some integration tests +- Reproducing the behavior on device is critical +- The tests cannot be written accurately for the JVM (e.g. UI) + +See [Physical Device Testing](device_testing.md) for how to leverage available physical devices. + +#### Espresso and UI Automator: core UI testing libraries +[Espresso] and [UI Automator] are Google's core UI testing libraries on Android: they provide APIs to select views, perform actions on them, and assert state without reaching into implementation details. + +**Espresso is *significantly* more reliable** than UI Automator because it provides **an API for `IdlingResource`s:** these APIs wait for some system to be idle before executing the next Espresso action. For example, the built-in `IdlingResource` will wait for the UI thread to be idle, allowing the test to be robust during asynchronous actions on the UI thread, animations, etc. You can write custom `IdlingResource`s. + +**UI Automator can perform actions outside the scope of the application,** on the entire system, unlike Espresso. You can use this if you need to close and reopen the app, interact with the notification tray, etc. + +These libraries can be used in the same test. However, due to the improved reliability, **Espresso is preferred for most interactions.** To use these libraries, add them to gradle, import them, and call them directly. + +See [this FFTV example of Espresso and UI Automator working together][espresso example]. + +#### Robot pattern: UI test architecture +UI tests are notoriously difficult to write: the code is often duplicated across tests, written imperatively, and needs frequent updates to keep up with changing UI. + +The Robot Pattern is an architecture that attempts to address these problems: its **key feature is separating the "what" from the "how", making the tests declarative.** A Robot pattern test may look like: +``` +navigationOverlay { + assertCanGoBack() + goBack() + assertCanNotGoBack() + +}.enterUrlAndEnterToBrowser("https://mozilla.org") { + assertBodyContent("Welcome to mozilla.org!") +} +``` + +For more on the Robot pattern including learning resources, see [the Firefox TV Architecture Decision Record](https://github.com/mozilla-mobile/firefox-tv/blob/master/docs/architecture/adr-0002-robot-pattern.md). See [this FFTV example of the Robot pattern][robot example]. + +### JVM Testing +**Most tests should be written to run on your development machine because of the speed benefits.** Typically these tests are written as "unit tests" where each test will test a non-overlapping set of functionality so, if they fail, you can precisely pinpoint what is broken. + +These tests can be written effectively with standard JUnit APIs. + +Out-of-the-box accessing the Android framework APIs is not supported, however. You can use [Robolectric][] to "shadow" the Android framework (see below). + +#### Robolectric: Android API shadows +The Android APIs are not available on your development machine and will cause your tests to throw exceptions when used. However, **[Robolectric] provides "shadows" that mimic the Android APIs on your development machine,** allowing you to write tests with working Android APIs. + +While the shadows are good enough to prevent crashes, sometimes they are incomplete: for example, the shadow for `Bitmap` may not return pixel data which would cause Bitmap tests to fail. In these cases, you can fix the issue by [writing custom shadows](http://robolectric.org/extending/), interacting with a mock, or testing on device instead. + +**Robolectric tests take longer to run than non-Robolectric tests** because they run additional setup code (e.g. initializing the `Application`): we are mildly concerned this may be non-negligible in very large test suites. + +To use Robolectric, annotate your test class with `@RunWith(RobolectricTestRunner::class)` (in Kotlin; [see this example][robolectric example]). + +Note: Robolectric can also be used for JVM-based UI testing but we don't have much experience with that yet. + +### Cross-platform testing technologies +Technologies that are used for both on-device testing and tests on your development machine are... + +#### JUnit: testing framework +[JUnit] is the **go-to unit testing framework on the JVM.** It provides ways to define tests and test suites and provides assertion methods like `assertEquals(expectedValue, actualValue)`. + +By default, all test suites on Android are defined using JUnit 4. The latest version is JUnit 5. + +#### Mockito: mocking framework +[Mockito] is the **go-to mocking framework on the JVM:** it includes mock, spy, and call count verification support. Mocks are used to implement custom functionality for classes without having to extend the classes because extending often requires a lot of boilerplate and fragile code. + +Mockito cannot be used to mock static methods. In these cases, you can also include [PowerMock] but know that mocking static methods is arguably considered a poor practice. + +See [this FFTV example of Mockito][mockito example]. + +Mockito does not interact well with Kotlin so we are considering alternatives, such as: + +#### MockK: alternate mocking framework +[MockK] is a popular **mocking framework for Kotlin:** it includes mock, spy, and call count verification support that better aligns with Kotlin APIs. It can **mock static methods, extension functions, and objects**. We have documentation written for [using MockK](https://notwoods.github.io/mockk-guidebook/) and [transitioning from Mockito](https://notwoods.github.io/mockk-guidebook/docs/mockito-migrate/). + +See [this Fenix example of MockK][mockk example]. + +#### MockWebServer: mock network interactions +The network is unreliable so it should be avoided during testing. Instead, you can use **[MockWebServer] to provide your own data from "the network".** You access it by making your calls to the URL it returns. This lets it work with `WebView`s. + +See [this FFTV example of MockWebServer][mockwebserver example]. + +## Best practices +This section is intended to be a non-exhaustive list of high-level guidelines (not rules!) when testing. + +- Don't depend on external factors, like the network, to ensure your tests are always reliable +- For unit testing your UI, architect your application to separate your model from your UI, e.g. with architectures like MVP, MVVM, and MVI. Without consciously separating these parts of your application, it may be too difficult to test +- For UI testing, disable animations: Espresso's handling of them is mediocre +- In addition to testing standard stuff like business logic and UI state, consider testing: + - Every "configuration" at least once: different screen sizes, different API levels, your A/B test experiments, l10n if you have different behavior in some locales + - Telemetry + - Accessibility + - Performance (event duration, CPU use, memory use, APK size) + +## Testing resources +Members of our team recommend, ["Working Effectively with Legacy Code"][legacy code] to learn how to introduce tests into an existing, untested project. + +[Espresso]: https://developer.android.com/training/testing/espresso/ +[UI Automator]: https://developer.android.com/training/testing/ui-automator + +[Robolectric]: http://robolectric.org/ + +[JUnit]: https://junit.org/junit4/ +[Mockito]: https://site.mockito.org/ +[MockK]: https://mockk.io/ +[PowerMock]: https://github.com/powermock/powermock +[MockWebServer]: https://github.com/square/okhttp/tree/master/mockwebserver + +[legacy code]: https://www.goodreads.com/book/show/44919.Working_Effectively_with_Legacy_Code + +[espresso example]: https://github.com/mozilla-mobile/firefox-tv/blob/f27a206cbee0e9bc7c9df9c4e93aae88198d211a/app/src/androidTest/java/org/mozilla/tv/firefox/ui/screenshots/SettingsTest.java#L51 +[robot example]: https://github.com/mozilla-mobile/firefox-tv/blob/f27a206cbee0e9bc7c9df9c4e93aae88198d211a/app/src/androidTest/java/org/mozilla/tv/firefox/ui/BasicNavigationTest.kt#L50 +[robolectric example]: https://github.com/mozilla-mobile/firefox-tv/blob/dd236b03560c3c438cb2c674f3c453f877e79bf5/app/src/test/java/org/mozilla/tv/firefox/ext/ContextTest.kt#L15 +[mockito example]: https://github.com/mozilla-mobile/firefox-tv/blob/11b9eb7be32787ebf919884a74a15d07781b4640/app/src/test/java/org/mozilla/tv/firefox/ext/AssetManagerTest.kt#L30 +[mockk example]: https://github.com/mozilla-mobile/fenix/blob/6bde0378a223b74f9c22e5e664a250ff7534dc11/app/src/test/java/org/mozilla/fenix/ext/ContextTest.kt#L104 +[mockwebserver example]: https://github.com/mozilla-mobile/firefox-tv/blob/4e8c9923dd6016ece5830d1eaaf58c1761d8e57f/app/src/androidTest/java/org/mozilla/tv/firefox/ui/IWebViewExecuteJavascriptTest.kt#L41 + +[Frictionless Android Testing]: https://www.youtube.com/watch?v=wYMIadv9iF8 diff --git a/mobile/android/docs/shared/android/ui-testing.md b/mobile/android/docs/shared/android/ui-testing.md new file mode 100644 index 0000000000..257944238d --- /dev/null +++ b/mobile/android/docs/shared/android/ui-testing.md @@ -0,0 +1,84 @@ +# Automated UI Testing +_This document is intended to explain how various technologies are used together to conduct automated UI testing for Android at Mozilla. This document is accessible for all developers including those that are new to the Mozilla community._ + +Recall, that the primary problem we try to solve with automated testing is preventing unintended changes, which often lead to bugs or poor user experiences. This problem is most common -- thus we benefit the most from thorough testing. + +When a Pull Request or merge request is created, we want to ensure that the changes are tested before they are merged. This is done by running a series of UI tests on the code. These tests are ran on Firebase Test Lab on virtual devices. + +_Recall that on Pull Requests, the status badge on Github will let you know whether it is green, running, or broken (red)._ + +But what is the flow of this process from creating a Pull Request to seeing UI test results? + +For simplicity, the flow of UI test execution starts with Taskcluster: + +# Taskcluster + +Taskcluster is a generic task execution service. Mozilla uses Taskcluster as the continuous (CI) pipeline to build and release Firefox. At Mozilla, we use Taskcluster to build, test, and run code tools for all commits and Pull Requests. + +For the sake of simplicity, when a Pull Request (or merge request) is created, a task is created on Taskcluster (FirefoxCI) that handles the building, signing, packaging and delivery of the Android application. + +Subsequently, a handoff to Firebase Test Lab occurs to provide an environment of UI test execution. + +For a more detailed document on Taskcluster see [here](https://github.com/mozilla-mobile/firefox-android/blob/main/docs/shared/android/taskcluster_guide.md). + +## Firebase Test Lab + +We use [Firebase Test Lab](https://firebase.google.com/docs/test-lab) to conduct automated on-device testing. Firebase Test Lab is a cloud-based testing platform that allows us to test our apps and websites in a way that is similar to how we would test them on a physical device. + +For more information on physical device testing see [here](https://github.com/mozilla-mobile/firefox-android/blob/main/docs/shared/android/device_testing.md). + +Executable Espresso and UI Automator UI tests run on virtual devices by default and the test suite will run automatically on all Pull Requests and merges on the `main` branch. Test results and test artifacts are immediately available after test execution. + +_Espresso and UI Automator are Google's core UI testing libraries on Android: they provide APIs to select views, perform actions on them, and assert state without reaching into implementation details._ + +For a more detailed document on testing styles and technologies used by the Android teams at Mozilla see [here](https://github.com/mozilla-mobile/firefox-android/blob/main/docs/shared/android/testing.md) + +# Treeherder + +[Treeherder](https://treeherder.mozilla.org/) is a reporting dashboard for Mozilla check-ins. It allows users to see result of automatic builds and their respective tests. A common use case is looking at a particular check-in and pulling up test logs and artifacts from the UI test job. + +For all Android applications, UI tests are classfied under the `ui-test-x86` job symbol. + +# Tying it all together + +Below is a very simplified flow diagram that demonstrates the hand-off from a Github event (e.g, Pull Request) to Taskcluster and Firebase Test Lab, and lastly back to Taskcluster and Treeherder. + +# Flow Diagram + +``` + Pull Request + or Merge + β + β + βββββββββΌββββββββ ββββββββββββββ + β β β β +βββ¬βββ€ Taskcluster βββββββββββββββββΊ Firebase β +β β β β β β +β β βββββββββ²ββββββββ ββββββββ¬ββββββ +β β β β +β β ββββββββββββββββββββββββββββββββ +β β +β β ββββββββββββββββ +β β β β +β ββββββββββββββββββββ€ Treeherder β +β β β +β ββββββββββββββββ +β +β ββββββββββββ +β β β +ββββββββββββββββββββββββ€ Github β + β β + ββββββββββββ +``` + +## Mozilla resources +* [Firebase Console](https://console.firebase.google.com/) +* [FirefoxCI](https://firefox-ci-tc.services.mozilla.com) +* [Taskcluster](https://taskcluster.net) +* [Taskcluster-wiki](https://wiki.mozilla.org/Taskcluster) +* [Treeherder](https://treeherder.mozilla.org) + +## Testing resources +* [Firebase](https://firebase.google.com) +* [Espresso](https://developer.android.com/training/testing/espresso) +* [UI Automator](https://developer.android.com/training/testing/ui-automator) diff --git a/mobile/android/docs/shared/android/workmanager_faq.md b/mobile/android/docs/shared/android/workmanager_faq.md new file mode 100644 index 0000000000..48e039c873 --- /dev/null +++ b/mobile/android/docs/shared/android/workmanager_faq.md @@ -0,0 +1,11 @@ +# WorkManager FAQ +The [WorkManager documentation](https://developer.android.com/reference/androidx/work/WorkManager) does not clearly explain all of the different edge cases that may be encountered. After some testing with this API, the edge cases that were tested are documented here for future reference: + +- When the device is turned off and turned on before the one-time scheduled job, does the job complete? + - Yes +- When the device is turned off and misses the one-time scheduled job time, does the job complete (i.e. βcatches upβ)? + - No +- What happens when exceptions are thrown inside a Worker? + - The job results in a `Failure`. By default, **this exception is not caught by Sentry**. +- If a job crashes, does it crash the foregrounded app? + - No diff --git a/mobile/android/docs/shared/android/writing_lint_rules.md b/mobile/android/docs/shared/android/writing_lint_rules.md new file mode 100644 index 0000000000..cd257bd70f --- /dev/null +++ b/mobile/android/docs/shared/android/writing_lint_rules.md @@ -0,0 +1,72 @@ +# Writing Custom Lint Rules Guide +This guide will help you add custom lint rules to your application so you can catch errors at compile time rather than at run time or during testing. + +If you get into trouble, _ask for help!_ These APIs can be confusing to get started with and it will save the team time if we share knowledge! + +### Comparison of systems +We have a few common lint systems for Mozilla Android apps: +- **detekt:** (probably) the go-to for analyzing Kotlin code +- **ktlint:** similar to detekt but use it instead if you don't want your lint rule to be suppressed, ever +- **android lint:** most useful when analyzing more than Kotlin code: Android-specific concerns (e.g. resource XML files), Gradle files, ProGuard files, etc. It is slow because it needs to compile the project and does multiple passes over the code; however, if your new lint check needs multiple passes, it can be useful. +- **Gradle tasks + hand-rolled check:** less performant but probably more familiar for devs to write. Use if you can't figure out how to use one of the other tools or if you're short on time + +## detekt +Be sure to read the gotchas section too. + +To add a lint check: +- Add a class that extends `Rule`. You need to override an appropriate `visit*` function to analyze the code and call `report` from it to trigger a violation. `visit*` uses the Visitor pattern so this should be intuitive if you're familiar with it. +- Initialize this class in `CustomRulesetProvider.kt` +- Add your lint rule to `config/detekt.yml` with `active: true` + +Here's [an example commit](https://github.com/mozilla-mobile/fenix/pull/15570/commits/5c438c918e640b2319b183ca1207bdc1667f62b1) where a lint rule is added and [the official docs](https://detekt.github.io/detekt/extensions.html). + +### Gotchas +Modifying lint rules will cause `./gradlew detekt` to fail because it breaks the gradle daemon. To work around this, do one of: +1. During development of custom lint rules, run without the daemon `./gradlew --no-daemon detekt` +1. Restart the gradle daemon between runs: `./gradlew --stop` + +The error you'll see if you reuse this invalid gradle daemon is: +``` +Property 'mozilla-detekt-rules' is misspelled or does not exist. + +FAILURE: Build failed with an exception. + +* What went wrong: +Execution failed for task ':detekt'. +> Run failed with 1 invalid config property. +``` + +detekt is lacking good documentation for writing custom rules: to understand how to write lint rules, look at our custom rules and the source of the rules built into detekt. You can also use `println` to output values inside `visit*` methods to understand what is available (ideally we can figure out how to launch a debugger to analyze it at runtime but we haven't yet). + +## ktlint +TODO: fill this out! + +We haven't written any ktlint rules so we're lacking knowledge here. + +## Android Lint +Custom Android lint checks are a great way to prohibit or warn about usages of certain classes and resources in a codebase and can be customized to give alternative suggestions. + +To add a lint check: +- Create a custom Detector that will scour the codebase for Issues to report (and add tests!) +- Make sure to be specific for which resource files we want to even look at using `appliesTo` and which elements in those files we care about using `getApplicableElements` +- Add the new detector to the LintIssueRegistry.kt file + +Here's [an example PR](https://github.com/mozilla-mobile/android-components/pull/6112) where lint rules are added (and tested!) to check XML files for incorrect usage. + +Here's a good [conference talk](https://www.droidcon.com/media-detail?video=380845392) about adding your own custom lint rules and an accompanying [repo](https://github.com/alexjlockwood/android-lint-checks-demo) with some great examples of what lint checks can do. + +## Gradle tasks + hand-rolled check +You can create Gradle tasks that, for example: +1. Read kotlin files into memory and analyze them line-by-line +1. Call out to grep, find, or other shell commands and analyze the output +1. Call out to custom python scripts + +If a violation is found, you can fail the Gradle task, failing the build. + +Unlike writing lint rules using other tools, there are some downsides: +- It's less performant: lint tools traverse the file hierarchy once so each lint rule we right adds an additional traverse, depending on the files analyzed +- It requires custom mechanisms to allow suppression of violations unlike in detekt, where you can simply declare `@Suppress("VIOLATION_NAME")` +- You have to know enough Gradle to get started. This is doable for straightforward cases but can quickly grow in complexity. +- You have to manually hook it into the correct build processes and this is tricky to do. Should it run in the pre-push hook? In TaskCluster CI (probably)? On every build? + +For an example of a Gradle task doing static analysis, see [LintUnitTestRunner](https://github.com/mozilla-mobile/fenix/blob/4a06e40e70c37df4157efc7f90afb682dbd3bb54/buildSrc/src/main/java/org/mozilla/fenix/gradle/tasks/LintUnitTestRunner.kt#L29). diff --git a/mobile/android/docs/shared/git_guide.md b/mobile/android/docs/shared/git_guide.md new file mode 100644 index 0000000000..424ce8011b --- /dev/null +++ b/mobile/android/docs/shared/git_guide.md @@ -0,0 +1,23 @@ +# Git guide +This guide is designed to help you get started with git and use its basics commands. + +- [Getting started](#getting-started) +- [First contribution](#first-contribution) +- [Good first bugs](#good-first-bugs) + +## Getting started +To get started with git, first install git from the given link or if you have already installed it then update to its latest version. +- [Install git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) + +After installing git successfully, make a account at github. +- [Get started with GitHub](https://help.github.com/en/github/getting-started-with-github/signing-up-for-a-new-github-account) + +## First contribution +Making a contribution is always good way to start in GitHub. +Let's make your first contribution using some simple steps and learning some commands. +- [Make your contribution](https://github.com/firstcontributions/first-contributions) + +## Good first bugs +Now, you have learned how to make contribution then try some of our easy bugs for you. +- [Our tips for finding easy bugs](https://github.com/mozilla-mobile/firefox-android/blob/main/docs/shared/android/CONTRIBUTING_code.md#finding-issues-to-work-on) +- [fenix `good first issue`s](https://github.com/mozilla-mobile/fenix/labels/good%20first%20issue) diff --git a/mobile/android/docs/shared/im/sentry_dsn_click.png b/mobile/android/docs/shared/im/sentry_dsn_click.png Binary files differnew file mode 100644 index 0000000000..94bac45bd9 --- /dev/null +++ b/mobile/android/docs/shared/im/sentry_dsn_click.png diff --git a/mobile/android/docs/shared/rfc/wallpapers_v2_network_improvements.md b/mobile/android/docs/shared/rfc/wallpapers_v2_network_improvements.md new file mode 100644 index 0000000000..5f1911ab88 --- /dev/null +++ b/mobile/android/docs/shared/rfc/wallpapers_v2_network_improvements.md @@ -0,0 +1,84 @@ +* Start date: 2022-06-21 +* RFC PR: [#112](https://github.com/mozilla-mobile/shared-docs/pull/112) +--- + +## Summary + +The wallpapers feature was introduced on Android and iOS in v98 during Q1 2022. Due to late scope changes, a networking solution was quickly put together for delivering wallpapers from a remote source. As part of a larger set of improvements planned for Q3 2022, it is intended to revisit this implementation to create a more scalable version. + +--- + +## Motivation + +The original version of the networking solution consisted of adding wallpaper image files to deeply nested directories which defined the properties of the wallpaper. For example, an Android wallpaper might be found at: +``` +<root>/mobile-wallpapers/android/<resolution>/<orientation>/<light/dark>/<wallpaper collection>/<wallpaper name> +``` + +This directory structure was confusing and difficult to manage. Copying several versions of a file to different locations was painful, even when automated. Automation would require consistent file naming for each new set of wallpapers, which can be hard to guarantee. + +Additionally, downloads were started automatically during app startup if a wallpaper was missing. + +In general, we would like to achieve the following goals: + +- it is easier for product, UX, and ENG to add new wallpapers +- it is possible to determine important metadata about a wallpaper, like its size or average color +- download is transparent to the user +- download is activated by the user +- require less bandwidth, if possible + +--- + +## Guide-level explanation + +Instead of relying on an (undocumented) convention for file paths, wallpaper metadata will be contained in a top-level JSON schema. Using a well-known format will help discoverability, and JSON's flexibility should allow for simpler long-term maintenance. It can also provide a more direct path to any required automation. This schema could define things like: + +- Android file path(s) +- iOS file path(s) +- thumbnail path(s) +- average color (for text and background coloring purposes) +- collection grouping for things like seasonal promotions +- wallpaper availability range +- wallpaper region availability + +This is not a finalized spec, but an example might look like the following: +```json +{ + "last-updated-date": "2022-01-01", + "collections": [ + { + "name": "McCollectionFace", + "available-locales": ["en-US", "es-US", "en-CA", "fr-CA"], + "availability-range": { + "start": "2022-06-27", + "end": "2022-09-30" + }, + "wallpapers": [ + { + "name": "Sunset", + "android-file-paths": ["android/path/to/sunset.svg"], + "android-thumbnail-path": "android/path/to/sunset-thumbnail.svg", + "iOS-file-paths": ["ios/path/to/sunset/high-resolution.pdf", "ios/path/to/sunset/low-resolution.pdf"], + "iOS-thumbnail-path": "ios/path/to/sunset-thumbnail.pdf", + "average-color": "0xADD8E6", + } + ] + } + ] +} +``` + +We will likely continue to ship some small number of wallpapers as part of our app binaries. This ensures that at least a few wallpapers are available for onboarding. Thumbnails for onboarding wallpapers and non-onboarding wallpapers may also be included. + +Additionally, including thumbnails will allow clients to keep download size relatively small. Thumbnails will need to follow the current eager downloading strategy that wallpapers currently do. This will allow thumbnails to be decoupled from app releases, but they will still be visible when the settings page is open. The current suggestion is to display some kind of action overlay above the thumbnails indicating that clicking them will cause a download. + +--- + +## Drawbacks + +- JSON becomes tied to app versions. Adding or changing fields could mean that older versions of the app are no longer able to parse the schema. + +## Rationale and alternatives + +- Generally, JSON is still parsable even if new fields are added, and old versions of the app wouldn't know what to do with new fields anyway. Updating the types of fields would lead more directly to problems. In those cases it would always be possible to add a new field instead, but this could create quite a lot of noise in the definition. Even if clients fail to fetch the latest metadata, it's quite possible users have downloaded the subset of wallpapers they are interested in already. +- Alternatively, we could introduce versioned schema that could be easily tied to client-side upgrades. For example, a list of JSON files like: `json/v1.json`, `json/v2.json`, etc. diff --git a/mobile/android/docs/shared/sentry_guide.md b/mobile/android/docs/shared/sentry_guide.md new file mode 100644 index 0000000000..d475dac072 --- /dev/null +++ b/mobile/android/docs/shared/sentry_guide.md @@ -0,0 +1,8 @@ +# Sentry: crash reporting +Each platform has its own documentation for how it uses Sentry: +- Android ([focus-android](https://github.com/mozilla-mobile/focus-android/wiki/Crash-Reporting-with-Sentry)) +- iOS ([firefox-ios](https://github.com/mozilla-mobile/firefox-ios/wiki/Crash-Reporting-with-Sentry)) + +## Getting the DSN/API keys for a project +To download the DSN, open your project in the Sentry dashboard and click here: +![Where to click to download the DSN](im/sentry_dsn_click.png) diff --git a/mobile/android/docs/shared/uplift_guide.md b/mobile/android/docs/shared/uplift_guide.md new file mode 100644 index 0000000000..e60b6211d1 --- /dev/null +++ b/mobile/android/docs/shared/uplift_guide.md @@ -0,0 +1,70 @@ +# Uplift Request Process Guide + +An uplift can be requested of a patch landed on main in order to expedite that change into an earlier release outside of the standard train model. + +The developer provides some information as part of the request to help inform the decision as to whether or not to accept the uplift. These requests are regularly monitored by Release Management. The release manager will then assess the potential risk/reward and make a decision on whether or not to accept the requested uplift. If the uplift is accepted, the release manager approves the request and grafts the patch to the Beta branch. If the request does not meet the criteria, the release manager may look for some additional information or reject the request and provide a reason. + +There are some exceptions to the uplift approval process, here it is acceptable to create and merge the backport without uplift approval: +- A PR for an initial experiment generated by a script and has no impact on functionality - [example PR](https://github.com/mozilla-mobile/firefox-android/pull/1265) +- A PR that fixes/improves a test and has no impact on functionality. + +## Beta/Release Uplift Steps + +For Beta and Release, the following steps outline the process: +1. A PR that targets Firefox-Android main has an associated Bugzilla bug. The PR is linked as an attachment to the Bugzilla bug. +2. The PR goes through the standard review process and merges into the main branch. +3. There are 4 entry points to an uplift request: +- The developer recognizes that the PR is a candidate to backport to a release branch during development. A developer should ask their engineering or product manager for uplift approval before requesting uplift for features or big bug fixes. + - Use Bugzillaβs βneedinfoβ to request engineering or product manager in the Bugzilla bug, so we have a record of the approvals. + - Some features and technical changes may need decisions and guidance from the leadership team that can affect what is released to our users in the Beta and/or Production channels. + - Uplifting small bug fixes doesnβt require approval from an engineering or product manager. +- An engineering or product manager may identify that a PR is applicable for an uplift request and reach out to the developer. +- The autonag bot may identify that a bug was fixed in main but Beta or Release are marked as affected. The bot will add a comment to the bug and needinfo the developer. +- Release Management may identify if a PR is a good candidate to backport and reach out to the developer via a comment in the bug. +4. If there is agreement and manager approval that the patch is suitable for uplift, the developer uses Mergify to create a [backport PR](https://docs.mergify.com/actions/backport/) for the applicable release branch. +- Example: `@Mergifyio backport releases_v111` +- NOTE: If there are merge conflicts, the developer is expected to resolve them prior to requesting approval. +- NOTE: Backports generally do not require an additional code review. However, if the patch requires any significant rebasing and/or re-architecting then itβs up to the developer to request a code review from the relevant code owner prior to requesting approval. +5. After the backport PR is attached to the bug (this should happen automatically), the developer views the Details of the attachment. +6. The developer selects β?β from the dropdown menu beside beta or release approval request. +- NOTE: The developer should choose beta or release depending on which channel the targeted version is currently on. +- NOTE: There can be a window of time around RC week where approval-mozilla-beta is requested, but no further beta builds are planned. In this scenario, the release manager will take care of changing the pending beta approval request to a release request instead. +7. The developer provides details for the approval request and clicks Submit. +- [Video Example of adding a uplift request](https://video.chevrel.org/demos/upliftRequestForm.mp4) πΊ +8. The approval flag is added to the attachment and the information from the form is added as a comment in the bug. +9. The release manager regularly checks Bugzilla for uplift requests. +10. The release manager reviews the uplift approval request on the bug. If approved, they will change the approval status flag from β?β to β+β and add a comment indicating the next build that will include that change. +The release manager may also reject the request. In that case, the release manager will change the approval status flag from β?β to β-β and provide a reason for the rejection. Please see the below section on uplift request rejection for additional information. +11. The release manager will then approve and merge the PR in Github and update the bug with a link to the commit and set the affected version to fixed. +- NOTE: The manual bug marking steps are temporary until automation is in place to do so. + +## Beta/Release Differences + +The uplift request process for Beta and Release are essentially the same, with some notable differences not outlined above: +- During Beta: + - Uplift requests are only tracked in Bugzilla. + - Beta uplift requests are approved/rejected daily during the Beta cycle. + - PRβs landed during beta are available in the next Beta build. The release manager will add a comment at the time of approval indicating what build that will be. +- During Release: + - Uplift requests are tracked in Bugzilla and also in a release checklist maintained by the release manager. + - Release uplift request approvals depend on where we are in the cycle. If the request is not a driver for an RC respin or a dot release, the request will pend as a potential ride-along until an appropriate RC respin or dot release is identified. + - Where relevant, the release manager will include a release note for the uplift to be included in the release notes published in the Google Play Store for that dot release. + +## Uplift Request Approval Form + +The uplift request approval form in Bugzilla is the same as for Desktop uplift requests, guidelines are available [here](https://wiki.mozilla.org/Release_Management/Uplift_rules#Guidelines_on_approval_comments_for_Beta_and_Release). + +## Uplift Request Rejection + +There are several scenarios when a release manager may reject an uplift request. Here are some common examples: +- The uplift request is too late in the cycle for a release. For example, if we are in release candidate week for the next release then the release manager is already preparing builds that will include the fix. +- The uplift request is for a large change that has not had enough time to bake in nightly or beta. +- The uplift request is flagged as risky without a clear plan to mitigate risk. + +When a release manager rejects an uplift request they will always provide a comment explaining the rejection. The release manager is always open to discussion if the developer does not accept the rejection. The discussion can be done via comments in the bug, a slack thread, an email exchange, or in a meeting. + +If further alignment is needed between the developer and the release manager, the final call is with the Product team. The Product team can provide perspective on risk vs. reward. + +## Backouts + +If a PR landed in main, main was branched to release, and later it was discovered the PR introduced a regression without a simple fix then we may decide to back out the PR from the release branch. This would follow the same process as an uplift request. diff --git a/mobile/android/docs/shared/versioning.md b/mobile/android/docs/shared/versioning.md new file mode 100644 index 0000000000..28a2ec2059 --- /dev/null +++ b/mobile/android/docs/shared/versioning.md @@ -0,0 +1,25 @@ +# Android Versioning + +This repository uses the same `version.txt` to define the version for Android Components, Focus, and Fenix. The following formatting corresponds to release branches +on the release track. + +## Format + +XXX.0 (where XXX is defined as the current desktop release #) + +*Please note: the initial release is `major.minor`, but the following releases will be `major.minor.patch`* + +* The first and second digit will be directly tied to the associated desktop release +* The third digit will be reserved for Android-only patch releases + +### Example +| | Desktop | Android | +| ------------------------------ | --------------------- | ------------------------------ | +| Fx XXX initial release | XXX.0 | XXX.0 | +| Desktop + Android dot release | XXX.0.1 | XXX.1.0 | +| Android-only patch | <sub>No change</sub> | XXX.1.1 | +| Desktop-only dot release | XXX.0.2 | <sub>XXX.2.0 is skipped </sub> | +| Android-only dot release | <sub>No change</sub> | XXX.2.1 | +| Desktop + Android dot release | XXX.0.3 | XXX.3.0 | +| Desktop-only dot release | XXX.0.4 | <sub>XXX.4.0 is skipped </sub> | +| Desktop + Android dot release | XXX.0.5 | XXX.5.0 | |