Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add experimental support for peer-to-peer updates #595

Merged
merged 238 commits into from
Jul 23, 2021
Merged
Show file tree
Hide file tree
Changes from 203 commits
Commits
Show all changes
238 commits
Select commit Hold shift + click to select a range
c825111
Add AppInfo native module for accessing sourceDir
gmaclennan Feb 17, 2020
d169df6
Add share sheet for sharing Mapeo APK
gmaclennan Feb 17, 2020
0dd3aa5
Test auto-updating app from an APK
gmaclennan Feb 18, 2020
28271b1
Test reading package signatures
gmaclennan Feb 18, 2020
710aee9
Prep APK for sharing and start upgrade server.
hackergrrl Jan 7, 2021
5b71221
get correct mapeo app version + show on settings
hackergrrl Jan 7, 2021
afd1c8f
remove old comments
hackergrrl Jan 22, 2021
257a046
implement upgrade storage module
hackergrrl Jan 29, 2021
4344edd
prevent 'filename' from being exposed on upgrades
hackergrrl Jan 29, 2021
0330de8
add createReadStream API to stream upgrade data
hackergrrl Jan 29, 2021
5175afa
chore: add eslint linting
hackergrrl Jan 29, 2021
7017242
test: move fake.apk to static/ subdir
hackergrrl Jan 29, 2021
e61fae4
test: formatting
hackergrrl Jan 29, 2021
b31cbc6
fix: make 'arch' an array of strings
hackergrrl Jan 29, 2021
5049f32
chore: add 'test' npm script
hackergrrl Jan 29, 2021
e289d74
add initial UpgradeServer implementation
hackergrrl Jan 29, 2021
b401b1f
add new UpgradeServer implementation
hackergrrl Jan 29, 2021
ca1e0c6
test: add edge case tests for upgrade server
hackergrrl Feb 1, 2021
f7b8b2b
move the upgrade discovery key to its own file
hackergrrl Feb 3, 2021
bc37167
have UpgradeServer accept a port to advertise on
hackergrrl Feb 3, 2021
9a49773
make UpgradeStorage#getAvailableUpgrades sync
hackergrrl Feb 3, 2021
5cf5717
refactor: consolidate apk->upgradeoption logic
hackergrrl Feb 5, 2021
e9cfc9f
Revert "make UpgradeStorage#getAvailableUpgrades sync"
hackergrrl Feb 5, 2021
9aae25f
refactor: use a file for downloaded upgrade info
hackergrrl Feb 5, 2021
672fde7
refactor: move LocalUpgradeInfo into a nodule
hackergrrl Feb 5, 2021
e12d102
refactor: rename createApkWriteStream
hackergrrl Feb 5, 2021
29d73f5
add work-in-progress UpgradeDownloader component
hackergrrl Feb 5, 2021
2f1ad41
chore: lint
hackergrrl Feb 5, 2021
c89a0e8
add download progress tracking to state
hackergrrl Feb 5, 2021
8082c9c
implement the UpgradeDownloader checker component
hackergrrl Feb 6, 2021
1214199
chore: cleanup
hackergrrl Feb 6, 2021
bcd89c2
implement the UpgradeManager
hackergrrl Feb 10, 2021
6cb2109
fix: ignore non-mapeo app keys
hackergrrl Feb 10, 2021
ececd6d
chore: add eslintrc for backend
hackergrrl Feb 10, 2021
1600a80
don't access UpgradeDownloader's internals
hackergrrl Feb 11, 2021
df1a725
auto-download upgrades that UpgradeManager finds
hackergrrl Feb 11, 2021
0f3a6e7
chore: lint
hackergrrl Feb 11, 2021
fa0798f
UpgradeDownload doesn't need version/arch/etc args
hackergrrl Feb 12, 2021
a073fba
fix bugs in case where server waits to shut down
hackergrrl Feb 12, 2021
e6374b1
upgrade-server runs its own http server
hackergrrl Feb 12, 2021
a970239
expose 'startTime' context for upgrade searches
hackergrrl Feb 13, 2021
e344383
chore: upgrade deps
hackergrrl Feb 17, 2021
bab6006
p2p-upgrades: pass in version to storage
hackergrrl Feb 17, 2021
96fc839
wip: integrate upgrademanager into server
hackergrrl Feb 17, 2021
a1c4686
chore: add missing dep from develop
hackergrrl Feb 17, 2021
44c970b
workaround for github:digidem/mapeo-mobile#521
hackergrrl Feb 17, 2021
3eb4eb3
wip: fixes
hackergrrl Feb 18, 2021
896085d
test: lint
hackergrrl Feb 18, 2021
d7d7919
fix: be able to listen for multiple ready events
hackergrrl Mar 4, 2021
1da8ee8
add upload info to sharing state
hackergrrl Mar 4, 2021
95295e6
DotIndicator: accept optional size parameter
hackergrrl Mar 4, 2021
b075ef0
wip: frontend implementation
hackergrrl Mar 4, 2021
642ee27
move upgrade state constants to a common module
hackergrrl Mar 5, 2021
105de81
backend: upgrade simulator tool
hackergrrl Mar 9, 2021
c32a9f6
implement upgrade state validation module
hackergrrl Mar 16, 2021
fbdf8be
use a common storage path for downloaded APKs
hackergrrl Mar 16, 2021
ffc311f
expose filename property from upgrade-storage api
hackergrrl Mar 16, 2021
eeb84c8
clone upgrade state context when modifying
hackergrrl Mar 16, 2021
a0d8e59
add more thorough upgrade state management
hackergrrl Mar 16, 2021
186c545
fix: expose upgrade filename instead of hash
hackergrrl Mar 16, 2021
9dab644
add more thorough state mgmt to upgrade-server
hackergrrl Mar 16, 2021
82fbc77
add more upgrade state mgmnt to upgrade-manager
hackergrrl Mar 16, 2021
2d8045c
wip: variety of frontend implementation upgrade changes
hackergrrl Mar 16, 2021
af5cdc4
prevent older APKs from trying to be installed
hackergrrl Mar 16, 2021
d898b4d
simplify frontend state handling of upgrades
hackergrrl Mar 18, 2021
15e7e53
add another effect to track peer + upgrade state
hackergrrl Mar 18, 2021
e8ff28c
test: fixes
hackergrrl Mar 22, 2021
70fc9d5
test: files aren't left over after write error
hackergrrl Mar 22, 2021
c91847e
feat: check hash upon upgrade download + cleanup
hackergrrl Mar 22, 2021
785f56f
storage: use a temp dir for in-progress downloads
hackergrrl Mar 24, 2021
1e2ef86
p2p-upgrades: make logging prefixes consistent
hackergrrl Mar 24, 2021
07dc302
lint
hackergrrl Mar 24, 2021
3a70cee
add debug logging to upgrade backend
hackergrrl Mar 24, 2021
d8d67b3
fix: write completed upgrade to root storage dir
hackergrrl Mar 26, 2021
845c24a
Internationalization example
gmaclennan Mar 31, 2021
a2bbced
fix(upgrade): remove TTL; re-announce regularly
hackergrrl Mar 31, 2021
fb33cc5
Update src/backend/lib/local-upgrade-info.js
noffle Mar 31, 2021
943f40d
fix: readd flipper code (merge issue)
hackergrrl Mar 31, 2021
94aeb60
use strings for upgrade state constants
hackergrrl Mar 31, 2021
4bcefa8
Internationalize text in update bar
gmaclennan Apr 1, 2021
ff6a5c7
Remove unused import
gmaclennan Apr 1, 2021
c686ca8
chore(linting): Combine duplicate imports
gmaclennan Apr 1, 2021
2040269
chore(linting): Remove unreachable code
gmaclennan Apr 1, 2021
5e645f0
Extract messages for translation
gmaclennan Apr 1, 2021
25957e5
java: add missing import
hackergrrl Apr 15, 2021
2799406
fix: prevent redownload of locally-stored upgrades
hackergrrl Apr 15, 2021
aa55be3
do not copy the APK; use privateStorage instead
hackergrrl Apr 20, 2021
3d5c51e
add optional param to upgrade simulator
hackergrrl Apr 20, 2021
53ab9ed
chore: fix line-endings for gradlew.bat (should be CRLF)
gmaclennan Apr 20, 2021
9a6731e
Merge branch 'develop' into share-apk-p2p
gmaclennan Apr 20, 2021
7579711
feat: Add minSdkVersion to AppInfo interface
gmaclennan Apr 15, 2021
8f3a651
chore: Add context to backend status errors
gmaclennan Apr 15, 2021
766920a
chore: Remove unused "flavor" option in createServer
gmaclennan Apr 15, 2021
7930929
chore: Don't try to support multiple "config" RPC messages
gmaclennan Apr 15, 2021
2beaec3
feat: Pass apkPath, minSdkVersion etc. to createServer()
gmaclennan Apr 15, 2021
520d947
chore: have UpgradeManager expose functions instead of events and aju…
luandro Apr 16, 2021
aa259c8
Revert "Test reading package signatures"
hackergrrl Apr 22, 2021
2ca714a
Remove redundant keys from backend setup
noffle Apr 22, 2021
a2e9326
Merge pull request #566 from digidem/share-apk-p2p-gregor2
noffle Apr 22, 2021
7db068b
remove unused error handling
hackergrrl Apr 22, 2021
5709722
Fix naming of apkVersion
gmaclennan Apr 26, 2021
76295a4
Merge branch 'share-apk-p2p' into issue-536-2
gmaclennan Apr 26, 2021
97195e7
Opt-in setting for p2p upgrades (#561)
luandro Apr 27, 2021
8c730aa
fix: Don't copy src/backend/test/fake.apk as part of CI Gradle build
gmaclennan Apr 27, 2021
aa13883
Update backend build script to only copy whitelisted files
gmaclennan Apr 27, 2021
4526fba
fix: use correct variable names
hackergrrl Apr 28, 2021
300cbd1
chore: Run backend tests on CI (#577)
gmaclennan Apr 28, 2021
288305e
fix(AppInfo): Remove minSdkVersion
gmaclennan Apr 28, 2021
6975948
Revert "fix: use correct variable names"
gmaclennan Apr 28, 2021
30b5fca
Merge branch 'share-apk-p2p' into issue-536-2
gmaclennan Apr 28, 2021
1664621
Add integration with server.js
gmaclennan Apr 28, 2021
6085f4a
Fix upgrade simulator for new upgrademanager pattern
gmaclennan Apr 28, 2021
59ecd6a
Update settings store key for new settings shape
gmaclennan Apr 28, 2021
a6fcaeb
Remove changes that should not be in this PR
gmaclennan Apr 28, 2021
70a4301
Merge branch 'share-apk-p2p' into issue-536-2
gmaclennan Apr 28, 2021
0b6abcb
chore: Fix backend tests CI
gmaclennan May 4, 2021
c7b7810
fix: upgrade semver sorting
hackergrrl Apr 28, 2021
a0e2e83
fix: add upgrade options listener only once
hackergrrl Apr 28, 2021
794c5dd
fix: check for upgrades after current d/l finishes
hackergrrl Apr 28, 2021
09433f7
fix: prioritize Downloading over ReadyToUpgrade
hackergrrl Apr 29, 2021
07db3f8
fix: reset upgrade state on download completion
hackergrrl Apr 30, 2021
6fd4449
fix: remove extra setState; fix param on a setState
hackergrrl Apr 30, 2021
5f0ad96
fix: remove setState(IDLE) on error
hackergrrl Apr 30, 2021
20dd635
test: fixes
hackergrrl May 3, 2021
569c511
fix: don't show search:IDLE as an error state
hackergrrl May 3, 2021
e7e33f0
fix: remove DOWNLOADED upgrade state
hackergrrl May 6, 2021
282b46c
clean up UpgradeManager download logic
hackergrrl May 6, 2021
f758f41
remove unused code
hackergrrl May 7, 2021
8a74c73
allow UpgradeCheck to re-check + choose latest version
hackergrrl May 7, 2021
49b7464
Merge pull request #580 from digidem/share-apk-p2p-redownload
noffle May 10, 2021
aa945af
Merge commit '49b7464e2ab170939139f32a1026e037f4edf7b9' into issue-536-2
gmaclennan May 11, 2021
5de77e4
fix: Fix tests (maybe?)
gmaclennan May 11, 2021
f5fbb3a
fix: Fix ApkInstaller for Android <7 (SDK version <24) (#586)
gmaclennan May 11, 2021
ea5872c
Merge pull request #567 from digidem/issue-536-2
noffle May 11, 2021
94e55ea
fix: Throttle upload/download progress events to 100ms
gmaclennan May 10, 2021
c145fcc
Fix lint issues
gmaclennan May 11, 2021
af689c9
fix: Throttle progress events to every 400ms
gmaclennan May 11, 2021
d751b0f
s/PROGRESS_THROTTLE/PROGRESS_THROTTLE_MS
hackergrrl May 11, 2021
a0533f7
Merge pull request #588 from digidem/p2p-update/fix-progress-throttle
noffle May 11, 2021
bb7933a
remove obsolete comment
hackergrrl May 12, 2021
e412b59
Merge branch 'develop' into share-apk-p2p
hackergrrl May 13, 2021
f121cec
Use same node as nodejs-mobile
gmaclennan May 12, 2021
520ee6f
WIP: Refactor in Typscript
gmaclennan May 18, 2021
fcbcaff
upgrade-storage tests & fixes
gmaclennan May 17, 2021
9ed956d
rename state type
gmaclennan May 17, 2021
17b0872
Fix upgrade candidate check for device.supportedAbis
gmaclennan May 18, 2021
cc0e1a8
fix storage test
gmaclennan May 17, 2021
81bfba3
Remove hardcoded filepaths from expected APK info
gmaclennan May 17, 2021
3fd2133
fix beforeAfterStream tests
gmaclennan May 18, 2021
b6cf635
UpgradeServer tests & corresponding fixes
gmaclennan May 18, 2021
b3121df
Additional tests for trying to write invalid APKs (no uncaught error)
gmaclennan May 18, 2021
697189a
fix: Don't query a peer if a request to that peer is already in progress
gmaclennan May 18, 2021
143e23c
Add UpgradeDiscovery test & fixes
gmaclennan May 18, 2021
3b5830c
delete upgrade-download.js (delete was lost in rebase)
gmaclennan May 18, 2021
3f9b218
Discovery test for peer going offline
gmaclennan May 18, 2021
d48ddb2
oops, remove test.only()
gmaclennan May 18, 2021
397bd54
Update Node version for CI tests (use same as nodejs-mobile)
gmaclennan May 18, 2021
86c3817
Finish UpgradeDiscovery tests
gmaclennan May 19, 2021
6f616c8
Fix for backend build scripts
gmaclennan May 19, 2021
093a14a
properly fix backend build scripts this time
gmaclennan May 19, 2021
a1d238c
Fix typo in comment
gmaclennan May 19, 2021
b712a10
Fix: once() on error
gmaclennan May 20, 2021
24247b0
fix: Add tests from duplexify
gmaclennan May 20, 2021
d6028ae
Fix for error state of AsyncService
gmaclennan May 20, 2021
e7e3d35
Add test to ensure different discovery keys do not find each other
gmaclennan May 20, 2021
a380a56
fix: An installer with the same hash as current APK is not an upgrade
gmaclennan May 20, 2021
5899fdd
Fix: An installer < version of current APK is not an upgrade
gmaclennan May 20, 2021
aca1a09
fix: fake-apk-info should have same applicationId as valid APK fixtures
gmaclennan May 20, 2021
281570f
chore: Add initial upgrade manager integration test
gmaclennan May 20, 2021
4270980
fix: Don't download an upgrade candidate that we already have in storage
gmaclennan May 20, 2021
f2840e0
feat: Allow state emit throttle to be changed for testing
gmaclennan May 20, 2021
dfc869f
chore: rename var
gmaclennan May 21, 2021
4020cf8
chore: rename var
gmaclennan May 21, 2021
7c0a547
chore: fix typo in comment
gmaclennan May 21, 2021
31aac88
fix: Remove incorrect setState in _stop()
gmaclennan May 21, 2021
b5a794e
chore: remove redundant dep
gmaclennan May 21, 2021
a1f8321
chore: rename function
gmaclennan May 21, 2021
cc8b32f
chore: Comprehensive logging
gmaclennan May 21, 2021
ef8f051
chore: make all class fields private instance fields
gmaclennan May 21, 2021
a8c4aa0
chore: Additional APK fixtures for testing, and consistent naming scheme
gmaclennan May 21, 2021
8ddc23c
chore: Setup UpgradeManager test scenarios
gmaclennan May 21, 2021
1e55875
chore: integration tests for UpgradeManager
gmaclennan May 24, 2021
7111985
chore: remove old tests
gmaclennan May 24, 2021
775abb7
chore: Ensure tmp folders created in tests are removed afterwards
gmaclennan May 24, 2021
e721fcb
chore: Double-check downloaded hash in UpgradeManager tests
gmaclennan May 24, 2021
4f62978
chore: Simplify abi check code
gmaclennan May 25, 2021
540370a
chore: fix hash error message
gmaclennan May 25, 2021
5dfab04
fix: Remove test.only()
gmaclennan May 25, 2021
ce787cc
WIP integrate new UpgradeManager API with server.js and front-end
gmaclennan May 26, 2021
ae636ef
Add script to build APKs for testing p2p upgrades
gmaclennan May 27, 2021
eb18d0f
Update Upgrade Simulator for new API
gmaclennan May 27, 2021
3181140
cleanup UpgradeBar code
gmaclennan May 27, 2021
6fce175
Fix filepaths for share to switch to cache-path vs. files-path
gmaclennan May 27, 2021
2c53f0a
fix: Fix upload progress
gmaclennan May 27, 2021
6f9def7
Allow upgrade bar to expand in height if needed
gmaclennan May 27, 2021
cb7f3f1
Change button text to "Install" from "Install update" due to space con..
gmaclennan May 27, 2021
b7d73f5
Don't show install button if there are active downloads / uploads
gmaclennan May 27, 2021
f191f42
Add Mapeo version to wifi bar (above upgrade bar)
gmaclennan May 27, 2021
1e8e858
Create small button variant for use in upgrade bar
gmaclennan May 27, 2021
9b5690a
Add error icon for error state
gmaclennan May 27, 2021
b2055cd
Use normal weight text in upgrade bar
gmaclennan May 27, 2021
96e106c
Cleanup unused imports
gmaclennan May 27, 2021
e6d37ae
Turn off debug logging in production builds
gmaclennan May 27, 2021
f66e4c4
chore: Reduce noise in adb logcat logs
gmaclennan May 28, 2021
be9ee82
fix: Don't debug log in production
gmaclennan May 28, 2021
31df054
fix: Wait for server to start before trying to start p2p-upgrades
gmaclennan May 28, 2021
d1d677e
fix: Don't query self for upgrades
gmaclennan May 28, 2021
143ee08
Don't use fastify.inject() for tests - test with actual requests via got
gmaclennan May 28, 2021
1fe305f
fix: Ensure storage tests fail if expected throw does not happen
gmaclennan May 28, 2021
7adcb9c
chore: use readJson from helpers
gmaclennan May 28, 2021
4218b8a
feat: Show "No app updates found" once at least one device is checked
gmaclennan May 28, 2021
b17bbf7
Add explanation for unusual code in upgrade-simulator
gmaclennan May 28, 2021
4781ded
feat: Use keep-alive to re-use same connection for upgrade check polling
gmaclennan Jun 1, 2021
c66297c
Add test for download completing before server close
gmaclennan Jun 1, 2021
ed49e4e
Add test for broken connection
gmaclennan Jun 1, 2021
12bfddb
Cleanup AsyncService code and fix bugs
gmaclennan Jun 2, 2021
0864eda
fix: Don't throttle state events, only download progress
gmaclennan Jun 2, 2021
2d32b2e
Fix manager tests
gmaclennan Jun 2, 2021
3bd2dd5
Determine update candidate based on new version naming scheme (#596)
luandro Jun 2, 2021
3e5eb67
Remove debug logging on CI (was used for debugging test failure)
gmaclennan Jun 3, 2021
fa065aa
chore: Fix to backend build script
gmaclennan Jun 3, 2021
84fbde0
chore: Clean up logging code
gmaclennan Jun 3, 2021
bc3bef7
Failing test for checkedPeers
gmaclennan Jun 3, 2021
598a4c1
fix: checkedPeers should only update after potential upgrades r checked
gmaclennan Jun 3, 2021
ec1f6cb
chore: typings for tests
gmaclennan Jun 3, 2021
da66f31
failing test for checkedPeers reseting after stop,start
gmaclennan Jun 3, 2021
cbac680
fix: Checked Peers should reset after start and stop
gmaclennan Jun 3, 2021
ae824ca
fix tests, stricter testing for empty arrays in state
gmaclennan Jun 3, 2021
781d040
fix: Don't use same keep-alive connection for downloads and polling
gmaclennan Jun 4, 2021
c2827bb
Update upgrades QA build numbers
gmaclennan Jun 4, 2021
3c6d684
fix typo
gmaclennan Jun 17, 2021
5f4520a
Merge branch 'develop' into p2p-update/typescript
gmaclennan Jul 6, 2021
0934614
Merge branch 'develop' into p2p-update/typescript
gmaclennan Jul 21, 2021
602c8e9
Merge branch 'develop' into p2p-update/typescript
gmaclennan Jul 21, 2021
3d41403
Fix "Go to Map" button style for empty observation list screen
gmaclennan Jul 23, 2021
d77f67c
fix e2e debug test config (for local testing)
gmaclennan Jul 23, 2021
56cda61
Increase server start timeout in attempt to fix e2e tests
gmaclennan Jul 23, 2021
7deb3df
Merge branch 'develop' into p2p-update/typescript
gmaclennan Jul 23, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion .github/workflows/android.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
name: Android
on: [pull_request]
jobs:
build:
backend-tests:
name: backend tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Node
uses: actions/setup-node@v2
with:
node-version: "12.16.3"
- name: Npm Install
run: |
npm ci
cd src/backend
npm ci
- name: Backend Tests
run: |
cd src/backend
npm test
e2e-tests:
name: e2e tests
runs-on: macos-latest
steps:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ build/
.gradle
local.properties
*.iml
.project
.settings
.classpath

# node.js
#
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
10.16
12.16.3
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ QA variant is created for production releases.

By default (since [e861e6a](https://github.com/digidem/mapeo-mobile/commit/e861e6a248cdd613cb9a33b9633cc8773c67c6cc)), if you build or run Mapeo via an npm script (e.g. `npm run android`), Mapeo will use the `version` field of `package.json` as the Android [version name](https://developer.android.com/reference/android/content/pm/PackageInfo#versionName). This value is shown to the user in the "About Mapeo" screen, is used for evaluating updates, and is shown in the Android System Settings as the version of the app. To override this version name, or to define the version name when building Mapeo directly via Gradle (e.g. `cd android && ./gradlew assembleAppRelease`), you may set the environment variable `ANDROID_VERSION_NAME` e.g. `ANDROID_VERSION_NAME=5.4.0-RC.56 ./gradlew assembleAppRelease`. This may be useful for testing version logic, or for building a "special" version such as a Release Candidate or internal testing build (see https://github.com/digidem/mapeo-mobile/blob/develop/bitrise.yml#L376-L412 for how our CI sets this value).

The Android [versionCode](https://developer.android.com/reference/android/R.styleable#AndroidManifest_versionCode) [defaults to `1`](https://github.com/digidem/mapeo-mobile/blob/develop/android/app/build.gradle#L178). You may override this by setting the `ANDROID_VERSION_CODE` environment variable. Android will only let you install a new version of the app over an existing version if the Version Code is equal to or greater than the installed app. In our CI we use the CI build count as the version code, which ensures that each subsequent release of Mapeo has a higher version code.
The Android [versionCode](https://developer.android.com/reference/android/R.styleable#AndroidManifest_versionCode) [defaults to `1`](https://github.com/digidem/mapeo-mobile/blob/develop/android/app/build.gradle#L178). You may override this by setting the `ANDROID_VERSION_CODE` environment variable. Android will only let you install a new version of the app over an existing version if the Version Code is equal to or greater than the installed app. In our CI we use the CI build count as the version code, which ensures that each subsequent release of Mapeo has a higher version code.

## Troubleshooting

Expand Down
9 changes: 6 additions & 3 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ loadDotEnv()

File releaseKeystore = file("mapeo-release-key.keystore")
def myVersionCode = (getenv("ANDROID_VERSION_CODE") ?: 1) as int
def myVersionName = getenv("ANDROID_VERSION_NAME") ?: getenv("npm_package_version") ?: "0.0.0"
println("Version name")
println(myVersionName)

bugsnag {
ndk true
Expand All @@ -196,7 +199,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode myVersionCode
versionName getenv("ANDROID_VERSION_NAME") ?: getenv("npm_package_version") ?: "0.0.0"
versionName myVersionName
archivesBaseName = "mapeo"

// Detox integration
Expand Down Expand Up @@ -276,8 +279,8 @@ android {

variantFilter { variant ->
def names = variant.flavors*.name
// For QA variants, we only build debug apk
if (names.contains("qa") && variant.buildType.name != "debug") {
// For QA variants, skip universal build
if (names.contains("qa") && variant.buildType.name == "universal") {
setIgnore(true)
}
}
Expand Down
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

<application android:name=".MainApplication"
android:usesCleartextTraffic="true"
Expand Down
128 changes: 128 additions & 0 deletions android/app/src/main/java/com/mapeo/ApkInstallerModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package com.mapeo;

import android.content.Intent;
import android.content.ActivityNotFoundException;
import android.app.Activity;
import android.net.Uri;
import android.os.Build;
import androidx.core.content.FileProvider;
import android.util.Log;

import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.BaseActivityEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import java.io.File;

// Largely borrowed from https://github.com/nodece/react-native-apk-installer-n
// Added ability to return promise if install cancelled as described in
// https://facebook.github.io/react-native/docs/native-modules-android#getting-activity-result-from-startactivityforresult
public class ApkInstallerModule extends ReactContextBaseJavaModule {

private final ReactApplicationContext reactContext;
private static final String TAG = "ApkInstaller";
private static final int INSTALL_REQUEST = 3033;
private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";
private static final String E_FILE_DOES_NOT_EXIST = "E_FILE_DOES_NOT_EXIST";
private static final String E_INSTALL_CANCELLED = "E_INSTALL_CANCELLED";
private static final String E_INSTALL_FAILED = "E_INSTALL_FAILED";

private Promise mPickerPromise;

private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {

@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
if (requestCode == INSTALL_REQUEST) {
if (mPickerPromise != null) {
if (resultCode == Activity.RESULT_CANCELED) {
mPickerPromise.reject(E_INSTALL_CANCELLED, "Install was cancelled");
} else if (resultCode == Activity.RESULT_OK) {
mPickerPromise.resolve(null);
} else {
Log.e(TAG, "Install failed, result code: " + resultCode);
mPickerPromise.reject(E_INSTALL_FAILED, "Install failed");
}
mPickerPromise = null;
}
}
}
};

public ApkInstallerModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
// Register this native module as Activity result listener
reactContext.addActivityEventListener(mActivityEventListener);
}

@Override
public String getName() {
return "ApkInstaller";
}

@ReactMethod
public void install(String filePath, final Promise promise) {
Activity currentActivity = getCurrentActivity();

if (currentActivity == null) {
promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
return;
}

File apkFile = new File(filePath);
if (!apkFile.exists()) {
Log.e(TAG, "installApk: file does not exist '" + filePath + "'");
promise.reject(E_FILE_DOES_NOT_EXIST, "Apk file does not exist");
return;
}

// Store the promise to resolve/reject when installer activity completes
mPickerPromise = promise;

Intent intent = new Intent();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// API24 and up has a package installer that can handle FileProvider content:// URIs
String authority = reactContext.getPackageName() + ".provider";
Uri apkUri;
try {
apkUri = FileProvider.getUriForFile(getReactApplicationContext(), authority, apkFile);
} catch (Exception e) {
Log.e(TAG, "installApk exception with authority name '" + authority + "'", e);
mPickerPromise.reject(e);
mPickerPromise = null;
return;
}
intent.setAction(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, reactContext.getPackageName());
} else {
// Old APIs do not handle content:// URIs, so use an old file:// style
// The apk file and any parent folders need to be world-accessible for the
// package installer to be able to access them.
apkFile.setReadable(true, false);
// WARNING: This assumes that this is in a folder one-level deep within
// the internal storage files dir (getFilesDir()).
apkFile.getParentFile().setExecutable(true, false);
Uri apkUri = Uri.fromFile(apkFile);
intent.setAction(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(apkUri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
}
try {
currentActivity.startActivityForResult(intent, INSTALL_REQUEST);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "ActivityNotFoundException", e);
mPickerPromise.reject(e);
mPickerPromise = null;
}
}
}
28 changes: 28 additions & 0 deletions android/app/src/main/java/com/mapeo/ApkInstallerPackage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.mapeo;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ApkInstallerPackage implements ReactPackage {

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();

modules.add(new ApkInstallerModule(reactContext));

return modules;
}

}
34 changes: 34 additions & 0 deletions android/app/src/main/java/com/mapeo/AppInfoModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.mapeo;

import android.content.pm.ApplicationInfo;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import java.util.Map;
import java.util.HashMap;

public class AppInfoModule extends ReactContextBaseJavaModule {
private static ReactApplicationContext reactContext;

AppInfoModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}

@Override
public String getName() {
return "AppInfo";
}

@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
ApplicationInfo ai = getReactApplicationContext().getApplicationInfo();
constants.put("sourceDir", ai.sourceDir);
return constants;
}
}
28 changes: 28 additions & 0 deletions android/app/src/main/java/com/mapeo/AppInfoPackage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.mapeo;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class AppInfoPackage implements ReactPackage {

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();

modules.add(new AppInfoModule(reactContext));

return modules;
}

}
27 changes: 13 additions & 14 deletions android/app/src/main/java/com/mapeo/MainApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,20 @@

import com.mapbox.rctmgl.RCTMGLPackage;

import com.mapeo.AppInfoPackage;
import com.mapeo.ApkInstallerPackage;

import java.util.Arrays;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

public class MainApplication extends Application implements ShareApplication, ReactApplication {
private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(
new BasePackageList().getPackageList(),
null
);
new BasePackageList().getPackageList(), Arrays.<SingletonModule>asList());

@Override
public String getFileProviderAuthority() {
return BuildConfig.APPLICATION_ID + ".provider";
return BuildConfig.APPLICATION_ID + ".provider";
}

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
Expand All @@ -46,16 +47,12 @@ public boolean getUseDeveloperSupport() {
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();

// Packages that cannot be autolinked yet can be added manually here, for example:
// Packages that cannot be autolinked yet can be added manually here, for
// example:
// packages.add(new MyReactNativePackage());

// Add unimodules
List<ReactPackage> unimodules = Arrays.<ReactPackage>asList(
new ModuleRegistryAdapter(mModuleRegistryProvider)
);
packages.addAll(unimodules);

packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider));
packages.add(new AppInfoPackage());
packages.add(new ApkInstallerPackage());
return packages;
}

Expand All @@ -79,7 +76,8 @@ public void onCreate() {
}

/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* Loads Flipper in React Native templates. Call this in the onCreate method
* with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
Expand Down Expand Up @@ -107,5 +105,6 @@ private static void initializeFlipper(
e.printStackTrace();
}
}

}
}
4 changes: 3 additions & 1 deletion android/app/src/main/res/xml/filepaths.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="media"
<files-path name="media"
path="media/" />
<cache-path name="upgrades"
path="upgrades/" />
</paths>
1 change: 1 addition & 0 deletions bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ workflows:
*unaligned.apk
*Test*.apk
mapeo-debug*.apk
fake.apk
- gradle_task: $GRADLE_TASKS
- [email protected]:
inputs:
Expand Down
Loading