NetCipher provides the interface for starting Tor on demand. It also
provides the mechanism to upgrade the TLS settings to the best possible,
based on what each device is capable of.
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Serbian (sr) by Слободан Симић(Slobodan Simić) <slsimic@gmail.com>
Currently translated at 13.8% (5 of 36 strings)
Translated using Weblate: Serbian (sr) by Слободан Симић(Slobodan Simić) <slsimic@gmail.com>
Currently translated at 100.0% (468 of 468 strings)
Co-authored-by: Слободан Симић(Slobodan Simić) <slsimic@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/sr/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/sr/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 21.5% (101 of 468 strings)
Added translation using Weblate: Frisian (fy) by vancha <tjipke@tutanota.com>
Co-authored-by: vancha <tjipke@tutanota.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/fy/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Korean (ko) by Myeongjin Lee <aranet100@gmail.com>
Currently translated at 97.6% (457 of 468 strings)
Co-authored-by: Myeongjin Lee <aranet100@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ko/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (36 of 36 strings)
Translated using Weblate: German (de) by nautilusx <translate@disroot.org>
Currently translated at 100.0% (36 of 36 strings)
Co-authored-by: nautilusx <translate@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/de/
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Bengali (Bangladesh) (bn-rBD) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 94.2% (441 of 468 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 87.6% (410 of 468 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 82.9% (388 of 468 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 76.9% (360 of 468 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 64.3% (301 of 468 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 57.4% (269 of 468 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 51.7% (242 of 468 strings)
Translated using Weblate: Bengali (Bangladesh) (bn-rBD) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 29.4% (138 of 468 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 48.0% (225 of 468 strings)
Translated using Weblate: Bengali (Bangladesh) (bn-rBD) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 29.0% (136 of 468 strings)
Co-authored-by: Oymate <dhruboadittya96@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/bn/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/bn_BD/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Catalan (ca) by Rafael Ruiz <rafael.ruiz@upc.edu>
Currently translated at 100.0% (468 of 468 strings)
Co-authored-by: Rafael Ruiz <rafael.ruiz@upc.edu>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ca/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Portuguese (pt) by ssantos <ssantos@web.de>
Currently translated at 100.0% (36 of 36 strings)
Translated using Weblate: Portuguese (pt) by ssantos <ssantos@web.de>
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Portuguese (Portugal) (pt-PT) by ssantos <ssantos@web.de>
Currently translated at 100.0% (36 of 36 strings)
Translated using Weblate: Portuguese (Portugal) (pt-rPT) by ssantos <ssantos@web.de>
Currently translated at 100.0% (468 of 468 strings)
Co-authored-by: ssantos <ssantos@web.de>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/pt/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/pt_PT/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/pt/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/pt_PT/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Arabic (ar) by Rex_sa <rex.sa@pm.me>
Currently translated at 100.0% (468 of 468 strings)
Co-authored-by: Rex_sa <rex.sa@pm.me>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ar/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Vietnamese (vi) by bruh <quangtrung02hn16@gmail.com>
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Vietnamese (vi) by bruh <quangtrung02hn16@gmail.com>
Currently translated at 100.0% (468 of 468 strings)
Co-authored-by: bruh <quangtrung02hn16@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/vi/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Ukrainian (uk) by Ihor Hordiichuk <igor_ck@outlook.com>
Currently translated at 100.0% (36 of 36 strings)
Translated using Weblate: Ukrainian (uk) by Ihor Hordiichuk <igor_ck@outlook.com>
Currently translated at 100.0% (468 of 468 strings)
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/uk/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/uk/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: Esperanto (eo) by ☆Verdulo <tomek@disroot.org>
Currently translated at 100.0% (36 of 36 strings)
Translated using Weblate: Esperanto (eo) by ☆Verdulo <tomek@disroot.org>
Currently translated at 100.0% (468 of 468 strings)
Co-authored-by: ☆Verdulo <tomek@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/eo/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/eo/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: German (de) by VfBFan <drop0815@posteo.de>
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: German (de) by VfBFan <drop0815@posteo.de>
Currently translated at 100.0% (468 of 468 strings)
Translated using Weblate: German (de) by VfBFan <drop0815@posteo.de>
Currently translated at 100.0% (468 of 468 strings)
Co-authored-by: VfBFan <drop0815@posteo.de>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/de/
Translation: F-Droid/F-Droid
* origin/fix-icon:
use Android Studio default Ctrl-Alt-L to format all AndroidManifest.xml
fully separate "Last Updated" icon from "Versions"
purge unused AboutActivity
fdroid/fdroidclient!1001
Currently translated at 100.0% (466 of 466 strings)
Translated using Weblate: Swedish (sv) by Jonatan Nyberg <jonatan.nyberg.karl@gmail.com>
Currently translated at 99.3% (463 of 466 strings)
Co-authored-by: Jonatan Nyberg <jonatan.nyberg.karl@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/sv/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (466 of 466 strings)
Translated using Weblate: German (de) by J. Lavoie <j.lavoie@net-c.ca>
Currently translated at 100.0% (466 of 466 strings)
Translated using Weblate: German (de) by J. Lavoie <j.lavoie@net-c.ca>
Currently translated at 100.0% (466 of 466 strings)
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/de/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/en_GB/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (466 of 466 strings)
Translated using Weblate: Esperanto (eo) by Verdulo <tomek@disroot.org>
Currently translated at 100.0% (466 of 466 strings)
Co-authored-by: Verdulo <tomek@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/eo/
Translation: F-Droid/F-Droid
Currently translated at 46.3% (216 of 466 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 46.7% (217 of 464 strings)
Co-authored-by: Oymate <dhruboadittya96@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/bn/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (466 of 466 strings)
Translated using Weblate: Russian (ru) by Andrey <andrey@mailbox.org>
Currently translated at 100.0% (466 of 466 strings)
Translated using Weblate: Russian (ru) by Andrey <andrey@mailbox.org>
Currently translated at 100.0% (36 of 36 strings)
Co-authored-by: Andrey <andrey@mailbox.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/ru/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ru/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
The system partition repos like shipped with CalyxOS are not really visible
to the user, they are built-in. So they should not prevent the warning
banner showing when the user has switched Over Data and Over WiFi to never.
Currently translated at 13.1% (61 of 464 strings)
Added translation using Weblate: Luxembourgish (lb) by Jeff <jeff.croise@gmail.com>
Co-authored-by: Jeff <jeff.croise@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/lb/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: Turkish (tr) by Orhan <orya@pm.me>
Currently translated at 100.0% (464 of 464 strings)
Co-authored-by: Orhan <orya@pm.me>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/tr/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: French (fr) by J. Lavoie <j.lavoie@net-c.ca>
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: German (de) by J. Lavoie <j.lavoie@net-c.ca>
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: French (fr) by J. Lavoie <j.lavoie@net-c.ca>
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: French (fr) by J. Lavoie <j.lavoie@net-c.ca>
Currently translated at 99.7% (463 of 464 strings)
Translated using Weblate: French (fr) by J. Lavoie <j.lavoie@net-c.ca>
Currently translated at 99.5% (462 of 464 strings)
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/de/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/fr/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/it/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (35 of 35 strings)
Translated using Weblate: Albanian (sq) by Besnik Bleta <besnik@programeshqip.org>
Currently translated at 94.2% (33 of 35 strings)
Translated using Weblate: Albanian (sq) by Besnik Bleta <besnik@programeshqip.org>
Currently translated at 100.0% (464 of 464 strings)
Co-authored-by: Besnik Bleta <besnik@programeshqip.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/sq/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/sq/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 37.0% (172 of 464 strings)
Translated using Weblate: Thai (th) by Pharadai <film041127@gmail.com>
Currently translated at 36.4% (169 of 464 strings)
Co-authored-by: Pharadai <film041127@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/th/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: French (fr) by lilim <lionel@les-miquelots.net>
Currently translated at 99.7% (463 of 464 strings)
Translated using Weblate: French (fr) by lilim <lionel@les-miquelots.net>
Currently translated at 99.5% (462 of 464 strings)
Translated using Weblate: French (fr) by lilim <lionel@les-miquelots.net>
Currently translated at 99.1% (460 of 464 strings)
Translated using Weblate: French (fr) by lilim <lionel@les-miquelots.net>
Currently translated at 98.9% (459 of 464 strings)
Co-authored-by: lilim <lionel@les-miquelots.net>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/fr/
Translation: F-Droid/F-Droid
Currently translated at 40.0% (14 of 35 strings)
Translated using Weblate: Romanian (ro) by Christian Eichert <c@zp1.net>
Currently translated at 31.4% (11 of 35 strings)
Translated using Weblate: Romanian (ro) by Christian Eichert <c@zp1.net>
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: German (de) by Christian Eichert <c@zp1.net>
Currently translated at 100.0% (464 of 464 strings)
Co-authored-by: Christian Eichert <c@zp1.net>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/ro/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/de/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ro/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: Vietnamese (vi) by bruh <quangtrung02hn16@gmail.com>
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: Vietnamese (vi) by bruh <quangtrung02hn16@gmail.com>
Currently translated at 100.0% (464 of 464 strings)
Co-authored-by: bruh <quangtrung02hn16@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/vi/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: Greek (el) by Michalis <michalisntovas@yahoo.gr>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: Michalis <michalisntovas@yahoo.gr>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/el/
Translation: F-Droid/F-Droid
Currently translated at 98.2% (456 of 464 strings)
Translated using Weblate: Vietnamese (vi) by bruh <quangtrung02hn16@gmail.com>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Vietnamese (vi) by bruh <quangtrung02hn16@gmail.com>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: bruh <quangtrung02hn16@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/vi/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: Bulgarian (bg) by 109247019824 <stoyan@gmx.com>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: 109247019824 <stoyan@gmx.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/bg/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: Portuguese (Portugal) (pt-rPT) by Sérgio Marques <smarquespt@gmail.com>
Currently translated at 100.0% (464 of 464 strings)
Translated using Weblate: Portuguese (Portugal) (pt-rPT) by Sérgio Marques <smarquespt@gmail.com>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Portuguese (pt) by Sérgio Marques <smarquespt@gmail.com>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: Sérgio Marques <smarquespt@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/pt/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/pt_PT/
Translation: F-Droid/F-Droid
Currently translated at 27.9% (127 of 455 strings)
Translated using Weblate: Tibetan (bo) by Hans-Christoph Steiner <hans@guardianproject.info>
Currently translated at 69.2% (315 of 455 strings)
Translated using Weblate: Armenian (hy) by Hans-Christoph Steiner <hans@guardianproject.info>
Currently translated at 50.5% (230 of 455 strings)
Translated using Weblate: Burmese (my) by Hans-Christoph Steiner <hans@guardianproject.info>
Currently translated at 45.9% (209 of 455 strings)
Translated using Weblate: Arabic (ar) by Hans-Christoph Steiner <hans@guardianproject.info>
Currently translated at 100.0% (455 of 455 strings)
Deleted translation using Weblate: English (United States) (en_US@rude) (b+en+US@rude)
Co-authored-by: Hans-Christoph Steiner <hans@guardianproject.info>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ar/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/bn_BD/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/bo/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/hy/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/my/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Vietnamese (vi) by bruh <quangtrung02hn16@gmail.com>
Currently translated at 5.7% (2 of 35 strings)
Translated using Weblate: Vietnamese (vi) by bruh <quangtrung02hn16@gmail.com>
Currently translated at 95.6% (435 of 455 strings)
Co-authored-by: bruh <quangtrung02hn16@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/vi/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/vi/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 2.6% (12 of 455 strings)
Added translation using Weblate: Sinhala (si) by HelaBasa <R45XvezA@protonmail.ch>
Co-authored-by: HelaBasa <R45XvezA@protonmail.ch>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/si/
Translation: F-Droid/F-Droid
refs #1869
Script to test this in an emulator with `adb root` in `adb shell`:
```bash
cd /data/data/org.fdroid.fdroid.debug/files
rm -f fake.apk; touch fake.apk; chown u0_a159.u0_a159 fake.apk ; dd if=/dev/zero of=fake.apk bs=1M count=635; touch -d 2020-02-02 fake.apk ; df -h; ls -lh
```
<
* privService-getInstalledPackages:
fail fast if privService.getInstalledPackages() isn't working
code formatting using Android Studio 4.1.2 defaults w/ 118 line length
Guard new privileged extension package manager query with API check
Add shared library packages to app cache database using F-DroidPrivilegedExtension query
fdroid/fdroidclient!967
If `privService.getInstalledPackages()` throws something other than a
`RemoteException`, this should fail as fast as possible. Crashing will give
users a prompt to send the crash report. using `finally` will just cause
weirdness since it might try to execute `compareToPackageManager()` even
when it is in the process of crashing.
Currently translated at 100.0% (35 of 35 strings)
Translated using Weblate: Norwegian Nynorsk (nn) by Karl Ove Hufthammer <karl@huftis.org>
Currently translated at 97.1% (34 of 35 strings)
Translated using Weblate: Norwegian Nynorsk (nn) by Karl Ove Hufthammer <karl@huftis.org>
Currently translated at 88.2% (30 of 34 strings)
Translated using Weblate: Norwegian Nynorsk (nn) by Karl Ove Hufthammer <karl@huftis.org>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: Karl Ove Hufthammer <karl@huftis.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/nn/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/nn/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Turkish (tr) by hayalci hayalci <gokdenizk@gmail.com>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: hayalci hayalci <gokdenizk@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/tr/
Translation: F-Droid/F-Droid
Currently translated at 17.1% (6 of 35 strings)
Translated using Weblate: Greek (el) by Michalis <michalisntovas@yahoo.gr>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Greek (el) by Michalis <michalisntovas@yahoo.gr>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Greek (el) by Michalis <michalisntovas@yahoo.gr>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Greek (el) by Michalis <michalisntovas@yahoo.gr>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Greek (el) by Michalis <michalisntovas@yahoo.gr>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: Michalis <michalisntovas@yahoo.gr>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/el/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/el/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 54.5% (18 of 33 strings)
Translated using Weblate: Spanish (es) by Jo <joaquinfc@protonmail.com>
Currently translated at 54.5% (18 of 33 strings)
Co-authored-by: Jo <joaquinfc@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/es/
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Turkish (tr) by <hgebel@yandex.com>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: <hgebel@yandex.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/tr/
Translation: F-Droid/F-Droid
Currently translated at 1.5% (7 of 455 strings)
Added translation using Weblate: Occitan (oc) by Quentin PAGÈS <quentinantonin@free.fr>
Co-authored-by: Quentin PAGÈS <quentinantonin@free.fr>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/oc/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Turkish (tr) by Oğuz Ersen <oguzersen@protonmail.com>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/tr/
Translation: F-Droid/F-Droid
Currently translated at 38.6% (176 of 455 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 34.9% (159 of 455 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 10.5% (48 of 455 strings)
Translated using Weblate: Bengali (Bangladesh) (bn-rBD) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 26.8% (122 of 455 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 7.0% (32 of 455 strings)
Translated using Weblate: Bengali (bn) by Oymate <dhruboadittya96@gmail.com>
Currently translated at 2.1% (10 of 455 strings)
Co-authored-by: Oymate <dhruboadittya96@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/bn/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/bn_BD/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Russian (ru) by Andrey <andrey@mailbox.org>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: Andrey <andrey@mailbox.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ru/
Translation: F-Droid/F-Droid
Currently translated at 10.5% (48 of 455 strings)
Added translation using Weblate: Nepali (ne) by Naveen Niraula <subtlenv@gmail.com>
Co-authored-by: Naveen Niraula <subtlenv@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ne/
Translation: F-Droid/F-Droid
Currently translated at 14.7% (67 of 455 strings)
Translated using Weblate: Somali (so) by Nadir Nour <dudethatwascool2@gmail.com>
Currently translated at 8.5% (39 of 455 strings)
Added translation using Weblate: Somali (so) by Nadir Nour <dudethatwascool2@gmail.com>
Co-authored-by: Nadir Nour <dudethatwascool2@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/so/
Translation: F-Droid/F-Droid
Currently translated at 13.4% (61 of 455 strings)
Translated using Weblate: Pashto (ps) by ورکنومی <wraknumay@pm.me>
Currently translated at 8.5% (39 of 455 strings)
Added translation using Weblate: Pashto (ps) by ورکنومی <wraknumay@pm.me>
Co-authored-by: ورکنومی <wraknumay@pm.me>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ps/
Translation: F-Droid/F-Droid
* tag 'mergeeme':
remove unused import
Fixed bug package signature info not included
Changed to static property
Fixed "apply suggestion" error
Replaced `equalsIgnoreCase()` with `equals()`
Apply 1 suggestion(s) to 1 file(s)
Added check platform signature available
fdroid/fdroidclient!943
Currently translated at 100.0% (34 of 34 strings)
Translated using Weblate: Ukrainian (uk) by Ihor Hordiichuk <igor_ck@outlook.com>
Currently translated at 100.0% (33 of 33 strings)
Translated using Weblate: Ukrainian (uk) by Ihor Hordiichuk <igor_ck@outlook.com>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Ukrainian (uk) by Ihor Hordiichuk <igor_ck@outlook.com>
Currently translated at 100.0% (33 of 33 strings)
Translated using Weblate: Ukrainian (uk) by Ihor Hordiichuk <igor_ck@outlook.com>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Ukrainian (uk) by Ihor Hordiichuk <igor_ck@outlook.com>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/uk/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/uk/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (34 of 34 strings)
Translated using Weblate: Polish (pl) by WaldiS <sto@tutanota.de>
Currently translated at 100.0% (33 of 33 strings)
Co-authored-by: WaldiS <sto@tutanota.de>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/pl/
Translation: F-Droid/F-Droid metadata
To further the goal of providing a fully localized experience based on the
user's Language Settings, this applies similar logic as the Latest Tab to
the apps that are featured for each category.
* commit 'a81140be4749189861b2961f84e2704eb5bb467b':
run Android Studio default code formatter with Ctrl-Alt-L
Add Repo.getFileUrl() method to get file URL in a standard way
RepoUrlsTest: Add new tests for correct repo URL formatting
fdroid/fdroidclient!935
* origin/master:
gitlab-ci: fix excluding @LargeTest from emulator jobs
use TAG to identify CleanCacheWorker to WorkManager
add WorkManagerTestRule to CleanCacheWorkerTest
move static helper method into its class: CleanCacheWorker
fdroidclient does not use variables for gradle dependencies
Add WorkManagerTestRule.
Use WorkManager to clean the cache.
Add AndroidX WorkManager.
fdroid/fdroidclient!959
If the job is successful, it should finish without coming closes to
the timeout. Extending the timeout will make it take longer to fail,
but since the job is flaky, and the related code is rarely touched, it
seems worth it.
https://gitlab.com/fdroid/ci-images-client/-/jobs/957371759
```
A fatal error has been detected by the Java Runtime Environment:
SIGSEGV (0xb) at pc=0x00007f6775b513c0, pid=1923, tid=0x00007f675eef6700
JRE version: OpenJDK Runtime Environment (8.0_275-b01) (build 1.8.0_275-8u275-b01-1~deb9u1-b01)
Java VM: OpenJDK 64-Bit Server VM (25.275-b01 mixed mode linux-amd64 compressed oops)
Problematic frame:
V [libjvm.so+0x92d3c0]
Core dump written. Default location: /builds/test/fdroidclient/app/core or core.1923
An error report file with more information is saved as:
/builds/test/fdroidclient/app/hs_err_pid1923.log
Compiler replay data is saved as:
/builds/test/fdroidclient/app/replay_pid1923.log
If you would like to submit a bug report, please visit:
http://bugreport.java.com/bugreport/crash.jsp
```
Currently translated at 99.1% (451 of 455 strings)
Translated using Weblate: Albanian (sq) by Besnik Bleta <besnik@programeshqip.org>
Currently translated at 99.7% (454 of 455 strings)
Co-authored-by: Besnik Bleta <besnik@programeshqip.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/sq/
Translation: F-Droid/F-Droid
Added translation using Weblate: English (United Kingdom) (en-rGB) by Chris Jr Williams <chrisjr4eva1987@gmail.com>
Co-authored-by: Chris Jr Williams <chrisjr4eva1987@gmail.com>
Currently translated at 72.7% (331 of 455 strings)
Translated using Weblate: Marathi (mr) by Mahem Jadhav <mahem4ever@gmail.com>
Currently translated at 54.5% (248 of 455 strings)
Translated using Weblate: Marathi (mr) by Mahem Jadhav <mahem4ever@gmail.com>
Currently translated at 37.1% (169 of 455 strings)
Translated using Weblate: Marathi (mr) by Mahem Jadhav <mahem4ever@gmail.com>
Currently translated at 37.8% (172 of 455 strings)
Added translation using Weblate: Marathi (mr) by Mahem Jadhav <mahem4ever@gmail.com>
Co-authored-by: Mahem Jadhav <mahem4ever@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/mr/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Swedish (sv) by Jonatan Nyberg <jonatan.nyberg.karl@gmail.com>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Swedish (sv) by Jonatan Nyberg <jonatan.nyberg.karl@gmail.com>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: Jonatan Nyberg <jonatan.nyberg.karl@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/sv/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: Gontzal Manuel Pujana Onaindia <thadahdenyse@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/eu/
Translation: F-Droid/F-Droid
Currently translated at 72.3% (329 of 455 strings)
Translated using Weblate: Lithuanian (lt) by Moo <hazap@hotmail.com>
Currently translated at 71.8% (327 of 455 strings)
Translated using Weblate: Lithuanian (lt) by Moo <hazap@hotmail.com>
Currently translated at 6.2% (2 of 32 strings)
Translated using Weblate: Lithuanian (lt) by Moo <hazap@hotmail.com>
Currently translated at 72.0% (328 of 455 strings)
Co-authored-by: Moo <hazap@hotmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/lt/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/lt/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Italian (it) by x <hardwired1.0@protonmail.com>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: x <hardwired1.0@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/it/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Italian (it) by Massimiliano Caniparoli <massic80@gmail.com>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: Massimiliano Caniparoli <massic80@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/it/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (32 of 32 strings)
Translated using Weblate: Hebrew (he) by Yaron Shahrabani <sh.yaron@gmail.com>
Currently translated at 100.0% (32 of 32 strings)
Co-authored-by: Yaron Shahrabani <sh.yaron@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/he/
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Esperanto (eo) by Verdulo <tomek@disroot.org>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Esperanto (eo) by Verdulo <tomek@disroot.org>
Currently translated at 100.0% (32 of 32 strings)
Co-authored-by: Verdulo <tomek@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/eo/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/eo/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
This means that sometimes the NearbyView is updated from a
BroadcastReceiver's Context, which is not an Activity. So this has to
try a little harder to fetch the Activity instance needed for the
prompt to request permissions to a folder on the USB. This adds a
failsafe to fallback to the file:/// scanning in SDCardScannerService.
The USB-OTG device can be plugged and unplugged anytime, so the Nearby view
should be updated each time the user switches to this screen. Registered
callbacks should handle updating the USB-OTG status while the Nearby view
is active.
This disables the verification of .pom files. .pom files can add
dependencies, so it would be good to have them verified. But since this
current setup requires all JAR to be verified, any new dependencies would
fail anyway:
https://docs.gradle.org/current/userguide/dependency_verification.html#sec:disabling-metadata-verification
In some cases everything works fine, like on gitlab-ci, and in other places
it always gives errors like this:
```
A problem occurred configuring root project 'client'.
> Dependency verification failed for configuration ':classpath'
4 artifacts failed verification:
- all-1.2.0.pom (com.sun.activation:all:1.2.0) from repository MavenRepo
- jvnet-parent-1.pom (net.java:jvnet-parent:1) from repository MavenRepo
- oss-parent-7.pom (org.sonatype.oss:oss-parent:7) from repository MavenRepo
- oss-parent-9.pom (org.sonatype.oss:oss-parent:9) from repository MavenRepo
This can indicate that a dependency has been compromised. Please carefully verify the checksums.
Open this report for more details: file:///home/hans/code/fdroid/client/build/reports/dependency-verification/at-1603359642220/dependency-verification-report.html
```
@glennmen and @eighthave both are getting that error.
* fdroidserver uses case-sensitive naming since it is based on GNU/Linux
filesystems, which are case-sensitive by default.
* "the application ID looks like a traditional Java package name, the naming
rules for the application ID are a bit more restrictive"
https://developer.android.com/studio/build/application-id
* Java is a case-sensitive language for all names used in .java files:
"In the Java programming universe, case-sensitive String keys are ubiquitous"
"Java package names... are case-sensitive"
https://docs.oracle.com/javase/8/docs/technotes/guides/preferences/designfaq.html
Currently translated at 3.2% (1 of 31 strings)
Translated using Weblate: Central Atlas Tamazight (tzm) by Hakim Oubouali <hakim.oubouali.skr@gmail.com>
Currently translated at 29.6% (135 of 455 strings)
Translated using Weblate: Central Atlas Tamazight (tzm) by Hakim Oubouali <hakim.oubouali.skr@gmail.com>
Currently translated at 28.1% (128 of 455 strings)
Translated using Weblate: Central Atlas Tamazight (tzm) by Hakim Oubouali <hakim.oubouali.skr@gmail.com>
Currently translated at 6.5% (30 of 455 strings)
Co-authored-by: Hakim Oubouali <hakim.oubouali.skr@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/tzm/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/tzm/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: French (fr) by Ldm Public <ldmpub@gmail.com>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: Ldm Public <ldmpub@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/fr/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (31 of 31 strings)
Translated using Weblate: Portuguese (Portugal) (pt-PT) by ssantos <ssantos@web.de>
Currently translated at 100.0% (31 of 31 strings)
Translated using Weblate: Portuguese (pt) by ssantos <ssantos@web.de>
Currently translated at 96.7% (30 of 31 strings)
Translated using Weblate: Portuguese (pt) by ssantos <ssantos@web.de>
Currently translated at 100.0% (455 of 455 strings)
Co-authored-by: ssantos <ssantos@web.de>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/pt/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/pt_PT/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/pt/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
OsmAnd will import map files from a file:// URL pointing to an OBF file,
but this currently only works for file:// and not the proper content://.
This uses a hack to disable the warning about file:// URIs but only for the
final stage of installing the .obf file.
Hopefully in the future, this can be changed to use a proper content:// URL
as I suggested to them in this merge request:
https://github.com/osmandapp/OsmAnd/pull/10043
Use Mockito to mock LocaleList rather than changing App.java. The only
reliably working emulator tests on gitlab-ci are emulator-22. The change to
App.java in 3406edefcd1807cc9352589ac86dbb725c3165b0 broke there:
E/ACRA ( 2231): Caused by: java.lang.NoClassDefFoundError: android.os.LocaleList
E/ACRA ( 2231): at libcore.reflect.InternalNames.getClass(InternalNames.java:55)
E/ACRA ( 2231): at java.lang.Class.getDexCacheType(Class.java:479)
E/ACRA ( 2231): at java.lang.reflect.ArtMethod.getDexCacheType(ArtMethod.java:191)
E/ACRA ( 2231): at java.lang.reflect.ArtMethod.getReturnType(ArtMethod.java:145)
E/ACRA ( 2231): at java.lang.reflect.Method.getReturnType(Method.java:184)
E/ACRA ( 2231): at java.lang.Class.getDeclaredMethods(Class.java:771)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.util.ClassUtil.getClassMethods(ClassUtil.java:1172)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector._addMemberMethods(AnnotatedMethodCollector.java:117)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector.collect(AnnotatedMethodCollector.java:49)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector.collectMethods(AnnotatedMethodCollector.java:40)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.introspect.AnnotatedClass._methods(AnnotatedClass.java:382)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.introspect.AnnotatedClass.memberMethods(AnnotatedClass.java:322)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addMethods(POJOPropertiesCollector.java:555)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collectAll(POJOPropertiesCollector.java:323)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getPropertyMap(POJOPropertiesCollector.java:287)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getProperties(POJOPropertiesCollector.java:186)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.introspect.BasicBeanDescription._properties(BasicBeanDescription.java:164)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.introspect.BasicBeanDescription.findProperties(BasicBeanDescription.java:239)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._findCreatorsFromProperties(BasicDeserializerFactory.java:292)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:276)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:224)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:220)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:143)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:414)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:458)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.createContextual(ObjectArrayDeserializer.java:128)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.DeserializationContext.handleSecondaryContextualization(DeserializationContext.java:696)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:496)
E/ACRA ( 2231): at com.fasterxml.jackson.databind.Objec
In the case where a non-standard region has been set for the primary
system language, the secondary locale will be used for localized
strings when available instead of the expected primary language.
For example, set system locales to [en-SE, ja-JP], that is English
with region Sweden, and Japanese with region Japan, most apps will
display English descriptions but those which have a Japanese
translation will display that instead.
This commit adds a fallback case for when the primary locale has not
matched any translations, but it's language part does.
Currently translated at 6.6% (2 of 30 strings)
Translated using Weblate: Belarusian (be) by Zmicer Turok <nashtlumach@gmail.com>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Belarusian (be) by Zmicer Turok <nashtlumach@gmail.com>
Currently translated at 98.4% (448 of 455 strings)
Co-authored-by: Zmicer Turok <nashtlumach@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/be/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/be/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Czech (cs) by zeritti <woodenmo@posteo.de>
Currently translated at 99.5% (453 of 455 strings)
Co-authored-by: zeritti <woodenmo@posteo.de>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/cs/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: French (fr) by Ldm Public <ldmpub@gmail.com>
Currently translated at 99.1% (451 of 455 strings)
Co-authored-by: Ldm Public <ldmpub@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/fr/
Translation: F-Droid/F-Droid
Currently translated at 3.3% (1 of 30 strings)
Translated using Weblate: Lithuanian (lt) by Kornelijus Tvarijanavičius <kornelitvari@protonmail.com>
Currently translated at 73.7% (331 of 449 strings)
Co-authored-by: Kornelijus Tvarijanavičius <kornelitvari@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/lt/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/lt/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Indonesian (id) by Adiitya Andre <adiiit.and@gmail.com>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: Adiitya Andre <adiiit.and@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/id/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Ukrainian (uk) by ihor_ck <igor_ck@outlook.com>
Currently translated at 100.0% (30 of 30 strings)
Translated using Weblate: Ukrainian (uk) by ihor_ck <igor_ck@outlook.com>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: ihor_ck <igor_ck@outlook.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/uk/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/uk/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Hebrew (he) by Yaron Shahrabani <sh.yaron@gmail.com>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: Yaron Shahrabani <sh.yaron@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/he/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Spanish (es) by Crisalis <tegaminorune@disroot.org>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: Crisalis <tegaminorune@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/es/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Icelandic (is) by Sveinn í Felli <sv1@fellsnet.is>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: Sveinn í Felli <sv1@fellsnet.is>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/is/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Japanese (ja) by ーーー <nnn1590@nnn1590.org>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: ーーー <nnn1590@nnn1590.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ja/
Translation: F-Droid/F-Droid
Currently translated at 62.5% (281 of 449 strings)
Translated using Weblate: Kabyle (kab) by Selyan Sliman Amiri <selyan.kab@gmail.com>
Currently translated at 61.9% (278 of 449 strings)
Co-authored-by: Selyan Sliman Amiri <selyan.kab@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/kab/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Portuguese (Portugal) (pt-rPT) by ssantos <ssantos@web.de>
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Portuguese (pt) by ssantos <ssantos@web.de>
Currently translated at 100.0% (30 of 30 strings)
Translated using Weblate: Portuguese (pt) by ssantos <ssantos@web.de>
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Portuguese (Portugal) (pt-rPT) by ssantos <ssantos@web.de>
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Portuguese (Portugal) (pt-PT) by ssantos <ssantos@web.de>
Currently translated at 100.0% (30 of 30 strings)
Co-authored-by: ssantos <ssantos@web.de>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/pt/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/pt_PT/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/pt/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/pt_PT/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 43.6% (196 of 449 strings)
Translated using Weblate: Macedonian (mk) by primarto24c8a9c6889c407b <prodavac3@protonmail.com>
Currently translated at 40.5% (182 of 449 strings)
Co-authored-by: primarto24c8a9c6889c407b <prodavac3@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/mk/
Translation: F-Droid/F-Droid
Currently translated at 99.5% (453 of 455 strings)
Translated using Weblate: Norwegian Bokmål (nb) by Allan Nordhøy <epost@anotheragency.no>
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Swedish (sv) by Allan Nordhøy <epost@anotheragency.no>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/sv/
Translation: F-Droid/F-Droid
Currently translated at 20.7% (93 of 449 strings)
Translated using Weblate: Berber (ber) by Hakim Oubouali <hakim.oubouali.skr@gmail.com>
Currently translated at 17.5% (79 of 449 strings)
Translated using Weblate: Berber (ber) by Hakim Oubouali <hakim.oubouali.skr@gmail.com>
Currently translated at 7.3% (33 of 449 strings)
Translated using Weblate: Berber (ber) by Hakim Oubouali <hakim.oubouali.skr@gmail.com>
Currently translated at 6.6% (30 of 449 strings)
Added translation using Weblate: Berber (ber) by Hakim Oubouali <hakim.oubouali.skr@gmail.com>
Co-authored-by: Hakim Oubouali <hakim.oubouali.skr@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ber/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Swedish (sv) by Jonatan Nyberg <jonatan.nyberg.karl@gmail.com>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: Jonatan Nyberg <jonatan.nyberg.karl@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/sv/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Estonian (et) by Priit Jõerüüt <hwlate@joeruut.com>
Currently translated at 23.3% (7 of 30 strings)
Translated using Weblate: Estonian (et) by Priit Jõerüüt <hwlate@joeruut.com>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: Priit Jõerüüt <hwlate@joeruut.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/et/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/et/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Chinese (Traditional) (zh-rTW) by Jeff Huang <s8321414@gmail.com>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: Jeff Huang <s8321414@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/zh_Hant/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Romanian (ro) by Licaon Kter <licaon.kter@protonmail.com>
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Romanian (ro) by Licaon Kter <licaon.kter@protonmail.com>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: Licaon Kter <licaon.kter@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/ro/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (455 of 455 strings)
Translated using Weblate: Esperanto (eo) by Verdulo <tomek@disroot.org>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: Verdulo <tomek@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/eo/
Translation: F-Droid/F-Droid
It turns out that some of the dependencies in the Google Offline Components
downloadable maven repository have difference to the ones Google publishes
to maven.google.com. WTF. In any case, the new Gradle Dependency
Verification feature handles this gracefully. I manually verified the
diffs between the two using diffoscope. One just differed by timestamps in
the ZIP header, and the other just differed by linefeeds at the end of the
file. Then I generated this metadata update using:
`./gradlew --write-verification-metadata pgp,sha256`
* https://developer.android.com/studio#offline
This fully replaces gradle-witness and goes far beyond what it offered. As
far as I can tell, this actually will verify every single artifact that
gradle downloads and uses.
This was generated in two passes to get both the PGP and the SHA256 info:
```
./gradlew --write-verification-metadata pgp,sha256 build connectedFullDebugAndroidTest --export-keys
./gradlew --write-verification-metadata sha256 build connectedFullDebugAndroidTest
```
Thanks to @vlsi who made me aware of this, and helped make it possible.
closes!837
Currently translated at 99.7% (448 of 449 strings)
Translated using Weblate: Dutch (nl) by 40e3004b-a296-47bd-a073-3dd8af36f77f <40e3004b-a296-47bd-a073-3dd8af36f77f@anonaddy.me>
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Flemish (nl-rBE) by 40e3004b-a296-47bd-a073-3dd8af36f77f <40e3004b-a296-47bd-a073-3dd8af36f77f@anonaddy.me>
Currently translated at 99.7% (448 of 449 strings)
Translated using Weblate: Dutch (nl) by 40e3004b-a296-47bd-a073-3dd8af36f77f <40e3004b-a296-47bd-a073-3dd8af36f77f@anonaddy.me>
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: French (fr) by 40e3004b-a296-47bd-a073-3dd8af36f77f <40e3004b-a296-47bd-a073-3dd8af36f77f@anonaddy.me>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: 40e3004b-a296-47bd-a073-3dd8af36f77f <40e3004b-a296-47bd-a073-3dd8af36f77f@anonaddy.me>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/fr/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/nl/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/nl_BE/
Translation: F-Droid/F-Droid
Currently translated at 100.0% (30 of 30 strings)
Translated using Weblate: Portuguese (pt) by ssantos <ssantos@web.de>
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Portuguese (Portugal) (pt-PT) by ssantos <ssantos@web.de>
Currently translated at 100.0% (30 of 30 strings)
Translated using Weblate: Portuguese (Portugal) (pt-rPT) by ssantos <ssantos@web.de>
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Portuguese (Portugal) (pt-PT) by ssantos <ssantos@web.de>
Currently translated at 100.0% (30 of 30 strings)
Translated using Weblate: Portuguese (Portugal) (pt-rPT) by ssantos <ssantos@web.de>
Currently translated at 100.0% (449 of 449 strings)
Translated using Weblate: Portuguese (pt) by ssantos <ssantos@web.de>
Currently translated at 100.0% (30 of 30 strings)
Co-authored-by: ssantos <ssantos@web.de>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/pt/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/pt_PT/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/pt/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/pt_PT/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Currently translated at 100.0% (30 of 30 strings)
Translated using Weblate: Polish (pl) by WaldiS <sto@tutanota.de>
Currently translated at 100.0% (30 of 30 strings)
Translated using Weblate: Polish (pl) by WaldiS <sto@tutanota.de>
Currently translated at 96.6% (29 of 30 strings)
Translated using Weblate: Polish (pl) by WaldiS <sto@tutanota.de>
Currently translated at 100.0% (449 of 449 strings)
Co-authored-by: WaldiS <sto@tutanota.de>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/pl/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid/pl/
Translation: F-Droid/F-Droid
Translation: F-Droid/F-Droid metadata
Updated by "Squash Git commits" hook in Weblate.
Translated using Weblate: Ukrainian (uk) by ihor_ck <igor_ck@outlook.com>
Currently translated at 100.0% (30 of 30 strings)
Translated using Weblate: Ukrainian (uk) by ihor_ck <igor_ck@outlook.com>
Currently translated at 83.3% (25 of 30 strings)
Translated using Weblate: Ukrainian (uk) by ihor_ck <igor_ck@outlook.com>
Currently translated at 80.0% (24 of 30 strings)
Translated using Weblate: Chinese (Simplified) (zh-CN) by kak mi <wavelake@outlook.com>
Currently translated at 100.0% (30 of 30 strings)
Translated using Weblate: Esperanto (eo) by Verdulo <tomek@disroot.org>
Currently translated at 100.0% (30 of 30 strings)
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/eo/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/uk/
Translate-URL: https://hosted.weblate.org/projects/f-droid/f-droid-metadata/zh_Hans/
Translation: F-Droid/F-Droid metadata
- remove constants annotation
- Most @Implementation methods in shadow classes are now protected instead of public.
Tests should always prefer to call SDK methods directly on Android classes rather
than on their shadows
We need compileSdk 28 and the required AGP and gradle versions, as well
as updating to compatible support library revisions.
minSdk and targetSdk needs to move to build.gradle from manifest.
buildToolsVersion isn't used anymore.
The autoVerify function seems to require that the app only declare domain
names in the IntentFilters that are set up with the "site association"
files. For F-Droid to support the verified app link, it would have to stop
matching play.google.com, amazon.com, etc. This autoVerify function also
triggers DNS lookups at the system level, which might not be forwarded over
Tor, in certain scenarios. So this just disables the whole feature.
https://developer.android.com/training/app-links/verify-site-associations
Liberapay was originally included using a numeric ID, since they had not yet
finalized the public URLs. Now it is a username. So this logic prefers
the username in Liberapay: field, and uses the old LiberapayID: as a
fallback. LiberapayID: will not override Liberapay: if it is already set.
This reuses the old database key since it is stored and processed as a
String anyway.
This was already done for list views because of the panic uninstall list
but we can easily apply the same logic to the tile view and app detail
view as well.
Otherwise when both are enabled the metadata from the archive gets
priority over repo which is not really what we want.
It also breaks a lot of icons, featuregraphics and screenshots.
Fixesfdroid/fdroidclient#1771Fixesfdroid/fdroidclient#1772Fixesfdroid/fdroidclient#1686
We also update the default repo priorities for existing installs if we
find the default repos in the original order.
The query is pretty annoying to write in java, here is the raw sql form.
UPDATE fdroid_repo
SET priority = ( SELECT SUM(priority)
FROM fdroid_repo
WHERE address IN ('https://f-droid.org/repo', 'https://f-droid.org/archive')
) - priority
WHERE address IN ('https://f-droid.org/repo', 'https://f-droid.org/archive') AND
'TRUE' IN (
SELECT
CASE
WHEN a.priority = b.priority-1 THEN 'TRUE'
ELSE 'FALSE'
END
FROM fdroid_repo as a
INNER JOIN fdroid_repo as b ON
a.address = "https://f-droid.org/repo" and b.address = "https://f-droid.org/archive"
)
This was blocking updates being scheduled when either data or wifi
updates were disabled. We only want to completely disable the update
service when both are disbaled though.
Ref: #1623
Fixes the following crash:
05-19 22:39:55.535 1037 24513 W WindowManager: Attempted to add application window with unknown token Token{2f841 null}. Aborting.
05-19 22:39:55.536 10844 10844 D AndroidRuntime: Shutting down VM
05-19 22:39:55.540 10844 10844 E AndroidRuntime: FATAL EXCEPTION: main
05-19 22:39:55.540 10844 10844 E AndroidRuntime: Process: org.fdroid.fdroid.debug, PID: 10844
05-19 22:39:55.540 10844 10844 E AndroidRuntime: android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@d8ae31 is not valid; is your activity running?
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at android.view.ViewRootImpl.setView(ViewRootImpl.java:891)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:372)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:128)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at android.app.Dialog.show(Dialog.java:454)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at org.fdroid.fdroid.views.AppDetailsActivity$7.onReceive(AppDetailsActivity.java:607)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:311)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:47)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:120)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:108)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at android.os.Looper.loop(Looper.java:166)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7529)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
05-19 22:39:55.540 10844 10844 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
The !isFinishing check was already in the installReceiver part, but
somehow missing in uninstallReceiver. There's also a reference to this
here: http://blackriver.to/2012/08/android-annoying-exception-unable-to-add-window-is-your-activity-running/
I don't understand this crash, especially as the dialouge still gets
shown after adding this check (possibly the parent activity is finishing
and then immediately restarting?). But this sems to realibly fix it.
This was happening when I installed an app, used a new settings entry to
unregister privext as a device owner (by calling it via binder/aidl) and
then trying to uninstall the app I just installed again, whithout
closing f-droid inbetween.
Previously everything from a repo staying inside the db when removing it
without disabling it first, the problem manifests when the repo is
readded later (or a mirror), as it would get a new id but all apk
entries still point to the original repoid.
So we now first disable a repo (which just calls
RepoProvider.Helper.purgeApps) before deleting it from the db.
closesBubu/fdroidclassic#29
This testing at the wrong point, namely in the app details where you are
already looking at the antifeatures which might be present.
In the list view there's an additional direct check with
isDisabledByAntiFeatures() anyway.
Fixfdroid/fdroidclient#1845
* Use separate receivers instead of one combined activity
to avoid showing the "Use F-Droid to handle Mass Storage"
prompt every time a drive is plugged in.
* Re-do the logic completely, and make it much more clearer.
* Also, Read external storage implies access media location
*ONLY* on apps not targetting API 29 or above, i.e <= 28
* This new permission comes courtesy of the Q December update.
* Read external storage implies access media location
References:
* ac7b10c135%5E%21/#F1
These strings are part of the install/uninstall UI which is originally
sourced from Android itself. So the translations should stay in sync with
Android's.
This is the same exact key, just the signed metadata is updated so that it
includes the new expiration date. This is the same as just updating this
key from the keyservers.
This might happen if the user denies storage permission.
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=16613, result=0, data=null} to activity {org.fdroid.fdroid/org.fdroid.fdroid.views.main.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.net.Uri android.content.Intent.getData()' on a null object reference
at android.app.ActivityThread.deliverResults(ActivityThread.java:4612)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4654)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:49)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1955)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7073)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.net.Uri android.content.Intent.getData()' on a null object reference
at org.fdroid.fdroid.nearby.TreeUriScannerIntentService.onActivityResult(TreeUriScannerIntentService.java:99)
at org.fdroid.fdroid.views.main.MainActivity.onActivityResult(MainActivity.java:270)
at android.app.Activity.dispatchActivityResult(Activity.java:7759)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4605)
... 11 more
java.lang.NullPointerException: Attempt to invoke virtual method 'android.net.Uri android.content.Intent.getData()' on a null object reference
at org.fdroid.fdroid.nearby.TreeUriScannerIntentService.onActivityResult(TreeUriScannerIntentService.java:99)
at org.fdroid.fdroid.views.main.MainActivity.onActivityResult(MainActivity.java:270)
at android.app.Activity.dispatchActivityResult(Activity.java:7759)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4605)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4654)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:49)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1955)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7073)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)
In order to support Android < 21, this calls `super` rather than `this`.
RelativeLayout}'s methods just use a 0 for the fourth argument, just like
this used to.
java.lang.NullPointerException: Attempt to read from field 'java.lang.String android.net.wifi.WifiConfiguration.SSID' on a null object reference
at org.fdroid.fdroid.nearby.StartSwapView.uiUpdateWifiNetwork(StartSwapView.java:226)
at org.fdroid.fdroid.nearby.StartSwapView.uiInitWifi(StartSwapView.java:211)
at org.fdroid.fdroid.nearby.StartSwapView.onFinishInflate(StartSwapView.java:111)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:876)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at org.fdroid.fdroid.nearby.SwapWorkflowActivity.inflateSwapView(SwapWorkflowActivity.java:488)
at org.fdroid.fdroid.nearby.SwapWorkflowActivity.showIntro(SwapWorkflowActivity.java:541)
at org.fdroid.fdroid.nearby.SwapWorkflowActivity.showRelevantView(SwapWorkflowActivity.java:468)
at org.fdroid.fdroid.nearby.SwapWorkflowActivity.access$100(SwapWorkflowActivity.java:86)
at org.fdroid.fdroid.nearby.SwapWorkflowActivity$1.onServiceConnected(SwapWorkflowActivity.java:135)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1652)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1681)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:440)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
fixes:
java.lang.NullPointerException: Attempt to read from field 'java.lang.String android.net.wifi.WifiConfiguration.BSSID' on a null object reference
at org.fdroid.fdroid.nearby.WifiStateChangeService.setSsid(WifiStateChangeService.java:265)
at org.fdroid.fdroid.nearby.WifiStateChangeService.access$100(WifiStateChangeService.java:59)
at org.fdroid.fdroid.nearby.WifiStateChangeService$WifiInfoThread.run(WifiStateChangeService.java:174)
java.lang.NullPointerException: Attempt to read from field 'boolean android.net.wifi.WifiConfiguration.hiddenSSID' on a null object reference
at org.fdroid.fdroid.nearby.WifiStateChangeService.setSsid(WifiStateChangeService.java:252)
at org.fdroid.fdroid.nearby.WifiStateChangeService.access$100(WifiStateChangeService.java:59)
at org.fdroid.fdroid.nearby.WifiStateChangeService$WifiInfoThread.run(WifiStateChangeService.java:174)
java.lang.NullPointerException: null receiver
at java.lang.reflect.Method.invoke(Native Method)
at cc.mvdan.accesspoint.WifiApControl.invokeQuietly(WifiApControl.java:178)
at cc.mvdan.accesspoint.WifiApControl.isWifiApEnabled(WifiApControl.java:189)
at cc.mvdan.accesspoint.WifiApControl.isEnabled(WifiApControl.java:198)
at org.fdroid.fdroid.nearby.WifiStateChangeService.setSsid(WifiStateChangeService.java:249)
at org.fdroid.fdroid.nearby.WifiStateChangeService.access$100(WifiStateChangeService.java:59)
at org.fdroid.fdroid.nearby.WifiStateChangeService$WifiInfoThread.run(WifiStateChangeService.java:133)
java.lang.NullPointerException: Attempt to invoke virtual method 'int android.app.AppOpsManager.checkOpNoThrow(int, int, java.lang.String)' on a null object reference
at android.provider.Settings.isCallingPackageAllowedToPerformAppOpsProtectedOperation(Settings.java:13730)
at android.provider.Settings.isCallingPackageAllowedToWriteSettings(Settings.java:13634)
at android.provider.Settings$System.canWrite(Settings.java:4793)
at cc.mvdan.accesspoint.WifiApControl.getInstance(WifiApControl.java:122)
at org.fdroid.fdroid.nearby.WifiStateChangeService.setSsid(WifiStateChangeService.java:240)
at org.fdroid.fdroid.nearby.WifiStateChangeService.access$100(WifiStateChangeService.java:59)
at org.fdroid.fdroid.nearby.WifiStateChangeService$WifiInfoThread.run(WifiStateChangeService.java:133)
This got missed in ef90fd2dfdb0b07aca21f4be34e2c418f092bf06
fdroid/fdroidclient!829
for f in `find app/src/ -type f -name \*.xml|xargs grep --no-filename -F '<org.fdroid.fdroid' | awk '{ print $1}' |sort -u`; do test -e app/src/*/java/`echo $f | sed -e 's,<,,' -e 's,\.,/,g'`.java || echo FAIL $f; done
Recursively search for index-v1.jar starting from the given directory,
looking at files first before recursing into directories. This is
"depth last" since the index file is much more likely to be shallow
than deep, and there can be a lot of files to search through starting
at 4 or more levels deep, like the fdroid icons dirs and the per-app
"external storage" dirs.
It is possible to enable the Hotspot AP on a device, and disable mobile
data. This setup will work fine for swapping, but the detection logic for
whether there is metered internet was blocking it. So this adds a new
state to represent and handle this condition.
F-Droid should be able to uninstall any app, in theory, not just the apps
that are listed in the index.
This lays some groundwork for moving swap's SelectAppsView to the standard
AppList elements used everywhere else. It also does a little bit towards
getting rid of InstalledApp in favor of just reusing App.
This will uninstall the list of apps that the user has setup in the Panic
Settings if Privileged Extension is installed. This also requires that the
user set up a trusted connection between a panic trigger app (e.g. Ripple)
and F-Droid.
It is possible for repo operators to specify a bad CurrentVersionCode for
an app that is also in another repo, and cause confusion in the suggested
version calculation. Or if one repo's index is very out of date. This
adds a fallback for these cases, so at least it'll stop the crash and
attempt the user's requested install.
Rx needs to be used as the basis of the whole system, it doesn't make sense
to just have one small part handled by Rx.
RxJava is still used in InstallAppProviderService, so that would have to be
tackled separately.
Since it is possible to connect to a peer via NFC, "Swap back", QR Code,
etc. once a peer is successfully used, it can show up in the StartSwapView
list of peers.
This also switches to always using getActivity().getSwapService() to make
it easily traceable where that is happening. It shouldn't be happening in
SwapViews...
java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:62)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6128)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
at org.fdroid.fdroid.localrepo.peers.BonjourPeer.equals(BonjourPeer.java:34)
at java.util.HashMap.put(HashMap.java:427)
at java.util.HashSet.add(HashSet.java:217)
at rx.internal.operators.OperatorDistinct$1.onNext(OperatorDistinct.java:62)
at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:202)
at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:162)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
https://stackoverflow.com/a/602660
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:851)
at java.util.HashMap$ValueIterator.next(HashMap.java:879)
at org.fdroid.fdroid.localrepo.LocalRepoManager.copyIconsToRepo(LocalRepoManager.java:296)
at org.fdroid.fdroid.localrepo.LocalRepoService$1.run(LocalRepoService.java:131)
SwapService is the thing that needs to be always running, and the last
thing killed. So it should start first, and stop last. So now, the user
clicking the button starts SwapService, which starts SwapWorkflowActivity.
This also eliminatings the "Loading" screen in favor of just showing the
StartSwapView with various inline progress indicators.
Instead of waiting for the user to make all the app selections, then click
next, this constantly regenerates the swap repo on each click of the app
list. This means that the swap repo is more likely to be immediately ready
when the user clicks next.
The Android back button provides a working back function, and the Swap
"close" button on the upper left already provides a reset function. So this
turns the "back" button to be a "try again" button which re-runs the
connection process.
The Receiver superclass is not reusing difficult code, but it is hiding the
simple list of UI configuration that it does.
This also eliminates the "error" TextView and just reuses the existing
TextView for error messages.
The most expensive part of this whole process is calculating the hash of the
whole APK. InstalledAppProvider already caches that, and the rest is OK to
query. If any particular part of the query is expensive, it could also be
moved to InstalledAppProviderService.
This moves all logic for setting up the local fdroid repo to its own
IntentService. That makes it much easier to interact with since things can
just use the static helper method to request it to update, and it'll do the
right thing.
Almost all of the nearby/swap view classes could be condensed into a single
base class that is instantiated in the view XML. This is the first step
towards making that happen.
It also lays the groundwork where "steps" are all SwapViews. The
original concept of "steps" put all steps together, whether
F-Droid could control them or not. For example, the Views were
mixed with the system Bluetooth prompts. This is the first step
towards converting the steps to always be SwapViews, which are
always under control of this app.
When coming back to a SwapView/step, it does not seem feasible to handle
automatically restarting things like permissions and Bluetooth prompts. If
there is a way, it should be possible to first load the proper SwapView
instance, then trigger the system prompt. The makes the SwapView a pure
View, without any Controller in it.
The date/time written to index.xml and index-v1.json should always be in
UTC format. These formats are often in the form of just a date, e.g.
2019-04-28. Those are then converted to UNIX seconds, which includes the
time. In the date only case, the time is assumed to be 00:00, which will
be different per time zone.
index-v1.json is better since it mostly uses Java-style UNIX time in millis
but the dates/times are parsed then stored in the local database in the old
format yyyy-MM-dd_HH:mm:ss which will result in different UNIX times when
the device is in different time zones.
fdroid/fdroidclient#1757
This also converts old Repo.lastUpdated values rather than just failing.
index.xml handling used to store the Repo "Last Updated" date used to store
the value as just an ISO date (2019-04-29), then the time was added. So if
date/time parsing fails, this falls back to trying to parse just the date.
null is returned when parsing fails, and the Latest Tab shows nothing if
the Last Updated is null.
Some related tests were also tweaked.
Hopefully:
closesfdroid/fdroidclient#1757
Throwable includes Errors and Exceptions. Fixes stacktraces like these:
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:325)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: java.lang.NoSuchMethodError: No virtual method toPath()Ljava/nio/file/Path; in class Ljava/io/File; or its super classes (declaration of 'java.io.File' appears in /system/framework/core-oj.jar)
at org.apache.commons.io.FileUtils.isSymlink(FileUtils.java:3107)
at org.apache.commons.io.FileUtils.deleteDirectory(FileUtils.java:1616)
at org.fdroid.fdroid.DeleteCacheService.onHandleWork(DeleteCacheService.java:32)
at android.support.v4.app.JobIntentService$CommandProcessor.doInBackground(JobIntentService.java:391)
at android.support.v4.app.JobIntentService$CommandProcessor.doInBackground(JobIntentService.java:382)
at android.os.AsyncTask$2.call(AsyncTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
... 3 more
java.lang.NoSuchMethodError: No virtual method toPath()Ljava/nio/file/Path; in class Ljava/io/File; or its super classes (declaration of 'java.io.File' appears in /system/framework/core-oj.jar)
at org.apache.commons.io.FileUtils.isSymlink(FileUtils.java:3107)
at org.apache.commons.io.FileUtils.deleteDirectory(FileUtils.java:1616)
at org.fdroid.fdroid.DeleteCacheService.onHandleWork(DeleteCacheService.java:32)
at android.support.v4.app.JobIntentService$CommandProcessor.doInBackground(JobIntentService.java:391)
at android.support.v4.app.JobIntentService$CommandProcessor.doInBackground(JobIntentService.java:382)
at android.os.AsyncTask$2.call(AsyncTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
This reverts commit c5daf1981a7f00de16e19120ac42575c0e4bc424.
Turns I was confused here. Yes, the job has to be called "pages" if the
job is deploying to GitLab Pages. But in the case of `fdroid nightly`, the
thing that is deploying to GitLab Pages is the .gitlab-ci.yml that is in
the *-nightly repo, which is auto-generated by `fdroid nightly`.
fdroid/fdroidserver#649
There are soo many of these:
org.fdroid.fdroid.net.HttpDownloaderTest > downloadThenCancel[avd27(AVD) - 8.1.0] FAILED
Test failed to run to completion. Reason: 'Instrumentation run failed due to 'Process crashed.''. Check device logcat for details
When auto-updates are enabled, the app should update itself last, to ensure
that all of the other apps are completely updated before this app is killed
as part of the update process.
closes#1556
java.lang.NoSuchMethodError: No virtual method toPath()Ljava/nio/file/Path; in class Ljava/io/File; or its super classes (declaration of 'java.io.File' appears in /system/framework/core-oj.jar)
at org.apache.commons.io.FileUtils.isSymlink(FileUtils.java:3107)
at org.apache.commons.io.FileUtils.deleteDirectory(FileUtils.java:1616)
at org.fdroid.fdroid.DeleteCacheService.onHandleWork(DeleteCacheService.java:30)
at android.support.v4.app.JobIntentService$CommandProcessor.doInBackground(JobIntentService.java:391)
at android.support.v4.app.JobIntentService$CommandProcessor.doInBackground(JobIntentService.java:382)
at android.os.AsyncTask$2.call(AsyncTask.java:304)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
java.lang.NullPointerException: println needs a message
at android.util.Log.println_native(Native Method)
at android.util.Log.e(Log.java:232)
at org.fdroid.fdroid.net.DownloaderService.handleIntent(DownloaderService.java:232)
at org.fdroid.fdroid.net.DownloaderService.access$000(DownloaderService.java:88)
at org.fdroid.fdroid.net.DownloaderService$ServiceHandler.handleMessage(DownloaderService.java:108)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.os.HandlerThread.run(HandlerThread.java:61)
This is needed since this affects the onProgress broadcasts, and sending
too many can peg the device's CPU. 1k was just too small. ANd 8k works
fine for Bluetooth.
fdroid/fdroidclient#1590
In some cases (e.g. when using Firefox Klar) and copying the URL
(of a link), then only the uri is set and not the text. This
prevented (before this commit) the autofill of the
add package source dialog in such cases.
java.lang.NullPointerException: Attempt to read from field 'java.lang.String org.fdroid.fdroid.data.App.packageName' on a null object reference
at org.fdroid.fdroid.views.swap.SwapAppsView$AppListAdapter$ViewHolder$2.onChange(SwapAppsView.java:294)
at android.database.ContentObserver.onChange(ContentObserver.java:130)
at android.database.ContentObserver.onChange(ContentObserver.java:145)
at android.database.ContentObserver$NotificationRunnable.run(ContentObserver.java:216)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:152)
at android.app.ActivityThread.main(ActivityThread.java:5497)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Normally, WifiStateChangeService finds the SSID when F-Droid starts. But if
the user hasn't granted location permissions yet, then WifiStateChangeService
won't have been able to read the SSID yet.
java.lang.NullPointerException: Attempt to invoke virtual method 'java.io.File java.io.File.getCanonicalFile()' on a null object reference
at android.os.storage.StorageManager.getStorageVolume(StorageManager.java:844)
at android.os.storage.StorageManager.getStorageVolume(StorageManager.java:838)
at android.os.Environment.isExternalStorageRemovable(Environment.java:725)
at org.fdroid.fdroid.views.main.NearbyViewBinder.<init>(NearbyViewBinder.java:85)
at org.fdroid.fdroid.views.main.MainViewController.bindSwapView(MainViewController.java:64)
at org.fdroid.fdroid.views.main.MainViewAdapter.onCreateViewHolder(MainViewAdapter.java:94)
at org.fdroid.fdroid.views.main.MainViewAdapter.onCreateViewHolder(MainViewAdapter.java:47)
at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6685)
java.lang.IllegalArgumentException: Failed to find storage device at null
at android.os.Environment.isExternalStorageRemovable(Environment.java:859)
at org.fdroid.fdroid.views.main.NearbyViewBinder.<init>(NearbyViewBinder.java:85)
at org.fdroid.fdroid.views.main.MainViewController.bindSwapView(MainViewController.java:64)
at org.fdroid.fdroid.views.main.MainViewAdapter.onCreateViewHolder(MainViewAdapter.java:94)
at org.fdroid.fdroid.views.main.MainViewAdapter.onCreateViewHolder(MainViewAdapter.java:47)
at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6685)
java.lang.ArrayIndexOutOfBoundsException: length=13; index=42
at sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(BaseCalendar.java:454)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2340)
at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2740)
at java.util.Calendar.updateTime(Calendar.java:2589)
at java.util.Calendar.getTimeInMillis(Calendar.java:1101)
at java.util.Calendar.getTime(Calendar.java:1074)
at java.text.SimpleDateFormat.parseInternal(SimpleDateFormat.java:1518)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1413)
at java.text.DateFormat.parse(DateFormat.java:356)
at org.fdroid.fdroid.Utils.parseDateFormat(Utils.java:577)
at org.fdroid.fdroid.Utils.parseDate(Utils.java:592)
at org.fdroid.fdroid.data.Apk.<init>(Apk.java:178)
java.lang.NumberFormatException: Not a number:
at android.icu.math.BigDecimal.bad(BigDecimal.java:3349)
at android.icu.math.BigDecimal.<init>(BigDecimal.java:526)
at android.icu.math.BigDecimal.<init>(BigDecimal.java:910)
at android.icu.text.DigitList.getBigDecimalICU(DigitList.java:278)
at android.icu.text.DecimalFormat.parse(DecimalFormat.java:2058)
at android.icu.text.DecimalFormat.parse(DecimalFormat.java:1931)
at java.text.DecimalFormat.parse(DecimalFormat.java:804)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2353)
at java.text.SimpleDateFormat.parseInternal(SimpleDateFormat.java:1615)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1528)
at java.text.DateFormat.parse(DateFormat.java:360)
at org.fdroid.fdroid.Utils.parseDateFormat(Utils.java:577)
at org.fdroid.fdroid.Utils.parseDate(Utils.java:592)
at org.fdroid.fdroid.data.App.<init>(App.java:311)
at org.fdroid.fdroid.views.whatsnew.WhatsNewAdapter.onBindViewHolder(WhatsNewAdapter.java:95)
at org.fdroid.fdroid.views.whatsnew.WhatsNewAdapter.onBindViewHolder(WhatsNewAdapter.java:19)
java.lang.ArrayIndexOutOfBoundsException: length=13; index=36
at sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(BaseCalendar.java:454)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2411)
at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2813)
at java.util.Calendar.updateTime(Calendar.java:3397)
at java.util.Calendar.getTimeInMillis(Calendar.java:1761)
at java.util.Calendar.getTime(Calendar.java:1734)
at java.text.SimpleDateFormat.parseInternal(SimpleDateFormat.java:1633)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1528)
at java.text.DateFormat.parse(DateFormat.java:360)
at org.fdroid.fdroid.Utils.parseDateFormat(Utils.java:577)
at org.fdroid.fdroid.Utils.parseDate(Utils.java:592)
at org.fdroid.fdroid.data.App.<init>(App.java:314)
at org.fdroid.fdroid.views.updates.UpdatesAdapter.onCanUpdateLoadFinished(UpdatesAdapter.java:241)
at org.fdroid.fdroid.views.updates.UpdatesAdapter.onLoadFinished(UpdatesAdapter.java:224)
at org.fdroid.fdroid.views.updates.UpdatesAdapter.onLoadFinished(UpdatesAdapter.java:67)
java.lang.NumberFormatException: For input string: "@2131034146"
at java.lang.Integer.parseInt(Integer.java:615)
at java.lang.Integer.parseInt(Integer.java:650)
at org.fdroid.fdroid.data.App.getMinTargetMaxSdkVersions(App.java:1092)
at org.fdroid.fdroid.data.App.initInstalledApk(App.java:769)
at org.fdroid.fdroid.data.App.getInstance(App.java:395)
at
org.fdroid.fdroid.localrepo.CacheSwapAppsService.onHandleIntent(CacheSwapAppsService.java:77)
at
android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:76)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.os.HandlerThread.run(HandlerThread.java:65)
AppUpdateStatusManager and InstallManagerService should be using only the
Canonical URL of the package since that is the global unique ID. The actual
URL used to download it needs to be isolated in DownloaderService, which can
entirely manage the mirror selection process. This is just a bunch of
renaming to make this all clearer.
This parses the String into a Uri once per Intent, rather than once per
broadcast. The Uri instance is also nicer to work with, since it is the
native URL format for Intents.
It should make the progress updates a bit more efficient also.
fdroid/fdroidclient#1742
Only DownloaderService really needs to know about the mirror tricks, the
rest of the process should only ever use the canonical URL to keep things
simple.
This method returns the URL that points to the canonical download
source for this package. This is also used as the unique ID for
tracking downloading, progress, and notifications throughout the
whole install process. It is guaranteed to uniquely represent
this file since it points to a file on the file system of the
canonical webserver.
* no-featured-app:
cleanup imports
fix padding of top item in Latest Tab
Remove feature graphic of first app from "Latest" view
See merge request fdroid/fdroidclient!807
This adds requirements before an app is shown on the Latest tab. It must
have all of these:
* name
* summary
* description
* license
* What's New entry
* at least some text localized
And then it must have at least one of these:
* screenshots
* feature graphic
The problem here is that oftentimes, the index fetch will happen
automatically in the background while the user is in a different app. If
the fetch fails, the warning text changed here is displayed as a toast,
but without this change there's no way to tell that it's coming from
F-Droid.
The merge_requests feature is really confusing, and doesn't seem to do
anything useful for what we need it to. Like it doesn't let new
contributors' merge requests run on the fdroid runners.
* Rename ids to something meaningful
* Remove inner layouts from constraint layout
* Use same text and button styles
* Make sure the background image doesn't overlap with the text
This also needs to handle mirror lists with 1 element, since mirrors can
now be disabled. If the user disables all mirrors, then there will be only one URL in the
list of mirrors. Asking for a random mirror in that case should not return
null, but the one enabled mirror.
closes#1696
```python
import glob
for f in glob.glob('metadata/*/*.txt'):
with open(f) as fp:
data = fp.read()
with open(f, 'w') as fp:
fp.write(data.rstrip())
fp.write('\n')
```
for f in metadata/*/short_description.txt; do data=`cat $f`; echo $data > $f; done
This spreads downloads across all available mirrors randomly. This could
definitely be improved, like choosing the fastest or nearest mirror, or
only .onion addresses on Tor. This will improve the current situation and
should reduce the load on f-droid.org a lot.
fdroidclient#1696
Remove unused code and simplify to only present args that are used. This is
remnants from:
fdroidclient#490
fdroidclient#606
fdroidclient!295
fdroidclient!242
Lots of people complain that the graphics aren't being downloaded. That's
because they never use F-Droid while on WiFi and the default prefs do not
allow downloading graphics while on Data. This sets the preference to
allow downloading graphics while on Data if only Data is enabled, and not
WiFi, when the user first starts F-Droid.
closes#1592
This makes it display nicely in RepoDetails, and is natural, since it is
the canonical URL. This also maintains the order as received from the
mirror entries in the index file.
The Read Timeout makes a SocketTimeoutException be thrown if the timeout
expires before data is available for reading from the connection's
returned InputStream. This should help the client switch to a new mirror
when the current mirror is too slow or overloaded.
This should make the timeout logic clearer, without changing the logic at
all. This does increase the timeouts, with the second pass using 1 minute
instead of 30 seconds, and the third pass using 10 minutes instead of 1
minute. Since this often or usually runs in the background, it should
allow some pretty long timeouts in the worst case.
It seems that ARM emulators timeout even when just trying to run the
assumeTrue() tests via Espresso. There needs to be one test still enabled
in the file, otherwise, the run fails with:
org.fdroid.fdroid.MainActivityEspressoTest > initializationError[Nexus_One_API_19(AVD) - 4.4.2] FAILED
java.lang.Exception: No runnable methods
at org.junit.runners.BlockJUnit4ClassRunner.validateInstanceMethods(BlockJUnit4ClassRunner.java:191)
The two excluded URLs seem to always resolve to IPv6 addresses first, then
fail since there isn't IPv6 connectivity. Donno why, but only on old android
versions, so just skip them there.
Compression seems to just give stacktraces:
HttpDownloaderTest I URL: https://en.wikipedia.org/wiki/Index.html
TestRunner I failed: downloadUninterruptedTest(org.fdroid.fdroid.net.HttpDownloaderTest)
I ----- begin exception -----
I java.io.EOFException
I at java.util.zip.GZIPInputStream.readFully(GZIPInputStream.java:206)
I at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:98)
I at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:81)
I at libcore.net.http.HttpEngine.initContentStream(HttpEngine.java:541)
I at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:844)
I at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:283)
I at libcore.net.http.HttpURLConnectionImpl.getHeaderField(HttpURLConnectionImpl.java:139)
I at libcore.net.http.HttpsURLConnectionImpl.getHeaderField(HttpsURLConnectionImpl.java:246)
I at org.fdroid.fdroid.net.HttpDownloader.download(HttpDownloader.java:111)
I at org.fdroid.fdroid.net.HttpDownloaderTest.downloadUninterruptedTest(HttpDownloaderTest.java:74)
I at java.lang.reflect.Method.invokeNative(Native Method)
I at java.lang.reflect.Method.invoke(Method.java:511)
I at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
I at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
I at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
I at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
I at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
I at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
I at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
I at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
I at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
I at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
I at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
I at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
I at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
I at org.junit.runners.Suite.runChild(Suite.java:128)
I at org.junit.runners.Suite.runChild(Suite.java:27)
I at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
I at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
I at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
I at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
I at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
I at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
I at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
I at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
I at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
I at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:384)
I at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1661)
This is insurance to make sure that packageNames are not abused for
exploiting F-Droid. The database queries already use SQL Prepared
Statements, but who know what else might be exploitable.
fdroid/fdroidclient#1588
Keep PRNGFixes as it is since it is security sensitive, standardized
code from Google. While F-Droid never wants to do anything with
hardware IDs at all, this code uses the Build.SERIAL as a seed for the
random number generator, so it is safe privacy-wise.
ACRA E ACRA caught a IllegalStateException for org.fdroid.fdroid.debug
E java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
E at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:62)
E at android.os.Handler.handleCallback(Handler.java:751)
E at android.os.Handler.dispatchMessage(Handler.java:95)
E at android.os.Looper.loop(Looper.java:154)
E at android.app.ActivityThread.main(ActivityThread.java:6128)
E at java.lang.reflect.Method.invoke(Native Method)
E at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
E at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
E Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a
null object reference
E at org.fdroid.fdroid.localrepo.peers.BonjourPeer.hashCode(BonjourPeer.java:41)
E at sun.misc.Hashing.singleWordWangJenkinsHash(Hashing.java:48)
E at java.util.HashMap.put(HashMap.java:423)
E at java.util.HashSet.add(HashSet.java:217)
E at rx.internal.operators.OperatorDistinct$1.onNext(OperatorDistinct.java:62)
E at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:202)
E at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:162)
E at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
E ... 7 more
The query was trying to figure out some thing about suggestedVercode
which shouldn't at all be necessary for setting the iconUrl.
The index already contains the icon pointing to the suggested version by
that repository, so we just take that regardless.
fdroid/fdroidclient#1569
```python
import glob
import os
import re
locale_pat = re.compile(r'.*values-([a-z][a-z][a-zA-Z-]*)/strings.xml')
translation_pat = re.compile(r'.*name="settings_label"[^>]*>"?([^"<]*).*')
for f in glob.glob('/home/hans/code/android.googlesource.com/packages/apps/Settings/res/values-[a-z][a-z]*/strings.xml'):
m = locale_pat.search(f)
if m:
locale = m.group(1)
if locale.endswith('-nokeys'):
continue
#print(locale)
with open(f) as fp:
m = translation_pat.search(fp.read())
if m:
word = m.group(1)
print(locale, '\t', word)
fdroid = '/home/hans/code/fdroid/client/app/src/main/res/values-' + locale + '/strings.xml'
if os.path.exists(fdroid):
with open(fdroid) as fp:
data = fp.read()
with open(fdroid, 'w') as fp:
fp.write(re.sub(r'menu_settings">[^<]+</string', 'menu_settings">' + word + '</string', data))
```
fdroid/fdroidclient#1569fdroid/fdroidclient#887
```python
import glob
import os
import re
locale_pat = re.compile(r'.*values-([a-zA-Z-]*)/strings.xml')
translation_pat = re.compile(r'.*name="corpus_name_websearch_nearby">([^<]*).*')
for f in glob.glob('/tmp/Velvet/res/values-*/strings.xml'):
m = locale_pat.search(f)
if m:
locale = m.group(1)
with open(f) as fp:
m = translation_pat.search(fp.read())
if m:
word = m.group(1)
print(locale, '\t', word)
fdroid = '/home/hans/code/fdroid/client/app/src/main/res/values-' + locale + '/strings.xml'
if os.path.exists(fdroid):
with open(fdroid) as fp:
data = fp.read()
with open(fdroid, 'w') as fp:
fp.write(re.sub(r'main_menu__swap_nearby">[^<]+</string', 'main_menu__swap_nearby">' + word + '</string', data))
```
For many languages, there are unavoidable long words needed for the labels
on the button bar, for example, the standard word for Settings can be up to
15 characters long:
https://gitlab.com/fdroid/fdroidclient/issues/1569#note_126469088
The BottomBar was scaling the active one up, and sizing all the fields based
on that size. This removes that animation, and sets all tabs to always have
the same text size. That makes it possible to make the spacing tighter.
This also sets the text truncating mode to "middle" which sticks an elipsis
in the middle of the truncated word and shows the start and end.
closes#1569closes!756
This adds a new IntentService to pre-process Intents that request a
new repo is added. Right now, this only handles Intents that come
from the new storage scanners.
This also adds a new case to the AddRepo UI logic to cover when an
incoming Intent is for a mirror that is already included in an enabled
repo. In that case, the user is show the Repo Details screen for the
repo that includes that mirror. This is done is a hacky way right now
since the only path through is to click the button. So this clicks
the button in code.
Creates an IntentService subclass for scanning removable "external
storage" for F-Droid package repos, e.g. SD Cards. This is intented to
support sharable package repos, so it ignores non-removable storage,
like the fake emulated sdcard from devices with only built-in storage.
This method will only ever allow for reading repos, never writing. It
also will not work for removeable storage devices plugged in via USB,
since do not show up as "External Storage"
* https://stackoverflow.com/a/40201333
* https://commonsware.com/blog/2017/11/14/storage-situation-external-storage.htmlcloses#1377
This uses the new Storage Access Framework, which was required for
accessing files on the SD Card starting in android-19. But the API
was really limited until android-21, and not really complete until
android-23 or even android-26. So the levels of usability will vary a
lot based on how new the version of Android is.
The mirror logic assumes that it has a mirrors list with at least once
valid entry in it. In the index format as defined by `fdroid update`,
there is always at least one valid URL: the canonical URL. That also
means if there is only one item in the mirrors list, there are no
other URLs to try.
The initial state of the repos in the database also include the canonical
URL in the mirrors list so the mirror logic works on the first index
update. That makes it possible to do the first index update via SD Card
or USB OTG drive.
* jsonLoader: (28 commits)
fix checkstyle complaints
force DBHelperTest.canAddAdditionalRepos() to run on CI
clean up whitespace in repo descriptions
rename parseXmlRepos to parseAdditionalReposXml
rename item lists to repoItems
rename defaultReposFile to additionalReposFile
separate defaultRepos from initialRepos, which includes additionalRepos
rename REPO_XML_ARG_COUNT to REPO_XML_ITEM_COUNT
fix additional_repos.xml handling to be properly parsed
move comments to javadoc
priority is NOT ignored, just additional_repos.xml is not allowed to set
fix DBHelperTest to actually load and parse additional_repos.xml
changed the tests: now testing only DBHelper.parseXmlRepos()
removed stars from imports
finished additional repos test
some minor style changes
minor style changes
implemented creating xml file on oem partition; not sure whether it works cause gradle runs forever (>20min)
started implementing test
removed priority from additional_repos.xml
...
closesfdroid/fdroidclient!705
additional_repos.xml has 7 <item> elements per repo, while default_repos.xml
has 8. The difference is that additional_repos.xml does not have the
"priority" <item> since it is not allowed to override anything that is set
in default_repos.xml.
see spec in !705
I missed this little detail ind64a55e013882a7d6b3de646955ed68647a82e97,
the super version of this throws an exception, so it stops the downgrade.
fdroid/fdroidclient!729
fdroidserver has always written "sha256" to the index.xml file, so client
should use the same. The Java hashers will correctly respond to both
"sha256" and "SHA-256", and the only place that the hashType is read from
the DB and used is in the swap repo index.xml generation, where it should
also use "sha256".
java.lang.Throwable: Explicit termination method 'end' not called
at dalvik.system.CloseGuard.open(CloseGuard.java:180)
at java.util.zip.Deflater.<init>(Deflater.java:171)
at kellinwood.zipio.ZioEntryOutputStream.<init>(ZioEntryOutputStream.java:35)
at kellinwood.zipio.ZioEntry.getOutputStream(ZioEntry.java:482)
at kellinwood.security.zipsigner.ZipSigner.signZip(ZipSigner.java:759)
at kellinwood.security.zipsigner.ZipSigner.signZip(ZipSigner.java:664)
at org.fdroid.fdroid.localrepo.LocalRepoKeyStore.signZip(LocalRepoKeyStore.java:213)
at org.fdroid.fdroid.localrepo.LocalRepoManager.writeIndexJar(LocalRepoManager.java:492)
at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:759)
at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:709)
at android.os.AsyncTask$2.call(AsyncTask.java:304)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
E StrictMode: A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
The emulator runs are super crashy on gitlab-ci, so just run the emulator
tests for the "full" build flavor. Once these prove stable, the task
should be switched to connectedCheck to test all the build flavors
ARM emulators are too slow to run these tests in a useful way. The
sad thing is that it would probably work if Android didn't put up the
ANR "Process system isn't responding" on boot each time. There seems
to be no way to increase the ANR timeout.
Sometimes the test suite just totally bombs out and fails on every single
job with the same Robolectric crash. Adding this line seems to fix it.
https://github.com/robolectric/robolectric/issues/3846
Here's the error:
java.lang.VerifyError: class org.robolectric.android.fakes.RoboMonitoringInstrumentation overrides final method specifyDexMakerCacheProperty.()V
Before, it was running:
* testBasicDebugUnitTest
* testBasicReleaseUnitTest
* testFullDebugUnitTest
Since there are no Robolectric tests for only "basic", and there are no
tests specific to "release" or "debug", those three runs will be running
the same tests, except for the handful of "full" tests. So running
testFullDebugUnitTest covers all cases.
NanoHTTPD has issues with HTTP Keep-Alive, especially when other requests
are mixed in, like the /request-swap POST or perhaps the F-Droid HEAD to
fetch the ETag before the GET.
This disables gzip encoding and sets a Content Security Policy while I'm at
it. APKs, PNGs, and JARs are already compressed, so gzip would only ever
cause problems. And the index page is meant to be viewed by browsers, so
having a CSP will limit potential malicious swap activity.
The webserver was totally broken since nanohttpd had changed so much since
the swap webserver was implemented. This syncs up with the sample file and
gets rid of our hacks. The only differences now are the stuff that is
removed since it is totally unused in F-Droid. This also adds a full test
suite.
this actually closes#248
The MIME Types only need to be set on files that we are actually using to
display in the browser. All others should not be set so that they cannot
be abused.
This is a quick hack to reuse the Latest view with a slightly simpler
layout. It makes the "basic" flavor fully functional as an fdroid client.
The goal here is just to have something simpler with as little new code as
possible. It is essential that the whitelabeling and "Light" aka "basic"
flavor does not increase the maintenance load.
closesfdroid/fdroidclient#48fdroid/fdroidclient!692fdroid/fdroidclient!695
PendingInstall means that the user considers the install still in process,
like when F-Droid gets killed in the background. There is unfortunately no
reliable way currently to ensure that removePendingInstall() is called when
the app is finally installed so we can't use it here.
This reverts a small part of 1c50e2891054b629e2af6b2d0b1fc89e0b1cf18b
closesfdroid/fdroidclient#1527fdroid/fdroidclient#1532
InstalledAppProviderService also updates the AppUpdateStatus of any
package installs that are still in progress. Most importantly, this
provides the final status update to mark the end of the installation
process. It also errors out installation processes where some outside
factor uninstalled the package while the F-Droid process was underway, e.g.
uninstalling via adb, updates via Google Play, Yalp, etc.
fdroid/fdroidclient#1536fdroid/fdroidclient#1357
This could definitely use a better design treatment, but at least it is
better than showing the "click to install" button again during the install
process.
closes#1357
This adds a new PendingInstall event which broadcasts that an install
process has started, but the state of it is not yet known, like
whether it needs to be downloaded still, or is ready to install. It
marks the very first step of the whole InstallManagerService process.
Installer events should only be directly related to the install process as
managed by the Installer set of classes. The newer AppStatusUpdate stuff
now tracks the whole lifecycle of the process.
This mostly reverts f0d6acd974548e24662a64271ae57922f74c3225 since there is
now the overarching concept of "Pending Install" to mark packages that are
somewhere in the whole process.
refs #828
refs #1357
For fullstack custom builds, they'll also need a whitelabel build of
Privileged Extension, which will have a different Application ID and
signing key than F-Droid Privileged Extension.
Before, if the Data/WiFi Settings made it so the update process is not
allowed to run and the device was not offline or in Airplane Mode, it would
show this Toast then it would show the "your device is offline" Toast.
This merges the triedEmptyUpdate preference into the lastUpdateCheck pref,
and uses that to determine whether the index update has ever run. It seems
that lastUpdateCheck used to be used for that, but was semi-disabled. Then
triedEmptyUpdate was added. This merges the two into lastUpdateCheck, which
also tracks the timestamp of the last index update.
The initial start time is getting pretty slow, so hopefully this will save
a little bit. It also makes it consistent with other places in the code,
like UpdateService.
This was doing a couple of things wrong:
* the scheduled job should always require a network, NONE doesn't work
* when the preferences change, it should cancel any scheduled job first,
so that if the user chooses to disable auto-updates, that takes effect
closes#1474closes#1451closes#1457
This changes the logic to only use a SharedPreference to track pending
installs, and to set the "pending install" mark as soon as possible
while waiting until final confirmation to unmark. Before, there was a
complicated combination of a SharedPreference and the use of the APK in the
cache as a mark.
!488
refs #962closes#1311closes#1031closes#1271
Since there are many ways to uninstall an app, including from Google
Play, {@code adb uninstall}, or Settings -> Apps, this method cannot
ever be sure that the app isn't already being uninstalled. So it
needs to check that we can actually get info on the installed app,
otherwise, just call it interrupted and quit.
closes#1435
I fixed it by using the same style as a standard preference:
* the summary text size was a bit too large
* the summary text should be allowed to wrap
closes#1450
java.lang.IllegalArgumentException: Could not parse [null/24]
at org.apache.commons.net.util.SubnetUtils.calculate(SubnetUtils.java:275)
at org.apache.commons.net.util.SubnetUtils.<init>(SubnetUtils.java:51)
at org.fdroid.fdroid.net.WifiStateChangeService.setIpInfoFromNetworkInterface(WifiStateChangeService.java:261)
at org.fdroid.fdroid.net.WifiStateChangeService.access$100(WifiStateChangeService.java:50)
at org.fdroid.fdroid.net.WifiStateChangeService$WifiInfoThread.run(WifiStateChangeService.java:132)
Also, app.installedApk.sig is set in App.initInstalledApk()
from 3a5ecc5e8ec6c820dbfdb788dc06f7dbb0699c18
refs #1305
refs #855
java.lang.NullPointerException
at org.fdroid.fdroid.data.App.getInstance(App.java:390)
at org.fdroid.fdroid.localrepo.CacheSwapAppsService.onHandleIntent(CacheSwapAppsService.java:77)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.os.HandlerThread.run(HandlerThread.java:60)
Since !705 will allow OEMs, ROM makers, etc. to add repos, there needs to
be a way for the user to prevent those from automatically installing or
uninstalling apps for users of the full F-Droid app, which guarantees user
control.
ACRA is sometimes problematic or annoying. Also, whitelabel versions might
want to disable ACRA. The setting the preference defaultValue is an easy
way to do that.
refs #1483
When debugging issues, tracking the client can be quite useful. This makes
an "Expert" preference that adds the app version and a randomly generated,
stored UUID to the query string each time it downloads an index or an app
package.
This is also useful in whitelabeling, for use cases where there needs to be
some kind of identifier to make it work.
If you quickly cycle between installing an app and uninstalling it, then
`app.installedApk` will still be null when AppDetails2.startUninstall()
calls InstallerService.uninstall(). It is better to crash earlier here,
before the Intent is sent with a null APK, because InstallerService is set
to receive Sticky Intents. That means they will automatically be resent
by the system until they successfully complete.
The notification that shows the download/parse progress of the index update
is now controled by the "Show available updates" preference. That means it
will not be shown at all in the notifications bar if that preference is
disabled. There will still be the header inside of F-Droid. Ideally, the
Updating process would be shown in the Updates tab.
This preference is meant for whitelabel builds that are meant to be
entirely controlled by the server, without user interaction, e.g.
"appliances". Some users have asked for such a thing, so it makes sense to
have it available as an expert preference. In general, we want to ensure
that installs/updates always show a notification so that the user is aware
of what is being installed on their computers. That is the same policy as
other app stores like Google Play, etc.
This should make them less scary to people who do not want to see them at
all. It also means that there can be quite a few expert preferences without
making the list super long for most users.
This labels all network, HTTP, and SSL related errors as CONNECTION_FAILED
so that the mirror selection logic will try the connection again with a new
mirror.
This removes a layer of redundancy where there were defaults set in the
Preferences class, as well as in preferences.xml. This makes it possible
for whitelabel versions to change the default values of the preferences by
changing it only in preferences.xml.
"full" is the original F-Droid app with all the features. It should still
build the exact same app after this change. "basic" is the smallest
version of F-Droid possible. It does not yet build, nor work.
This also only runs one emulator test by default, then runs 3 SDK
levels for final commits. The single default test is the optimized
F-Droid system image included in the Docker image.
Before, push requests were only supported when using index.xml. This adds
support for using push requests in index-v1.json. `fdroid update` has been
generating them in both index versions for a while now.
These calls to bouncycastle were just used because the library was
there. Now with the upcoming 'basic' build flavor, there will be no
need for bouncycastle. It is required for ZipSigner signing of swap
indexes, and TLS support in the swap NanoHTTPD webserver.
With gradle build flavors, it is possible to specify things like
'myflavorImplementation' but only if the 'dependencies' section is after
the 'android' section where the build flavors are declared. How 1982 of
them to make where things are declared in the file have meaning.
With more whitelabeling support, we need this workaround to avoid
trying to call a null instance when the whitelabeled version does not
include all of the possible preferences.
Stripping the `+` form the license link will direct to the wrong spdx
page. (This would also need changing anyway because of spdx v4.0.0)
closesfdroid/fdroidclient#1472
This changes the logic of Utils.getBinaryHash() to return null on failure
rather than only throwing exceptions. That makes it easier to handle these
failures where Utils.getBinaryHash() is called.
#1305#855
This will instead lead users with Privileged Extension to the F-Droid
uninstall screen which will probably fail. But that's better than crashing
in my opinion.
Introduced in c095a85c3dd3c505951bebb52e4ae010c69cc9f9
This gives a lot more flexibility to the user to cover bandwidth, power,
and privacy issues related to network traffic. The current implementation
does not represent these prefs as well as it should. For example, it does
not force the traffic over the preferred network type if the other type is
set to "never". Instead it just tracks the "unmetered" status of the
active network, and acts based on that.
closes#1381
This follows the Material preferred style using Switches instead of
CheckBoxes for boolean preferences. This leaves the "expert" preferences
as CheckBoxes to differentiate them, and make them stand out as something
different.
These are in fact needed:
/export/share/code/fdroid/client/app/src/main/res/values/dimens.xml:25: Error: The resource R.dimen.fixed_width_padding appears to be unused [UnusedResources]
<dimen name="fixed_width_padding">2dp</dimen>
~~~~~~~~~~~~~~~~~~~~~~~~~~
/export/share/code/fdroid/client/app/src/main/res/values/styles.xml:162: Error: The resource R.style.SwapTheme_StartSwap appears to be unused [UnusedResources]
<style name="SwapTheme.StartSwap" parent="AppThemeLight">
~~~~~~~~~~~~~~~~~~~~~~~~~~
/export/share/code/fdroid/client/app/src/main/res/values/styles.xml:166: Error: The resource R.style.SwapTheme_StartSwap_Text appears to be unused [UnusedResources]
<style name="SwapTheme.StartSwap.Text" parent="@style/SwapTheme.StartSwap">
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The new JobScheduler API can opportunitistically run a job based on whether
there is good internet, connected to power, etc. This is very useful for
running updates. Ideally, updates would always happen in the background
while on unmetered internet and connected to power.
#588
Each time the device connects to a wifi network, this waits for 2 minutes,
then if the wifi is still connected, it re-schedules the index update to
happen now. The goal is to favor unmetered networks as much as possible
when downloading the index and any automatic app updates.
This is only needed on older platforms, JobScheduler handles this for us on
android-21+
This changes the flow of the update triggering so that any Intent sent to
UpdateService can potentially trigger an update, depending only on the
state of the internet and the "Only on WiFi" preference. Instead of having
a timer that checks every hour to see if it is time to run the update, just
let AlarmManager send a trigger Intent based on the timing in the
"Update Interval" setting.
The update schedule is reset each time F-Droid restarts, and also each time
the user returns from the settings, so if AlarmManager fails us in the time
being, the updates will be rescheduled next time F-Droid is restarted, the
device is rebooted, etc.
refs #662
HttpDownloaderTest doesn't get run in gitlab-ci since it was too flaky with
internet connections in the emulator. So these were missed until I manually
ran the tests.
688057b3e7e214db49566b84d5b3dcd0db30dc2b
195aaae7e52dc1c47741965904ed17bdc816a71c
df08e84e7829652d7999eee5451080a012b00a1e
Some devices send multiple copies of given events, like a Moto G often
sends three {@code CONNECTED} events. So they have to be debounced to
keep the {@link #BROADCAST} useful.
For now, swap repos are only trusted as long as swapping is active. They
should have a long lived trust based on the signing key, but that requires
that the repos are stored in the database by fingerprint, not by URL
address.
#295#703
For mirroring to work on multiple repos, this must be stored and used per-
repo. The timeout and number of tries seem fine to keep global to reduce
the total amount of mirror churn when this logic is searching.
Apps that are built as part of the ROM and signed by the platform keys
should very rarely be swapped. This removes them from the default
list by comparing the signing keys.
This filter is deliberately only included on the list function and not on
the search function. If people want to share system apps, they'll be able
to find them with the search function, but the system apps won't show up
by default.
https://source.android.com/devices/tech/ota/sign_builds#certificates-keyscloses#440
This should make swap remember if Bluetooth/WiFi was disabled when swapping
started, then automatically disable it when swapping is done. This also
makes swapping remember the swap "visibility" that the user set, and restore
that when the user starts swapping again. There are logic bugs elsewhere
in the whole thing that prevent this from always working, but the state
should be set and stored properly.
If F-Droid gets killed during the install/update process, then the install
procedure would keep getting readded and redownloaded since it is a sticky
Intent. The test is very specific so that this does not block things like
installing updates with the same versionCode, which happens sometimes, and
is allowed by Android.
#1271
The "Only on WiFi" pref originally only controlled index updates, but now
it makes sense to include all of the various files that are downloaded.
#1381
The standard pattern is to pass a Context in rather than call things like
getPackageManager in. It should only pass a PackageManager if that is
actually being reused.
This shouldn't change the logic at all.
The permissions from uses-permission and uses-permission-sdk-23 should be
combined into a single list of permissions that are being requested for the
current SDK version. The previous code was overwriting one or the other,
based on the order that Jackson happen to call setRequestedPermissions().
closes#1139#890#1394
admin#65
This completes the work started in 195aaae7e52dc1c47741965904ed17bdc816a71c
closes#1395closes#1400
# Conflicts:
# app/src/main/java/org/fdroid/fdroid/UpdateService.java
In order to save disk space and memory, at a cost of some CPU time,
this makes sure that all downloaded images are not bigger than the
device can support. A nice side effect of this process is that EXIF
information is stripped from JPEG files since they are read into a
Bitmap, then written out as a PNG. This should complete the JPEG EXIF
stripping started in 2a3aaacf2347679f30e2c8feffb92f25bb882c8b with
considerExifParams(false)
!653
This adds some case normalization to both the scheme and the host. This was
previously messing up TreeUri content:// URLs like this:
content://com.android.externalstorage.documents/tree/1AFB-2402%3A/document/1AFB-2402%3Atesty.at.or.at%2Ffdroid%2Frepo
Turning them into:
content://com.android.externalstorage.documents/tree/1AFB-2402:/document/1AFB-2402:testy.at.or.at/fdroid/repo
java.net.URL barfs on custom URL schemes, and making it handle them is
really hard. Basically, there needs to be a Handler stub class, then
URL.setURLStreamHandlerFactory() must run when F-Droid starts, since
it has to be set before any URL instance is used. This all leaves
some weird logic that gives the false impression that URLConnection
will handle these custom schemes.
Switching to Uri/urlString throughout the code matches the other
classes that use urlString as the unique ID, and this doesn't add more
lines of code.
This was int because it was written arond UrlConnection.getContentLength()
which returns an int. But that doesn't make sense since this will
definitely handle files large than 16MB.
!647#1192
This lets people add any URL as a mirror to an existing repo. The UX is
people add URLs via any of the normal ways of adding a new repo via Intents,
like clicking URLs, QRCodes, etc.
If anything wants to craft an Intent to send directly to F-Droid with an
arbitrary but valid path, that seems like a fine thing to support. The
IntentFilters will still only match on the well known paths, so that the
user doesn't see F-Droid claiming all HTTP URLs.
The repo instance variable has long since been unused, but has just been
left there as a vestige. Now its presence is blocking RepoUpdater.
getSigningCertFromJar() from being a static method that can be reused when
checking for repos on SD Cards and other removable storage devices.
This saves the levels of indirection that leads to a FileInputStream being
created in LocalFileDownloader. Since there are already special cases for
assets:// and drawable://, it seems a natural place to put the file://
case. Also, since this is used to load icons when scrolling through lists
of apps, this is particularly sensitive to inefficient loading.
This also removes custom code that UIL provides better.
This uses the total RAM that the device comes with as a rough measure of
the devices capabilities. That is then used to set how many parallel
threads UIL can use.
Instead of setting the same thing at each place its used, this puts all the
settings in one place. For the most part, they are the same everywhere.
This makes it a lot easier to optimize how UIL works since all the settings
are in one place.
No need to slow down UIL by making it do a cache check since
CleanCacheService already does that in a low priority background service.
The default FileNameGenerator just uses imageUri.hashCode() so its safe an
faster than ours. So just use the default.
Also, no need to set threadPriority() since we are using the default
This was only partially hooked up and often not even populated.
It was added in 4895e2d790ec3b91fa4271a24e1ea0ae69d362f4, but things have
changed a lot now. We should be moving towards preferring the drawable XML
vector icons, which will scale nicely for all DPIs.
The previous commit makes this issue a lot easier to see. ApkFileProvider
getSafeUri() was already making the right URI for SDK_INT < 24, but then
this bit of logic was using the original URI, which didn't work. Installing
from the app's cache dir triggered a "Parse Error". The Android default
installer API needs file:// URIs from getFiles().
closes#1310
This hopefully makes apparent which pieces are only related to APKs, and
which pieces are used for all installable file types (media, OTA ZIPs, etc)
ExtensionInstaller only works on < android-20 anyway, so that's self-
enforcing in terms of URI scheme: it'll only ever see file:// URIs.
This fixes the following crash:
* Install an app form F-Droid
* go to home screen
* uninstall app
* quickly switch to F-Droid the button will still show 'run' for a few
seconds
* launch the app you just uninstalled
This makes the build reproducible, and makes the files smaller.
metadata/en-US/images/phoneScreenshots/screenshot-dark-details.png | Bin 358916 -> 309386 bytes
metadata/en-US/images/phoneScreenshots/screenshot-dark-home.png | Bin 277413 -> 224844 bytes
metadata/en-US/images/phoneScreenshots/screenshot-dark-knownvuln.png | Bin 158903 -> 123484 bytes
metadata/en-US/images/phoneScreenshots/screenshot-knownvuln.png | Bin 66707 -> 41670 bytes
As nice as it would be to help the users, F-Droid is not well positioned to
help the user with this problem. The Android OS itself should do it. Plus
this issue has been open a long time, without much work on it, and the
existing solution is causing crashes.
#855!440!581
Utils.getBinaryHash() is used in a lot of places in the code, so its not
easy to handle this specific issue. Here's one example:
org.fdroid.fdroid.Utils$PotentialFilesystemCorruptionException: java.io.IOException: read failed: EIO (I/O error)
at org.fdroid.fdroid.Utils.getBinaryHash(Utils.java:426)
at org.fdroid.fdroid.AppUpdateStatusService.findApkMatchingHash(AppUpdateStatusService.java:159)
at org.fdroid.fdroid.AppUpdateStatusService.processDownloadedApk(AppUpdateStatusService.java:110)
at org.fdroid.fdroid.AppUpdateStatusService.onHandleIntent(AppUpdateStatusService.java:65)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.os.HandlerThread.run(HandlerThread.java:60)
The NullPointerException fixed by the previous commit had a warning to that
effect. This fixes almost all the warnings to make the warnings clearer:
* unused method
* unused result of File.delete()
* can have reduced visibility
* single char static "" strings can be '' chars
* jif-afWarningQrCodeScan:
do not include english string in translations
Correct check style errors
Add style for the poor QR code scanning autofocus capability warning
Add 'poor QR code scanning capability' translations
Call to the camera autofocus checker in the view
Add camera characteristics checker
fdroid/fdroidclient!649
closes#260
Those classes consist of 1 abstract class, which provide a factory for
2 classes each of which implements different behaviors according to
the Android API version.
Files in the cache can be deleted at any time, without warning. F-Droid's
CleanCacheService can do it, the user can do it in Settings --> Apps, etc.
So when working with files from the cache, the methods need to be extra
defensive, checking that the file that they were given still exists.
closes#1305
Squashed commit of the following:
commit f6f528d67e9bef367cfb8a3a8eaaced06233df4a
Author: Hans-Christoph Steiner <hans@eds.org>
Date: Tue Feb 13 16:24:53 2018 +0100
remove android xml quoting
commit d7251cc20980841ca83fd27f1e4f60c5d99460ac
Author: anonymous <>
Date: Tue Feb 13 14:23:12 2018 +0000
Translated using Weblate (German)
Currently translated at 99.7% (409 of 410 strings)
commit 60f449e154fa0cd2fc986781836bad491a964866
Author: Andreas Kleinert <Andy.Kleinert@gmail.com>
Date: Tue Feb 13 14:22:48 2018 +0000
Translated using Weblate (German)
Currently translated at 99.7% (409 of 410 strings)
commit b16f2f6f58ed06264c8414c90ae9cc3dad9433d6
Author: Hans-Christoph Steiner <hans@guardianproject.info>
Date: Tue Feb 13 15:03:19 2018 +0000
Translated using Weblate (Hebrew)
Currently translated at 100.0% (410 of 410 strings)
commit 66601011e3cbdd64d9b68432bfff13b17ca90f4b
Author: Hans-Christoph Steiner <hans@eds.org>
Date: Tue Feb 13 16:15:52 2018 +0100
check for invalid mixing for format stringsj
commit cdf2e7063297d4f61259a3354a946fffdfd58114
Author: Danial Behzadi <dani.behzi@ubuntu.com>
Date: Tue Feb 13 10:46:48 2018 +0000
Translated using Weblate (Persian)
Currently translated at 100.0% (410 of 410 strings)
commit e7e37ad42c94091e2ec402caac5883272275c8c4
Author: ezjerry liao <ezjerry@gmail.com>
Date: Mon Feb 12 15:26:00 2018 +0000
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (410 of 410 strings)
commit 0850c89e297f6256babdc7f087b242cf102ef267
Author: nautilusx <mail.ka@mailbox.org>
Date: Sat Feb 10 14:32:57 2018 +0000
Translated using Weblate (German)
Currently translated at 99.2% (407 of 410 strings)
commit 52d6426b2a413dae1ff2c33814ec9df895eac41b
Author: Kristjan Räts <kristjanrats@gmail.com>
Date: Sun Feb 11 12:25:23 2018 +0000
Translated using Weblate (Estonian)
Currently translated at 100.0% (410 of 410 strings)
commit a308ae180dbdac2fc65076e7f35ba4549ad9f6ea
Author: Allan Nordhøy <epost@anotheragency.no>
Date: Sat Feb 10 00:37:43 2018 +0000
Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.2% (407 of 410 strings)
commit fb44f4cd22f7cb6ef03265d76374646d8b554066
Author: jschwender <joachim.schwender@web.de>
Date: Fri Feb 9 21:08:25 2018 +0000
Translated using Weblate (German)
Currently translated at 99.2% (407 of 410 strings)
commit 450a30bbc18908e53cb10027a30332646a1b6224
Author: Julien Lepiller <roptat@lepiller.eu>
Date: Fri Feb 9 13:31:44 2018 +0000
Translated using Weblate (French)
Currently translated at 99.0% (406 of 410 strings)
commit 309f8b3527f176bd1cf9cd82757978ee2c3a941f
Author: Yunyang Liu <ensigma96@gmail.com>
Date: Fri Feb 9 12:50:13 2018 +0000
Translated using Weblate (Chinese (Simplified))
Currently translated at 97.0% (398 of 410 strings)
commit b4d2fbe00e7c1b276c0dde64844c18935419f3fc
Author: Rivo Zängov <rivozangov@gmail.com>
Date: Fri Feb 9 10:13:12 2018 +0000
Translated using Weblate (Estonian)
Currently translated at 98.5% (404 of 410 strings)
commit 6ec7f716405dc4efc3a12204f1ed97aaad09ba45
Author: Luiz Fernando Ranghetti <elchevive@opensuse.org>
Date: Sat Feb 10 02:58:33 2018 +0000
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (410 of 410 strings)
commit c8af9171ac4d5fdcecb8faaabbc5c58da4053b1f
Author: Ldm Public <ldmpub@gmail.com>
Date: Fri Feb 9 13:17:30 2018 +0000
Translated using Weblate (French)
Currently translated at 99.0% (406 of 410 strings)
commit 04005a0277edca69cc6885d779e2d49673c0d97e
Author: Julien Lepiller <roptat@lepiller.eu>
Date: Fri Feb 9 13:16:05 2018 +0000
Translated using Weblate (French)
Currently translated at 99.0% (406 of 410 strings)
commit 5af1c4d24d25eeb03bffe953e4c5ce7aa8a68697
Author: monolifed <monolifed@gmail.com>
Date: Thu Feb 8 12:25:29 2018 +0000
Translated using Weblate (Turkish)
Currently translated at 100.0% (410 of 410 strings)
commit 4c56b7725905ecc6b4be49f9f4accc95859a46bc
Author: Osoitz <oelkoro@gmail.com>
Date: Fri Feb 9 10:59:06 2018 +0000
Translated using Weblate (Basque)
Currently translated at 100.0% (410 of 410 strings)
commit 7d73c299ceab39b2c674fadc2a32b2154f96e060
Author: Ajeje Brazorf <lmelonimamo@yahoo.it>
Date: Wed Feb 7 20:24:39 2018 +0000
Translated using Weblate (Sardinian)
Currently translated at 99.5% (408 of 410 strings)
commit b0027266898267619b0eb20e206a968c5e0c527e
Author: Felipe Rodrigues <bidu.pub@gmail.com>
Date: Wed Feb 7 13:41:04 2018 +0000
Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.7% (409 of 410 strings)
commit 0207c99f39fb5c43b530c6fa05c4c3f2edc278e6
Author: azumukupoe <azumukupoe1999@gmail.com>
Date: Wed Feb 7 13:19:06 2018 +0000
Translated using Weblate (Japanese)
Currently translated at 100.0% (410 of 410 strings)
commit 439b5eeee86d2dc5a7eb9d06890ff3aa62aad4d0
Author: monolifed <monolifed@gmail.com>
Date: Thu Feb 8 12:19:09 2018 +0000
Translated using Weblate (Turkish)
Currently translated at 100.0% (410 of 410 strings)
commit f3921bb42db891bf1cfa4c8e3d1699aba41f4807
Author: Yaron Shahrabani <sh.yaron@gmail.com>
Date: Wed Feb 7 08:08:20 2018 +0000
Translated using Weblate (Hebrew)
Currently translated at 100.0% (410 of 410 strings)
commit 74c0eb25e6dcc75836bb01fd96b8c04de8cd4a4c
Author: Verdulo <tomek@disroot.org>
Date: Wed Feb 7 18:04:14 2018 +0000
Translated using Weblate (Polish)
Currently translated at 100.0% (410 of 410 strings)
commit 272b00b8dfa0c530a9a51f6b980b0d311c921bfd
Author: Viktar Vauchkevich <victorenator@gmail.com>
Date: Tue Feb 6 14:06:00 2018 +0000
Translated using Weblate (Belarusian)
Currently translated at 98.2% (403 of 410 strings)
commit b28a9e57dd21c5f28dbf3555cadca0a20770a337
Author: Takumi Shoji <azumukupoe1999@gmail.com>
Date: Wed Feb 7 13:06:00 2018 +0000
Translated using Weblate (Japanese)
Currently translated at 100.0% (410 of 410 strings)
commit 56422c2d9ec3de0401793a7e0767c376925cc88f
Author: Sérgio Marques <smarquespt@gmail.com>
Date: Tue Feb 6 11:56:27 2018 +0000
Translated using Weblate (Portuguese (Portugal))
Currently translated at 98.0% (402 of 410 strings)
commit cd4ab7fdb3844f39c2f686b2ce0571b6de622cbb
Author: Licaon Kter <licaon.kter@protonmail.com>
Date: Tue Feb 6 09:05:10 2018 +0000
Translated using Weblate (Romanian)
Currently translated at 95.8% (393 of 410 strings)
commit ca68defd60db953419364758502b9a330b43598f
Author: Ldm Public <ldmpub@gmail.com>
Date: Tue Feb 6 07:38:41 2018 +0000
Translated using Weblate (French)
Currently translated at 98.2% (403 of 410 strings)
commit d0931b98aae70cfefee5e4b13f460aa7156c4270
Author: Verdulo <tomek@disroot.org>
Date: Tue Feb 6 19:35:39 2018 +0000
Translated using Weblate (Esperanto)
Currently translated at 100.0% (410 of 410 strings)
commit bf7173ca6ba9c7b3d9b6f361c2614827ee887e81
Author: Nathan Follens <nathan@email.is>
Date: Tue Feb 6 11:29:34 2018 +0000
Translated using Weblate (Dutch)
Currently translated at 100.0% (410 of 410 strings)
commit cdb4adc18c5ac818a16cb287624226093fe70a47
Author: Yaron Shahrabani <sh.yaron@gmail.com>
Date: Tue Feb 6 09:06:28 2018 +0000
Translated using Weblate (Hebrew)
Currently translated at 100.0% (410 of 410 strings)
commit 93dcc2a0ad171ab4b846e032756ca14c7bd04f04
Author: Sveinn í Felli <sv1@fellsnet.is>
Date: Tue Feb 6 07:41:40 2018 +0000
Translated using Weblate (Icelandic)
Currently translated at 100.0% (410 of 410 strings)
commit 5b359ea0fd1a5d8a5c3f67c2b448cf61c6c57424
Author: ezjerry liao <ezjerry@gmail.com>
Date: Tue Feb 6 01:45:45 2018 +0000
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (410 of 410 strings)
commit 254dc5f0ad07349ba3f6e39fce37fc4cb17c88d1
Author: anonymous <>
Date: Fri Feb 2 16:09:15 2018 +0000
Translated using Weblate (German)
Currently translated at 100.0% (402 of 402 strings)
commit 44b823af4261f4a24b1fde8dc6bc662894583e2f
Author: Licaon Kter <licaon.kter@protonmail.com>
Date: Fri Feb 2 07:34:53 2018 +0000
Translated using Weblate (Romanian)
Currently translated at 96.7% (389 of 402 strings)
commit 0069bef97bcb4d5e87d8fd50c5023dcbe2ae563e
Author: Viktar Vauchkevich <victorenator@gmail.com>
Date: Wed Jan 31 19:20:15 2018 +0000
Translated using Weblate (Belarusian)
Currently translated at 100.0% (402 of 402 strings)
commit 92042d49087f950908a2d312a027976f44554205
Author: Марс Ямбар <mjambarmeta@gmail.com>
Date: Tue Jan 30 17:04:37 2018 +0000
Translated using Weblate (Ukrainian)
Currently translated at 97.7% (393 of 402 strings)
commit 0555d776876940629b1fc1f5fb99b98c139c5a98
Author: Xuacu Saturio <xuacusk8@gmail.com>
Date: Tue Jan 30 20:22:42 2018 +0000
Translated using Weblate (Asturian)
Currently translated at 100.0% (402 of 402 strings)
commit 2e9a284da728fc530f09640f0e33bcdf91947bce
Author: Luca D'Amico <damico.luca91@live.it>
Date: Mon Jan 29 16:40:19 2018 +0000
Translated using Weblate (Italian)
Currently translated at 100.0% (402 of 402 strings)
commit 7640aa3613cbf1d73093ee5445a21be699bfb178
Author: yamabiko <dragonfly@cryptolab.net>
Date: Mon Jan 29 16:38:41 2018 +0000
Translated using Weblate (Italian)
Currently translated at 100.0% (402 of 402 strings)
commit ffc447abaf1af07114b039c028805549e662894d
Author: Luca D'Amico <damico.luca91@live.it>
Date: Mon Jan 29 16:38:27 2018 +0000
Translated using Weblate (Italian)
Currently translated at 99.7% (401 of 402 strings)
commit 248e7df90d2aac7b3d189de0b241a44522202be0
Author: yamabiko <dragonfly@cryptolab.net>
Date: Mon Jan 29 16:38:20 2018 +0000
Translated using Weblate (Italian)
Currently translated at 99.7% (401 of 402 strings)
commit ce561bd4eff83280816322f0a74d6cf1695dd249
Author: Luca D'Amico <damico.luca91@live.it>
Date: Mon Jan 29 16:36:47 2018 +0000
Translated using Weblate (Italian)
Currently translated at 99.2% (399 of 402 strings)
commit 06d21c188e263b74fb5b1e200d207373418de1c7
Author: yamabiko <dragonfly@cryptolab.net>
Date: Mon Jan 29 16:36:14 2018 +0000
Translated using Weblate (Italian)
Currently translated at 99.2% (399 of 402 strings)
commit 2afc5deb08c8e5c41820f4a99cf82d8381eaec82
Author: リー <meluten@gmail.com>
Date: Sun Jan 28 12:15:27 2018 +0000
Translated using Weblate (German)
Currently translated at 100.0% (402 of 402 strings)
commit 28ebd01fbade7bf960a69e3cbfd88e1d32fb6b2c
Author: Yunyang Liu <ensigma96@gmail.com>
Date: Fri Jan 26 14:58:52 2018 +0000
Translated using Weblate (Chinese (Simplified))
Currently translated at 98.2% (395 of 402 strings)
This moves towards the standard Android Studio Ctrl-Alt-L code format with
only whitespace changes. This just removes this one kind of space, since
its widespread, and easy to track. I did this using:
sed -i 's," />,"/>,g' app/src/main/AndroidManifest.xml
extended info on things we already have:
* PRODUCT is another name for BRAND/PHONE_MODEL
* TOTAL_MEM_SIZE can be derived from hardware name
* DISPLAY is also available by looking up the hardware
* STACK_TRACE_HASH should make automated sorting easier
new info:
* PACKAGE_NAME is only new info if the user is using a whitelabel app
* AVAILABLE_MEM_SIZE can be used to track users, but only when provided at
high resolution, e.g. once per second or higher. Most users only send a
single crash report. High frequency reporters send under 100, which is
still orders of magnatude below what is needed to track users.
new file: app/src/main/res/drawable-hdpi/ic_az_white.png
modified: app/src/main/res/drawable-hdpi/ic_last_updated_black.png
new file: app/src/main/res/drawable-hdpi/ic_last_updated_white.png
new file: app/src/main/res/drawable-mdpi/ic_az_white.png
modified: app/src/main/res/drawable-mdpi/ic_last_updated_black.png
new file: app/src/main/res/drawable-mdpi/ic_last_updated_white.png
new file: app/src/main/res/drawable-xhdpi/ic_az_white.png
modified: app/src/main/res/drawable-xhdpi/ic_last_updated_black.png
new file: app/src/main/res/drawable-xhdpi/ic_last_updated_white.png
new file: app/src/main/res/drawable-xxhdpi/ic_az_white.png
modified: app/src/main/res/drawable-xxhdpi/ic_last_updated_black.png
new file: app/src/main/res/drawable-xxhdpi/ic_last_updated_white.png
new file: app/src/main/res/drawable-xxxhdpi/ic_az_white.png
modified: app/src/main/res/drawable-xxxhdpi/ic_last_updated_black.png
new file: app/src/main/res/drawable-xxxhdpi/ic_last_updated_white.png
modified: app/src/main/res/layout/activity_app_list.xml
new file: app/src/main/res/drawable-hdpi/ic_az_black.png
new file: app/src/main/res/drawable-hdpi/ic_last_updated_black.png
new file: app/src/main/res/drawable-mdpi/ic_az_black.png
new file: app/src/main/res/drawable-mdpi/ic_last_updated_black.png
new file: app/src/main/res/drawable-xhdpi/ic_az_black.png
new file: app/src/main/res/drawable-xhdpi/ic_last_updated_black.png
new file: app/src/main/res/drawable-xxhdpi/ic_az_black.png
new file: app/src/main/res/drawable-xxhdpi/ic_last_updated_black.png
new file: app/src/main/res/drawable-xxxhdpi/ic_az_black.png
new file: app/src/main/res/drawable-xxxhdpi/ic_last_updated_black.png
Things like permission support, locales, etc. can change when Android is
updated. So the database should be rebuilt from scratch with a fresh
download of the indexes.
closes#780
The database currently only stores the active language. So if the
user changes the system language of the phone, then the language of
all the app descriptions will be out of sync until the next update.
This forces an update when the locale is changed. This functionality
is also needed for events like OS upgrades.
closes#225
It was tried until it got an IP address, but that will only happen if there
is a wifi device configured. Since WifiStateChangeService is started when
F-Droid starts, WifiStateChangeService could run for days if someone never
connected to WiFi in that time.
WifiStateChangeService is also started by NETWORK_STATE_CHANGED_ACTION so
it should start each time there is a change to the WiFi connection.
It was already behaving like a singleton, but the code was spread around in
other classes. DBHelper does not use a private constructor though since
the tests prevent it.
Yes, this is an ugly and old style, but mixing styles only makes it worse.
Plus it breaks the tests in some cases, since it makes Preferences depend
on Resources.
This commit allows F-Droid to hide itself from the laucher.
It can be hidden either as response to a panic trigger
or as a manual action by long pressing the floating search button.
The latter needs to be explicitly enabled in the settings.
Once hidden, a semi-functional fake calculator app appears in the
launcher that can be used to bring F-Droid back by entering a
pre-defined PIN.
While a large buffer might make things slightly faster, the smaller buffer
size should play much nicer when F-Droid is doing things in the background.
Since calculating the hash is part of the update procedure, which can now
happen in the background, this method will be often running in the
background.
The tests showed no difference in time between the large and small buffer.
This gathers all files that are not tracked by git into a named ZIP file,
which is available for 1 week to download from the Pipelines page for the
fork that ran the build.
Donno how that magic number slipped in, this is the actual flag.
/builds/eighthave/fdroidclient/app/src/main/java/org/fdroid/fdroid/acra/CrashReportSender.java:31: Error: Must be one or more of: Intent.FLAG_GRANT_READ_URI_PERMISSION, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, Intent.FLAG_FROM_BACKGROUND, Intent.FLAG_DEBUG_LOG_RESOLUTION, Intent.FLAG_EXCLUDE_STOPPED_PACKAGES, Intent.FLAG_INCLUDE_STOPPED_PACKAGES, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, Intent.FLAG_ACTIVITY_NO_HISTORY, Intent.FLAG_ACTIVITY_SINGLE_TOP, Intent.FLAG_ACTIVITY_NEW_TASK, Intent.FLAG_ACTIVITY_MULTIPLE_TASK, Intent.FLAG_ACTIVITY_CLEAR_TOP, Intent.FLAG_ACTIVITY_FORWARD_RESULT, Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP, Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS, Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT, Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED, Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY, Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, Intent.FLAG_ACTIVITY_NEW_DOCUMENT, Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, Intent.FLAG_ACTIVITY_NO_USER_ACTION, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT, Intent.FLAG_ACTIVITY_NO_ANIMATION, Intent.FLAG_ACTIVITY_CLEAR_TASK, Intent.FLAG_ACTIVITY_TASK_ON_HOME, Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS, Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT, Intent.FLAG_RECEIVER_REGISTERED_ONLY, Intent.FLAG_RECEIVER_REPLACE_PENDING, Intent.FLAG_RECEIVER_FOREGROUND, Intent.FLAG_RECEIVER_NO_ABORT, Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS [WrongConstant]
MODE_APPEND is only for openFileOutput
/builds/eighthave/fdroidclient/app/src/main/java/org/fdroid/fdroid/localrepo/SwapService.java:105: Error: Must be one or more of: Context.MODE_PRIVATE, Context.MODE_WORLD_READABLE, Context.MODE_WORLD_WRITEABLE, Context.MODE_MULTI_PROCESS [WrongConstant]
There used to be a single loader which would get all apps which have
updates available. This was restarted when we were notified about new
apps requiring updates.
Then, in 7424220 I introduced a second loader responsible for getting
apps with known vulnerabilities. This change caused the bug in #1203,
because it changed the loaders from a single loader with ID = 0, to two
different loaders with arbitrary IDs. However, there was still a line of
code responding to when repo updates completed and we learn about new
updateable apps, and this was asking for a loader with an ID of 0 like
before. This crashed when the loader was completed and we tried to pase
the results.
This is fixed ensuring that both loaders are restarted upon learning of
new updateable apps. To prevent this disconnect in the future, they are
also extracted into the same method.
The workaround is fugly, so we really don't want to include it in the
modern code. Luckily, we have old index support there :-D
closes#1014closes#1202closes#1208#111
If a repo is set with the gitlab-ci Secret Variable DEBUG_KEYSTORE and
there is a repo named the same as this repo with -nightly appended,
then this will automatically generate an fdroid repo of each build
produced by gitlab-ci runs on the master branch.
closes fdroidserver#256
* App and index downloads fall back to a list of mirrors defined
by the repository.
* The changes have been made trying to keep the original download
code untouched, and only using the mirror logic when the download
fails due to a connection error / timeout.
* The mirrors are tried in a randomized manner, and with proper
timeouts. The download is aborted after the tries exceed the
number of mirrors, times 3 for a total of 3 different timeout
values (10s, 30s, and 1m)
* The mirror code isn't used for any images yet, most of which is
handled by an external library.
Closes: #35
The connected10 test runner has been at least as reliable as connected24,
and provides valuable coverage, especially for localization related
crashers.
Also does this with the additional field for ignoring vuln apps.
This should be safe, because there is a check for if (columnExists())
which will only pass if people don't already have this column.
Fixes#1181.
It seems that LocaleList does not necessarily return the "Language
Priority List" in the order that the user has prioritized things. So
we have to kind of fake it by first adding the default locale, then
adding the locales from LocaleList based on longest order first (e.g.
de-AT then de).
#987
The swipe-to-refresh from the latest tab is now also implemented
for the categories and updates tab. It was a bit weird before how
you could swipe to refresh on the first tab, which would show
"Updating repos" at the top. It would then also show the same
message on the categories tab, but you couldn't swipe-to-refresh
that tab. Additionally, several people have requested this on the
updates tab, the tab where it probably makes the most sense.
Fix#1079.
The controller in charge of dismissing an item will have an insight into
whether it will cause a re-query for an existing cursor or not. If a re-query
will occur in response to a `ContentResolver#notifyChange()` invokation (in this
case in response to updating `AppPrefs`), then the `UpdatesAdapter` doesn't
need to rebuild itself yet. If it is a status update, then it should update
the adapter right away.
Seeing as the controller was already returning one thing (a message to be
displayed in a `Toast` and now it also needs to return an opinion on whether
to rebuild the adapter or not, this has been extracted into a value object
which has a message and a rebuild adapter flag.
Items which can be updated (but have not yet been downloaded or queued for download)
will act as if the user selected "Ignore this update" from the app details view.
Items which represent app statuses (e.g. downloading, downloaded ready to install,
installed ready to run) will have the status removed. If required, we will also
forget that they are ready to install, so they wont be there next time.
Used to work, then the default join from `fdroid_app` to `fdroid_apk`
was removed for performance reasons. This adds the join back, but requires
queries to explicitly opt in to the join if they require it. The specific
query for known vulns is not a performance problem, because sqlite is able
to narrow the result set quite substantially before requiring a join onto the
fdroid_apk table anyway (e.g. by using the "installed app" table).
Untested because there are no apps in current repos which exhibit this
behaviour which have newer versions. Right now I'm testing with com.waze
from testy.at.or.at which only has the one version.
I'm also unsure of how important this is seeing as most the time it will
prompt people to update anyway.
Note that I don't think the query will work correctly across multiple repos,
because it is currently only querying the app with the "preferred
metadata".
Also use a newer version of testy.at.or.at index for the index-v1 test,
because it includes apks with "KnownVuln" anti features whereas the
older version did not.
When explicitly given an fdroidrepo(s) intent, it seems silly to restrict it based on
a path of /fdroid/repo, because it is plainly obvious it is an F-Droid repo.
Manifest and NewRepoConfig both had to be amended to allow this behaviour.
Fixes#1171.
There is a specific POSIX error "EIO" which seems to be the "general
purpose we don't know what went wrong but its probably bad" exception.
Our investigations in #855 resulted in the conclusion that it is likely
due to some sort of filesystem corruption or something like that.
Either way, it is annoying many people, so we need to prevent it or
ignore it, rather than prompting the user to submit a bug report.
After much investigation it was unable to be reproduced other than by
one of the original bug reporters. As such, this change ignores it.
Unfortunately Java `IOException`s don't have an API for getting the
errno of a POSIX IO error. Thus, this change results to parsing the
exception message instead :(
Fixes#855.
This improves performance when we need to decide whether or not apps are
installed or not while scrolling through large lists.
Fixes#1143.
Also change Jackson tests to properly ignore App#isApk.
This join resulted in one row for each apk in the result set (before
doing a GROUP BY), instead of one row for each apk. That is a large
difference in number of rows and resulted in much more work for sqlite.
Turns out this join wasn't required.
Some queries are deferred, and then forced to run by Android by invoking
`getCount()`. Under these circumstances, the measured speed of the query
execution is 1ms.
This adds speed logging around `getCount()` in case that is the first time
the query is run.
clbin is just for making the logs easy to read, if it fails, the build
should not be marked as failed, especially since the logs are probably
not needed if the rest of the job succeeded.
This is in the spirit of the setting, where users can see which apks are
available even though they are not installable.
Adds a message explaining why it is incompatible (i.e. because the
signature doesn't match the installed version).
PNG crunching is not a deterministic process, especially the way aapt does
it. This makes the F-Droid builds not reproducible. The easy solution to
this is to pre-crunch the PNGs and commit them to git. It also makes the
final APK a tiny amount smaller, for whatever reason.
https://medium.com/@duhroach/smaller-pngs-and-android-s-aapt-tool-4ce38a24019d
Instead of including the etag in the HTTP GET request and letting the
server set the Response Code depending on whether the etag machines, this
makes the client first issue a HEAD request, which is uses to get the etag
and the file size. We need to do the HEAD beforehand anyway to get the
file size for resumable downloads, and this approach prevents the server
from using the etag as a form of tracking cookie:
http://lucb1e.com/rp/cookielesscookies/closes#562
- Icon transition is no longer jumping in first frame (caused by different padding)
- Icon is no longer cropped at start of transition (caused by missing changeImageTransform)
- Toolbar icons are no longer animated. Introduced in !561 by changing the icons programmatically
This caused problems when users then tried to action the pending
install, where it would no longer have enough information to install the
app. Although it may be technically possible to keep enough information
around in memory to make the app installable, but it is not worth the
effort. If a user intentionally disables a repo, we should no longer be
responsible for keeping information about its apps around.
Fixes#995.
There are some ACRA reports with this IllegalStateException getting hit.
It used to be that it was only ever because we forgot to request the
correct fields from the database. However now I'm not sure that this is
the only source. Perhaps it is also possible in response to parcelling
apk instances, or maybe something else? Either way, this should provide
further info about whether the apk doesn't belong to a repo for some
reason.
There are two methods which allow callers to choose which fields to
return. These were originally added for performance, so you only ask for
what you need. However empirically the performance gain doesn't mean
anything, because it is dwarfed by the query that was just executed.
However, it does open the code up to bugs because we forget to ask for
the right fields. So now it just returns all fields when querying for
apks.
While investigating #1086 which was about swap being busted, I
discovered that we recently introduced a worse bug when working with
multi sig stuff. The swap process, when installing an app (or even when
listening for if a user started installing - before they even did
anything), would ask for an apk from any repo. This is wrong, because we
should only ask for the apks from the swap repo when presented with a
swap dialog.
By fixing this so that it asks for a specific apk, this may also
fix the issue in #1086, because that was about us not asking for enough
info from the database for each Apk which was returned. Now we just
return all columns, because the performance overhead should be minimal,
but it prevents this class of bugs, where we didn't fully populate
our value object. However, I'm not confident that it is fixed, because I
was unable to reproduce it due to the other crash fixed in this change.
Relevant crash:
```
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.fdroid.fdroid.data.Apk.getUrl()' on a null object reference
at org.fdroid.fdroid.views.swap.SwapAppsView$AppListAdapter$ViewHolder.setApp(SwapAppsView.java:311)
at org.fdroid.fdroid.views.swap.SwapAppsView$AppListAdapter.bindView(SwapAppsView.java:422)
at org.fdroid.fdroid.views.swap.SwapAppsView$AppListAdapter.newView(SwapAppsView.java:414)
at android.support.v4.widget.CursorAdapter.getView(CursorAdapter.java:269)
at android.widget.AbsListView.obtainView(AbsListView.java:2349)
at android.widget.ListView.makeAndAddView(ListView.java:1864)
at android.widget.ListView.fillDown(ListView.java:698)
...
```
Each app insert required asking the database for the ID of each
category an app is in. Given the categories don't change (ever)
but are only appended to, we can cache the results in a static
Java variable for increased performance.
This reduced the "repo persister" logic for me from 50 seconds
for main F-Droid and 100 seconds for Archive, down to 15 seconds
and 30 seconds respectively.
Now that we need only "insert" new apps rather than"
* Identify if an app exists
* If so, update
* If not, insert
There is much less code required for all of this stuff.
When preparing a temp database to write to, don't copy all apps/apks.
Instead, only copy those _not_ belonging to the repo we are updating.
In an ideal world, we'd not even need to copy them, but we need
their IDs to be in the temp database so that we don't accidentally
use the same auto-generated ID as the main database.
This also means that we can drop the check for "does this app exist,
and hence should we UPDATE it instead of INSERTing it?" and always
just insert it.
Then, when copying the temp table back to disk, first delete all
apps/apks _belonging to the repo being updated_. Then, copy back the
apks/apps we found in the repo. This again improves performance because
we no longer need to bopy back and forth data which we know
wont change (as evidenced by the fact it belongs to a differen trepo).
I don't think this was possible earlier before we did the work to
support repo priorities properly. That is because we had a single app
which was serviced by several repositories. Now, we have multiple
entries in the `fdroid_app` table, for each repo which supports
that app.
The test files used in Issue763MultiRepo.java are signed using MD5, which
is now considered invalid. So if that test is run on any recent Java, it
will fail with a signature error. This updates the test files to be signed
with SHA1 instead.
* index.microg.jar is a new version fetched from https://microg.org
* index.antox.jar was resigned with the testy.at.or.at key, since antox
website doesn't exist anymore.
RepoUpdater.prepareRepoDetailsForSaving() was broken here because the Repo
properties were being set before calling it, and then the Repo instance was
passed to it for comparison. So the comparison was always saying the value
was unchanged. In IndexV1Updater, the flow doesn't need those checks.
This also fixes the bug where added repos never had their name/description/
icon/etc show up in ManageRepos and RepoDetails.
@cde found this bug working on mirror support, thanks!
related to #35closes#1016
This is not really a useful way to tell the user that the index might be
out of date. It just adds confusion and makes people think that F-Droid
isn't quite smart enough to know what's going on.
The install process automatically sanitizes filenames to avoid exploits
that put attack code in the filename. Media files are also installed using
this logic, so the installed check needs to use sanitized file names to be
accurate.
This will be more important as people work with media, since it is quite
common to use spaces in filenames generated by humans. Media files will
not be built by fdroid, so most likely, they will have human-generated
names.
* This installer is invoked when for non-apk/media files, and
copies them to an appropriate folder on the sdcard.
* We also introduce a FileInstallerActivity to ask for storage
permissions at runtime, as needed by Android 6.0 and above,
and handle the install/uninstall process.
* A toast is shown with the install path after installation.
TODO:
* Manage Installed Apps screen doesn't show media files.
* For non apk files, the signature column would be NULL always,
and in SQLite NULL = NULL is false, but NULL IS NULL is true.
See http://www.sqlite.org/lang_expr.html Operators
If an app is downloaded into our cache, but an app with the same hash is
already installed, don't bother notifying people about it.
Extracted the logic for finding the path to an apk on disk (given a
PackageInfo object) to also be used by AppUpdateStatusSerice.
Change logging so that if we discard a downloaded file we Log.i instead
of Utils.debugLog. This is so that when debugging a release build we can
see what is happening, because this specific problem was easier to debug
with release builds (rather than setting up a custom fdroid repo).
Before mult-signature support, the process of marking an app as
installed in the `InstalledAppProvider` didn't have any side effects
beyond its own table. Now, it is also responsible for calculating the
`suggestedVersionCode` of the associated app as well.
This means old tests around suggested versions no longer work. This is
because they would insert an App, and set the
`Cols.SUGGESTED_VERSION_CODE` using a `ContentValues`. This was then
overwritten by the `InstalledAppProvider` asking for the real
calculation for suggested versions. That is - it would check for
relevant apks and figure out which was best.
To make the old tests correct, they need:
* To be able to "install" apps with the correct signature.
* To insert the relevant apks into the database, not just depend on the
presence of an `app`.
Previously, it was only done on repo update.
Now it is done whenever an app is installed or unisntalled. The query to
update the suggested version for each app is quite slow when run at the
end of a repo update. However in this change, we are limiting the query
to only update a single app, which means that performance should not be
a problem.
When a single repo provides apks with multiple signatures, then we need
to be able to select the preferred one. This adds tests for this which
fail, because that feature has not yet been implemented.
This is a conservative fix. If we wanted to really highlight the
feature graphic functionality and reward upstream devs for keeping
metadata up to date, then we could also take apps which were recently
updated, and prioritise them over new apps if they have a feature
graphic.
Fixes#938.
Also fixed display of feature graphic in main screen by getting full
path to image, not the relative path (e.g. "en-US/featureGraphic.png").
This creates a hard dependence between `RepoUpdater` and
`UpdateService`. However this could be trivially extracted by moving the
helper methods from `UpdateService` to `RepoUpdater`, and making the
broadcasts more "repo updater" oriented. That would also require
changing the broadcasts which `UpdateService` listens for.
Reuses the "commiting" message to indicate how many apps have been
processed so far.
Refactors existing progress handling between `RepoUpdater` and
`UpdateService` to use `LocalBroadcastManager` in preference to
`ProgressListener`. Still needs to use `ProgressListener` to talk
between `RepoUpdater` and the `Downloader` +
`ProgressBufferedInputStream`.
The only change that is related to something more important than
notifications is the fact that now `IndexV1Updater` makes use of the
`indexUrl`. To do so, because it is final, the base class constructor
delegates to `getIndexUrl()` which is overriden by the v1 updater.
This is required because we want to differentiate between broadcasts
coming from different repo update processes.
Fixes#1054.
This was setup to work correctly, but for two problems:
* The `cursor.close()` in `CategoriesViewBinder` stops the cursor from
being requeried when required.
* The `AppProvider` was not notifying correctly after deleting apps
belonging to a repo.
Fixes#1028.
Moved methods around so the class is more coherent when reading from top
to bottom.
Added some comments.
Formatted lines to be under 120 chars. No longer suppress line length checkstyle messages.
This breaks out subclasses for each specific type of app list item,
allowing for code reuse, but also letting the specific business logic
belonging to each different app list item to be separate.
This is particularly helpful in the following situation:
* In the search results, it is great to be able to render "App
downloaded, ready to install" in the same manner as the update tab.
* In the installed app list, this is not desired. Indeed, the status
text which should be shown should reference the currently installed
version and whether the user has ignored any updates.
By separating the AppListItemController into subclasses, it reduced the
need to handle several different types of text view (e.g.
"installedStatus", "status", "ignoredStatus", "downloadReady"), and
replace them all with a "status" and "secondaryStatus" TextView. What is
displayed in status and secondaryStatus is up to the individual
subclasses of AppListItemController.
Previously, there were different pieces of business logic, invoked at
different times, which would touch subsets of the UI.
This change rips that out, and replaces it with a single place where the
UI is setup. This can always be called safely, and it will render the
correct data for the current state of the app (e.g. downloading, waiting
for install, etc).
The AppListItemState class is a dumb object which keeps track of what is
supposed to be displayed in the UI. The AppListItemController now
creates a different AppListItemState depending on what state the list
item is in. This AppListItemState is then used to bind the values of
each UI widget.
All of the binding code is now in the single `resetView()` method, but
all of the business logic for what the view should look like is
separated into different `getViewState*()` methods.
This separation should make it easier to make sense of the UI code, and
hopefully should be testable should somebody choose to write tests for
it in the future.
The docs say that initLoader tries to reuse existing cursors.
The error message was "IllegalStateException: attempt to re-open an
already-closed object: SQLiteQuery: ...".
There may be a bigger problem around suggested versions being null at
all, but that is getting looked at in a different feature set (i.e.
multi signature support) and will come in time. This fixes the immediate
problem some people were having and sending crash reports for in 0.104.
STACK_TRACE=java.lang.NullPointerException: Attempt to read from field 'java.lang.String org.fdroid.fdroid.data.Apk.versionName' on a null object reference
at org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter$HeaderViewHolder.bindModel(AppDetailsRecyclerViewAdapter.java:425)
at org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter.onBindViewHolder(AppDetailsRecyclerViewAdapter.java:244)
at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:6310)
at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:6343)
...
Introduced in 97fd3f0.
* F-Droid cannnot uninstall system apps, only their updates,
but even with the privileged extension, that can get complicated.
* Let's just not allow uninstalling system apps, the phone's settings
app can happily disable/re-enable system apps, and also uninstalls
their updates on disabling.
If the client fails due to some bug in handling index-v1.jar, then it will
be totally stuck, even if index.jar would have worked. This creates a new,
temporary "expert" preference to force the client only use the old XML
index file. Worst comes to worst, we can tell people to enable this to
upgrade.
Once everything proves stable, we can remove this.
This started with the work of @kingu, it cleans up some of the language,
including:
* upgrade --> update
* application --> app
* internet --> Internet
closes!508
... when PackageInstaller is the installer (privext).
* In the case where the Privileged Extension is installed,
but the installation happens through DefaultInstaller still
due to something like a permission mismatch,
that is set as the installer package name.
* We cannot install packages installed by that via the system methods,
so fallback to DefualtInstaller for uninstalling as well when the
app is installed by PackageInstaller
The fact that Cursors are used with the apk provider is more of an
implementation detail (to some extent). It is a crappy, leaky
implementation right now, but still an implementation detail.
This should probably be done on the database level, if purely for the
fact that we have a good set of unit tests for that. However it is still
quite clean to do so here.
This is really the intention of the method, given it used to accept
a version code and a package name. Now it optionally accepts a sig
also. If present, it will restrict the query to apks with that sig.
Also added to the multi-sig tests to ensure this method takes it into
consideration.
There is some magic conversions going on so that booleans get
converted into integers, but they are only on Android. Under
robolectric, it throws a class cast exception instead.
Some were removed and left removed if they were run during tests,
because the tests are supposed to be automated and the noise they added
would not have helped diagnose a failure.
Also removed the dead code around "uses-feature" which will never
get implemented, especially as it is in the XML index.
The main problem is that we were using an index on fdroid_apk.vercode,
when it should have been using an index on fdroid_apk.appId. There are
thousands of apks which would match based on vercode, but only two or
three which match based on appId. This improves performance of the
calculate-suggested-vercode query from 25,000ms to 100ms.
Produces the following output:
D Explain:
D SCAN TABLE fdroid_app
D EXECUTE CORRELATED SCALAR SUBQUERY 0
D SEARCH TABLE fdroid_apk USING INDEX apk_vercode
D EXECUTE CORRELATED SCALAR SUBQUERY 1
D SEARCH TABLE fdroid_app AS innerAppName USING INTEGER PRIMARY KEY (rowid=?)
D EXECUTE CORRELATED SCALAR SUBQUERY 2
D SEARCH TABLE fdroid_package AS pkg USING INTEGER PRIMARY KEY (rowid=?)
D SEARCH TABLE fdroid_installedApp AS installed USING INDEX sqlite_autoindex_fdroid_installedApp_1 (appId=?)
There are two possibilities here, one is the number of correlated sub
queries (three seems a bit excessive). Alterantively, it could be the
fact that one of the inner queries is using a string index (appId=?)
instead of an integer primary key.
The history of this is that #974 identified a problem, which was fixed
in !497. That MR added test coverage for the bug.
However, the fix for it actually added a huge performance hit, on the
order of 30 seconds or so when calculating the suggested version.
This fixes that performance problem by removing the need for a sub
query. The end goal is to take the following query:
```
UPDATE app
SET suggestedVersion = (
SELECT MAX(apk.version)
FROM apk
WHERE ...
)
```
and the `WHERE` clause needs to somehow join the outer `app` table with
the inner `apk` table, such that the repo in question does not matter.
It can't just join directly from `apk.appId -> app.rowid`, because the
`app` is specific to a given repository, but we want to select the
`MAX(apk.version)` from every related apk, regardless of repo.
This commit solves it by joining the inner `apk` table onto an
intermediate `app` table, which is used purely so that we can select
apks where their `packageId` is the same as the `packageId` of the app
being updated.
Carrie specified colours earlier, and they were added to the code.
However they were not being read correctly. This changes that so that
lowercase resource names (e.g. "category_games") are used instead.
It also adds the final category artwork, for "Games" which was
missed prior.
The rest still generate colours and patterns if they don't have a colour
or an image specified.
With a 1 second debounce, I was getting the view to refresh
several times in response to large apps being processed (e.g.
Firefox, OSMAnd, etc). This was on a (relatively) recent Moto X
2nd Gen, so it would be even more visible on an older device.
The side effect of updating frequently is that the main list
of apps flashes regularly in front of the user (see #986).
This "update the view" is only in response to a background
task that is expected to take several seconds (e.g. 30 seconds)
anyway, so waiting 3 seconds instead of 1 is not particularly
problematic.
If F-Droid was actually removed, then we wouldn't even
have an installed app cache (we aren't even on the device
any more). As such, ignore all requests to remove F-Droid
because it complicates the installed apk cache. Specifically,
there is a race condition between the "compare apk cache to
package manager" and the "package removed receiver", where
the later was overriding the former.
There is a persistent shared preference which dictates whether apps
have been successfully downloaded and are ready to install. When
the `InstallManagerService` used to receive an `ACTION_INSTALL_COMPLETE`
broadcast, it would update this preference to no longer be installing.
However, this never got received in the case of F-Droid updating itself.
In that case, we need to instead wait for the system to broadcast an
`Intent.ACTION_PACKAGE_ADDED` intent. This change waits until that
point before removing the preference.
Fixes#1027.
gitlab's diff views wrap badly when lines are longer than 118. Android
Studio places a grey line in the UI at 120.
@SuppressWarnings("LineLength") is added to a bunch of files to prevent
making this commit huge. People can remove that as they work on those
files, and fix the issues then.
I also ran Android Studio's default Ctrl-Alt-L code formatter, where it was
easy to do, and I was already in the file.
The response to receiving PendingDownload was always a more specific
case of the Downloading event. By removing it, the code which was listening
for Downloading events is capable of doing everything that the PendingDownloading
listeners were doing.
Same as how AppDetails2 was recently cleaned up to depend more on
AppUpdateStatusManager.
In addition, it also removes items from the "X apps can be updated"
lower part of the "Updates" view when they are present in the upper half
(i.e. the half showing feedback about the current download/install
progress).
No longer do we try to nicely maintain the state of the adapter in "Updates"
in order to notify the recycler view about changes to its underlying data.
Instead, we just rebuild the entire structure each time a new thing needs
to be shown/removed.
This means no more smooth scrolling to the relevant item after it is
changed, but it results in a far less buggy interface.
This means that we no longer need to receive an APK_URL and then
directly ask the status manager for the relevant status object.
This causes problems when consecutive updates happen in the same event
loop, e.g. download started + download complete. In this case, the
receiver will receive two events for the same app. When it asks for the
associated status object for the first (download started) event, it will
receive a status that says "download complete ready to install". This is
because the status object has already been updated by the second event.
Furthermore, the broadcast manager must receive a copy of the status
object, not the original object. This is because the broadcast manager
doesn't parcel the relevant extras until the end of the event loop. This
means that if the status is changed twice in one frame, then both
parcels will end up looking the same. By sending through a copy instead,
this ensures that any listener receives the statuses in the correct
order, rather than two parceled versions of the same status
notification.
Also, make sure to correctly update the app details view when te user
leaves then returns to the view. Prior to this, the user would need to
wait for a download event to be received. However even that was broken,
because the download listener was not being added correctly once the
user returned to the app details screen.
Even though the categoyr mage loader explicitly says not to cache
images on disk (because they are not coming from the network anyway),
UIL still uses the `FilenameGenerator` to come up with a disk cache name.
Because the file name generator takes the "path" of the URL being
downloaded, and the categories are loaded like "drawable://2134234",
there is no path. As such, the file name ends up being meaningless.
This results in the image loader testing for the existance of the file
on disk (even though we asked not to cache on disk), and then failing
with an IOException (that gets swallowed).
By providing a meaningful name from the file name generator, it now
works as expected.
Fixes#1039.
Lots of languages really need the <plurals> tags to make sense, so
this also makes lint exit with an error when it finds strings that
should be <plurals>
closes#883
Previously, it would either send "base.apk" (in earlier versions of
F-Droid when bluetoothing an apk from the /data/app/... dirs), or
"install-[random].apk" (if recent F-Droid when copying file to a safe
place to expose via a FileProvider.
This now writes the file to, e.g. "F-Droid-0.103.1.apk" before sending.
Note that this means files are more likely to be overridden when being
sent, if the same apk from two different repositories is either:
* Sent via bluetooth
* Prompted to install via the system package manager
However this should still never let malicious people write to that
folder.
This ensures that the `PendingIntent.FLAG_UPDATE_CURRENT` doesn't
continually override earlier intents with the last app to be notified.
This could probably equally be done by leaving the request code as 0 and
removing the FLAG_UPDATE_CURRENT out, however it seems much more
semantic to have a separate request code for each different pending
intent.
Fixes#1021.
The hack that goes through and checks whether a language is present in
the APK seems to cause random strings to switch to English when the
app is running. So this removes that hack, and instead switches the
Languages menu to a hard-coded list. Languages that are not present
or close to complete were removed from the old list.
closes#943closes#1010
Reuses the code that the installer uses, when it broadcasts to
the relevant installer that an Apk is available for install.
This used to do the following:
* Copy file to a private directory
* Make the file world readable (so that PM can access it)
* Send a file:// URI to the installer
The file:// URI is no longer supported for reasons explained in
the support lib FileProvider class. Now a content:// URI is required,
and that must explicitly grant permission to certain packages.
The existing code here used to grant permission to
org.fdroid.fdroid.privileged, and this code now also grants it to
com.android.bluetooth. I see no security threat with exposing these
files to both applications, because the .apk files only ever:
* Were downloaded from the public internet into a (potentially public)
cache dir.
* Were sourced from an `ApplicationInfo#publicSourceDir, in which
case any app can access that anyway.
Fises #837.
If the user can set the language using the Setting app, then there is
not reason to use the Languages hack. This then clears the preference
if it matches the language of the system-wide locale. This also
removes the current system-wide language from the Languages menu.
closes#943
Since we have all these lovely scripts for cleaning up the
translations, gitlab-ci is a handy way to enforce that they get used.
Since weblate merges happen via merge requests, this will work nicely
now. I can't think of any false positives that will arise, but we
shall find out!
XML namespaces are a massive pain to deal with in, and they are totally
unneeded in the translation files. xmlns:tools is only needed in the
source file to ignore some lint warnings.
All feature graphics are called `featureGraphic.png`, and so our cache
was presuming all feature graphics were the same image. By including
the full path from the server in the cached name, we don't overwrite
images any more.
Right now, org.fdroid.fdroid.privileged.ota and FFupdater do not
provide any icons and it seems that that triggers this crash:
ACRA caught a NullPointerException for org.fdroid.fdroid
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.lastIndexOf(int)' on a null object reference
at org.fdroid.fdroid.FDroidApp$5.generate(FDroidApp.java:282)
at com.nostra13.universalimageloader.cache.disc.impl.BaseDiskCache.getFile(BaseDiskCache.java:167)
at com.nostra13.universalimageloader.cache.disc.impl.BaseDiskCache.get(BaseDiskCache.java:98)
at com.nostra13.universalimageloader.cache.disc.impl.LimitedAgeDiskCache.get(LimitedAgeDiskCache.java:74)
at com.nostra13.universalimageloader.utils.DiskCacheUtils.findInCache(DiskCacheUtils.java:36)
at org.fdroid.fdroid.NotificationHelper.getLargeIconForEntry(NotificationHelper.java:506)
at org.fdroid.fdroid.NotificationHelper.createUpdateNotification(NotificationHelper.java:300)
at org.fdroid.fdroid.NotificationHelper.createNotification(NotificationHelper.java:191)
at org.fdroid.fdroid.NotificationHelper.access$400(NotificationHelper.java:37)
at org.fdroid.fdroid.NotificationHelper$1.onReceive(NotificationHelper.java:106)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
* Avoids crashes when trying to treat non apk files, such as
privileged extension ota update.zip as apks
* Doesn't do anything at all for now.
One issue with this is the app is always in the not installed state,
so what would be appropriate here would be to change the text of the
install button to download, and update that accrodingly.
However that is outside of the scope of this change.
TODO:
Add toast indicating the user that they need to install
manually, in case of OTA update.zip
This zeros out the etag in the fdroid_repo table and then asks
the update service to perform an update.
The end result is that changing the language will result in the metadata
being shown in that language.
Note: This doesn't immediately work due to pending changes around
Locale.setDefault() and the change in case from Summary to summary in
the server metadata.
This reverts to the previous behaviour before 8faf151.
Then, the InstalledAppProviderService would queue up a series of
changes, and only notify after 1 second of nothing being added to
the queue. This was good because CursorAdapters and LoaderManagers
would not continually requery the database several times a second (only
once at the end), but it meant there was a lag in the AppDetails screen
getting updated after installing/upgrading/deleting packages.
This restores that behaviour where general events (e.g. "some misc apps
in the database were changed") are "debounced" for 1 second. However it
also emits a more specific "package org.blah.com was changed" instantly.
In the long term, it would be good to remove any dependency on
ContentObservers and `notifyChange()` altogether, in preference of
either LocalBroadcastManager or RxJava. However this will depend on how
we go about changing the database layer in the future. The fact we now
depend on ContentProviders means that it would be a big change to move
away from LoaderManager + notifyChange().
Fixes#986.
connected24 tests are frequently failing saying that while an emulator
is running, its not compatible. This is bizarre. This just adds debug
output to help troubleshoot that.
I have found no explanation of what should work and why, but language
choosers seem to always include this line. This also seems to fix the
bug:
closes#943
This makes the license a link to the spdx.org page for the app's
license. I think this is an improvement over the way the license was
displayed before 0.103 since it provides a direct link to the actual
text of the license.
The license icon is a modified version of the public domain icon:
https://commons.wikimedia.org/wiki/File:Cc-sa_white.svgcloses#960
* Addition: Try to get apk details via InstalledAppProvider too.
* In certain cases, such as the "UnifiedNlp (no GAPPS)" app on a device
with actualy GAPPS / GMS installed, apk can be null which leads
to a crash
* Ask InstalledAppProvider for the app's details too, like it
was done in the old UX AppDetails.
* Also seen when uninstalling app with a signing key different,
fixes#985
In android-24 and newer, the user can specify multiple languages in a
priority list. Therefore, the locale chooser logic here does not need
to work so hard to find a language match. For example, if the user
wanted to see country-specific variants, they would add them to the
preference list.
With older versions of Android, the pref is only a single locale. So
chances are that someone who specified de_AT would rather see de or
de_DE than en_US. Same goes for es_AR, ar_EG, etc. This could annoy
Chinese speakers, since someone who sets zh_TW could potentially see
zh_CN, which are written pretty differently.
Android 24 and later provides tons of languages, and a way to rank
multiple languages instead of choosing one. The Languages pref is a
big hack and can be problematic, so its better to disable it when its
not needed. This will make it so it is no longer possible to set
F-Droid to a language that the system does not support.
#943
There was a bug where the repo with the highest priority would be
responsible for specifying the suggested version code. When doing so, it
would only select from the list of apks available in that repo. This
improves the calculation so that when any given repos app gets a
suggested version code assigned, it selects from _all_ available apks,
not just those from the repository in question.
Fixes#974.
This allows us to test "installing" Adaway, which has a native code
dependency that the default Robolectric setup doesn't support (defaults
to armeabi-v7a).
The version check guarded against downgrades, and would not notify the
user if it found a downgrade in the apk cache. However this was from
before we could ask `AppUpdateStatusManager#isPendingInstall(hash)`. Now
we don't need to care whether it is an upgrade or a downgrade, because
there is a more authoritative source as to whether this apk is
interesting to us or not.
There may be multiple apk files with the same hash. Although it is not a
security issue to install one or the other (they are exactly the same
binary), they may have different metadata to display in the client.
Thus, it may result in weirdness if one has a different
name/description/summary etc).
This change takes each of the matching Apk objects from the database,
then asks them where they expect to be downloaded. It matches this
against the File that we are looking at and only returns if they match.
According to the following this is not supported:
* http://stackoverflow.com/a/13471695/2391921
This uses the approach in that SO answer, by extracting the attribute to
instead point at a drawable, and have one drawable for each theme.
Fixes#979.
PMD does not like manually throwing NPEs, even if they have more verbose
information than the default NPE. As such, use an
`IllegalArgumentException` instead.
Instead of showing them below the icon, it now puts the icon + name +
author + last updated into a single layout which can grow if the app
name or author wraps to a second line. The buttons are now below this
additional layout.
Although I'm unsure of exactly why this is `null`, it seems sensible
that there is a possibility of null icons (e.g. for .zip files or other
media). As such, this just adds a guard condition to ensure that the
`iconUrl` is not null.
Fixes#981.
Received the following crash report, where the user said it crashed
while trying to install the priviledged extension:
```
java.lang.NullPointerException: Attempt to read from field 'android.content.pm.Signature[] android.content.pm.PackageInfo.signatures' on a null object reference
at org.fdroid.fdroid.installer.ApkSignatureVerifier.getApkSignature(ApkSignatureVerifier.java:70)
at org.fdroid.fdroid.installer.ApkSignatureVerifier.hasFDroidSignature(ApkSignatureVerifier.java:54)
at org.fdroid.fdroid.installer.ExtensionInstaller.installPackageInternal(ExtensionInstaller.java:53)
at org.fdroid.fdroid.installer.Installer.installPackage(Installer.java:265)
at org.fdroid.fdroid.installer.InstallerService.onHandleIntent(InstallerService.java:77)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:194)
at android.os.HandlerThread.run(HandlerThread.java:61)
```
Not sure how to address it yet, so adding more specific excetpion for
if it happens in the future.
This pulls all the categories out of the database at once for sorting,
rather than sorting in SQLite. This is to prevent having to store the
localized category names in the database (and hence having to update
them when the locale is changed).
Fixes#967.
Previously they were left to be sorted however SQLite parsed the query.
This turned out to result in them beign sorted by repos first, then
names. For example, all of the GP apps would be at the bottom of the
list.
Fixes#965.
Use SharedPreferences to keep track of whether we are in the middle of
an install for a particular apk or not. If sothen the presence of an
.apk file in the cache means we need to tell the user (in the updates
tab) that a file is ready to install.
Previously this only worked for apps which were being upgraded to the
latest available version. Now it works for apks being upgraded from an
old version to a newer-but-not-newest version. Perhaps more importantly,
it also now works for newly installed apps.
This has a problem where if a user installs, then uninstalls an app,
they are still notified about installing it. This is because the apk
is in the cache and the code doesn't know whether it is there from
the initial install, or because it has been redownloaded by the user
to install some time after uninstalling.
The only pending intents that were not explicit were the four from
the NotificationHelper class. These now explicitly specify the
NotificationBroadcastReceiver as their destination, which is not
exported. That then forwards the intents onto relevant methods of
AppUpdateStatusManager.
With this, it was leaving the app's locale set to the last language on
the LOCALES_TO_TEST list, which is 'zu' (Zulu). For some dialogs, it
was actually using that locale, so showing OK/Cancel in Zulu despite
the app being in a different language.
#943
We've had a number of crashes due to bad formats in various
translated strings. This test runs through all of the translated
strings and tests them with the same format values that the
source strings expect. This is to ensure that the formats in the
translations are correct in number and in type (e.g. {@code s} or
{@code s}. It reads the source formats and then builds {@code
formats} to represent the position and type of the formats. Then
it runs through all of the translations with formats of the
correct number and type.
I couldn't get the Resources stuff working in Robolectric, so I
made this an emulator test.
The change to the Swedish translation included in this commit are
fixes for issues that these tests found.
closes#923
Java's Map.get() returns null if there is no match, so this was always
setting each entry to whatever value was in the highest priority
locale, whether it had contents or what null. Now, this will fall
through the priority list of locales until it finds actually contents.
A TreeSet apparently does not really maintain insertion order, while a
LinkedHashSet does. This ensures that the insertion order of locales
is preserved in localesToUse so that the prioritization is correct.
Just let the SecurityException be throwing where it originated.
From PMD: "A catch statement that catches an exception only to wrap it
in a new instance of the same type of exception and throw it should be
avoided".
This cleans up a little from !482. Ctrl-Alt-L and Ctrl-Alt-O before
committing! :-)
* On API >= 24, in cases when the installer package name is not set
to privext, the system won't let us uninstall.
* Fallback to the DefaultInstaller so that uninstall still works.
* When there's a permission mismatch (#951, #890), the fallback
DefaultInstaller is invoked, which enforces file and content schemes
for API < 24 and >= 24 respectively.
* Use content URI in that case, which allows the fallback to work.
The script checks for `<string/>` elements which have misformated
arguments in them. It Now also checks for `<plural><item /></plural>`
elements too.
closes!472
<plurals> handles the grammar needed for numbers/quantities in various
languages. Like in a number of slavic languages, numbers ending in 0,
1 or others have separate grammar. In English, there is just 1 and
then all the rest (0 days, 2 days, 3 days, etc). <plurals> does not
handle multiple strings for different quantities, like having a
different string for each number case. For that, we have to do it in
Java and have multiple <strings>
!472
Prior to this, it would only update the "Uninstall"/"Run"/"Upgrade"
buttons after a fresh install, or an uninstall.
This change is a bit more liberal in how often we try to update the
view, due to a race condition with PackageManager and AppDetails2.
AppDetails2 listens for InstalledAppProviderService in onResume, but
sometimes that is too late (the notification has already fired).
fdroidserver currently only supports a single WhatsNew field that
comes from the CurrentVersionCode of the app. Google Play and
fastlane supply support a WhatsNew field per-release, but we don't use
that data anywhere, and implementing that in the data structures would
add a lot of complexity since Apk would then need to have its own
"localized" section like App does.
The "Video" field is just a URL pointing to a video.
closes#910
I added Esperanto because someone asked, and Shqip since it is not
available in Android 5.1 and people who speak Shqip would be likely to
open the language menu to switch away from English, then they'd see
Shqip as an option. This still won't take effect until those
languages are fully translated.
closes#941
The point here is to use the English work all lowercase, so make sure
that the English lowercasing rules are always active, regardless of
the system's locale.
The original single language description gets stuck straight into
App.description by Jackson. getLocalizedEntry() might return a null,
in which case it was overriding the original description. This only
overrides the original description if there is actually a localized
description.
* Replace hardcoded color values with references to style.xml,
which in turn has different values for light and dark theme.
* Force reload the activity to get the theme applied.
TODO:
* Swap uses it's own theme, need to figure out a way to
handle that. Currently the main Nearby screen which you get
to from the bottom navigation is ok, but anything after
that is light / custom themed.
Given the only feedback available to the user that they initiated a
download once clicking the version from the list is up top, this scrolls
the recycler view to the top to show that feedback.
Also shows the selectable background when they touch the version list
items.
Previously, it assumed that featureGraphic et al. were always present if
the localised entry was present. This is not the case, so we only return
a URL if we can actually find the entry we are looking for.
Prior to this it was black, which looked broken. This also ensures that
the blue is shown behind the dynamic colour when it is time to ease in
that colour.
This is hitting a lot of plurals, causing the CI builds to fail. But
unfortunately, its getting it wrong. Its actually for a format in a string
like "Updated today" in languages like pt that need special cases for 0/1
items. In this case, it makes no sense to say "Updated 0 days ago".
Although the adapter tries to keep in sync with the app status update
manager, there may be times when this is not successful. In such
circumstances, it seems safe to just guard against invalid situations,
rather than trying to assert an error or fall over.
Fixes#922.
There was some confusion in the user tests about how to launch an app
once it was installed. Hopefully this small change goes towards fixing
some of that confusion. Instead of just showing "X installed
successfully" in the app list, it now shows a "Run" button next to it.
When navigating to Updates -> Show Apps and then downloading an item, it
shows the download progress inline for that item. After this is
complete, it then shows a tick icon next to the item. The long term goal
should be to remove the list item from under "Show Apps" so that it is
only shown at the top of the "Updates" view. However this will require
more work. In the meantime, we can alleviate some confusion by replacing
the "Tick" icon with a button that says "Update" (like the other buttons
in the Update view).
When dismissing an "X installed successfully" intent, it should also
dismiss the relevant item from the "Updates" screen. This was not
happening. Upon investigation, I noticed that when I dismissed a
notification, it was passing through the Apk which I installed over a
day ago. This is because it was reusing a previous pending intent rather
than creating a new one.
Now that we've moved the first screen to "Latest", we always want to do
our best to show something there. This preference is pretty redundant in
light of this.
Previously, the definition of "New" was whether or not the added and
last updated dates were the same. This made sense, because we only
showed apps from the past few weeks (depending on preferences) as new.
Now that we show up to 200 apps in the first screen, regardless of age,
this check is no longer helpful.
It seems pointless to only restrict "Latest" to items within the last X
days. When you only have the GP repo enabled, or other repos with less
apps that are updated less frequently than the main repo, this screen
always ends up empty. This change shows the last 200 updated items
instead of those updated in the last X days.
The text is more comprehensive on the main screen than on categories,
because this is the view that all users will see when they first open
F-Droid.
Fixes#879.
Previously this was only shown in the notifications.
This does not show the full progress of the update, but at least it
provides a rudimentary level of feedback. In the future it can be
modified to show more substantial feedback if required.
This results in a slightly larger apk (e.g. 500KiB), but it reduces the
scope for bugs greatly. We still get all the benefits of only having to
maintain a single density-independent vector (rather than several
density dependent PNGs and all the work that involves).
The class of bugs that it solves is that there are several places where
vectors cannot be used, and you wont notice when developing on a device
newer than 5.0. For example, notification icons, `TextView`
and its `android:drawableStart` attribute.
Fixes#913.
It was assuming there is always a priv ext preference. However, we
remove the priv ext preference the first time the fragment is opened. In
these circumstances, the preference no longer exists, resulting in a
NPE.
Unused for now, but like with the `LoggingQuery`, it is helpful to
be used for debugging purposes. For example, used this to quickly
figure out that it took 7 seconds to fix the PRNG stuff in FDroidApp
onCreate().
The CardView extends FrameLayout. This layout has some problems with
margins: http://stackoverflow.com/questions/5401952/framelayout-margin-not-working.
These can be overcome in most situations by swithcing from a margin to
some padding on the child view. The reason it is okay to do this in
most cases is because the child view is usually a layout such as a
ConstraintLayout anyway. For such cases, the difference between margin
and padding is not much different, because there are usually not any
background colours or borders applied (where padding vs margin would
usually make a difference).
Previously it would show a grid patter for the first five items, and
then resort to list items. This continues the grid pattern indefinetly.
Fixes#866.
Left some more unused strings which are a bit more general purpose and
perhaps should wait until after a stable release to remove. The
rationale for this is that we may want to revert to part of the old
terminology in certain places, and don't want to have to ask everyone to
translate everything again.
These are loaded dynamically at runtime based on a sanitized version
of the category names. Thus, the static lint tool cannot pick up that
they are indeed used.
We no longer prefix the list of categories with "Whats New", "Recently
Updated", and "All". The new UI doesn't require this. The only place
they were being used now were in the tests.
This was implemented before because the main screen of the three tab
layout needed to update in response to the list of installed apps being
installed. When we scan the list of installed apps upon starting
F-Droid, we didn't want to have to requery for the list of installed
apps every time we found a new installed app. For this reason, we
"debounced" these requests (accumulated them for 1 second, and then let
go of a notification only after 1 second of inactivity).
This is no longer a feature, and so we can afford to fire the
notification instantly.
We were jumping the gun before, and asking to refresh the app details
adapter before the installed app service was able to notify us of a
change to the apps installed version.
This should be refactored to use broadcast receivers instead of
content observers (which are tied to the implementation of a content
provider). However this is currently a straight port from app details 1
to app details 2.
On newer devices, it takes the icon, removes colour, and uses that. This
looks weird because:
* The head and body look too close together once you remove the shading
from the launcher icon.
* The eyes dissapear because they are white (not transparent) in the
launcher icon.
Prior to this, it would still show the "Ready to update" list item.
Now it updatpes the description to say "successfully installed" and
removes the "Update" button.
If you open AppDetails, initiate a download + install, and then navigate
away, it still pops open the install dialog for you. This is because it
never deregisters the broadcast receiver.
This change maintains the behaviour of always having the broadcast
receiver. This is because it is only added when the download
completes, and would require further refactoring to change that.
Instead, we listen for the receiver, but we ask if the AppDetails view
for the apk in question is actually visible to the user. If not, we
don't try to initiate the package manager.
Previously it showed the number of things which were eligible for
update. This will become less and less important the more people we can
switch over to automatic updates. However, given the privext vs unknown
sources thing, we will always have a lot of users who need to be
notified that they need to take further action to complete an install.
Prior to this, they were in whatever order they came from the metadata.
This resulted in weidrness because the repo which was updated last will
have its apps shown last. We are trying to move away from the repo
update order being important.
In the future, this sort order should take into account better
heuristics, but for now this is at least deterministic.
The database still treats repos with a _low_ number as _low_ priority.
This means it sounds weird when you say "Repo with priority 1 is the
least important", but other than that, everything works as expected now.
Technically we could recreate the query to update the repo metadata
within DBHelper, but that is difficult because it is sort of build into
the content providers. Unfortunately, we are unable to access content
providers from the DBHelper.
In the future if we are able to migrate away from content providers to a
more dumb data access layer, then we could reuse the query to update the
metadata priorities in the DBHelper. However that is a tomorrow problem.
To limit the possible oddness of having the installer package being
something different than F-Droid, only set it to priv ext on android-24
and newer, since its required there.
There is still quite a bit to figure out in the data format of the
per-package "What's New" entries, and its breaking the tests, so move
the placeholder code to the one spot where the placeholder whatsNew
entry is used.
App.getAllScreenshots() works nicely here, but its probably a temporary
measure until we figure out how to handle the various kinds of
screenshots (TV, Wear, etc).
This is based on @pserwlyo's work. The App and Apk classes currently
need just the public instance variables auto-filled by Jackson, so
everything else is considered opt-in, via @JsonProperty declarations.
This is currently only used for setLocalized(), setUsesPermission(),
and setUsesPermissionSdk23().
# Conflicts:
# app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java
The parser should accept additional elements to each
uses-permission entry, in case more XML attributes is added to
<uses-permission>. <uses-permission> has had two attributes
since the beginning.
Other changes to this JSON data structure are bad index-v1
format, and will cause crashes:
* Removing an element e.g. null from a uses-permission entry
would be invalid index-v1 JSON, since that structure mirrors
the uses-permission AndroidManifest.xml element, which has a
long standing fixed definition of name/maxSdkVersion. That
should crash so that fdroidserver authors know they are
generating invalid index-v1.
* setting versionCode to anything but an int is invalid index-v1
JSON, and should crash. versionCode has been defined as an
32-bit signed integer value since the beginning of Android.
* <uses-permission android:name=""> has been defined as a string
since the beginning of Android.
https://developer.android.com/guide/topics/manifest/uses-permission-element.html
repoId is used in Repo, App, and Apk instances to point to the Repo data
in the database. It does not come from the index files, but rather the
client database.
An important security protection is erroring when the index-v1.jar is
older than what is currently in the database. If the current or older
jar is allowed to be parsed, then a malicious server or
Man-In-The-Middle could replay old version of the index-v1.jar to
prevent the clients from learning about updates that fix security issues
The image loading code for the app cards was presuming that the icon
returned did indeed exist. In this case, it crashed due to trying to
decode a `null` image.
I noticed that when returning to the settings fragment (e.g. by closing
then reopening F-Droid while viewing), it will attempt to re-remove the
priviledged preference. This causes a crash, so we check to see that we
still have the preference before deciding to remove it.
index-v1 does not send empty values. The description was historically
set to "No description available" on the server side, and in
index.xml. The database then inherited this behavior, and does not
support no description. In the long run, it would be good to sync up
the database with the index-v1 metadata, but perhasp then we'd have to
add a million null guards, which wouldn't be worth it.
Tell the Jackson JSON parser to ignore App/Apk fields that should never
come from the index, but instead are set locally to keep track of the
current state of things on the device.
There are two forms of tests to enforce that the proper things get
ignored. It is not possible to do this with decorators alone, so I
chose to use @JsonIgnore and leave the variables we want filled in
undecorated. Also, all of the instance variables in Apk/App/Repo
should come directly from the index metadata so that they are pure
data classes. Currently some state info is stored in them, those are
decorated with @JsonIgnore.
The tests then include lists of accepted and ignored properties, and
anything that is not in those lists will cause the tests to fail. So
if someone is adding a new instance variable, they will get a fail
until the tests are updated. One set of tests actually writes blank
instances out as JSON since that's the easiest test to write, and
Jackson treats @JsonIgnore the same in both directions. Then there is
another test that reads a JSON file with added, unsupported values to
make sure that they are properly ignored.
Having Jackson set to ignore unknown fields in the incoming JSON data,
instead of throwing an Exception, means that we can add any fields to the
JSON without having to rev the index version, and older clients will still
parse it fine. This is basically the same as in index.xml.
This sets the App instance variables using the localized index-v1 fields.
It trys to fill as many fields as possible, falling back to locales of the
same language, then finally English.
This is based on the Jackson JSON parser's ability to map a JSON key to a
method, e.g. @JsonProperty("localized")
This adds support for parsing the new index-v1.json data as defined in
fdroidserver!221. This new index metadata format is required to
support localization, graphics, screenshots, etc.
refs #15
This adds support for the index fields: uses-permission and
uses-permission-sdk-23. For most index fields, Jackson handles directly
mapping the incoming data to the instance vars based on the matching
field/var names. For uses-permission*, methods are declared for
handling those properties in the incoming index.
These fields will be ignored when using the v0 index.xml format.
* Move the privileged extension installed check above
the check whether the apk to be installed is privext.
* This lets privext updates work when it is already installed.
* The new PackageInstaller APIs, being used by the privext on Android
7.0 and above aren't happy with uninstall being done by an app
other than the original installer.
* Set it to the privileged extension if that is enabled and being used,
to make uninstalling work
This notification is kind of weird, because the only ways it can be
dismissed is by:
* Swiping a notification away from the notification drawer.
* Closing and reopening F-Droid.
However I think the UX is still pretty nice:
* Tells the user that it worked.
* Allows them to navigate to it if desired.
In a future MR I will remove this class completely, but this just
ensures that touching a notification will not send the user to the old
AppDetails (instead sending them to AppDetails2).
Default padding specified is 12dp which is not very generous.
This reduces it to 2dp for seemingly good effect. Yet to see what it
looks like with longer translations of other languages, but time will
tell.
Move logic which parses intent and forceably sets the text of our search
input to onCreate(), not onResume(). onCreate() is invoked each time a
new intent is sent to open up this activity. That is, each time a new
category is opened or a new search request is received. onResume() is
called much more often than this, including when the user is directed to
a new activity and then returns to the search screen after hitting back.
In this case we don't want to remove the search query the user had and
replace it with the data in the original intent.
The previous language hinted at the fact we auto download and install
updates like Google Play. This is not the case (unless you have
priv-ext). To clarify, now we "Automatically _fetch_ updates", ready to
install when the user initiates the install.
Fixes#839.
Locale.getDefault() returns the default for the current JVM (or whatever
runtime Android calls it these days). By asking the configuration, we
will get the Locale that the user has selected from within the F-Droid
preferences.
Replicant is committed to follow the GNU Free System Distribution
Guidelines (FSDG)[1]. Apps with certain anti-feature flags in F-Droid
violate these guidelines and thus shouldn't be available in the
F-Droid client on Replicant[2].
Issue #564 discusses this, although only the case of having apps with
anti-features optionally filtered. To be compliant with the FSDG
guidelines, all violating apps must not be accessible and there
shouldn't be a setting to make them visible. Not all anti-features in
F-Droid violate the FSDG guidelines, so no need to filter all of them.
Signed-off-by: Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de>
[1] https://www.gnu.org/distros/free-system-distribution-guidelines.html
[2] https://redmine.replicant.us/issues/1629
The default behaviour of gradle when encountering a transitive
dependency which is the same as an explicit dependency, but where the
transitive dependency has a higher version, is to bump the depdendency
which was explicitly added. This meant that the addition of the
bottom navigation library implicitly bumped our support lib to 25.3.0
due to its dependence on it.
The options are:
* Change the 3rd party lib to support 25.2.0 instead of 25.3.0.
* Explicitly exclude the transitive support lib dependency in our build
script (what we have done in the past, e.g. with acra).
* Bump our explicit dependency.
Given the nature of the changes from 25.2.0 and 25.3.0, it seemed like
it was simplest to bump our dep. However, there is a bug
https://code.google.com/p/android/issues/detail?id=251302 which causes
a function we depend on in SwitchCompat to require API 14. Therefore,
this change excludes the 25.3.0 transitive dependencies, allowing our
25.2.0 dep to get used.
In the process, I've noted that there were a few places we opted for
excluding the transitive dependency in the past. These have now been
removed because we have a higher version than they do, and thus they
will no longer drag our old version forward.
The previous dependency extended the support library bottom nav. It did
this at the expense of lots of reflection. This is pretty brittle and
likely to break in future releases as the support lib gets updated. In
the mean time we need to have a fully working bottom nav, so this commit
includes a different dependency.
Most 3rd party bottom navigation panes look great. All that I found
require at least API 11, mostly API 14 (due to animations I suspect).
I've forked one of the most popular ones on GitHub and made it support
back down to API 10. My fork is added as a dependency until upstream
accepts the PR.
If they don't ever, then we can reconsider what our options are.
The two times I've swapped the bottom nav implementation have both been
trivial. The code generally only touches the MainActivity and its
layout.
Shows a red badge over the "Updates" menu item.
The updates badge is a bit hacky. There are indeed libraries which
implement a bottom nav which have support for badges built into
them. However they target API 14. There are also other badge
libraries which just deal with rendering, but for the cost of
another dependency, it is not particularly difficult to create a
`TextView` with a background and position it ourselves.
Right now, it will allow us to set the selected item more nicely.
In the near future, we will use this dependency to control the visual
display of the bottom nav better (e.g. icons/text appearance/animation).
Note that there is a bit of development going on at the Android team,
and the official bottom navigation view may oneday support all of these
features. In that case, we should probably switch back.
* Show selectable background behind "Show/Hide apps" button.
* Scroll to the relevant place in the list when showing/hiding apps.
* Only show one line (ellipsized) of the apps to update in header.
This will read downloaded .apk files from the disk cache in the background.
For each apk that corresponds to an app which can be updated, the status
manager is notified.
Even though it doesn't matter here because it is going from one side of
the parent all the way to the other. I hope it makes it easier if we
completely avoid right/left and only ever use start/end. Then searching
for RTL problems will be easier.
Alows for more flexibility in what we are able to display, including:
* Prompting users to donate to frequently updated apps
* Showing messages from package maintainers to users
* Marking apps for later installation when offline
Most of these are not yet implemented, but will be able to when
required, whereas they were not able to in the previous UI.
There were a few different options around, but some of the best ones
which provided the most flexibility when adding diverse/complex
viewTypes to a recycler view target a minsdk above 10.
The "adapterdelegates" library still offers a big improvement on vanila
adapters, especially for the Updates view.
This is in response to identifying a bug with the way priorities work
with categories. Two repos may both specify different categories for
the same package. In this case, F-Droid should only select the
categories from the highest priority repo. Well, it is not to say that
this is the most preferable option, but it is consistent with other ways
that repo priorities are used.
Doing this required tweaking our `IconDownloader` which we give to the
UIL init method in FDroidApp. It only knew how to load from HTTP, but we
needed it to fetch `drawable://` images too (which the library
supports). In addition, it has been renamed `ImageDownloader` as it also
is now used for screenshots/feature images.
This is currently needed for screenshot placeholders, but might be
useful in the future as well. Note that the default BaseImageDownloaded
supports this, as well as content:// and drawable:// protocols.
This was not updating the versions expand icon correctly when the user
clicked on it. It was working when they scrolled away and returned to
the versions list. Now it works for both.
Although these didn't used to fail at all, they now _always_ fail due to
the Docker setup we run on GitLab. Until we get that fixed, the CI is
not very helpful if it fails every time, and we merge anyway. This
allows us to at least correctly use the "Merge when succeeds" behaviour.
Fixes#882.
Couldn't figure out the exact cause of going from Inkscape .svg to
Android VectorDrawable, so redrew it in inkscape and this time it works.
*shrug*.
This is as per the mockup in issue #840, and does the following:
* Adds a new `PreferencesCategory` of "My Apps" at the top of the
preferences screen.
* Adds a "Manage Installed Apps" preference, and moves the
"Repositories" preference into this category.
* Repeals an existing change which prevented "updateable" apps from
appearing in the list of "installed" apps. This is because the two
lists of apps are no longer displayed alongside eachother.
* Enhances the `AppListItemController` to also be able to display
whether or not the currently installed version is the recommended
version or not.
* Also adds option to display whether the user has asked to ignore any
updates for any specific apps.
Moved intent-filter from FDroid to MainActivity. Can test from the
command line with:
```
adb shell am start -a android.nfc.action.NDEF_DISCOVERED \
-d fdroidrepo://10.0.1.10:8888/fdroid/repo
```
Show an install button after the download is complete so the user can
click it to initiate an install. This is in preference to popping up
install manager activities on their behalf, because they may have queued
up several apps to update.
Things that are still not right:
* BottomNavigationView doesn't read out the title of items when selected.
Created this issue: https://code.google.com/p/android/issues/detail?id=230595&thanks=230595&ts=1482125499
* TTS reader combines the app name and summary without a pause which is jarring.
* Touching the background of the recycler view in the categories view reads all category names.
* Likely other problems too.
No longer bind the views as they become visible. This resulted in
the same view being bound multiple times, but that was unneccesary.
Given that there is only five types of view, and each view type only
ever gets used once, the binding can happen when the view holder is
created, rather than each time it is shown. This fixed a few bugs to
do with views being inflated multiple times.
Shows progress and download complete now, but left a few TODOs
lying around. These are mainly around the ability to then deal with
installing an app once download is complete.
The styles used by the app details showed good padding on either
side of the buttons text. This was because they had a certain amount
of screen space to fill up which resulted in nice empty space on either
side of the text. Other buttons do not have this type of layout, so
need to have a minimum amount of padding thrust upon them.
Required breaking out into values-v17 too, so refactored common styles
into base style to make this easier.
As per the main screens feature image behind the "Recently added"
items, also use the same abstract artwork as a placeholder for the
feature image in app details.
Draws two rows of triangles, each coloured randomly according to
the dominant colour in the apps icon.
Given that the colour is probably assigned to the FeatureImage in response to
a network request finally downloading an image, there is a period of no feature
image. After the colour is provided, then if it is set instantly it tends to
look jerky. This eases in the colouring of the feature image.
Happy to discuss whether this is a good idea or not, but right now
there is no way to update repositories so often you are left with
an empty first screen.
This doesn't worry about state management (e.g. remembering
whether we are refreshing or not and showing this when we resume the
activity). Instead, it listens for the refresh listener, and when
triggered it will set the refreshing state to not refreshing. For now
the notification can act as the feedback that something is happening.
This is a little bit flakey at this point, because the weird asynchronous nature of
adding fragments. If swiping to the second-to-last entry on the bottom navigation view,
it will populate the settings fragment in the UI and then it will dissapear. Need to
fix this.
Show a "Chip" in the search box whcih indicates the user is viewing
a particular category. This chip:
* Gets remtoved when the user presses backspace from in front of it.
* Can be re-added by typing the name of a category and then a colon.
* Follows the material design guidelines.
* Has an accessibility hint that tells screen readers it is a category name.
This is different to the old categories drop down, because that also
included meta-categories of "Whats New" and "Recently Updated". Given
we now show them on the first page, this categories screen can do away
with them.
Each category entry loads a few apps to show to the user.
Note: The "View all" button next to each category doesn't currently
go anywhere. It will soon be hooked up to an app list that is filtered
to the selected category.
Not fully featured yet, because it doesn't listen for broadcasts
from the installers, but it is shows the correct list of apps and
allows users to queue up downloads of all updateable apps.
Smooshes the recently updated and recently added lists into one,
and adds a status line under each app saying which of the two it
is (i.e. "Recnelty Updated" or "Whats New".
It doesn't load up the entire swap activity at this point. Instead it
is an entry point to direct the user to that activity.
Also added stubs for the remaining screens which need to be implemented
to the MainAdapter and MainController.
The fragment was quite straightforward to roll into the activity. Most
of the code moved across almost exactly as is.
Also added a theme for the toolbar so that in the future it will be
easier to support dark/night themes as well.
The following official Android support libraries were added:
* recyclerview-v7
* cardview-v7
* design
* support-vector-drawable
* constraint-layout
* palette
Also, add code to AppDetails2 to match AppDetails, keeping track of
currently viewed app. Moved the nulling of this info to onStop instead
of onPause, since alerts may be shown on top of the details page, while
still visible.
The `setApkInternal` method had to infer the intent of the caller
based on the arguments which were passed on, and then do specific
things depending on the input. Instead, this change has three
distinct actions which can happen (add/remove/update). Each of
these methods does only one thing, and doesn't have to guess
the intent of the caller. The only exception may be "add", which
will (for convenience) delegate to "update" if it already knows
about the apk in question.
The only time `status == null` was when coming from `removeApk()`. By
moving the logic out of `setApkInternal()` into `removeApk()` it makes
it easier to reason about `setApkInternal()` as it now does less. Also,
it was doubling up on the `syncrhonized (appMapping)` and `if (entry !=
null)` logic which is no longer required, because `removeApk()` was
already doing that.
While here, also make explicit the fact that `status` can no longer be
`null`.
Because of the way that this can be misused without the compiler knowing
(e.g. by forgetting to call `endBatchUpdates()`) it may be safer to move
it to an internal implementation detail of the class.
It could probably be done away with completely if the `notify*` methods
were moved out of the respective `*ApkInternal()` methods, but that
requires more significant refactoring to get right without code
duplication.
It seems that `setContentIntent()` will do fine if we pass it a null
argument. The default value is `null` anyway, and it doesn't mandate
a non-null argument.
Given that none of the callers need the functionality of the builder,
lets make it explicit that we don't expect the builder to be further
customized once it is returned. Instead, return a notification to hint
that no further customization is required/desired.
Given that some places where this method is invoked does a null check,
it seems reasonable to make this assumption explicit. That way if devs
use the method in the future, then they will be aware of the contract.
Neither pmd/checkstyle/many-devs are particularly precious about
hard rules of when to wrap a line. In this case we could've brought the
`ErrorDialogActivity` onto the same line as `putExtra`, but instead
opted to chain the method calls as per the `getAppDetailsIntent` method.
Whether a category is "available" or not is not a function of whether it
is in the category table or not. Rather, it is a function of whether there
are any active apps/apks which are in that category. Thus, don't notify
after inserting a category (the notification was wrong anyway as it was
trying to notify the AppProvider Uri instead of the ContentProvider one).
Instead, do it after a repo update is complete.
This adds support for the index fields: icon, mirrors, and antiFeatures.
icon and mirrors are for Repo, they've been around a while on the server
side, but just never used on the client side.
For Apk, this adds a new per-APK antiFeatures field so that each APK can
be individually marked. For example, when tracking is added or removed,
vulnerabilities are discovered and fixed, etc.
These fields will be ignored when using the v0 index.xml format, they
will be used by the upcoming index-v1 format: !422
In the v0 index format, empty descriptions were filled in with boilerplate
text. The v1 index format instead leaves empty descriptions empty, and
lets the various consumers (fdroidclient, web interfaces, etc) decide what
to show. The database and code still assume that the description will not
be null, so instead this ensures there is something in the database, but it
will be an empty string instead of a null. In the future, it would
probably make sense to standardize empty values on null or something.
This makes the name of the instance variables in the App class match the
names of the metadata fields in the new fdroidserver .yml YAML format. This
means that the Jackson parser can automatically instantiate instances for
us, which will be more efficient and maintainable.
These names aren't great, but it would a ton of work to rename the field
names in all of the metadata files, the docs, fdroidserver code, etc.
RequiresRoot is obsolete as a metadata field since there is the SUPER_USER
permission. This `requirements` array was set up to handle other things,
but that was never implemented.
APKs installed in /system} will often have zeroed out timestamps, like
2008-01-01 (ziptime) or 2009-01-01. So instead anything older than 2010
every time since we have no way to know whether an APK wasn't changed as
part of an OTA update. An OTA update could change the APK without changing
the versionCode or lastUpdateTime.
closes#819
gitlab-ci used to run all of our jobs in parallel, now it mostly seems
to run them sequentially. So splitting up the various parts of the CI
suite into separate jobs mostly slows things down. This combines the
static tests into one job (lint, pmd, checkstyle, tools) with the JVM
tests aka Robolectric. That makes three jobs from the previous six.
[ant:checkstyle] [ERROR]
/export/share/code/fdroid/client/app/src/main/java/com/geecko/QuickLyric/view/AppCompatListPreference.java:35:29:
Name 'mDialog' must match pattern '^[a-z][a-z0-9][a-zA-Z0-9]*$'. [MemberName]
Introduced in 79ecffc91c8856e7ceb6a65441d19a6272195426
Also ensure it shows this animation correctly when expanding "Versions".
This is done by changing from `notifyDataSetChanged()` to the more
specific `notifyItemRange(Inserted|Removed)`, which ensures the
"Versions" item doesn't get rebuilt midway through an animation.
Fixes#817.
As discussed in #817, this preference is not useful on Android >= 5.0.
As such, the preference should just be removed. However, it should stay
if the privileged installer is already installed (e.g. via update.zip or
included as part of the ROM).
Translators:
Ahmad Zafrullah Indonesian
Allan Nordhøy Norwegian Bokmål
Balázs Úr Hungarian
Hasan İlingi Kurdish
Jonatan Nyberg Swedish
Juraj Harasta Czech
Michalis Greek
Michalis Spanish
Nick Bishop Greek
Verdulo Esperanto
yiannakis Greek
Fixes Issue #750.
This new class makes sure to use the correct `AlertDialog.Builder` from
the support lib. This in turn ensures the correct styles get applied to
the result alert dialog.
Doesn't change anything, just removes a deprecation warning.
AppCompatActivity currently extends ActionBarActivity and doesn't
provide any further imnplementation.
Translators:
Adrià García-Alzórriz Catalan
Ajeje Brazorf Sardinian
Alaa Issa Arabic
Alberto Moshpirit Spanish
Alexander Georgievskiy Russian
András Lengyel-Nagy Hungarian
André Marcelo Alvarenga Portuguese (Brazil)
Anteri Finnish
Athmane MOKRAOUI Arabic
Claus Rüdinger German
Cyxae Dexyc French
dark159123 Danish
Dmitriy Bogdanov Russian
Enol P Asturian
ezjerry liao Chinese (Traditional)
Gregor Santner German
Hsiu-Ming Chang Chinese (Traditional)
Jean-Baptiste French
John Doe Turkish
Jonatan Nyberg Swedish
Kheireddine Mkh Arabic
Lari Oesch Finnish
Luca Bianchi Italian
Marcelo Santana Portuguese (Brazil)
Marian Hanzel Slovak
Michael German
Mladen Pejaković Serbian
msrn Finnish
naofum Japanese
Olexandr Nesterenko Ukrainian
Osoitz Basque
Raphaël Barman French
Sveinn í Felli Icelandic
Sylvia van Os Dutch
Tobias Bannert German
Verdulo Esperanto
Verdulo Polish
Yaron Shahrabani Hebrew
zmni Indonesian
Also tried a more specific naming scheme for dimens to make it clearer where
they are to be used. This increased the padding on the left/right of the collapsable
lists. It also decreased the padding above/below.
Still needs some better assets for the actual donate buttons, but now it includes
the relevant text about donating to developers. It also puts the donation options
in a grid layout and lets them flow across so that if there is more than three, they
will end up on the second line.
Correct meeting time
With the discussion on IRC [starting here](https://botbot.me/freenode/fdroid-dev/2016-11-14/?msg=76442333&page=1), the time in `README.md` is wrong.
> We are on `#fdroid` and `#fdroid-dev` on Freenode. We hold weekly dev meetings on `#fdroid-dev` on Tuesdays at 20h UTC, which usually last half an hour.
[It should be](https://botbot.me/freenode/fdroid-dev/2016-11-15/?msg=76516527&page=1) be:
> We are on `#fdroid` and `#fdroid-dev` on Freenode. We hold weekly dev meetings on `#fdroid-dev` on Thursdays at 8:30h UTC, which usually last half an hour.
See merge request !424
Required changing some local variable names to prevent the same
`view` variable being declared multiple times. Otherwise it should
be a verbatim change from if statements to switch statements.
Required a couple of undesirable changes, such as:
* sCollated -> collated
* mFDroidApp -> fdroidApp (note the lower case 'd')
Otherwise it was relatively minor given how many member variables there are
in the code base.
Allows the two menu items "Ignore All Updates" and "Ignore This Update"
to be checked and save the relevant preferences to the database in response.
The old code waited until the activity was paused before saving the
preferences to the database. This code does not, and as such incurs
a database write on the main UI thread as soon as the user checks the
menu items. However that database code has recently been refactored so
it should be much more performant. If it turns out to still be problematic
then we can revert to the old behaviour of hodling onto any state changes until
onPause then persisting to the database.
Allows the type checking to be done by the compiler rather than the developer.
It was possible here because there is only two types of view, and the first type
will only have one or zero entries in the adapter. Thus, I've swapped the usage
of a `String` type for a `null` and checked for null instead of `instanceof String`.
Changed the helpful comments to a Javadoc comment, as tooling such as editors
will be more likely to make use of it like that.
Renamed to emphasise that only trailing new lines are stripped.
Added a basic test for this function to ensure it only strips trailing,
and also that it does actually strip trailing slashes.
Although the `textView` in `DonateViewHolder is currently not used, it was
pointing to an id which was not in the layout. This has been fixed in case
future devs choose to use this text view. Alternatively, we could remove it
completely if we don't think it is going to be used in this upcoming UI work.
Similar to the litecoin/bitcoin/flattr stuff, we need to check that a
proper URI can be handled via an intent. This previously just checked
whether the email address could be handled without the mailto: prefix.
Doesn't do anything except create an app with no versions,
no donate links, anything like that, and ensure that the adapter
is able to create the view holders for each resulting item.
In the future we can beef this up to check more exotic conditions,
such as calling `updateItems(App)` with different apps, each
with different combinations of versions, donation links, permissions,
etc.
This extracts the functionality from the old AppDetails which prefixes
donation links with the relevant scheme (bitcoin: or litecoin:) or URL
(https://flattr.com/thing/) into the App class.
The adapter has a hard coded assumption that mApp is never null.
This documents it as such by making the member variable @NonNull.
This is not perfect, because the consumer of this class doesn't quite
seem to check this constraing properly, however at least within the
class it adds some explicit documentation that is understood by editors
and lint that this is a non-nullable field.
Each call site of the `getHeaderView()` method needed to do a null
check and then it would call `setProgress()`. This has been replaced
with two methods `setProgress()` and `clearProgress()` to make it a
bit less repetative and harder to accidentally get a NPE in the future
by invoking `getHeaderView()` incorrectly.
Translators:
Ajeje Brazorf Sardinian
Alberto Moshpirit Spanish
Andreas Nordal Norwegian Bokmål
Clara Chido Shona
Enol P Asturian
E T Turkish
ezjerry liao Chinese (Traditional)
Licaon Kter Romanian
naofum Japanese
Nebojsa Tausanov Macedonian
Nutchanon Wetchasit Thai
Osoitz Basque
Sylvia van Os Dutch
Tawanda Mugari Shona
Verdulo Esperanto
Verdulo Polish
Yaron Shahrabani Hebrew
YFdyh000 Chinese (Simplified)
zmni Indonesian
Ensure categories are not shown unless there are apps in them
**Note: To be hot fixed into 0.102.1 also when merged.**
Fixes#806. Also adds tests to hopefully prevent this from regressing in the future.
Ensure app-category join table is cleared out properly upon disabling repo.
There are certain things we can leave in the database even when they are not being used. The criteria for this is:
* Could it be used again in the future?
* Can it be excluded from queries easily while it is unused?
Examples are entries in the package table, and entries in the category table.
This fixes a problem where entries in the category-app join table stayed in the database, causing categories to be considered as "in use" when really there were no apps in those categories. These rows need to be removed, because when new apps are added again in the future, they will have different primary keys. These different primary keys mean that the rows in the category-app table will never be useful again, and thus should be removed.
See merge request !418
Unfortunately, something in the way that the docker image is created is
making it impossible to run `android update sdk`. Even though it runs as
root, it cannot upgrade things, and in the process, it seems to break the
Android SDK bits that are there.
Installing Android Support Repository, revision 40
Failed to rename directory /android-sdk/extras/android/m2repository to /android-sdk/temp/ExtraPackage.old01.
Failed to create directory /android-sdk/extras/android/m2repository
Done. Nothing was installed.
A problem occurred configuring project ':app'.
> Failed to notify project evaluation listener.
> You have not accepted the license agreements of the following SDK components:
[Android Support Repository].
Before building your project, you need to accept the license agreements and complete the installation of the missing components using the Android Studio SDK Manager.
Alternatively, to learn how to transfer the license agreements from one workstation to another, go to http://d.android.com/r/studio-ui/export-licenses.html
> Could not resolve all dependencies for configuration ':app:compile'.
> Could not find com.android.support:support-v4:24.2.1.
Required by:
project :app
> Could not find com.android.support:appcompat-v7:24.2.1.
Required by:
project :app
> Could not find com.android.support:support-annotations:24.2.1.
Required by:
project :app
There are certain things we can leave in the database even when they
are not being used. The criteria for this is:
* Could it be used again in the future?
* Can it be excluded from queries easily while it is unused?
Examples are entries in the package table, and entries in the category table.
This fixes a problem where entries in the category-app join table stayed in
the database, causing categories to be considered as "in use" when really there
were no apps in those categories. These rows need to be removed, because when
new apps are added again in the future, they will have different primary keys.
These different primary keys mean that the rows in the category-app table will
never be useful again, and thus should be removed.
Fixes#806.
Fixed long version overriding app name (issue #322)
Also, fixed deprecated "singleLine" property to "maxLines="1"".
Also removed reduntant (legacy) padding declarations, for the files used exclusively by newer APIs which override those declarations with new ones.
See merge request !417
Rework net for 0.102
This is a collection of targeted fixes for the %"0.102" release. Most of them are quite narrowly targeted bug fixes. I couldn't avoid reworking the update scheduling in order to fix some of the listed bugs. This is the only part that seems possible to have regressions. In any case, if there are regressions, they will be in a very limited chunk of the code, in `UpdateService`, which we have no plans to touch in %"0.103 - UX Overhaul" so it'll be easy to do a 0.102.1 release.
See merge request !415
Also, fixed deprecated "singleLine" property to "maxLines="1"".
Also removed reduntant (legacy) padding declarations, for the files
used exclusively by newer APIs which override those declarations with new ones.
This introduces three network states:
1. completely disconnected
2. connected only via metered networks
3. connected via unlimited networks
This allows the update process to use bandwidth better, especially when the
user has enabled the "Only on WiFi" setting. It also helps prevent silly,
cryptic error messages in the update process is triggered when there isn't
internet available.
I tested this with:
* 4G only, but not set up for internet
* 4G only, with internet
* 4G + WiFi
* WiFi only airplane mode
* no internet at all, full airplane mode
closes#793closes#774
Its really easy to use USB Ethernet devices with ChromeOS and some Android
devices like Android TV. ChromeOS now supports Android apps. Since really
the goal is to avoid metered networks, and ethernet is very rarely metered,
this fits in with the user expectations around the preference. And if it
doesn't, there are very few people using Ethernet with F-Droid right now,
so whatever harm does happen will affect an extremely limited number of
people.
First, this is more honest than just using the default since it is saying
what the actual software is. Second, it protects identity, since the
default User Agent on Android can have a lot of info in it, for example:
"Dalvik/2.1.0 (Linux; U; Android 5.1; XT1039 Build/LPBS23.13-17.3-1)"
The real solution would involve figuring out where to handle this in the
right spot in the lifecycle. Since AppDetails is being totally replaced,
this is just to stop the crashing.
closes#802
InstalledAppProviderService tries to keep a running log of what is actually
installed on the device. It seems that ApplicationInfo.sourceDir and
related things sometimes returns a dir rather than an APK. So try to find
an APK in that folder.
closes#801
Store categories in separate category table
Currently, the category that an app is in is recorded in the database via the `fdroid_app.categories` column, containing a comma separated list of strings. This makes it hard to query. The existing code to get a list of categories was pretty bad as a result.
This moves to a different data model whereby categories are stored in a separate table. Each repo is free to specify that an app is in arbitray caregories (as with before). This is represented by a join table between categories and app metadata.
The end result is that categories are much more a first class citizen than before, and they will be able to be queried easier - which is important for the new UI.
Note that the categories table need never be emptied, it can keep being appended to. The reason is that if there are no apps in a particular category (represented by no corresponding rows in the join table) then the category will not be shown to the user.
See merge request !409
Improve performance when changing "Unstable Updates" preference
This is only a partial solution to #520.
It does as I suggested in the issue comments, by doing less work when the preference is checked. The proper solution is an `IntentService` which queues requests to update these details, but that is a (slightly) larger change for the future.
I also noticed it wasn't correctly notifying the UI of the change, so this now notifies the list of apps which can be updated. That was hard to test though, so not sure if it updates the UI correctly or not. It shouldn't do it _incorrectly_, but it may not work. The reason it should work is because the `AppListFragment` (baseclass of `CanUpdateAppsFragment`) uses a cursor loader with the `AppProvider.getCanUpdateUri()` URI. This `CursorLoader` should automatically attach an observer for that URI and requery if required.
See merge request !414
Fall back to InstalledAppProvider when trying to identify the apk to uninstall.
Extracted `getInstalledApk()` method so that it could be better documented, and
makes the `uninstallApk()` more consise. It will now throw an `IllegalStateException`
if no apk is found, because as issue #800 shows we will end up with a NPE otherwise.
Fixes issue #800.
See merge request !413
Extracted `getInstalledApk()` method so that it could be better documented, and
makes the `uninstallApk()` more consise. It will now throw an `IllegalStateException`
if no apk is found, because as issue #800 shows we will end up with a NPE otherwise.
Fixes issue #800.
During development of a new feature, I noticed a bug occuring only after using
the new feature for several days. This was because the metadata only infrequently
changes in ways which cause certain code paths to be hit. By having the f-droid.org
metadata from several days apart in the test suite, it allows for testing more
of these cases. In the future, even later versions of the metadata can be added
to ensure that we can update happily from old to new metadata.
This ensures that all of the relevant joins are in place, so that when
the updater asks to `queryPackageName()` then it can assume that we have
already joined onto the `Schema.PackageTable`.
Renamed the `Schema.AppMetadata.Categories.CATEGORIES` constant into the
`Schema.AppMetadata.ForWriting.Categories.CATEGORIES` constant. This is
to make it very clear that it is not for reading from the database.
When updating existing apps or inserting new apps, instead of splatting
a comma separated list into a single sqlite3 column, we now put it into
several rows in the `CatJoinTable`. This is done after deleting existing
categories, to make sure that if the app has been removed from a category,
then this is reflected during the update.
It was hidden some time ago, and nobody seems to miss it.
Also, we will be redoing this view soon anyway. In the meantime,
this category stuff is changing and this view should be removed.
Be a little more concise about when we need to run migration for v64.
Before it is not apparant that the migration introduced for v64 is associated with that particular version. This is because the guard condition used to bail out from the upgrade is more closely related to a previous migration.
This is due to a flaw with the desigh of `resetTransient()`, whereby it always resets the database to the schema _of the current F-Droid version being run_, not of the tables as they stood at the time of the particular migration being introduced.
This clarifies the guard condition for v64 by allowing it to query whether the schema has been created fresh or not during this particular invocation of `onUpgrade()`
**Note:** this is less to do with the v64 migration, but rather I came across the exact same issue while working on category table related changes. At that point I realised I made a slight mistake with the `resetTransient()` method.
**Also:** I'm happy to entertain other designs if anybody is that interested. One other approach is to change the guard condition to:
```
if (version >= 64 || fieldExists(db, ApkTable.NAME, "...")) {
... add field ....
}
```
However that suffers a little bit when the migration is a little more complex and checking if a field exists may not be enough.
See merge request !408
For some reason, the existing approach of "select * and then see if the
column of interest is in the results set" didn't work as expected under
tests. Perhaps SQLite is caching the list of columns for the purpose of
`select *` even after running an `alter table add column` query?
Either way, I couldn't figure out why it wasn't working as expected.
This left us with two options:
* Try to `select columnToCheck` and see if it throws an exception
* Query columns using `PRAGMA table_info.
The exception thrown when a column doesn't exist is not specific enough
for our code to check that this is the exact exception that occured. It
is not possible to say: `try { ... } catch (SQLiteColumnNotFound e) { ...}`
unfotunately. Also, there is a cost associated with unwinding the stack
to process an exception, which means exceptions probably shouldn't be
used in unexceptional circumstances such as this.
This change instead uses `PRAGMA table_info(tableOfInterest)` and then
iterates over the cursor looking for the relevant column. Even if the
performance is worse than the stack unwinding of an exception, it is
more concise and less hacky.
The fact there are arbitrary migrations at the top of the file (between
`onCreate()` and `onUpdate()` makes it harder to scan this file.
This changeset moves these methods verbatim, without changing any of
the method bodies or signatures.
Before it is not apparant that the migration introduced for v64 is
associated with that particular version. This is because the guard
condition used to bail out from the upgrade is more closely related
to a previous migration.
This is due to a flaw with the desigh of `resetTransient()`, whereby
it always resets the database to the schema _of the current F-Droid
version being run_, not of the tables as they stood at the time of
the particular migration being introduced.
This clarifies the guard condition for v64 by instead explicitly asking
if the columns of interest exist yet in this particular invocation of
`onUpgrade()`.
Fix npe verifying perms
Fixed a NPE for apps with no permissions.
Here is an example of the logging output for a couple of apps too after my change:
```
D/ApkVerifier(10929): Checking permissions
D/ApkVerifier(10929): Actual:
D/ApkVerifier(10929): None
D/ApkVerifier(10929): Expected:
D/ApkVerifier(10929): None
```
and
```
D/ApkVerifier(10929): Checking permissions
D/ApkVerifier(10929): Actual:
D/ApkVerifier(10929): android.permission.READ_EXTERNAL_STORAGE
D/ApkVerifier(10929): android.permission.WRITE_EXTERNAL_STORAGE
D/ApkVerifier(10929): android.permission.SET_WALLPAPER
D/ApkVerifier(10929): android.permission.SET_WALLPAPER_HINTS
D/ApkVerifier(10929): android.permission.WRITE_SETTINGS
D/ApkVerifier(10929): Expected:
D/ApkVerifier(10929): android.permission.SET_WALLPAPER
D/ApkVerifier(10929): android.permission.READ_EXTERNAL_STORAGE
D/ApkVerifier(10929): android.permission.SET_WALLPAPER_HINTS
D/ApkVerifier(10929): android.permission.WRITE_SETTINGS
D/ApkVerifier(10929): android.permission.WRITE_EXTERNAL_STORAGE
```
See merge request !407
This wouldn'tve actually found the problem in the previous commit,
due to the null happening before checking permissions while logging perms.
However, still seems like a nice test to have so that the method itself
handles nulls correctly.
Improved category tests
In preparation for implementing the [new category UI](https://gitlab.com/fdroid/fdroidclient/uploads/01d865e65604c41b0a472f0d39e7f1a7/Categories.png) I will be refactoring the database so that categories get their own table. In preparation for that, this MR improves the categories tests so that they also test the ability to query apps based on their categories.
See merge request !406
The previous category tests only checked that certain categories
would indeed find their way into the database if certain app metadata
is saved. It didn't check the other direction, using these categories
in queries.
Support extended <uses-permissions/> tags
`<uses-permission-sdk-23>` is a new tag for requesting permissions on _android-23_ and above. `<uses-permission/>` and `<uses-permission-sdk-23>` both can have optional _maxSdkVersion_ values, and these need to be fully supported in order for the Privileged Extension to be able to check the APK permissions against the index.xml permissions. This is needed so that it can prompt the user once, then download and install the APK after that. Its also needed for transparent background updates to check if the permissions have changed in the update.
This gets closer to the complete support, really the full permissions should be stored in the database. Then the only
conversion would happen ewhen parsing the XML. Right now, it still stores the old F-Droid permissions names, i.e. without
`android.permission.`. Then those are converted when they are loaded from the DB into an Apk instance.
See merge request !402
<uses-permissions/> tags can have min and max SDK to take effect. This is
not supported currently, and it necessary especially with the privileged
installer so it can properly represent the permissions that an APK is
requesting.
For example:
<uses-permission
android:name="android.permission.MANAGE_ACCOUNTS"
android:maxSdkVersion="22" />
<uses-permission-sdk-23
android:name="android.permission.CAMERA" />
<uses-permission-sdk-23
android:name="android.permission.CALL_PHONE"
android:maxSdkVersion="23" />
android.content.pm.PackageInfo is the Android class for representing data
about an APK/package. Since Apk.permission is the same thing, we should
use the same name.
Android won't protect us from other apps sending other Intents to these
receivers, so at least check that the action string matches what its
looking for. This is based on a lint recommendation.
Test all database migrations from db-version/42 onwards
While a bit arbitrary choosing 42, it is from more than two years ago. We can use this as our new baseline, in that this test will always check upgrades from 42 onwards, with each database bump.
Once the test was up and running, it immediately identified a bug in the database migration for v50 and v57. These have been fixed in this branch too.
Fixes#778.
See merge request !403
The migration resulted in a query being run which was broken. The query
was broken because it was dynamically generated by Java code. This Java
code resulted in a valid migration when until very recently when the
query was refactored to deal with a new DB structure. Now the query is
no longer suitable to be run against a DB_VERSION 49 database.
To resolve this, the migration now hard codes the query to a string
which is executable when the DB_VERSION is 49.
It was a little arbitrary to choose this date. However it was when the database
looked quite close to what it looks like now and it is from well over two years
ago. Going into the future, this test may as well always start out at 42 forever
more to ensure that database migrations from that point continue to work for
all future database migrations.
For upgrades from DB version earlier than 63, the whole table is recreated
by resetTransient() in migrateToPackageTable() so the upgrade method for
the OBB tables only needs to run when the database is at exactly version 63
This was mistakenly added to cd9582c9902dd4ac9218acfd69872f3eebcd3d93 when
it was rebased on !375.
support for APK Extension files aka "OBB"
OBB files are used by lots of apps like games and MAPS.ME to distribute large chunks of data. This adds basic support for distributing OBB files via F-Droid. The idea is that they are installed before the APK, so that once the APK is installed, the OBB files are already in place and ready to use. This also provides an F-Droid-specific Intent method for apps to fetch the OBB download URLs in case the app itself needs to handle the OBB download/update. That is similar to how it works in Google Play.
The fdroidserver changes are already merged: https://gitlab.com/fdroid/fdroidserver/merge_requests/143https://developer.android.com/google/play/expansion-files.html
See merge request !383
If a user clicks install, then uninstall on AppDetails, then there was not
yet a chance to refresh the App instance, and therefore app.installedApk
will still be null. This is really just a workaround for now, because
AppDetails needs a full refactoring.
This implements the APK Extension Files spec for finding, downloading, and
installing OBB files that are extension packs for APKs.
This needs WRITE_EXTERNAL_STORAGE since "installing" OBB files is just
copying them to a specific path on the external storage.
https://developer.android.com/google/play/expansion-files.html
This takes the APK file hash checker and turns it into a generic static
utility method for checking that a given file matches a given hash. This
will be needed as F-Droid handles other file types, like OBB and media.
By sending an Intent to F-Droid, it will reply with the full download URL
to the OBB file, if one exists for the currently installed version of the
requesting app.
This makes it easier to track the relationship between the index XML and
the database tables where that data is ultimately stored and used. There
are a few mismatches between the XML tag and database column names, so
those are just marked with a comment.
This makes it much easier to find all the spots in the code that need
changing when adding new columns/data to the APK table, like the OBB stuff.
In Android Studio, just Ctrl-Click on any table constant definition, and
then it lists all the places its used. Any new data will need to be added
in all of those locations.
OBB files are used in apps that need more than 100 megs to work well. This
is apps like MAPS.ME or games that put map info, media, etc. into the OBB
file. Also, OBB files provide a mechanism to deliver large data blobs that
do not need to be part of the APK. For example, a game's assets do not
need to change often, so they can be shipped as an OBB, then APK updates do
not need to include all those assets for each update.
https://developer.android.com/google/play/expansion-files.html
Properly support multiple repositories with the same app
Fixes#511.
Provides proper support for multiple repos in the database + updater (despite not being able to reorder them in the UI yet).
# Info
Previously, each app had only one row in the app table. This caused problems when its metadata was updated, because it would get overriden if two repositories provided the same app. This change:
* Creates a new `package` table which acts as the table that includes one row for each app (like the `app` table did previously)
* Re-purposes the existing `app` table as an `app_metadata` table, where each package can have multiple rows (one for each repository that provides that package)
This results in some queries which are slightly more complex, but the overall performance should not change too much. In the long term, it should have a net positive impact on performance, because we no longer need to join between tables based on a package name. Now we almost exclusively use integer IDs to join between tables. There are also appropriate indexes which make the queries avoid full table scans in all cases I'm aware of.
I realise this is a big MR, but it is as small as I could make it without submitting half finished stuff which breaks `master`. I've done my best to merge smaller MRs before hand, but I was unable to identify anything else to pull out of this MR as a separate thing.
## Migration
All app/apk metadata is dropped, and then the repos are asked to update themselves again next time F-Droid starts.
Additionally, existing repositories have their `priority` changed to something more meaningful. On current master, if you add two custom repositories in addition to the four default ones, you will get the following:
|rowid|address|priority|
|---------|-------|-------|
|1|https://f-droid.org/repo|10|
|2|https://f-droid.org/archive|20|
|3|https://guardianproject.info/fdroid/repo|10|
|4|https://guardianproject.info/fdroid/archive|20|
|5|http://10.0.0.6:8888/normal-repo/repo|10|
|6|http://10.0.0.6:8888/conflicting-repo/repo|10|
Note how the priority defaults to 10 for each additional repository which is added. This MR should change the priorities so they look like so:
|rowid|address|priority|
|-------|-------|--------|
|1|https://f-droid.org/repo|1|
|2|https://f-droid.org/archive|2|
|3|https://guardianproject.info/fdroid/repo|3|
|4|https://guardianproject.info/fdroid/archive|4|
|5|http://10.0.0.6:8888/normal-repo/repo|5|
|6|http://10.0.0.6:8888/conflicting-repo/repo|6|
Here is a snipped from the logcat from `DBHelper` during the migration:
```
Setting priority of repo https://f-droid.org/repo to 1
Setting priority of repo https://f-droid.org/archive to 2
Setting priority of repo https://guardianproject.info/fdroid/repo to 3
Setting priority of repo https://guardianproject.info/fdroid/archive to 4
Setting priority of repo http://10.0.0.6:8888/normal-repo/repo to 5
Setting priority of repo http://10.0.0.6:8888/conflicting-repo/repo to 6
```
All newly added repositories on this branch will get the next highest available priority.
# Future work
One thing which is not yet supported fully: Suggested version code.
Two repositories can end up with the same exact .apk file. If that .apk is the "suggested version", then we should eliminate the idea of "suggested version code" and instead have a "suggested apk" (which implicitly includes the repository it comes from, so we choose the one with the better priority). Right now, we kind of assume that it doesn't matter which repo provides the suggested apk, as long as one of them has an .apk with th ecorect version code and signing key.
I guess it doesn't _particularly_ matter from a security perspective, because a malicious repo wont be able to trick a user into installing an apk with a different signing key, but it would be good to iron this out.
See merge request !375
Clarify teminology (around providers) and misc cleanup
I extracted all of what I consider cosmetic changes from !375 into this changeset. There should be no behaviour change, except for:
* Bug fix in `DBHelper`
* Different handling of priorities for newly created repos
Everything else if method renames or other misc cleanups.
After this, I'll pull out a different set of changes from !375 and then rebase it on this again.
See merge request !400
Many times in the past, we would ask for an apk based on its package name
and its version code. However multiple repositories provide apks with the
same package name and version code, and such queries would (seemingly)
nondeterministically choose one of these matching apks. This clarifies the
wording in the code around when we explicitly ask for a given apk, and
when we kind of guess which one we want.
Most the time we have an `App` handy, which has a specific repo associated
with it. This allows us to be more specific about requesting `Apk`s.
The times we are kind of guessing is when we rely on the "suggested version
code" of an apk by clicking the misc "Install" or "Upgrade" button in
app details. In the future, we'll need to clear this up so that a more
specific apk is chosen when touching these buttons.
The query which dynamically figured out the preferred metadata based on
repo priority ended up being quite slow (although it did work). On lower
end devices, it has the potential to make F-Droid quite sluggish. By
optimistically precalculating the preferred metadata where possible, we
don't need to ask the question during the usual usage of F-Droid, only
when:
* Repo priorities are changed (there is not currently a UI for this, but
there are tests)
* Repos are enabled/disabled
* Repo updates are performed
Includes:
* One of the functions querying for apps did not correctly specify
the repository the repos came from.
* Fix deletion code which refered to incorrect field.
* Cleanup code style in some places.
Two repositories can (and always could) end up with the same exact .apk file.
If that .apk is the "suggested version", then we should eliminate the idea of
"suggested version code" and instead have a "suggested apk" (which implicitly
includes the repository it comes from, so we choose the one with the better
priority). Right now, we kind of assume that it doesn't matter which repo
provides the suggested apk, as long as one of them has an .apk with the correct
version code and signing key.
It shouldn't _particularly_ matter from a security perspective, because
a malicious repo wont be able to trick a user into installing an apk with a
different signing key, but it would be good to iron this out.
This commit adds a TODO explaining this for th ebenefit of any CRer.
The tests are in the .updater packate to make them easier to run as
a suite in Android Studio. Now the package can be right clicked and
ran to run all the tests to do with updating.
The index jar files were updated so as to include info in most
metadata fields (e.g. URLs/descriptions/summary/etc) to show that
that particular part of metadata came from a specific repo. This
will allow more specific tests to show that we can indeed query for
an app with metadata provided by the repo with the highest priority.
Required for future work which will be better able to deal with multiple repos
providing the same app.
Instead of migrating data into that table, we will drop and recreate the tables.
This is because before this feature is out, we'll need to do that anyway.
It is often helpful during debugging to be able to dump the contents
of an SQL result `Cursor` to the debug watch list. This is difficult
to do under normal circumstances. This adds a utility method really
only designed to be used during interactive debugging, which will do
its best to build a `Map` for each row in the `Cursor`. This can then
be used to test queries while the debugger is paused.
Even though this is not used yet, it will be a requirement in the
near future for the `RepoProvider` to be the one who decides what
the priority of new repositories is. This will prevent clients of
this provider from specifying wrong priorities that result in gaps
For example, if we accidentally ended up with priorities of
1, 2, 4, and then 5, this would cause problems if the user tried to
drag the second repo to the position of the 4th repo. It is easier
to do these priority shuffles if we can assume that the priorities
are contiguous.
Originally, I hoped that the arguments a method took would help enough
to differentiate the intent of that method. This was the case for methods
such as `getContentUri()` and `find()`. However they are a little confusing
to work with, so this change renames a bunch of methods to be more specific.
In addition, it makes some renames from app -> package which will help with
the upcoming change to add a `package` table to the database.
optionally keep install history
This adds the functionality for keeping the install/uninstall history along with a preference to enable it as a custom build option for allowing another app to read it. Keeping the history has many uses, including:
* "popularity contest"
* displaying install history locally in F-Droid
* reporting to IT device manager for tracking activity for malware, etc.
This is ready to be merged, but it is based on !386, so its marked WIP.
@dschuermann this touches some of your architecture, please review :)
See merge request !392
Since e69a6d5a8f24e7745516001f58bee49e05f2ea9e, the Apk instance is
provided in the constructor and is available as a final instance variable.
No need to pass it around. Thanks to @pserwylo for spotting this.
ACTION_INSTALL_STARTED was being sent twice per transaction with the
default installer. Also, it should be sent as the first step of the install
process.
For now, this is disabled by default and hidden in the expert preferences
since it doesn't do anything yet inside of F-Droid. It is useful now for
whitelabel builds to fetch the install history from another app. #396
This allows a designated app to read the install history from F-Droid via a
ContentProvider. The app is designated by the packageName defined in the
string install_history_reader_packageName.
The install and uninstall history has lots of uses, including displaying
to the user in the app itself, reporting to the Device Administrator to
enable tracking of installs/uninstalls from the admin's app repo, etc. It
can also be used as part of a "popularity contest" #396
Now that the packageName is included in the Installer broadcast Intents,
it can be used to fetch the app name from the database, if all other ways
fail.
If F-Droid or InstallManagerService get killed while an install is in
progress, that install will ultimately broadcast back to
InstallManagerService to manage the notifications. The state is gone
since things have been killed, so include the Apk instance in the
Intent that is included in the broadcasts so that
InstallManagerService can fetch all required info from the database.
closes#698
Push requests for install/uninstall
This is the first basic implementation of the idea of "push requests" from the server. This implements two requests that the server can ask of all clients: `install` and `uninstall`. A default repository in the client can be marked to `ignore` or `always` accept push requests in this merge request. There is also the sketch of a third option called `prompt` which gives the user the standard "Just Once/Always" choice; that is not yet implemented.
This allows central management of app installs/uninstalls for a pool of devices, as well as other ideas.
A use case for this feature is documented here:
https://f-droid.org/wiki/page/Whitelabel_Builds
See merge request !386
F-Droid shouldn't crash if a push request includes a bad package name. This
just makes it silently ignore those push requests. If its a debug build,
it will send a message to logcat. I'm not sure this is best way to handle
this, but this is better than crashing the app. This will make it harder
for repo operators to debug issues with push requests.
This allows whitelabel versions of apps to specify built-in app repos that
have push requests accepted by default. This is useful for the case where
there is a central manager of the core apps that are installed.
https://gitlab.com/fdroid/fdroidserver/issues/177
This is a step towards supporting easy whitelabeling, using gradle flavors.
This allows the whitelabel version to set the default repos just by making
their own default_repos.xml in app/src/whitelabel/res/values. That one
will then override the built-in F-Droid one.
Include crash in ACRA subject
This makes it so the first chunk of the crash is put in email subject. This also lays the foundation for other ACRA customizations.
This is ready to be merged, but it is based on !386, so its marked WIP.
See merge request !391
TLS 1.2 support
At long last, there is a tested version of NetCipher that supports SNI. This uses that release to enable good TLS support and Tor for all repos. This moves the HTTP tests to the emulator, so that things are tested on the actual OS.
See merge request !398
Update to the latest NetCipher, which now fully supports SNI, in order to
support TLS 1.2 on all supported platform levels. Without this, a repo
that is TLS 1.2 only will be unusable on all but the most recent versions
of Android.
#431
There are oddities with the way that Android has implemented the network
stack, as compared to OpenJDK or Oracle JDK. So running the tests on the
local JVM, i.e. Robolectric, will not provide good test coverage for real
world use cases.
update to latest support lib bugfix version: v24.2.1
include the bugfix version of the support libs we are using since this is the last test before a stable release.
See merge request !397
This makes sure that the latest version of the core SDK components are
all current before running, which means that the builds will keep
working even when the docker image gets out of date. Then we can
finish the task we are working on before having to deal with updating
the docker image. Without that update line, we have to drop everything
and update the docker image when things are out of date.
Disk space and bandwidth is cheap, developer time is very scarce. We
should aim to keep the tests working as much as possible so that we
waste less developer time. Updating everytime only means it downloads
a little bit of XML each time, that's nothing compared to what gradle
downloads on every build.
Translators:
Ajeje Brazorf Sardinian
Enol P Asturian
Marian Hanzel Slovak
Michael Moroni Italian
Mladen Pejaković Serbian
Sérgio Marques Portuguese (Portugal)
Sveinn í Felli Icelandic
Дмитрий Михирев Russian
This is far less brittle at runtime, but slightly more work at dev time.
The following things are undesirable but make it much easier to write:
* Use of `CREATE_TABLE_APP.replaceFirst(...)` to create the temp tables.
* Having to specify a list fo columns twice in `Schema` (`ALL_COLS` + `COLS`).
The `replaceFirst` means we don't need to maintain two separate create table
statements. It is a little messy because there is no compile time guarantee
that we are creating a valid SQL statement at the end, just our knowledge
that a create table statment tends to have the table name first and it
probably wont cause problems.
The `ALL_COLS` + `COLS` is required so that we don't have to type out a list
of fields when copying data in `TempAppProvider`. Otherwise, whenever a new
column is added, developers would need to know that it also needs to be added
to this third place. Currently it is in the `Schema` and the `CREATE_TABLE_*`
statements where one needs to add a new column. These are both intuitive and
hopefully easily discoverable. Having to add it to the `TempAppProvider` is
less intuitive and likely to result in bugs.
When performing the old style `CREATE TABLE ... AS SELECT ...` (CTAS) statement,
no indexes are added. In addition, rowid is not added. Even if manually
specifying an autoincrement column in the original schema, this autoincrement
column does not get recreated with the CTAS statement. So instead, this change
reuses the original `CREATE TABLE` statement which explicitly defines all of the
relevant columns. In addition, it explicitly adds an autoincrement integer primary
key. This has the same semantics as the existing implicit `rowid` column that
sqlite creates. From from https://sqlite.org/autoinc.html:
> In SQLite, a column with type INTEGER PRIMARY KEY is an alias for the ROWID
> (except in WITHOUT ROWID tables) which is always a 64-bit signed integer.
However, as it is explicit now, is copied when doing the
`INSERT INTO ... SELECT ...` statement to get data from the real table to the
temp table in preperation for updates (and back again after the update has
populated the temp table).
Note that this makes the `INSERT INTO ... SELECT ...` statements slightly more
brittle, because now we need the table definition used to create the temp table
(from `DBHelper.CREATE_APP_TABLE`) to have the same column order as those in the
real `fdroid_app` table. While this may sound like a silly comment to make, it
is important because database migrations can result in a database having the
correct set of columns, but in a different order to how they were specified
in the original create table statement.
If a database migration performs an `ALTER TABLE ... ADD COLUMN ...` the column
will be added at the end. If at the same time the `CREATE TABLE` is changed so
that the new column is specified as the second to last column in the list of
columns, then the `INSERT INTO ... SELECT ...` will not work as expected.
fix emulator tests on android-10
We have to work harder to get writeable dirs in the android-10 emulator, that seems to be the old issue on android-10.
See merge request !390
Remove any whitespace from fingerprint EditText input so that copy pasting is easier for users.
Hey,
I just added a line to remove the whitespace from fingerprints when adding a new Repo. Some repositories like https://microg.org have their fingerprint separated with spaces so copy pasting directly from there is more cumbersome if fdroid doesn't remove the whitespace by itself.
Hope it's ok. :)
See merge request !389
Translators:
Agus Galician
Ajeje Brazorf Sardinian
Alberto Moshpirit Spanish
El Virolo French
Koen Glotzbach Dutch
Licaon Kter Romanian
Michael German
Mohamad Hasan Al Banna Indonesian
naofum Japanese
Olexandr Nesterenko Ukrainian
Osoitz Basque
Pyae Sone Burmese
Verdulo Esperanto
Verdulo Polish
YFdyh000 Simplified Chinese
handle install broadcasts after InstallManagerService was killed
If InstallManagerService was killed, it'll forget all of its state. If it is killed while an install process is running, and that install fails,
InstallManagerService will receive a broadcast about the error but then it can't find anything about the app in question besides its download URL.
That is enough to control the notification, but not enough to get the name of the app in question. This is a workaround by showing the APK filename when the app name cannot be found. Ideally, the packageName would somehow magically be delivered to InstallManagerService in this case, but the
Installer stuff doesn't always have it to send.
With android-23, there is getActiveNotifications(), which we might be able to use to stash the packageName and fetch it as needed.
See merge request !387
Translators:
Adrià García-Alzórriz Catalan
Ajeje Brazorf Sardinian
Christophe CHAUVET French
Enol P Asturian
Laura Arjona Reina Spanish
Licaon Kter Romanian
Michael German
Mladen Pejaković Serbian
naofum Japanese
Osoitz Basque
Swyter Spanish
Sylvia van Os Dutch
Tobias Bannert German
Verdulo Esperanto
Verdulo Polish
Viktor Alojzije Coric Croatian
Waqar Ahmed Urdu
YFdyh000 Simplified Chinese
When the Privileged Extension is working, then enabling the preference
"Automatically download updates" will actually install those updates in the
background. So the preference should communicate that to the user. So now
it serves as a global "allow background updates"
#16closes#106
This gets rid of the notifications that say "Tap to Install Unknown", and
instead just cancels the notification. The downloaded APK will still be
cached, so when the user goes to click install or update again, it won't
need to download it again.
closes#758
If InstallManagerService was killed, it'll forget all of its state. If it
is killed while an install process is running, and that install fails,
InstallManagerService will receive a broadcast about the error but then it
can't find anything about the app in question besides its download URL.
That is enough to control the notification, but not enough to get the name
of the app in question. This is a workaround by showing the APK filename
when the app name cannot be found. Ideally, the packageName would somehow
magically be delivered to InstallManagerService in this case, but the
Installer stuff doesn't always have it to send.
With android-23, there is getActiveNotifications(), which we might be able
to use to stash the packageName and fetch it as needed.
Add option to hide anti feature apps
Added option under Setting to grey out apps requiring anti-features similar to ignoring rooted apps on a non-rooted device.
See merge request !384
final fixes for 0.101
I think we can release 0.101 after this bug fix for #699 and support update. What do you think @mvdan @pserwylo @dschuermann ?
See merge request !385
This should be reverted once #698 is fixed. If execution has gotten this
far into InstallManagerService, there should always be App and Apk
instances. That is enforced when Intents are received by this Service.
This was saying that the Privileged Extension is enabled but not properly
configured. This is because the preference logic changed to default to on
unless the user explicitly disabled it. So using the Privileged
Extension based on whether its installed and whether the user has disabled
it.
related to ea0700d406101b7ed6907b1dbd2918dbc214f435
I guess APKs could disappear, or perhaps not be readable.
closes#699
Here's the stacktrace:
java.io.FileNotFoundException:
/system/priv-app/ATT_Ready2Go/ATT_Ready2Go.apk: open failed: ENOENT (No such file or directory)
at org.fdroid.fdroid.Utils.getBinaryHash(Utils.java:405)
at org.fdroid.fdroid.data.InstalledAppProviderService.onHandleIntent(InstalledAppProviderService.java:164)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.os.HandlerThread.run(HandlerThread.java:61)
use Privileged Extension by default, when installed
Now that the 0.2 release of Privileged Extension is tagged, it is starting to be integrated into ROMS, and is easy to flash to ROMed devices, it is time to make F-Droid use the Privileged Extension by default, if it is installed.
See merge request !382
This totally changed the logic of the "Use Privileged Extension" preference
making it based on whether the Privileged Extension is installed and usable
rather than storing the user's selection. This code now only stores when
the user manually disables using the Privileged Extension even when it is
installed.
closes#729
The root install method that is available via the "Expert" preferences does
not work on newer than android-19. So when that's the case, this hides
that item from the preferences screen entirely.
Move code causing verify error into separate helper class
Fixes#748.
I'm not 100% sure on how the `@TargetApi` and `VerifyError` work
together. However it is something along the lines of:
* Class loader needs `CleanCacheService`.
* At this point, it loads the bytecode for that class and verifies
that it all makes sense.
* The bytecode within the method targeted at API 21 is not understood
by earlier APIs, because the entire `Os` class was introduced in 21.
* By putting it into a different class, that class is only loaded
at runtime on devices with API of 21 or higher.
Previously, `@TargetApi` + the relevant guard condition to check
the build version at runtime suffices to prevent this. However it seems
that if the entire class does not even exist on earlier APIs, then it
is no longer good enough.
See merge request !379
I'm not 100% sure on how the `@TargetApi` and `VerifyError` work
together. However it is something along the lines of:
* Class loader needs `CleanCacheService`.
* At this point, it loads the bytecode for that class and verifies
that it all makes sense.
* The bytecode within the method targeted at API 21 is not understood
by earlier APIs, because the entire `Os` class was introduced in 21.
* By putting it into a different class, that class is only loaded
at runtime on devices with API of 21 or higher.
Previously, `@TargetApi` + the relevant guard condition to check
the build version at runtime suffices to prevent this. However it seems
that if the entire class does not even exist on earlier APIs, then it
is no longer good enough.
Ensure that description is not null when parsing index.
At time of writing (and for some time before), fdroidserver has forced
a description of "No description available" for apps which don't have
descriptions at all:
* https://gitlab.com/fdroid/fdroidserver/blob/0.6.0/fdroidserver/metadata.py#L876
However, if the description is not set for whatever reason, it should not
crash the client.
Identified in the now closed#739.
See merge request !380
At time of writing (and for some time before), fdroidserver has forced
a description of "No description available" for apps which don't have
descriptions at all:
* https://gitlab.com/fdroid/fdroidserver/blob/0.6.0/fdroidserver/metadata.py#L876
However, if the description is not set for whatever reason, it should not
crash the client.
Big cache update
So I messed up the caching a bit with my update in the past, so this is a big update to fix lots of bugs (hopefully) and add a couple of nice cache clean up features. This should move us towards making F-Droid maintain itself more and more. More comments in the commits.
See merge request !378
The icon files are downloaded for each version of the app. Over time, old
versions will pile up. This cleans out the ones that have not been used in
over a year.
On < android-21, this will delete icons that were downloaded over a year
ago even if they are still in use because it is only possible to check
mtime, not atime.
If CleanCacheService runs while an APK is being installed, it should not
delete the APK that is in the process of being installed. This does that
by only deleting those files if they are older than an hour. Same goes for
the index files.
#738
It was passing the wrong time value in the recursion, which made for a
really old "olderThan" time. This also then flipped the logic on the
next round through the recursion, causing files to be deleted even if
"Keep Cache Time" was set to "Forever".
closes#719closes#736
Before, CleanCacheService was only scheduled at app start for once a day.
If the user selects a time less than a day, then CleanCacheService should
run more frequently.
closes#719
In android-21, they exposed the formerly internal method for getting stat
structs of files. From that, we can get the last access time, which is a
much better way to determine which files to delete rather than last
modified time.
closes#644
Hash fixes
Two semi-related commits about hashes. This standardizes all APK hashes to be all lowercase like in the _fdroidserver index.xml_. The other then stops swallowing hash-related exceptions so we have a chance of debugging the issue. @pserwylo we discussed a0f716c0db705f137749eef3d4964b8ba1050b18 in relation to #699.
See merge request !377
By catching the exception here and returning null, the problem is then
passed on further down the line where it is harder to debug. The hash is
required wherever this method is called, so this should fail immediately.
#699
ApkVerifier Tests
This are some tests for ApkVerifier. More will follow when we merge https://gitlab.com/fdroid/fdroidserver/merge_requests/150 and implement parsing of permissions with min and max sdk versions.
NOTE: This androidTest cannot run as a Robolectric test because the required methods from PackageManger are not included in Robolectric's Android API.
The corresponding exception by robolectric:
```
org.fdroid.fdroid.installer.ApkVerifierTest > testVerifier FAILED
00:31:18.241 [DEBUG] [TestEventLogger] java.lang.NoClassDefFoundError: java/util/jar/StrictJarFile
00:31:18.241 [DEBUG] [TestEventLogger] at java.lang.Class.getDeclaredMethods0(Native Method)
00:31:18.241 [DEBUG] [TestEventLogger] at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
00:31:18.241 [DEBUG] [TestEventLogger] at java.lang.Class.getDeclaredMethod(Class.java:2128)
00:31:18.241 [DEBUG] [TestEventLogger] at org.robolectric.util.ReflectionHelpers.callStaticMethod(ReflectionHelpers.java:224)
00:31:18.241 [DEBUG] [TestEventLogger] at org.robolectric.internal.bytecode.RobolectricInternals.performStaticInitialization(RobolectricInternals.java:54)
00:31:18.241 [DEBUG] [TestEventLogger] at org.robolectric.internal.bytecode.ShadowWrangler.classInitializing(ShadowWrangler.java:119)
00:31:18.241 [DEBUG] [TestEventLogger] at org.robolectric.internal.bytecode.RobolectricInternals.classInitializing(RobolectricInternals.java:18)
00:31:18.241 [DEBUG] [TestEventLogger] at android.content.pm.PackageParser.<clinit>(PackageParser.java)
00:31:18.241 [DEBUG] [TestEventLogger] at android.content.pm.PackageManager.getPackageArchiveInfo(PackageManager.java:3545)
00:31:18.241 [DEBUG] [TestEventLogger] at org.fdroid.fdroid.installer.ApkVerifier.verifyApk(ApkVerifier.java:56)
00:31:18.241 [DEBUG] [TestEventLogger] at org.fdroid.fdroid.installer.ApkVerifierTest.testVerifier(ApkVerifierTest.java:78)
00:31:18.242 [DEBUG] [TestEventLogger] at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
[...]
00:31:18.244 [DEBUG] [TestEventLogger]
00:31:18.244 [DEBUG] [TestEventLogger] Caused by:
00:31:18.245 [DEBUG] [TestEventLogger] java.lang.ClassNotFoundException: java.util.jar.StrictJarFile
00:31:18.245 [DEBUG] [TestEventLogger] at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
00:31:18.245 [DEBUG] [TestEventLogger] at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
00:31:18.245 [DEBUG] [TestEventLogger] at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
00:31:18.245 [DEBUG] [TestEventLogger] at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
00:31:18.245 [DEBUG] [TestEventLogger] at org.robolectric.internal.bytecode.InstrumentingClassLoader.loadClass(InstrumentingClassLoader.java:124)
00:31:18.245 [DEBUG] [TestEventLogger] at java.lang.Class.getDeclaredMethods0(Native Method)
00:31:18.245 [DEBUG] [TestEventLogger] at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
00:31:18.245 [DEBUG] [TestEventLogger] at java.lang.Class.getDeclaredMethod(Class.java:2128)
00:31:18.245 [DEBUG] [TestEventLogger] at org.robolectric.util.ReflectionHelpers.callStaticMethod(ReflectionHelpers.java:224)
00:31:18.245 [DEBUG] [TestEventLogger] at org.robolectric.internal.bytecode.RobolectricInternals.performStaticInitialization(RobolectricInternals.java:54)
00:31:18.245 [DEBUG] [TestEventLogger] at org.robolectric.internal.bytecode.ShadowWrangler.classInitializing(ShadowWrangler.java:119)
00:31:18.245 [DEBUG] [TestEventLogger] at org.robolectric.internal.bytecode.RobolectricInternals.classInitializing(RobolectricInternals.java:18)
00:31:18.245 [DEBUG] [TestEventLogger] at android.content.pm.PackageParser.<clinit>(PackageParser.java)
00:31:18.245 [DEBUG] [TestEventLogger] at android.content.pm.PackageManager.$$robo$$getPackageArchiveInfo(PackageManager.java:3545)
00:31:18.245 [DEBUG] [TestEventLogger] at android.content.pm.PackageManager.getPackageArchiveInfo(PackageManager.java)
00:31:18.245 [DEBUG] [TestEventLogger] at org.fdroid.fdroid.installer.ApkVerifier.verifyApk(ApkVerifier.java:56)
00:31:18.246 [DEBUG] [TestEventLogger] at org.fdroid.fdroid.installer.ApkVerifierTest.testVerifier(ApkVerifierTest.java:78)
00:31:18.246 [DEBUG] [TestEventLogger] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[...]
```
See merge request !367
More misc code cleanup around database code
I'm pulling out the final bit of unrelated code from my database refactor branch in the hope of making the final diff easier. This cleans up a few switch statements with only one option, closes some cursors, and removes some dead code. Comments in the commits explain the dead code.
See merge request !374
F-Droid Privileged Extension fixes
A couple of small fixes related to the process of finalizing the new Privileged Extension for a real release! @dschuermann hopefully just renaming is enough when installing FPE via the root method. Or do you think we should handle removing existing installs in the old location?
See merge request !376
AS picked up that the statement is always false, so the body of the if is
never executed. This is indeed the case, because the constructor assigns
the object which is being checked for null.
The code only existed so that it could be used in a test. Subsequently,
a further test was written to test this code (used by the first test).
Since none of the code is actually used in the app, it has been removed.
Renamed AppTable to AppMetadataTable
See #511 for details. This is in prep ration for having an even more normalized `fdroid_package` table. That table will be the authoritative reference of what "packages" are known about in the client. The "app" table (now "metadata") will be specific to each repository which provides different metadata about that app.
This is a fairly straightforward "Rename Interface" refactoring from Android Studio" and is done so as to minimize the diff in a forthcoming MR.
See merge request !373
See #511 for details. This is in prepration for having an even more normalized
`fdroid_package` table. That table will be the authoritative reference of what
"packages" are known about in the client. The "app" table (now thought of as "app metadata") will
be specific to each repository which provides different metadata about that app.
Move user specified data to separate table
Right now, the "Ignore update for version X" and "Ignore all updates for this app" are stored in `fdroid_app`. This means that if a repo is disabled then re-enabled, these preferences are lost. This MR separates out the user specified metadata from the metadata provided by the repository, such that one can change without affecting the other.
For convenience sake, this drops the `fdroid_app` and `fdroid_apk` tables rather than migrating them, and then sets a flag in preferences that forces F-Droid to do an index update when started. This is done _after_ migrating already existing user preferences out of `fdroid_app` and into `fdroid_appPrefs`.
See merge request !372
The test was using a `findIgnored` method in `AppProvider`, which only
existed for the purpose of testing. The test has been changed to instead
check for apps which would end up in the "can update" list (which is really
where the "ignored" apps are useful).
In the process, realised that using appId as a foreign key is worse than
packageName, because appId can get removed and added again, but it will
be different when the same app is inserted a second time. In order to
maintain the association of which apps have preferences stored against
them, they need to be stored against something with a bit more semantic
meaning. Thus, join onto package name instead.
This is a more concise syntax to say the same thing, and avoids an
OR clause in the where - which is often the cause of slowness in
many queries. Not sure if it was problematic in these cases, however
this COALESCE syntax is still more consise.
With no indexes at all, a join between X and Y tables would require a full
table scan of Y for each row in X. With an index on the relevant field in
Y, it would require an index lookup on the join field in Y for each row in
X, which contains a pointer to the row of interest in Y. This row is then
looked up and the relevant value extracted. By using a covering index (one
which includes all fields required to satisfy the query, with the first field
being the one which is looked up in the join), then once the index has been
searched, there is no need to then go to table Y because all the relevant
data is already in the index.
This offers a marginal performance improvement.
Merge download broadcast receivers
Previously, for all 4 states broadcast receivers were registered separately. These have now been merged into one receiver. IMHO this makes the code more readable and structured.
See merge request !368
Use AOSP Installer if permission check fails
If the old repo index is used and the permission check fails in ApkVerifier, this allows a fallback to the AOSP DefaultInstaller to show all permissions.
This has been discussed in https://gitlab.com/fdroid/fdroidclient/issues/704
Unfortunately, this shows our permission screen before download and then afterwards when the ``ApkVerifier`` fails the permission screen of the AOSP DefaultInstaller, i.e., the user sees two permission screen which she needs to acknowledge. This should only happen if an old repo format is used, thus I think this is okay. I don't know of any other solution to this problem.
See merge request !369
Make App and Apk parcelable and fix related installer NPEs
Installations fails currently due to
```
885 ACRA E Caused by: java.lang.NullPointerException
885 ACRA E at org.fdroid.fdroid.installer.InstallManagerService.getAppName(InstallManagerService.java:327)
885 ACRA E at org.fdroid.fdroid.installer.InstallManagerService.createNotificationBuilder(InstallManagerService.java:318)
885 ACRA E at org.fdroid.fdroid.installer.InstallManagerService.onStartCommand(InstallManagerService.java:158)
885 ACRA E at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2039)
885 ACRA E ... 10 more
```
This bug has been introduced in https://gitlab.com/fdroid/fdroidclient/merge_requests/359 where the packageName has been removed from the toContentValues() method.
The usage of ContentValues to send App/Apk objects to services was an hack in my opinion.
Thus, this PRs introduces proper parceling of App and Apk classes.
@pserwylo @eighthave @mvdan
See merge request !362
The usage of ContentValues to send App/Apk objects
to services was an hack in my opinion.
This hack broke in https://gitlab.com/fdroid/fdroidclient/merge_requests/359
where the packageName has been removed from the
toContentValues() method, which leads to NPEs in
the services.
Remove now unused package name from apk table
The package name is only stored in the `fdroid_app` table now, so we need to remove it form the `fdroid_apk` table. Under normal circumstances, I'd normally just leave unused fields in the DDL (the SQL which defines the tables) and never use it from within the Java code. However in this case, the package name formed part of the primary key of this table. Seeing as we are not inserting into that column any more, it isn't okay to leave it there but instead it must be removed so that we can put a more appropriate primary key on the table. In this case, the new primary key is `appId` + `vercode` + `repoId`.
I think this is the final merge request before I submit a MR with repo priorities.
See merge request !359
Wherever the "package name" of an apk is required, it can be requested by
asking for `Schema.ApkTable.Cols.App.PACKAGE_NAME`. Note the `App` which
indicates that it is in fact pulling this data from the `fdroid_app` table rather
than the `fdroid_apk` table.
Correctly identify the repo for a given URL to fix HTTP Auth.
**NOTE: Based on !355 (If that one gets merged first, I can rebase this, or else we can merge this one for both commits)**
When downloading arbitrary URLs using F-Droid (e.g. icons, .apk files, indexes) then it may be the case that the repo requires authentication. As such, we try to infer the repository based purely on the URL.
The old code took the basename of the URL, which means remove the last fragment (e.g. "index.jar") and use the remaining portion of the URL to lookup the repo.
This is broken for many reasons, partly because of the presence of a query string, partly because there are other things which are not just in the root directory of the repo (e.g. "/icons/*.png").
This new method iteratively peels off the right most segment of the URLs path, then looks to see if a repo exists at that address.
Note that this breaks down if you have nested repositories on a server, where one of the repositories is nested inside a directory that F-Droid knows about, such as "icons". In such a case, the following repositories:
* https://f-droid.org/repo (requires auth)
* https://f-droid.org/repo/icons (doesn't require auth)
will break down. If requesting something from the repo requiring auth:
* https://f-droid.org/repo/icons/org.fdroid.fdroid.png
Then it will lookup the database and find the repo which lives in "/icons" and doesn't require auth (or requires a different auth username/password). Not sure there is a lot that can be done about this without major refactoring. Such refactoring would require making sure a `Repo` is always given to a downloader for any HTTP request, and is probably a bit out of scope of this bug.
Also added tests for this behaviour.
Fixes#711.
See merge request !357
Firstly, this causes #721, possibly due to a bug in "Barcode
Scanner" whereby it seems to ignore the scheme when in caps,
assuming it is "http".
The relevant RFC is:
> RFC3986 (Uniform Resource Identifier (URI): Generic Syntax
In section 3.1, it describes the scheme:
> Although schemes are case-insensitive, the canonical form is
> lowercase and documents that specify schemes must do so with
> lowercase letters. An implementation should accept uppercase
> letters as equivalent to lowercase in scheme names (e.g., allow
> "HTTP" as well as "http") for the sake of robustness but should
> only produce lowercase scheme names for consistency.
Secondly, it is not valid to uppercase URLs at will. Although it
seems that there is some sort of more-compact-QR-generating-logic
that doesn't justify this. Funnily enough, I can't find anything
in RFC3986 about the case-insensitivity of URI paths. However
consider the following:
* https://i.imgur.com/fn33EcW.jpg
That is a valid path to an image. If we upper case it:
* HTTPS://I.IMGUR.COM/FN33ECW.JPG
or lower case it:
* https://i.imgur.com/fn33ecw.jpg
Then the server is entitled to treat it differently and indeed
it does. Both the upper case and lower case are no both 404's.
When downloading arbitrary URLs using F-Droid (e.g. icons, .apk files, indexes)
then it may be the case that the repo requires authentication. As such, we try
to infer the repository based purely on the URL.
The old code took the basename of the URL, which means remove the last fragment
(e.g. "index.jar") and use the remaining portion of the URL to lookup the repo.
This is broken for many reasons, partly because of the presence of a query string,
partly because there are other things which are not just in the root directory
of the repo (e.g. "/icons/*.png").
This new method iteratively peels off the right most segment of the URLs path,
then looks to see if a repo exists at that address.
Note that this breaks down if you have nested repositories on a server, where
one of the repositories is nested inside a directory that F-Droid knows about,
such as "icons". In such a case, the following repositories:
* https://f-droid.org/repo (requires auth)
* https://f-droid.org/repo/icons (doesn't require auth)
will break down. If requesting something from the repo requiring auth:
* https://f-droid.org/repo/icons/org.fdroid.fdroid.png
Then it will lookup the database and find the repo which lives in "/icons"
and doesn't require auth (or requires a different auth username/password).
Not sure there is a lot that can be done about this without major refactoring.
Such refactoring would require making sure a `Repo` is always given to a downloader
for any HTTP request, and is probably a bit out of scope of this bug.
Also added tests for this behaviour.
Fixes#711.
Prevent crash for servers that don't send etags with repo indexes
While working on #711, I noticed this bug using my [Mock HTTP Auth server](https://f-droid.org/wiki/page/PHP_Mock_Repository).
This fix always captures timestamps during repo updates, even if it is the same as last time. This is because we are dependent on it later on in the repo update process. Specifically, when updating from a HTTP server that doesn't send out etags with its responses, it will trigger a full blown repo update every time, even if all the values in the index are the same (name, description, etc). This is as distinct from better behaving servers that send etags, in which case we will only do a partial update (i.e. persist the "last updated time"). In such a case, the remainder of the update process will proceed, and ask for this timestamp.
See merge request !356
Always capture timestamps, even if it is the same. This is because we are dependent
on it later on in the repo update process. Specifically, when updating from a HTTP
server that doesn't send out etags with its responses, it will trigger a full blown
repo update every time, even if all the values in the index are the same (name,
description, etc). This is as distinct from better behaving servers that send etags,
in which case we will only do a partial update (i.e. persist the "last updated time").
In such a case, the remainder of the update process will proceed, and ask for this
timestamp.
Correctly delete single repo, not all repos.
In a recent commit, I cleaned up the code which deletes repo. At that point, instead of maybe concatenating strings together, sometimes with an `AND` statement,
it was changed to use the slightly better `QuerySelection`. This class is preferable because it doesn't need the developer to know whether there was
any previous constraints, and thus it knows whether to prepend an `AND`.
The problem arose because `QuerySelection` is effectively an immutable class. Calling `add()` on it returns a new copy with a different set of constraints.
The code which deleted the repo did not use this copy, and thus the resulting query had zero constraints.
The fix is to use the return value of `add()` correctly. It would've been easier to identify this bug if we had a lint check for "unused return values", though it is likely that that would get annoying very quickly.
Fixes#717.
See merge request !354
In a recent commit, I cleaned up the code which deletes repo. At that point,
instead of maybe concatenating strings together, sometimes with an `AND` statement,
it was changed to use the slightly better `QuerySelection`. This class is
preferable because it doesn't need the developer to know whether there was
any previous constraints, and thus it knows whether to prepend an `AND`.
The problem arose because `QuerySelection` is effectively an immutable class.
Calling `add()` on it returns a new copy with a different set of constraints.
The code which deleted the repo did not use this copy, and thus the resulting
query had zero constraints.
The fix is to use the return value of `add()` correctly. It would've been
easier to identify this bug if we had a lint check for "unused return values",
though it is likely that that would get annoying very quickly.
Fixes#717.
Use an integer primary key to join `fdroid_app` and `fdroid_apk` rather than the apps package name.
**Disclaimer:**
I realise this is a big change, but it needs to be done at some point, and it is not amenable to smaller changes, due to the fact that the app/apk relationship is so ingrained throughout F-Droid. Luckily, we have really quite comprehensive test coverage of the F-Droid `ContentProvider`s which helps to confirm that nothing should be majorly broken here.
**Some points of note:**
This is the first part of implementing #511, whereby the DB is refactored to better support multiple repositories.
Instead of joining `fdroid_app` and `fdroid_apk` tables using the package name, join based on an integer id autogenerated by sqlite. By default sqlite calls this `rowid` and it exists for every table, unless you've specified your own `NUMBER AUTO INCREMENT PRIMARY KEY` field. We have not done this for `fdroid_app`, so `rowid` is indeed the key we use in this MR. The package name was previously `id` in both the app and apk tables. Now `fdroid_app` makes use of `rowid` and `fdroid_apk` has a foreign key called `appId`.
The `ApkProvider` used to get away with only really querying the `fdroid_apk` table, and thus it didn't have to prefix any of the field names in the query with the table name. However now it always joins onto the `fdroid_app` table also, and as such, there are many places where field names needed to be prefixed with the table name (e.g. the `apk` alias or the `app` alias) to ensure the SQL is unambiguous when fields with the same name exist in both tables. The catch is, we want to reuse helper functions that build fragments of SQL, such as "Query based on package name". These helper functions are used both when updating and deleting apks (where field table prefixes are not allowed) and also in select statements (where they are required). Thus this changes comes with an `includeTableAlias` argument added to many of these methods (e.g. `ApkProvider.queryApp`).
There is still a package name column in the `fdroid_apk` table (the `id` field). This will be removed in future MRs and replaced with the package name from the joined `fdroid_app` table.
The `RepoPersister` used to dump apps in the db, then dump apks into the db. Now it needs to be a bit more nuanced, and dump apps into the db, _then ask the db what `rowid` was assigned to the apps_. This is then used when dumping the apks into the db. This also required some changes to how the `TempAppProvider` and `AppProvider` interact. In the interests of reusing code, both of these are able to provide operations on a similarly structured table but one is an in memory table (`temp_fdroid_app`) and the other is on disk (`fdroid_app`). In the past this was simpler, because the only interaction with the `TempAppProvider` was by using lists of `ContentOperation`s. Whereas now that we need to ask more substantial questions of the `TempAppProvider` other than "Insert this thing" or "update that thing", we needed to implement the `query` method in `TempAppProvider` similar to how it is in the base class `AppProvider`. As such, the common code for the base class and subclass `query` methods was extracted into `AppProvider.runQuery()`.
I tried to minimize the changes to the test suite as much as possible, so that it is possible to verify that they pass under the same conditions as before this change. However some changes were required to support the notion that apks depend on an app and its rowid, whereas this was not the case before. Thus there is some more boilerplate in the tests to ensure that inserting an apk ensures an app entry is present in the db too.
See merge request !345
Right now there is only one test in there anyway, so hopefully this is
a good tradeoff in terms of our time wasted vs not being able to run
those tests.
Moved index adding to a helper function, so that the same mistake isn't made
again. That is, indexes should be the same whether upgrading or creating a
database. Thus, the code to add indexes should always be the same regardless
of the reason for an index being added. The `IF NOT EXISTS` syntax helps
to allow the same queries to add during creatin and migration of database.
The fact that sqlite chose to do a "FULL TABLE SCAN" right off the bat when showing a list
of apps results in it having to do extra work at the end of the query to sort. If the scan
can be utilise an index, then the sorting is already done and a b-tree need not be constructed.
Thus, this introduces indexes for both the `name` column (used to sort most lists of apps)
and the `added` column (used to figure out the `Whats New` apps).
This is required because SQLite "rowids" are 64 bit integers:
> all rows within SQLite tables have a 64-bit signed integer
> key that uniquely identifies the row within its table."
https://sqlite.org/lang_createtable.html#rowid
This is important for the ability to refactor the database for better performance in the future.
See #511 for details.
For those interested in the details, here is a query plan of selecting the "All" category of apps before
this commit:
* `SCAN TABLE fdroid_app USING INDEX app_id`
* `SEARCH TABLE fdroid_apk USING INDEX apk_id (id=?)`
* `SEARCH TABLE fdroid_repo USING INTEGER PRIMARY KEY (rowid=?)`
* `SEARCH TABLE fdroid_installedApp AS installed USING INDEX sqlite_autoindex_fdroid_installedApp_1 (appId=?)`
* `SEARCH TABLE fdroid_apk AS suggestedApk USING INDEX sqlite_autoindex_fdroid_apk_1 (id=? AND vercode=?)`
* `USE TEMP B-TREE FOR ORDER BY`
And here is a query plan of afterwards:
* `SCAN TABLE fdroid_app`
* `SEARCH TABLE fdroid_apk USING INDEX apk_appId (appId=?)`
* `SEARCH TABLE fdroid_repo USING INTEGER PRIMARY KEY (rowid=?)`
* `SEARCH TABLE fdroid_installedApp AS installed USING INDEX sqlite_autoindex_fdroid_installedApp_1 (appId=?)`
* `SEARCH TABLE fdroid_apk AS suggestedApk USING INDEX apk_appId (appId=?)`
* `USE TEMP B-TREE FOR ORDER BY`
The things of note are:
* `SCAN TABLE` doesn't use an index, which means that it is really using the rowid index. Shouldn't behave much differently.
* The second item now uses an integer primary key index rather than a package name index. Should increase search speed marginally which was the goal of this commit. As more apks exist, the speed improvement will also increase.
Removed `TargetApi` annotation from class, pushing it to methods, and fix API problems.
Fixes#708.
On gingerbread (in my case, 2.3.4) F-Droid will reliably crash whenever it views `AppDetails` with the following error:
```
E java.lang.NoSuchMethodError: android.content.Context.getDrawable
E at org.fdroid.fdroid.privileged.views.AppSecurityPermissions$MyPermissionGroupInfo.loadGroupIcon(AppSecurityPermissions.java:111)
E at org.fdroid.fdroid.privileged.views.AppSecurityPermissions$PermissionItemView.setPermission(AppSecurityPermissions.java:158)
E at org.fdroid.fdroid.privileged.views.AppSecurityPermissions.getPermissionItemView(AppSecurityPermissions.java:396)
E at org.fdroid.fdroid.privileged.views.AppSecurityPermissions.displayPermissions(AppSecurityPermissions.java:370)
E at org.fdroid.fdroid.privileged.views.AppSecurityPermissions.getPermissionsView(AppSecurityPermissions.java:348)
E at org.fdroid.fdroid.AppDetails$AppDetailsSummaryFragment.buildPermissionInfo(AppDetails.java:1381)
E at org.fdroid.fdroid.AppDetails$AppDetailsSummaryFragment.setupView(AppDetails.java:1348)
E at org.fdroid.fdroid.AppDetails$AppDetailsSummaryFragment.onCreateView(AppDetails.java:1095)
E at android.support.v4.app.Fragment.performCreateView(Fragment.java:2074)
E at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
E at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1286)
E at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:758)
E at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1671)
E at android.support.v4.app.Fragment.performStart(Fragment.java:2096)
E at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1142)
E at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1286)
E at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1268)
E at android.support.v4.app.FragmentManagerImpl.dispatchStart(FragmentManager.java:2148)
E at android.support.v4.app.FragmentController.dispatchStart(FragmentController.java:212)
E at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:625)
E at org.fdroid.fdroid.AppDetails.onStart(AppDetails.java:424)
E at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1129)
E at android.app.Activity.performStart(Activity.java:3791)
E at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1620)
E at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
E at android.app.ActivityThread.access$1500(ActivityThread.java:117)
E at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
E at android.os.Handler.dispatchMessage(Handler.java:99)
E at android.os.Looper.loop(Looper.java:130)
E at android.app.ActivityThread.main(ActivityThread.java:3683)
E at java.lang.reflect.Method.invokeNative(Native Method)
E at java.lang.reflect.Method.invoke(Method.java:507)
E at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:864)
E at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:622)
E at dalvik.system.NativeStart.main(Native Method)
```
This is because `AppSecurityPermissions` uses a method that was not added until a later API. This class is used by `AppDetails` without an API verison check around its access. Thus, we call methods on this that are unsupported when on gingerbread.
By removing `TargetApi` from the class, it unearthed a couple of locations where methods were being invoked without first guarding against the build version correctly. These two locations have been fixed and a `TargetApi` attached to the more narrowly defined method which requires it.
See merge request !353
This class is used by `AppDetails` without an API verison check around its access.
Thus, we call methods on this that are unsupported when on gingerbread.
By removing `TargetApi` from the class, it unearthed a couple of locations
where methods were being invoked without first guarding against the build version
correctly. These two locations have been fixed and a `TargetApi` attached to the
more narrowly defined method which requires it.
Added LoggingQuery for diagnostics during debug mode.
While working on #511 and investigating database performance, I found myself really
wanting to understand the bits of F-Droid which were slow and those which were fast
due to the database. I created this thing, and thought I'd create a separate MR as it is
not directly related to the other stuff, but rather a general utility class that should be helpful.
SQLite has a very nice "EXPLAIN QUERY PLAN" command (https://sqlite.org/eqp.html).
It is not really meant to be used in production code, as per the docs, but it is
super helpful at diagnosing missing indexes or other performance problems with
databases. I find it much better than, for example, the MySQL alternative.
This commit routes queries from the `ApkProvider` and `AppProvider` through a
`LoggingQuery` which (in debug builds) times queries, and if they take longer
than a certain threshold, outputs them to logcat. In addition, it will then
ask sqlite to explain its query plan for that same query, and output that to
logcat too.
I created this as a separate class because it has zero dependence on any F-Droid related
stuff and might be helpful to other people too.
See merge request !350
SQLite has a very nice "EXPLAIN QUERY PLAN" command (https://sqlite.org/eqp.html).
It is not really meant to be used in production code, as per the docs, but it is
super helpful at diagnosing missing indexes or other performance problems with
databases. I find it much better than, for example, the MySQL alternative.
This commit routes queries from the `ApkProvider` and `AppProvider` through a
`LoggingQuery` which (in debug builds) times queries, and if they take longer
than a certain threshold, outputs them to logcat. In addition, it will then
ask sqlite to explain its query plan for that same query, and output that to
logcat too.
Revert to build-tools 23 until we can have 64-bit
As long as we're stuck with 32-bit on the buildserver, avoid both target
and build-tools 24. Necessary to do an alpha.
See merge request !349
Ensure database fields referred to by `Schema.*Table.Cols.*` constants
**This is based on top of !346.** When that is merged, I'll rebase this again and then remove the WIP.
The goal of this is to ensure that all string literals which refer to database columns are replaced with constants from the relevant `Schema.*Table.Cols` interface.
The only exceptions are fields which no longer exist and are referred to in the `DBHelper` class (e.g. the `fdroid_repo` table had an `id` column but that is now `_id`).
This should not change **any** behaviour in the client app, all semantics should stay **exactly** the same.
See merge request !347
Remove LITERAL_DO from the config in RightCurly as we want this:
do {
foo;
} while (bar);
Not this:
do {
foo;
}
while (bar);
This went unnoticed as LITERAL_DO was broken in RightCurly in earlier
Checkstyle versions.
Refactor database schema constants
**Note:** When this is merged I'll rebase !347 and remove its WIP.
## Summary
In order to do the database changes required for #511, I've found that it is difficult due to my inclination to switch between referring to database columns by either a Java constant such as `AppProvider.DataColumns.NAME` and string literals such as `"name"`. All string literals should be migrated to constants, and that will happen in my next MR. In order to prepare for this, I've gathered together the constants into one common place: `org.fdroid.fdroid.data.Schema`. This is going to be the authoritative place for the schema to be stored going forward, and will help when reasoning about the database structure.
Although it seems large, this change is a fairly straightforward find/replace job. It also passes all tests for which there is good test coverage in the content providers.
## Changes
Create `Schema` interface to make it simpler to replace string literals with constants.
Right now, table names are in `DBHelper.TABLE_*` constants, and each tables fields are
in `*Provider.DataColumns.*` constants. This brings them all into a predictable location.
In addition, it makes it easier to statically import `Schema` so that instead of, e.g.,
* `AppProvider.DataColumns.PACKAGE_NAME`
We can choose one of the following, based on our current context:
* `Schema.AppTable.Cols.PACKAGE_NAME`
* `AppTable.Cols.PACKAGE_NAME`
* `Cols.PACKAGE_NAME`
In the worst case, it isa couple of chars shorter than now. In the best case, if we are
writing a class that primarily deals with Apps (e.g. App.java or AppProvider.java) then
we get a big win with just `Cols.PACKAGE_NAME`.
Having these things slightly shorter may seem like it is pointless, but the length of
each constant probably contributed to my lack of willingness to use constants instead
of string literals when constructing queries.
In the future, this should be moved towards something more akin to:
> http://openhms.sourceforge.net/sqlbuilder/
and I hope that extracting all the schema stuff into one interface may help that.
See merge request !346
Right now, table names are in `DBHelper.TABLE_*` constants, and each tables fields are
in `*Provider.DataColumns.*` constants. This brings them all into a predictable location.
In addition, it makes it easier to statically import `Schema` so that instead of, e.g.,
* `AppProvider.DataColumns.PACKAGE_NAME`
We can choose one of the following, based on our current context:
* `Schema.AppTable.Cols.PACKAGE_NAME`
* `AppTable.Cols.PACKAGE_NAME`
* `Cols.PACKAGE_NAME`
In the worst case, it isa couple of chars shorter than now. In the best case, if we are
writing a class that primarily deals with Apps (e.g. App.java or AppProvider.java) then
we get a big win with just `Cols.PACKAGE_NAME`.
Having these things slightly shorter may seem like it is pointless, but the length of
each constant probably contributed to my lack of willingness to use constants instead
of string literals when constructing queries.
In the future, this should be moved towards something more akin to:
> http://openhms.sourceforge.net/sqlbuilder/
and I hope that extracting all the schema stuff into one interface may help that.
Translators:
Allan Nordhøy Norwegian Bokmål
Allan Nordhøy Swedish
ezjerry liao Traditional Chinese
halcyonest Korean
Kristjan Räts Estonian
Mikkel Kirkgaard Nielsen Danish
riotism Chinese (Hong Kong)
Thomas Craig Simplified Chinese
xinxinxinxinxin French
YFdyh000 Simplified Chinese
java.lang.NullPointerException
at org.fdroid.fdroid.Utils.clearOldFiles(Utils.java:347)
at org.fdroid.fdroid.CleanCacheService.onHandleIntent(CleanCacheService.java:51)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:194)
at android.os.HandlerThread.run(HandlerThread.java:60)
Also change the overrides from onCreate to init as suggested in the
changelog:
https://github.com/ACRA/acra/wiki/ChangeLog#acra-490-rc-1-2-may-2016
The behaviour should be very similar, although overriding the wrong
method (which we were doing) could cause all sorts of weird issues.
Those that are sometimes false positives but could still point out valid
issues should be warnings, not disabled entirely.
The first two are warnings already, the third is an error.
a couple of fixes, including enabling lint errors to fail the CI build!
Three fixes, including enabling lint errors to fail the CI build! Comments in the commit message. 09eea0d40bcf6b7a5612ef719177fd4ab2d2193b should be cherry-picked into stable-v0.100 for 0.100.1. Its already in my repo as fb70aada63029e430f2b4f2fb68427e719b63753.
See merge request !341
This is currently baffling me as to how it can happen. This isn't a pretty
fix but it is better that letting F-Droid crash. db9bdc31 was supposed to
make it so that only one thread at a time ever updated the static vars on
FDroidApp.
closes#690
UpdateService.onHandleIntent() starts with a time check for whether an
update is actually scheduled. Before, UpdateService put up a notification
when it started. This changes it so that the notification is put up after
the check, so it should only show the notification if UpdateService is
actually going to run, and no longer when it is just waking up to check the
time.
!307#662
The spongycastle issue is taking a long time to get resolved, has not yet
affected us, and would be a lot of work to fix in a different way. So the
'InvalidPackage' error is just disabled for now.
Provide content Uris to downloaded apks via FileProvider
* moves apk verification back inside the Installer class
* uses support libs FileProvider for content Uris
* move apk file caching and storage methods into ApkFileProvider class
Some of the ugly version checks for Android N can be removed after Android N has been released. Unfortunately Google decided to keep SDK version at 23 for Android N dev preview and only change the CODENAME, thus ``Build.VERSION.SDK_INT <= Build.VERSION_CODES.M`` returns true on Android N preview :/ , see https://commonsware.com/blog/2016/03/17/backwards-compatibility-n-developer-preview.html
Tested on Android N dev preview 3 emulator, Android 6 stock and Android 5.1 rooted with priv extension.
See merge request !331
Tests + Refactorings in preperation for #564 (Filter anti features)
As described in #564, there is a small amount of ground work to be done in order to support a UI for filtering anti features. This is the first stage of that. A subsequent MR will add a database migration to put anti features in their own table, and have a join table between apps and anti features. See commit messages for more detailed descriptions.
See merge request !339
The two helper methods alleviate the need for copious null checks. They also provide
consistent behaviour when there are zero elements (i.e. they return null, rather than
an empty string or empty array, as was the case before).
This is a combination of:
* `String[].split(",")` and
* `TextUtils.join(",", values)`
It seems a bit wastefull to have our own implementation of these two things
which lightly wrap this code, and produce a datastructure which is non standard
and foreign to Java developers.
* moves apk verification back inside the Installer class
* uses support libs FileProvider for content Uris
* move apk file caching and storage methods into
ApkCache class
Extracted new API styles dependent on API 16 and 17 into values-17.
This involved the typical Android design pattern of a "Base" style in
`values/styles.xml`, then the an empty normal style which uses that Base style
as a `parent` also in `values/styles.xml`, and finally any API specific
styles in another version of the normal style in a `values/styles-v17` folder.
Same was done for android:actionBarStyle moving it into into values-v11.
This time, didn't worry about the base style, because there was not much to be
gained. by doing so.
Fixes#582.
See merge request !335
We missed an off-by-one in my previous DB change:
90467bf8bf2f8e4a46cb1db563154df4035bf746
This causes the installed app parsing to happen on each start when on any
build that is on db-version/56. Its not a big deal since the broken code
was not shipped at all, even in an alpha.
In order to avoid having null guards making the code ugly, use a "blank"
instance of Apk which will work for the various comparisons. This fixes
this crash:
fixes#688
java.lang.NullPointerException
at org.fdroid.fdroid.installer.InstallManagerService$4.onReceive(InstallManagerService.java:243)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
Improve order by/selection logic for database layer.
This was extracted from the postponed !311.
The order by stuff previously only allowed specifying a particular field. We should also be able to sort based on arbitrary expressions, and such expressions will require the ability to bind arguments using the "?" syntax. This change provides a Java abstraction for the order by, and improves the handling of selection arguments that need to bind to "?" so that both the selection (i.e. `WHERE` clause) and the `ORDER BY` clause can provide arguments as required.
See merge request !329
Make sure to query package manager for `PackageInfo` when required.
A previous commit accidentally pushed the code which queries the
`PackageManager` to a different method, but then still used the
`packageInfo` which was supposed to be populated by that code.
This change rectifies this, and in the process also clarifies/documents under
what circumstances the `PackageManager` needs to be queried, rather than
relying on the incoming intent.
Fixes#686.
See merge request !336
A previous commit accidentally pushed the code which queries the
`PackageManager` to a different method, but then still used the
`packageInfo` which was supposed to be populated by that code.
This change rectifies this, and in the process also clarifies/documents under
what circumstances the `PackageManager` needs to be queried, rather than
relying on the incoming intent.
Fixes#686.
This involved the typical Android design pattern of a "Base" style in
`values/styles.xml`, then the an empty normal style which uses that Base style
as a `parent` also in `values/styles.xml`, and finally any API specific
styles in another version of the normal style in a `values/styles-v17` folder.
Same was done for android:actionBarStyle moving it into into values-v11.
This time, didn't worry about the base style, because there was not much to be
gained. by doing so.
The order by stuff previously only allowed specifying a particular field.
We should also be able to sort based on arbitrary expressions, and such
expressions will require the ability to bind arguments using the "?" syntax.
This changes provides a Java abstraction for the order by, and improves
the handling of selection arguments that need to bind to "?" so that both
the selection (i.e. WHERE clause) and the ORDER BY clause can provide
arguments as required.
Ask for all fields from the content provider.
**To be cherry-picked into `master` as well as this merge into `stable-v0.100`**
Fixes#684.

Ask for all fields from the `ContentProvider`.
It is not a particularly expensive operation in the scheme of things. When we are going to the database, the bottlneck is in disk access for the actual query of the database tables (looking up indexes, performing joins, etc). The difference between retrieving two columns or all the columns when the query is for a handful of apps is inconsequential.
Thus, it is better to be safe than sorry and just ask for all the things so that our value objects are correctly populated. There are indeed suitable times to only request specific fields, but I'd suggest that they are probably only when we need to ask a question about thousands of apps at a time, and then the heap space becomes a concern.
See merge request !328
It is not a particularly expensive operation in the scheme of things. When we
are going to the database, the bottlneck is in disk access for the actual query
of the database tables. The difference between retrieving two columns or
all the columns when the query is for a handful of apps is inconsequential.
Thus, it is better to be safe than sorry and just ask for all the things so
that our value objects are correctly populated.
Translators:
Adrià García-Alzórriz Catalan
Daniel Martí Catalan
Enol Puente Asturian
ezjerry liao Traditional Chinese
Jonatan Swedish
Miss Min Persian
Nutchanon Wetchasit Thai
Sergio Oller Catalan
Permissions UI in AppDetails
* Removes the "m" prefix and some unnecessary TODOs
* Re-uses the permission list from the privileged installer for the list in AppDetails:

See merge request !332
include targetSdkVersion in Apk
In order to work well with the Android 6.0+ permissions, the client needs to know whether an APK has been built against android-23 or higher.
@pserwylo @dschuermann how does this look?
See merge request !323
Check permissions for unattended installer
This PR introduces the class ``ApkVerifier`` which checks the permissions of the downloaded apk file against the expected permissions from the F-Droid listing (``Apk`` class).
* I removed ``AndroidXMLDecompress`` because everything which it has been used for can also be done with ``PackageManager.getPackageArchiveInfo()``, to the best of my knowledge. I even asked in at a similar project why ``PackageManager.getPackageArchiveInfo()``may not be enough: https://github.com/jaredrummler/APKParser/issues/3 It turns out in our case it should do everything we need.
* The code responsible for sanitizing the local apk file and making it world readable has also been moved into ``ApkVerifier`` for now. This can change in a later PR when I introduce the FileProvider for downloaded apks.
We still need to check the target sdk version (see TODO in ``ApkVerifier``). This depends on https://gitlab.com/fdroid/fdroidclient/merge_requests/323
See merge request !322
Remove first-time dialogs for extension installer
The root mechanism will not be useful in 5.1 and later and has been shown to be error prone.
See merge request !330
Port most tests to JVM via Robolectric library
Fixes#607.
This ports all but one of the tests from `app/src/androidTest` to `app/src/test` to be run on the JVM.
I would've liked to port the final one, but it must be run on Android because we are testing the ability of an Android OS to perform symlinks. Also, it is not a bad thing in itself to have tests run on an emulator, just that those which _can_ be run on the host JVM should be. In the future, there will no doubt be other tests which are required to run on the JVM. At the very least, we should be able to run tests on faster emulators now because we are not constrained by the failing provider tests.
This branch required only very minimal changes to the client code, but resulted in the removal of a whole bunch of crappy mocking code that I had to add in order to support testing of the content providers (i.e. navigating around all of the final/hidden/etc apis in Android).
See merge request !327
Like PMD, we also had to add a concession to allow static imports.
This time, it was achieved by moving the assertions to a more generally
named `Assert` class, and then allowing static imports from that.
To appease PMD, we now have a three rulesets in `config/pmd/*.xml`:
* `rules.xml`: The bulk of the rules, used by both main and test code.
* `rules-main.xml`: Rules specific to the andoid client code.
* `rules-test.xml`: Rules specific to test code.
The rationale is because checkstyle by default checks for "too many static
imports", which is a fair call. However in JUnit4 code, it is common to
import many `assert*` static methods.
The tests pass, but there is a lingering message that gets logged:
```
Jun 08, 2016 7:31:13 AM com.almworks.sqlite4java.Internal log
WARNING: [sqlite] [DETACH DATABASE temp_update_db]DB[1][C]: exception when clearing
com.almworks.sqlite4java.SQLiteException: [1] DB[1] reset [no such database: temp_update_db]
at com.almworks.sqlite4java.SQLiteConnection.throwResult(SQLiteConnection.java:1309)
at com.almworks.sqlite4java.SQLiteConnection.throwResult(SQLiteConnection.java:1282)
at com.almworks.sqlite4java.SQLiteConnection.cacheStatementHandle(SQLiteConnection.java:1211)
at com.almworks.sqlite4java.SQLiteConnection.access$900(SQLiteConnection.java:54)
at com.almworks.sqlite4java.SQLiteConnection$CachedController.dispose(SQLiteConnection.java:1606)
at com.almworks.sqlite4java.SQLiteStatement.dispose(SQLiteStatement.java:187)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections$4.call(ShadowSQLiteConnection.java:421)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections$6.call(ShadowSQLiteConnection.java:449)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections$6.call(ShadowSQLiteConnection.java:443)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
```
The `temp_update_db` is the one used for repo updates, but I thought that it
correctly gets dropped/detached by the `TempAppProvider` when required. In fact,
given the nature of the error message (no such database: temp_update_db), that
hints at the fact that it is indeed dropped. I'm struggling to figure out what
causes this, but it should not be harmful to the running of the tests. If a test
actually fails, then it is picked up correctly by JUnit.
Many of the `Mock*` classes are there to deal with idiosyncrosies of
the Android SDK, including `final`/package local/`@Hide` annotations/etc.
They are no longer required with robolectric tests.
Get around silly `final` methods in `ContentResolver` with Mockito and `delegatesTo`.
The Robolectric library presumes that people always want to test content providers by
manually invoking the `query`/`update`/`delete` methods on the `ShadowContentResolver`.
While that is a great feature for testing, we have helper methods that require testing,
and these methods accept either a _real_ `ContentResolver` or `Context`. Robolectric
did some cool magic in terms of intercepting runtime calls to content resolvers and
forwarding them to the "shadow" verison, to deal with final/package private/etc methods.
However, as a side effect, the `ShadowContentProvider` _is not a `ContentProvider` as
far as the Java compiler is concerned.
By utilising Mockito + `delegatesTo` method, we are able to achieve what is required:
* An actual `ContentProvider` instance.
* It forwards calls to the `ShadowContentProvider` provided by Robolectric.
Robolectric provides testing support for Android via the JVM, including testing
of content providers. In order to get these tests to work, we need to avoid
the default behaviour of starting up FDroidApp.onCreate(). This method has a lot
of static state which fails if set multiple times. Instead of trying to ensure
we correctly zero out that state each test, it is preferable to instead never
bother with that in the first place. Expecially when that is not what is under
test (as is the case with content provider tests).
This makes testing of the function easier, as the method previously expected
a real file to exist on disk for which it could then hash. This instead allows
mock hash values to be inserted when under test.
Other than this, the semantics remain exactly the same as before, and the
expensive hashing is still done on a worker thread as part of the `IntentService`.
Translators:
Ab Arabic
Adrià García-Alzórriz Catalan
Adrià García-Alzórriz Spanish
ageru French
Ajeje Brazorf Sardinian
ezjerry liao Traditional Chinese
Francesco Giordano Italian
Frank Ludviksson Spanish
Helder Santana Portuguese (Brazil)
Kristoffer Grundström Swedish
Licaon Kter Romanian
Marian Hanzel Slovak
Massimiliano Caniparoli Italian
Mladen Pejaković Serbian
Mutante Citta Italian
naofum Japanese
Olexandr Nesterenko Ukrainian
Prasanna Venkadesh Tamil
Sérgio Marques Portuguese (Portugal)
Tobias Bannert German
Verdulo Esperanto
Verdulo Polish
The weblate commits now take two forms:
Translated using Weblate ($LANG)
Added translation using Weblate ($LANG)
Adapt the regex to reflect both, avoiding the latter form from screwing
everything up.
Service crash fixes
3 relatively simple crash fixes. The two related to `WifiStateChangeService` have already be included in `stable-0.100`, 7385d320b42af960be63c9c179e1cbf186c1398a should be cherry-picked into `stable-0.100` after this is merged.
I already have 198ad843c1fabc8cf57ffe85c77230288cd6d7a4 ready in my stable-0.100 branch
See merge request !317
Changes made during CR of 299
Here is a collection of small changes I implemented while CRing 299. The most interesting is probably the nice opportunity to use a little bit of RX. This is a good example of where it is a useful API, to "debounce" requests by 1 second (i.e. collect all requests but only respond to the last one after X time units). The `PublishSubject` sits there for the duration of the service, and passively receives events when required. However, it only emits events to the subscriber after one second because before subscribig we ask it to debounce events.
See merge request !319
Historically the providers were responsible for notifying about inserts/deletes
for this table. However this is no longer the case with the new service responsible
for throttling the rate with which these notifications occur.
The `parseApp` method was previously accepting an `Intent`, which could
have been anything. Given it was only used once, this now pushed the
creation of that `Intent` into the `parseApp` method, and also reduced the
visibility of the method as it is only used once at time of writing.
This should not be a particularly expensive opperation,. Also, at time of
writing it is only used in a background thread, and only used once in that
thread (i.e. not in a loop or anything like that).
Now that we have RX as a dependency, it can be used as a nice concise way to
achieve certain tasks. Rate limiting is one thing it does well - via the
`debounce` mechanism:
http://reactivex.io/documentation/operators/debounce.html
The semantics of this code is the same as before, limiting content change notifications
to one per second.
Since refactoring the installed app cache stuff, these methods are no longer
required for testing purposes. This is because the tests directly ask the
content provider to insert relevant apps, rather than testing the broadcast
receiving functionality.
InstalledAppProviderService
I worked a bunch to get the swap code to represent the lifecycle of things better. This includes making `SwapService` stay alive as long as anything related to swap is running. `SwapService` is then becomes the one thing that stores all the state of swap, then the state does not need to be stored in `SwapWorkflowActivity` or any of the swap views.
I would like to include this in 0.100, but only if y'all think it won't delay the release. These changes are pretty much entirely contained in the swap stuff. There is some changes to `App`, but those are in the `App(Context context, PackageManager pm, String packageName)`, which is the constructor that is only used for the swap stuff. It also touches `RepoXMLHandler` but that change only adds `public` to the `IndexReceiver` interface.
See merge request !299
caught java.lang.StringIndexOutOfBoundsException:
at java.lang.String.substring(String.java:1651)
at java.lang.String.subSequence(String.java:2040)
at org.fdroid.fdroid.data.App.setFromPackageInfo(App.java:298)
at org.fdroid.fdroid.data.App.<init>(App.java:268)
at org.fdroid.fdroid.localrepo.CacheSwapAppsService.onHandleIntent(CacheSwapAppsService.java:78)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:59)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.os.HandlerThread.run(HandlerThread.java:60)
This adds a check of whether the database has the current APK in it, based
on PackageInfo's lastUpdateTime field. This avoids recalculating the hash
of the whole APK, which is quite time and resource intensive.
The APK hash is useful for comparing whether something is exactly the same
file as something else. For example, to compare whether the installed APK
matches something that f-droid.org hosts. The "last update time" is a fast
way to check whether the information is current.
InstallAppProviderService now processes install and delete events one at a
time, where InstalledAppCacheUpdater made a batch of changes which it ran
all at once. This means that InstallAppProviderService will send out a
flood of notifications when first initializing, since it will index every
single installed app and send a notification for each one. This makes the
GUI lock up. This commit puts a rate limit on those notifications if they
start coming fast. They are limited to one per second.
InstalledAppCacheUpdater was a custom Service-like thing with some
threading issues. InstalledAppProviderService is an IntentService that
relies on the built-in queue and threading of the IntentService to make
sure that things are processed nicely in the background and one at a time.
This changes the announcing so that each app added/changed/deleted triggers
a new annoucement. This keeps the UI more updated, and makes the Installed
tab show something as soon as possible, rather than waiting for the all of
the install apps to be processed. This becomes more important as more
stuff is added to InstalledAppProvider, like the hash of the APK.
This also strips down and simplifies the related BroadcastReceivers.
BroadcastReceivers work on the UI thread, so they should do as little work
as possible. PackageManagerReceiver just rebadges the incoming Intent and
sends it off to InstalledAppProviderService for processing.
The SwapService is the central container for all things swap. If anything
at all related to swap is active, then SwapService needs to be running.
That also means that stopping SwapService should stop all things related to
swapping, including any screens or notifications.
fixes#258https://gitlab.com/fdroid/fdroidclient/issues/258
Since it takes a chunk of time to generate and write the app index.jar when
swapping apps, this service starts running in the background immediately
when SwapService starts. It first indexes the installed apps that were not
cached, then caches apps based PACKAGE_ADDED broadcasts. It does not index
system apps, since there are many and they are rarely swapped.
If the install process is interrupted, then InstallManagerService is no
longer managing it. It will make the announcements and set the
notification, then forget about that APK.
InstallManagerService and DownloaderService both use the download URL as
the unique ID to represent a given APK install through the whole lifecycle
of the install and download process. This converts the installer stuff to
use the same semantics. A Uri instance is mostly used there because its
the most useful format, but ultimately, the String, Uri, and int all derive
from the exact same URL. This then removes the local APK URI from use in
the installer broadcasts.
While I normally think reusing terms from Android is the best thing to do,
"originating URI" drives me nuts because it is almost nonsense English.
"Originating" is a verb in the continuous form, meaning that it is an
action that is ongoing. A URI is a static thing, and in this case, a URI
that points to a file that is completely downloaded. I left the term in
place for DefaultInstaller because it wraps PackageManager, which is where
that term originates.
This handles "Use strings instead of Uris in InstallManagerService for
urlString" as listed in #680
IntentServices can get a null Intent if they are restarted after being
killed. So this should be properly handled.
"[The intent] may be null if the service is being restarted after its
process has gone away, and it had previously returned anything except
START_STICKY_COMPATIBILITY."
https://developer.android.com/reference/android/app/IntentService.html#onStartCommand(android.content.Intent,%20int,%20int)
ANDROID_VERSION=5.1.1
APP_VERSION_NAME=0.99.2
BRAND=samsung
PHONE_MODEL=SM-G901F
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Intent.getStringExtra(java.lang.String)' on a null object reference
at org.fdroid.fdroid.UpdateService.onHandleIntent(UpdateService.java:342)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.os.HandlerThread.run(HandlerThread.java:61)
Some of these devices do shitty things.
htc_europe HTC EVO 3D X515m
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:278)
at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:864)
Caused by: java.lang.NullPointerException
at java.net.NetworkInterface.getNetworkInterfacesList(NetworkInterface.java:286)
at java.net.NetworkInterface.getNetworkInterfaces(NetworkInterface.java:262)
at org.fdroid.fdroid.net.WifiStateChangeService.setIpInfoFromNetworkInterface(WifiStateChangeService.java:202)
at org.fdroid.fdroid.net.WifiStateChangeService.access$300(WifiStateChangeService.java:37)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:99)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:71)
at android.os.AsyncTask$2.call(AsyncTask.java:264)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
... 5 more
java.lang.NullPointerException
at java.net.NetworkInterface.getNetworkInterfacesList(NetworkInterface.java:286)
at java.net.NetworkInterface.getNetworkInterfaces(NetworkInterface.java:262)
at org.fdroid.fdroid.net.WifiStateChangeService.setIpInfoFromNetworkInterface(WifiStateChangeService.java:202)
at org.fdroid.fdroid.net.WifiStateChangeService.access$300(WifiStateChangeService.java:37)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:99)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:71)
at android.os.AsyncTask$2.call(AsyncTask.java:264)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:864)
java.lang.NullPointerException: Attempt to invoke virtual method
'android.os.Parcelable android.content.Intent.getParcelableExtra(java.lang.String)' on a null object reference
at org.fdroid.fdroid.net.WifiStateChangeService.onHandleIntent(WifiStateChangeService.java:56)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.os.HandlerThread.run(HandlerThread.java:61)
fixes#559
InstallerService
This merge request mainly introduces the ``InstallerService``. Many files have been touched and reworked in this merge request, due to the following changes:
* After download of an apk in ``InstallManagerService``, the ``InstallerService``is started an kicks off the installation process. For unattended installers this directly runs through without any user interaction, for the default installer a new PendingIntent containing ``DefaultActivityInstaller`` is returned that is either stuffed into the notification or directly started from ``AppDetails``
* Using local broadcasts, ``InstallManagerService`` and ``AppDetails`` are informed of state changes in the installation process
* ``DefaultActivityInstaller`` is a wrapper around the default installation APIs of Android
* If the unattended ``PrivilegedInstaller`` is available, a permission screen is shown before download
* Actual error codes and messages are displayed in notification or dialog on fail, especially interesting when using the ``PrivilegedInstaller``
* The process for installing the Privileged Extension has been moved into an own installer for logic seperation, called ``ExtensionInstaller``
Some design considerations:
* I try to use Uris where ever possible. At some points this clashes with the usage of ``urlString`` in ``InstallManagerService``. This could be fixed in a later merge request
Some other TODOs are left, but I would like to do them after this merge request has been merged if it's okay, as this one is already too huge:
* Check if apk permissions are the same as announced in the permission screen for ``PrivilegedInstaller``
* In ``Installer.newPermissionCount()``, I need the target SDK before download to check if it's targetting Android M, which does not require the permission screen
* Introduce FileProvider for Android N
* Redesign layout of ``InstallConfirmActivity``
* Remove "cancel" icon for installing progress in AppDetails
See merge request !300
The onConnected callback of ServiceConnection is always
executed on the main looper of the context that is used
to create the service binding. Thus the old code resulted
in a deadlock and then in a timeout of the Thread.wait()
method.
The check for permissions is now called inside install and
uninstall callbacks, where it works asynchronously.
app.uninstallable is only used in AppDetails. It is only set when
generating App instances from installed APKs for the swap stuff. Since it
is initialized to false and used as !app.uninstallable, it is always true
when used. So it was doing nothing.
This needs to be thought out more so this is not entirely complete for
#628. AppDetails needs to know whether its a system app to provide proper
feedback and swap needs to know whether its a system app with an update
installed, otherwise it should ignore it.
Decent Java editors have all sorts of nice ways to show javadoc comments,
whether they are for public or private APIs. So comments should be in that
format whenever possible.
To generate swap's index.jar, lots of information about all the installed
APKs needs to be parsed. That can take a long time. Some of that can be
stored in InstalledAppProvider. This prepares for those changes.
Also, turns out that packageInfo.applicationInfo provides enough info, so
there is no need to use pm.getApplicationInfo(). And the metadata from
GET_META_DATA was not even being used.
The previous logic was putting the header on some 4" screens while not
putting it on a 7" tablet. Tested with:
* Samsung Galaxy Tab 3 7"
* Azpen A727 7"
* Xiaomi 4.5"
* Lenovo 4"
* emulators...
Use initLoader instead of resetartLoader. Call onResume after setting category.
While working on !311, I had some logging in place in the `AppProvider`. I ended up straying into an investigation of issue #606 and noticed with my logging that we have been doing more queries than required for some time, because the loader was being forceably recreated when it could instead be reused in onResume.
Perhaps more importantly, we were querying twice for list of apps to display. The first time is "Give me all the apps" because we haven't set the category yet. Then we set the category, which causes us to ask for "Apps matching this category". The fix for this is for the `onResume` method (which results in a cursor being created) to be called _after_ setting the selected category is set. This ensures that the query is run has the correct category the first time, and needn't be run again.
I am not confident that this fixes the issue in #606, but I haven't seen it reproduced since I've implemented this fix.
See merge request !312
While investigating the infamous issue #606, I noticed these two things which
were a little off. Firstly, we were doing more queries than required, because the
loader was being forceably recreated when it could instead be reused in onResume.
Also, the onResume method (which results in a cursor beign created) should be
called _after_ setting the selected category. This ensures that the query
which is run has the correct category the first time, and needn't be run again.
I am not confident that this fixes the issue, but it seems to help, and I believe
it is the correct thing to do even if it doesn't fix 606.
My guess is that is from IPv6, but those should be filtered out in this
code before it gets to the crash point. Here's the stacktrace:
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:300)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
Caused by: java.lang.IllegalArgumentException: Value [64] not in range [0,32]
at org.apache.commons.net.util.SubnetUtils.rangeCheck(SubnetUtils.java:339)
at org.apache.commons.net.util.SubnetUtils.calculate(SubnetUtils.java:264)
at org.apache.commons.net.util.SubnetUtils.<init>(SubnetUtils.java:51)
at org.fdroid.fdroid.net.WifiStateChangeService.setIpInfoFromNetworkInterface(WifiStateChangeService.java:222)
at org.fdroid.fdroid.net.WifiStateChangeService.access$300(WifiStateChangeService.java:37)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:99)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:71)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
... 4 more
java.lang.IllegalArgumentException: Value [64] not in range [0,32]
at org.apache.commons.net.util.SubnetUtils.rangeCheck(SubnetUtils.java:339)
at org.apache.commons.net.util.SubnetUtils.calculate(SubnetUtils.java:264)
at org.apache.commons.net.util.SubnetUtils.<init>(SubnetUtils.java:51)
at org.fdroid.fdroid.net.WifiStateChangeService.setIpInfoFromNetworkInterface(WifiStateChangeService.java:222)
at org.fdroid.fdroid.net.WifiStateChangeService.access$300(WifiStateChangeService.java:37)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:99)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:71)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
This is very related to #660 but this time, I can't see any other way to
solve it but a null guard. I don't think it is possible to guarantee that
the Downloader.ACTION_INTERRUPTED receiver will be unregistered since
onDestroy() might not even be called.
java.lang.NullPointerException
at org.fdroid.fdroid.installer.InstallManagerService.removeFromActive(InstallManagerService.java:328)
at org.fdroid.fdroid.installer.InstallManagerService.access$400(InstallManagerService.java:58)
at org.fdroid.fdroid.installer.InstallManagerService$4.onReceive(InstallManagerService.java:212)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5353)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:830)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:646)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
at dalvik.system.NativeStart.main(Native Method)
android.bluetooth.BluetoothSocket.isConnected() is only 14+
java.lang.NoSuchMethodError: android.bluetooth.BluetoothSocket.isConnected
at org.fdroid.fdroid.net.bluetooth.BluetoothConnection.open(BluetoothConnection.java:37)
at org.fdroid.fdroid.net.bluetooth.BluetoothClient.openConnection(BluetoothClient.java:31)
at org.fdroid.fdroid.net.BluetoothDownloader.<init>(BluetoothDownloader.java:30)
at org.fdroid.fdroid.net.DownloaderFactory.create(DownloaderFactory.java:56)
at org.fdroid.fdroid.RepoUpdater.downloadIndex(RepoUpdater.java:97)
at org.fdroid.fdroid.RepoUpdater.update(RepoUpdater.java:131)
at org.fdroid.fdroid.UpdateService.onHandleIntent(UpdateService.java:377)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:59)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.os.HandlerThread.run(HandlerThread.java:60)
When building APK instances from installed apps, the minSdkVersion and
maxSdkVersion needs to be parsed directly from the APK's Android Manifest,
since PackageManager does not provide a method to get it how we need it.
Previously, the whole AndroidManifest.xml file was parsed entirely twice,
once for minSdkVersion then for maxSdkVersion.
The original logic had maxSdkVersion=0 meaning infinity. That was changed
to be a very large value SDK_VERSION_MAX_VALUE, but getMinMaxSdkVersion()
was still returning 0 for APKs where maxSdkVersion was not set.
This is a follow up on fc0df0dcf4dd0d5f13de82d7cd9254b2b48cb62d
Apparently, the CREATOR field is not (yet?) needed in the tests, since
they work without it. This gets us closer to making lint errors fail
the CI builds.
closes#580
notification update improvements
Ok, I made a mistake in c35d327fa4c503425c629d49778c001b08ecb833 / !301 that messed up installing. Turns out it was a simple fix: 0838a15792fd70afde4e3743296ce88c961a299e This also removes the _Cancel_ button from the one active download notification to provide a working, albeit verbose, notification UX for 0.100.
After this commit, these commits and the ones from !301 should be included in `stable-0.100` and a new alpha tagged :)
See merge request !306
This is the recommended way to deal with updating Notifications. Each new
update should be a new Notification, but the same Builder instance should
be used to generate each new Notification instance.
New installs where being caught up in the logic to check whether a download
is still in progress after InstallManagerService got killed. Also checking
whether Intent was just redelivered lets the new installs through while
screening out the inactive Intents that were redelivered. This logic also
cancels the notification for any download that was in progress when the
InstallManagerService was killed.
#660
Check repo index timestamps
The Update Framework documents provide a [nice discussion of possible attacks](https://github.com/theupdateframework/tuf/blob/develop/SECURITY.md) against update systems. One example is a "rollback attack", where the attacker just serves the old signed `index.jar` to keep all clients from updating their apps. That allows the attacker to exploit known vulnerabilities in those un-updated apps.
While this is a reasonably hard attack, this fix is an important step towards removing the requirement for trusting the web server operator. Ultimately, it should be able trusting the index signing key only. Then it doesn't matter were the files come from, it just matters that they are verifiably signed by the index signing key.
This does not address "freeze attacks" since it allows an index update with the same timestamp. I did that deliberately to slowly ramp up the security checks in order to avoid problems along the way. Code-wise, blocking freeze-attacks is mostly a matter of changing the timestamp check from `<` to `<=`.
See merge request !302
Couple more 0.100 fixes
This is a couple more bug fixes towards finally releasing 0.100. These should be cherry-picked into `stable-0.100`.
See merge request !301
Installer instances always copy the APK to a safe place to run the install
from. That copy needs to be deleted. Until we have the whole lifecycle in
InstallManagerService, we need this hack. It should be handled on the
broadcast from InstallerService to say that its complete.
#611!300
Including the App and Apk instances in the Intent that starts
InstallManagerService ensures that the needed data is present in the
Service no matter what happens outside of the Service. For example, if the
index is updated or cleared while an install is in progress, the install
process still needs to know the name and packageName of the app to update
the Notification.
A cleaner but more labor-intensive way to implement this would be to make
App and Apk properly implement the full Parcelable interface. That would
require tests to check that the Parcelable methods have all the same fields
as toContentValues() and the database.
closes#660https://gitlab.com/fdroid/fdroidclient/issues/660
A hacked fdroid server could "replay" old index.jar files known to have
apps with vulnerabilities in it. That provides a long window of time for
exploiting that vulnerability. By checking that the timestamp of an update
is never older than the current index, this attack is prevented.
Its officially possible to have the ROM's filesystem with any name. While I
have never seen that in practice, Android does provide an easy method to
get the real name. Plus this should help avoid typos and the like, and
make it easy to track things that rely on that filesystem path.
This makes it easy to tell which debug build a device is running, since the
versionName now automatically describes the exact commit that was built,
based on `git describe`, e.g.: 0.100-alpha7-33-gc2e8e8a
For release builds, i.e. builds from commits that are tagged as releases,
the versionName will be just the tag name: 0.100-alpha8
closes#664https://gitlab.com/fdroid/fdroidclient/issues/664
For some odd reason, something is sending a URL to be downloaded that then
results in a null Apk instance. My first guess was because it was being
canceled, but the interrupted receiver is not even registered yet. My
second thought is that something is sending a download and cancel Intent at
the same time. In any case, its something to keep in mind when reworking
InstallManagerService once InstallerService comes along.
closes#660https://gitlab.com/fdroid/fdroidclient/issues/660
The App(Context context, PackageManager pm, String packageName) constructor
was not setting App.icon, which is required for lots of things. This makes
it always get set, since its just a standard file name, and it does not
have to even exist yet.
When reworking this in 7f10be18c6dd0b69e2fdbae98d09b197e60af443, I confused
the "Processing" with the "Downloading", probably because I thought those
steps were combined, but they are not. Also, I forgot that Downloader
instances do not broadcast status. So its just a matter of setting up the
right ProgressListeners.
https://gitlab.com/fdroid/fdroidclient/issues/633
Since Intents can come in any time, whether WifiInfoThread is running or
not, the global static vars for storing the WiFi settings info should only
be updated from the WifiInfoThread. Otherwise, the WiFi settings could be
nulled out between the time of the null guard and the execution in code
like this:
if (!TextUtils.isEmpty(FDroidApp.ipAddressString) && netmask != null) {
FDroidApp.subnetInfo = new SubnetUtils(FDroidApp.ipAddressString, netmask).getInfo();
fixes#589https://gitlab.com/fdroid/fdroidclient/issues/589
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:304)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.IllegalArgumentException: Could not parse [null/24]
at org.apache.commons.net.util.SubnetUtils.calculate(SubnetUtils.java:275)
at org.apache.commons.net.util.SubnetUtils.<init>(SubnetUtils.java:62)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:89)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:70)
at android.os.AsyncTask$2.call(AsyncTask.java:292)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
... 4 more
java.lang.IllegalArgumentException: Could not parse [null/24]
at org.apache.commons.net.util.SubnetUtils.calculate(SubnetUtils.java:275)
at org.apache.commons.net.util.SubnetUtils.<init>(SubnetUtils.java:62)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:89)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:70)
at android.os.AsyncTask$2.call(AsyncTask.java:292)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Correctly expand list of nearby people to the entire height of the screen.
NOTE: To be cherry-picked into stable-v0.100
If the device is small, then the "Conenct and trade apps with people
near you" header takes up too much space and we end up not being able
to see any nearby people at all, even if they are in the list. As
such, this also removes that header for "small" and "ldpi"
devices. During testing I found that "small" was not enough, because a
240x400 screen is considered "medium" and there is not enough
space. ldpi seems to be a reasonable metric for "that header is going
to be taking valuable space and should not be shown then".
All larger devices retain the header and seem to look nice.
This also pushes the "Can't find what you're looking for?" message and
associated buttons right to the bottom of the screen. This is more in
line with the original design.
Fixes#604.
Screenshots
No good
The image below shows a 240x400 device. This falls into the "medium"
screen size, and so would include the header if I only used
"layout-small". As such, I also removed the header for
"layout-ldpi". It doesn't look absolutely terrible, but remember that
this is an emulator without Bluetooth, and as such it is missing a UI
element for enablign Bluetooth which would take up the remaining real
estate:
emu-240x400
Good
Large devices work well now, with all of the empty space below
available for the "Nearby Devices":
large_device
Also, small devices should exclude the header and provide just enough
space for the list of pears (as shon by this 240x320 device):
emu-240x320dp
Here is a more normal sized phone (my Moto X 2nd gen with normal DPI
settings):
moto_x_2nd_gen__largish_
closes!291
See merge request !291
If the device is small, then the "Conenct and trade apps with people near you"
header takes up too much space and we end up not being able to see any nearby
people at all, even if they are in the list. As such, this also removes that
header for "small" and "ldpi" devices. During testing I found that "small" was
not enough, because a 240x400 screen is considered "medium" and there is not
enough space. ldpi seems to be a reasonable metric for "that header is going to
be taking valuable space and should not be shown then".
All larger devices retain the header and seem to look nice.
This also pushes the "Can't find what you're looking for?" message and associated
buttons right to the bottom of the screen. This is more in line with the original
design.
Fixes#604.
CI: Add android-10 connectedCheck task
GitLab doesn't currently support any kind of build or env matrix, so we
have to duplicate this code. This is due to change in upcoming releases,
at which point I'll fix this.
See merge request !293
Ensure `FLAG_AUTO_REQUERY` is used for main app list adapters.
**NOTE: This has a sister MR which is targeting the `stable-v0.100` and thus should *not* be cherry-picked across to that branch.**
Although this is deprecated, and will result in queries being run on the main thread, it is required to fix#606. In the future, `LoaderManagers` should be used to address the concerns of querying on the main thread.
See merge request !294
Although this is deprecated, and will result in queries being run on the
main thread, it is required to fix#606. In the future, `LoaderManagers`
should be used to address the concerns of querying on the main thread.
GitLab doesn't currently support any kind of build or env matrix, so we
have to duplicate this code. This is due to change in upcoming releases,
at which point I'll fix this.
Don't allow download notifications to be cancelled, other than our "Cancel" action on them.
*NOTE: To be cherry-picked into stable-v0.100*
As a user, I expect removing a notification of a pending action to cancel that pending action. This change makes the pending notifications uncancellable, unless users use our cancel action added to the events by @paresh. If the user is on an older device that doesn't support these type of rich notifications, then they will need to touch the pending download notification, which will take them to the `AppDetails` activity for that app. From there, they can cancel the install, which will remove the app from the pending download queue.
Until the notifications are reworked, this is the simplest solution. In the near future, the notifications will likely be combined into one more intelligent notification with better defined semantics.
See merge request !292
Until the notifications are reworked, this is the simplest solution.
In the near future, the notifications will likely be combined into one
more intelligent notification with better defined semantics.
Installer manager cosmetic changes
This branch as some cosmetic code changes related to the `InstallManagerService`. I have a few more that I'll be throwing in here too as I go along. By and large, it is renaming, annotating, minor refactoring. The semantics should not change at all with this change, any fixes to the `InstallManagerService` will be done through separate MRs. They are from issues listed in [this comment](https://gitlab.com/fdroid/fdroidclient/merge_requests/278#note_11762414) during my CR and testing of !278.
I realise that cosmetic changes often come with baggage because we all have slightly different ideas of what Java code should look like, but in general, I will strive to only make changes if I believe strongly that they will:
* Improve understanding of the code (i.e. renaming, extracting methods)
* Prevent future errors by new contributors (e.g. @NonNull/@Nullable annotations, extracting classes to encapsulate logic which requires developers to know they need to change multiple things at once)
I try not to make changes because:
* I have a different opinion about formatting.
* Hopefully any other unreasonable or disagreeable reason.
See merge request !282
Type parameters can be ommited if defined and declared in same statement.
`onStart()` is deprecated and not required, as we target APIs > 5.
`Intent.FLAG_ACTIVITY_CLEAR_TASK` is not supported on APIs < 11 but we target 8.
WifiStateChangeService fixes
`WifiStateChangeService` had a number of issues, including being run often when there was no change, and being run twice at start-up. Also, its _complete_ broadcast was being sent twice, in effect. That made for a lot of flaky behavior for the things that rely on that information, namely wifi swap.
See merge request !289
Before, it would change fields in a final Repo instance, which means that
things could be out of sync when accessed. Now it swaps out the old one
with a new Repo instance in one step.
The local repo variables are now declared volatile so that they are more
predictable when accessed from various threads (WifiStateChangeService,
SwapService, etc.)
askServerToSwapWithUs(NewRepoConfig) was unused, so I removed it.
The IntentService provides the nice incoming Intent queue. It also runs
the Intent in a thread, so even the initial check is now in a very low
priority thread. The queuing prevents the incoming Intents from competing.
This also simplifies the code since the lifecycle is more automatic now.
The `android:process` statement in AndroidManifest.xml causes another
process to be created to run CrashReportActivity. This was causing lots of
things to be started/run twice including CleanCacheService and
WifiStateChangeService.
Lint says that only GET_META_DATA and GET_SHARED_LIBRARY_FILES are allowed.
This contradicts Android's documentation where GET_UNINSTALLED_PACKAGES
is also allowed.
Fixes#605
This was crashing when coming to SwapAppsView because some of the flow
changed related to the new DownloaderService and InstallManagerService.
Also, this lazy loading is a tiny optimization that we cannot afford right
now, there are far too many lifecycle bugs with swap.
Be more explicit about what we're running. This also means that we wont
run the "read log here" stuff if the build failed, which didn't make any
sense. That should only be run if the unit tests fail.
Advantages:
* Failing unit tests don't keep the android tests from running
* CI should be overall faster as the tasks get run in parallel and the
former `gradle` task was the longest by far
This will later simplify the multiplexing of the android tests onto
multiple emulators.
Enable a few more PMD rules
I did one last pass over https://pmd.github.io/pmd-5.4.1/pmd-java/rules/index.html and these are the only sane rules left that I could find.
So I'm pretty much done with adding PMD stuff :) Don't know if anyone feels strongly in favour of adding findbugs, but I think checkstyle+PMD is good for now.
See merge request !283
Enable PMD java-basic
This is the fixes necessary to enable PMD's `java-basic` ruleset. I think there will be a few rules in there that will largely be annoying, so we'll need to ultimately decide whether to use `// NOPMD` or just specify the rules we want from `java-basic`.
See merge request !280
This should fix the PMD error:
"Check the value returned by the skip() method of an InputStream to see if
the requested number of bytes has been skipped."
Installer manager fixes
Builds on the recently merged `InstallManagerService` to fix a few minor UX bugs:
* Cancellation of pending downloads now removes notifications.
* No longer shows "Downloading Downloading {AppName}" in notification, just "Downloading {AppName}"
* If cached file is same size but corrupted, remove it and then continue with the process of downloading + installing
See merge request !281
The check was set up to only cancel when the `AppDetails` for that app
was shown. This is the correct behaviour for the 'complete' event, but
not the cancel. The cancel event should always result in the relevant
notification being removed.
If we are capable of bailing earlier rather than later, then we should. This way,
if a hash doesn't match, the file will be removed and a new download will begin,
as expected. The alternative is to let the installer catch the unmatching hashes.
By then though, it is too late to really do anything meaningfull and it becomes
more difficult to recover in a way that the user would expect.
Due to the earlier refactoring of `getNotificationTitle()` (or something like that)
to `getAppName()`, it was still returning `getString(downloading_apk, appName)` instead
of just the app name.
InstallManagerService
This provides an over-arching `Service` for managing the whole install process, from checking the cache, downloading files, handling the notification. Ultimately, it should probably also handle starting and tracking progress of the final installation steps.
Note: this does undo some of the `Notification` handling stuff, putting it back to one notification per APK. I did that to get that part working OK for the short term, giving us time to figure out what the whole picture should look like. I think @pserwylo has it pretty well sketched out in #592. But I have no strong feelings about the notification stuff for 0.100, so I'm happy to shape this MR accordingly, provided its only a little work.
See merge request !278
I wrestled with this a bunch, it seems quite difficult to make the Cancel
button on the notification responsive. This collection of minor changes
made it more reliable, but its still kind of flaky. I think the problem
might be related to the fact that it is creating a whole new Notification
instance, with the accompanying Intent and PendingIntent instances, for
every single download progress update.
closes#652https://gitlab.com/fdroid/fdroidclient/issues/652
If an APK was queued to download but had not started downloading yet, it
was not able to be fully canceled because ACTION_INTERRUPTED was not sent.
That meant that the UI never got updated, even though the APK was removed
from the queue.
#652https://gitlab.com/fdroid/fdroidclient/issues/652
This logic is pretty basic for now, it'll have to be expanded a lot to
support the different UX between priv and non-priv installs. For example,
priv updates will be able to happen entirely in the background. Those will
then require leaving a notification to tell the user that the app was
updated so nothing can transparently install updates without the user
knowing. When the user is an active part of each install, like the
non-priv experience requires, then keeping the "app installed" notification
feels like just extra noise.
The Event class is no longer needed once there is specific ProgressListener
instances for each type of progress update. The sourceUrl serves as the
unique ID, like with DownloaderService and InstallManagerService.
fixes#633https://gitlab.com/fdroid/fdroidclient/issues/633
This allows the install process to have consistent data, even if the index
database changes while an APK is making its way through this process.
This also provides a set of packageNames to be easily queried.
Standardizing on Apk as the internal data type means that most of the data
that is needed for the whole lifecycle of a given APK going through this
process will be available regardless of the database changes. Once App
instances are also included, then all of the data should be available
separately from the database. This is important to support parallel
operation. The index could be updated and an app could disappear while an
APK of that app is being downloaded. In that case, it should not show
blank notifications.
Also, in AppDetail, the Apk instance is completely loaded from the db, so
there should not be any nulls on the essential bits like packageName and
download URL.
This keeps the IDs standard throughout the code: either urlString when it
should be a String, or urlString.hashCode() when it should be an int. It
also follows the naming convention in DownloaderService helper methods,
e.g. getIntentFilter(). "create" to me doesn't necessarily mean also "get"
Using @NonNull or @Nullable is fine when it is actually useful, like the
compiler can catch errors, but it also adds a lot of noise when reading the
code. For example, @NonNull here will just make people avoid thinking.
Context can never be null anywhere in Android, that's a given throughout
the Android API. And in this code, urlString is the unique ID used
throughout the process, so if its ever null, nothing works.
This keeps DownloaderService tightly focused on downloading, and makes it a
lot easier to manage Notifications since InstallManagerService's lifecycle
lasts as long as the Notifications, unlike DownloaderService.
DownloaderService is structured to be as simple as possible, and as tightly
matched to the downloading lifecycle as possible, with a single queue for
all requests to avoid downloads competing for bandwidth. This does not
represent the possibilities of the whole install process. For example,
downloading can happen in parallel with checking the cache, and if an APK
is fully cached, there is no need for it to go through the DownloaderService
queue.
This also lays the groundwork towards simplifying DownloaderService even
more, by moving the Notification handling to InstallManagerService. That
will provide a single place to manage all aspects of the Notifications that
has a lifecycle that is longer than the Notifications, unlike an Activity
or DownloaderService.
Fixes#624.
The `AppDetails` activity was not correctly asking for the active
download url string when being resumed. This change recalculates the
value when being resumed now.
Added assertions that both apkName and repoAddress need to be populated
in order to call `getUrl()`. Also verified that this is the case for all
usages of this method, which it should be. All `Apk` objects which currently
have `getUrl()` called on them are loaded using the `ApkProvider.Helper.findById()`
method without specifying which columns to load (which defaults to all).
Addresses a bug found in MR !273 whereby removing `stopForeground` results
in a persistent "Downloading ..." notification even though it was cancelled.
In the process of doing this, it also addresses / Fixes#621 by ensuring
that all downloads of apks are done in a foreground service, regardless
of the preference used for foreground updater service updating.
DownloaderService fixes
This aims to simplify the `DownloaderService` a bunch to make it easier to understand. It also fixes some bugs with the download queue and related progress reports.
This is groundwork for the upcoming `InstallManagerService` which will manage the whole process of installing an app, from checking the cache, to downloading, to running the install process.
See merge request !273
DownloaderServiceTest is just a stub of a test that doesn't really test
anything yet. It is now causing a NullPointerException, so its a problem:
java.lang.NullPointerException
at org.fdroid.fdroid.net.DownloaderService.notifyDownloadComplete(DownloaderService.java:313)
at org.fdroid.fdroid.net.DownloaderService.handleIntent(DownloaderService.java:287)
at org.fdroid.fdroid.net.DownloaderService$ServiceHandler.handleMessage(DownloaderService.java:104)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.os.HandlerThread.run(HandlerThread.java:60)
This method provides the exact same results as the underlying method it
uses, intent.getStringExtra()
This was added in 0163d6efa6013181c2e6554760e5fa6e67a6daf9
There is already a method for reproducibly generating an int based on
a the contents of a String, thanks to @pserwylo for finding it! So
urlString.hashCode() is used as the ID for Notifications and the
DownloaderService queue (msg.what). This entirely replaces the
QUEUE_WHATS hack. Both requestCode and msg.what just need to be
unique int, and that value can always be generated by the urlString.
This also fixes a bug preventing removing correct URL from Downloader queue.
b66810944fec802aa119c0e5ec8b7875930a2c22 made a change that breaks removing
the correct item from the queue. The `what` value must be fetched based on
urlString and fed to serviceHandler.removeMessages(what). The commit made
it use the `what` value from the last item that was queued. Those will
often be different things.
This also removes all stopForeground(true) calls except for the one in
onDestroy(). The nature of an IntentService, which DownloaderService
is basically a copy of, is to quit running once it is no longer
processing Intents. That is also the time to remove the notification.
Before, these were not being reliably canceled before the final COMPLETE or
INTERRUPTED notification went out. This moves closing the progress Timer
to a finally block after the Timer is setup, which should hopefully
guarantee the progress reports are always stopped.
This prevents any more progress reports from being sent, in case there is
one pending anywhere. downloaderProgressListener needs to be volatile to
ensure that the two threads are seeing the same values.
This was an omission on my part when putting together the DownloaderService
in !248
Having the notification as its own Service is overkill and really only
serves to increase complexity. The notification stuff should not take much
time or resources at all.
use latest android testing support setup
With 2.0 of Android Studio and gradle pluging coming out shortly, I wanted to start trying to use the improved testing setup. So this is a place to track that stuff until it stabilizes.
See merge request !252
Test downloads with actual files, not dynamically generated things.
Testing with the progress reports is really hard with multiple URLs, so
just test progress with a single URL for now, and multiple URLs can still
be tested without the progress check.
fixes#650https://gitlab.com/fdroid/fdroidclient/issues/650
This is the common pattern I've seen in travis-ci builds. It should
speed things up a little bit since the adb connection process will
happen in parallel with waiting for the screen lock to be dismissed.
Two NPE fixes, cleanup
@eighthave I couldn't find why `setExecutable()` isn't used anymore. The fact that it is now unused code begs the question: why don't we need to set the APK download folder as executable anymore?
See merge request !279
Mirror the check already being done in the other fragment. As reported
via ACRA:
ANDROID_VERSION=4.4.4
APP_VERSION_NAME=0.100-alpha6
STACK_TRACE=java.lang.NullPointerException
at org.fdroid.fdroid.AppDetails$AppDetailsHeaderFragment.updateViews(AppDetails.java:1524)
at org.fdroid.fdroid.AppDetails$AppDetailsHeaderFragment.updateViews(AppDetails.java:1519)
at org.fdroid.fdroid.AppDetails.refreshHeader(AppDetails.java:624)
at org.fdroid.fdroid.AppDetails.onAppChanged(AppDetails.java:549)
at org.fdroid.fdroid.AppDetails.access$000(AppDetails.java:100)
at org.fdroid.fdroid.AppDetails$AppObserver.onChange(AppDetails.java:141)
at android.database.ContentObserver$NotificationRunnable.run(ContentObserver.java:180)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5146)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:732)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:566)
at dalvik.system.NativeStart.main(Native Method)
This was very probably introduced with the new CleanCacheService stuff.
Reported via ACRA:
ANDROID_VERSION=6.0.1
APP_VERSION_NAME=0.100-alpha6
STACK_TRACE=java.lang.NullPointerException: Attempt to get length of null array
at org.fdroid.fdroid.Utils.clearOldFiles(Utils.java:349)
at org.fdroid.fdroid.Utils.clearOldFiles(Utils.java:351)
at org.fdroid.fdroid.Utils.clearOldFiles(Utils.java:351)
at org.fdroid.fdroid.CleanCacheService.onHandleIntent(CleanCacheService.java:54)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:66)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.os.HandlerThread.run(HandlerThread.java:61)
There is nothing to clear if the directory could not be obtained for
some reason, so this seems like a reasonable fix. Anything is better
than a crash anyway.
See the following crash reported via ACRA:
java.lang.NullPointerException
at org.fdroid.fdroid.net.DownloaderService.getNotificationTitle(DownloaderService.java:188)
at org.fdroid.fdroid.net.DownloaderService.createNotification(DownloaderService.java:167)
at org.fdroid.fdroid.net.DownloaderService.handleIntent(DownloaderService.java:274)
at org.fdroid.fdroid.net.DownloaderService$ServiceHandler.handleMessage(DownloaderService.java:107)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.os.HandlerThread.run(HandlerThread.java:61)
I'm not sure what the source of the null App was (could be that we
couldn't access the DB for some reason, or perhaps that somehow the app
wasn't in it anymore). In any case, it doesn't hurt to double check
here.
If the app is null, simply fall back to the title without the app name.
For some reason, even when showing the entire description some words at
the end of lines and paragraphs still get cut off. This results in
little parts of the description being completely inaccessible.
Ellipsizing is just cosmetic, so ditch it on 2.X altogether. I tested
and proved that this fixes the issue with an android-10 x86 emulator,
after reproducing the bug.
Keep the ellipsizing on newer versions, as it doesn't misbehave on
those.
Fixes#510.
The only remaining error was ClipboardCompat, which was unnecessarily
exposing three top-level classes. Make the two implementation classes be
nested, private and static.
Installer UI updates and lint fixes
This updates UI classes of the installer to reflect the changes of AOSP's installer and fixes some lint errors.
* No more differentiation between personal and device permissions (didn't even notice that there was a differentiation here...)
* Icon for "other" permission group
I carefully merged only the changes from AOSP that are related to us and decided against maintaining different versions of the AppSecurityPermissions.
There will be more changes and discussions to the installer UI coming in the next days.
See merge request !277
Translators:
Adrià García-Alzórriz Spanish
Alberto Moshpirit Spanish
Danial Behzadi Persian
Enol P Asturian
Kristjan Räts Estonian
Marcelo Santana Portuguese (Brazil)
Mohamad Hasan Al Banna Indonesian
Verdulo Esperanto
Verdulo Polish
checkstyle: bump to 6.18
Also, forgot to simplify the checkstyle setup in the extension after we
moved from an ant folder hierarchy to a gradle one. Do that now.
See merge request !272
CleanCacheService
This creates `CleanCacheService` to do all of the cache clean up at the lowest possible priority. It also adds a preference to set how long to keep cached APKs.
See merge request !260
Updater speed improvements during "Saving Application Details"
I've been able to reproduce #324 OutOfMemory errors on an emulator with 12MiB of heap space. This branch did _not_ have an OOM error when updating with F-Droid and F-Droid Archive repos enabled. They successfully update without problem.
Fixes#45.
The "Saving Application Details" stage of repository updating is where each apk has its suggested version calculated, icon URLs calculated, etc. These all require [correlated subqueries](https://en.wikipedia.org/wiki/Correlated_subquery) resulting in a full scan of the apk table for each row in the app table. This takes in the order of 25 seconds on my Moto X 2nd Gen.
This branch improves this process by doing the queries in an [sqlite in-memory database](https://sqlite.org/inmemorydb.html), with the results transferred to the database on disk when done. The time required drops from 25 seconds to ~0.5 seconds on my device.
*Note:* I was hoping this would also improve the "Processing ..." Part of the udpater, given it was inserting into an in memory table instead of on disk. If it did have any effect, it was negligible though, so that part is still likely slower than it could be. Each 50 apps (and their associated apks) takes between 150ms and 500ms on my Moto X.
*Secondary Note:* When creating the in memory database, I create indexes for some columns as per before. This technically should slow down the inserts we do, however in practice they had almost no effect. As such, I've left the index creation there because it is required for the correlated subqueries to not suck.
See merge request !269
Security-sensitive code should not be changed unless there is a good reason
to do so. It is too easy to introduce bugs. This change does not address
an issue, so I'm reverting it. See comment in javadoc header for the class.
This reverts commit 2074718391c2c17a974218bc6565cce2dc05407e for just the
RepoUpdater.java file.
This schedules CleanCacheService to run regularly, and delete files older
than the value set in the new "Keep cached apps" preference. It auto-
migrates the old "Cache packages" pref to the new one. The default cache
time for people who did not have "Cache packages" enabled is one day.
This moves the cache file deletion to a dedicated IntentService that runs
at the lowest possible priority. The cache cleanup does not need to happen
with any kind of priority, so it shouldn't delay the app start or take any
resources away from foreground processes.
This also changes the logic around the "Cache packages" preference. The
downloader always saves APKs, then if "Cache packages" is disabled, those
APKs are deleted when they are older than an hour.
This also simplifies Utils.deleteFiles() since the endswith arg is no
longer needed.
* if there is a file there, remove it
The paths are all from the system, so are safe. No SanitizedFile is needed.
Plus, this method was not checking if the original and sanitized versions
where different, and instead just creating the sanitized version. I worry
that could cause odd bugs.
By putting these comments into javadoc, they are directly describing the
code where it is, and there are many tools in IDEs for searching, viewing,
sorting, etc. javadoc comments. Plain comments do not have those tools.
Fixes for priv extension install
For now, disable install of privileged extension on Android >=5.1 until we found better methods.
Also, fix crash of dialogs on Android 6 using a workaround for transparent activities and some code simplifications.
See merge request !259
This way, error lines like this one:
Waiting for emulator to start: unknown: error: no devices found
Become:
Waiting for emulator to start: error: no devices found
Otherwise, we'd interpret this as "booting", which is wrong:
$ adb -e shell getprop init.svc.bootanim
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
error: no devices found
This increases the speed of the complex queries required at the end
of the update process to:
* calculate suggested version codes
* figure out icon urls
* etc,
by two orders of magnitude.
This was there as a workaround for #1, but that has subsequently
been fixed. Thus, the hack is no longer required. Also removed an
additional `AND` because it is already performed in the `JOIN`.
I supsect this last one would've been eliminated by the sqlite
optimizer anyway, but the query is slightly simpler now.
This fix doesn't improve performance as much as I'd hoped, but it
is something.
Translators:
Danial Behzadi Persian
fastest noob Turkish
Fert Bálint Hungarian
Green Lunar Hebrew
Jean-Baptiste French
Karola Marky Japanese
Kristjan Räts Estonian
Licaon Kter Romanian
Marian Hanzel Slovak
Mohamad Hasan Al Banna Indonesian
naofum Japanese
Tobias Bannert German
YFdyh000 Simplified Chinese
Added cancel action to notifications.

See merge request !267
This happened to me when I clicked on an app link for a new app which
was already in the repo, but I had not ran an index update yet since the
app got built. Stack trace prior to the fix follows.
java.lang.RuntimeException: Unable to destroy activity {org.fdroid.fdroid/org.fdroid.fdroid.AppDetails}: java.lang.NullPointerException:
Attempt to invoke virtual method 'void android.support.v4.content.LocalBroadcastManager.unregisterReceiver(android.content.BroadcastReceiver)' on a null
object reference
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3865)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3883)
at android.app.ActivityThread.-wrap5(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1417)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5461)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.support.v4.content.LocalBroadcastManager.unregisterReceiver(android.content.BroadcastReceiver)' on a null object reference
at org.fdroid.fdroid.AppDetails.unregisterDownloaderReceivers(AppDetails.java:469)
at org.fdroid.fdroid.AppDetails.onDestroy(AppDetails.java:569)
at android.app.Activity.performDestroy(Activity.java:6422)
at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1143)
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3852)
... 9 more
Fixes a crash reported by ACRA:
java.lang.RuntimeException: Unable to destroy activity {org.fdroid.fdroid/org.fdroid.fdroid.AppDetails}: java.lang.NullPointerException
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3281)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3299)
at android.app.ActivityThread.access$1200(ActivityThread.java:133)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4812)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:559)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at org.fdroid.fdroid.AppDetails.cleanUpFinishedDownload(AppDetails.java:442)
at org.fdroid.fdroid.AppDetails.onDestroy(AppDetails.java:567)
at android.app.Activity.performDestroy(Activity.java:5366)
at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1124)
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3268)
... 11 more
This used to be the case, which is why only minimal changes were
required to bring it back. This also makes it take the same files that
checkstyle does, which is more consistent.
Also remove the 2.11 restriction on the root build.gradle file. It's
unnecessary, as the Android plugin will already error if the version is
too old. This means that the build will work on any version that is new
enough, which should be 2.10-2.12 at the time of writing.
Misc fixes/improvements to apk downloading
This started as a fix to #625, but quickly turned into a mini rampage to fix and/or improve a few different things in the `DownloaderService`. Hopefully the commits are self explanatory as to what they fix, but of course feel free to ask questions if you have any.
See merge request !265
Most of the logging is purely for debugging purposes during development.
As such, it has been moved to `Utils.debugLog`. Also provided more context
in some of the descriptions, so devs reading the logs without the sourcecode
will hopefully be able to infer more about what is happening. Left the error
logging as `Log.e` as it may be more informative.
Without this, if the first download in the queue is downloaded,
the notification will persist with details of that first download
until the next download begins. With this change, the notification
is remoived immediately after cancelling the download.
There was a race condition such that when the cancel `Intent` is
received, it would queue up a message on the download thread to stop
downloading. This was followed by cancelling the notification on the
thread the `Intent` was received on. As a result of the message taking
some time to get to the other thread, it would likely cause another
progress notification to be shown after we had already removed that
notification - making it show again after being cancelled.
The code in the download thread progress handler, which updates the
notification with progress, is not perfect. It does a check before
deciding to broadcast progress and update the notification. However,
the check happens at the beginning of the group of expressions. As
such, there is likely a small change that `isActive` returns true,
then the notification is cancelled on the other thread, and finally,
the download thread updates the progress again. However this is better
than it was before where the notification didn't go away.
Previously, apks were not being removed from the queue once download
was completed, only when removed. It didn't cause any specific issues
here, but I removed downloads from the queue after completion to prevent
potential problems in the future. Now the queue is a better representation
of what is to be downloaded.
Previously, navigating back to an app which is in the queue
qould indeed grey out the "Install" button and show the text
"Downloading..." in that disabled button. However, it woulnd't show
any sort of progress. This change shows an indeterminite progress
bar with the text "Waiting to start download..." underneath.
Happy to receive input on the best terminology if that is not
desirable.
In order to do this, I had to be more specific about when
the header fragment is updated. Previously, `headerFragment.updateViews()`
would get called by the `onResumeFragments()` activity method.
This was redundant because the `onResume()` method of the fragment
also invokes `updateViews()`. Thus, that call was removed (though
we still need to obtain a reference to the fragment in
`onResumeFragments()`.
When an Activity is destroyed, the next time it is created should
reinitialize all of the UI stuff again. Thus, there is no point to
clearing up the UI state before leaving. More importantly, this was
causing a problem when navigating back and forth through activities
via the downloader service notifications:
```
E java.lang.RuntimeException: Unable to destroy activity {org.fdroid.fdroid/org.fdroid.fdroid.AppDetails}: java.lang.IllegalStateException: activity is already destroyed
...
E Caused by: java.lang.IllegalStateException: activity is already destroyed
E at android.nfc.NfcActivityManager$NfcActivityState.<init>(NfcActivityManager.java:126)
E at android.nfc.NfcActivityManager.getActivityState(NfcActivityManager.java:176)
E at android.nfc.NfcActivityManager.setNdefPushContentUri(NfcActivityManager.java:252)
E at android.nfc.NfcAdapter.setBeamPushUris(NfcAdapter.java:830)
E at org.fdroid.fdroid.NfcHelper.disableAndroidBeam(NfcHelper.java:68)
E at org.fdroid.fdroid.AppDetails$AppDetailsHeaderFragment.updateViews(AppDetails.java:1507)
E at org.fdroid.fdroid.AppDetails$AppDetailsHeaderFragment.updateViews(AppDetails.java:1490)
E at org.fdroid.fdroid.AppDetails$AppDetailsHeaderFragment.removeProgress(AppDetails.java:1473)
E at org.fdroid.fdroid.AppDetails.cleanUpFinishedDownload(AppDetails.java:442)
E at org.fdroid.fdroid.AppDetails.onDestroy(AppDetails.java:567)
E at android.app.Activity.performDestroy(Activity.java:6169)
E at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1141)
E at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3726)
E ... 10 more
```
This is related to `onDestroy` calling a method which ends up calling
UI related stuff that assumes the `Activity` is around to interact with.
Fixes#625, as devices pre 4.0 require such `PendingIntent`s to
be passed to the notificaitons `setContentIntent` method. As the
`DownloaderService` handles both apks and repo updates, the pending
intent will either point to the relevant `AppDetails` screen for
an apk download - or the main F-Droid activity if it is any other
sort of download (i.e. a repo update download).
As per #fdroid-dev:
+mvdan | paresh: look at what lint says
+mvdan | src/main/res/transition-v21/app_list_item_click.xml:2: The resource R.transition.app_list_item_click appears to be unused
+mvdan | are you aware of this?
paresh | mvdan, oh yes
paresh | it is useless
paresh | I think I planned on adding some transition
paresh | you can remove it safely
Translators:
Danial Behzadi Persian
ezjerry liao Traditional Chinese
Mohamad Hasan Al Banna Indonesian
Olexandr Nesterenko Ukrainian
Osoitz Basque
Paresh Chouhan Hindi
Verdulo Esperanto
Rename java names to match Android standards
As seen in #37. I also did a few more that weren't mentioned there, like a few in Apk.
This is basically (case insensitive):
* `*version` -> `*versionname`
* `*vercode` -> `*versioncode`
* `pubkey` -> `signingCertificate`
See merge request !263
Translators:
Adrià García-Alzórriz Catalan
ageru French
Alberto Moshpirit Spanish
Allan Nordhøy Norwegian Bokmål
Enol P Asturian
ezjerry liao Traditional Chinese
Green Lunar Hebrew
Kristoffer Grundström Swedish
Ldm Public French
Marcelo Santana Portuguese (Brazil)
Mladen Pejaković Serbian
naofum Japanese
Pander Dutch
Sérgio Marques Portuguese (Portugal)
Tobias Bannert German
Verdulo Esperanto
Verdulo Polish
Yaron Shahrabani Hebrew
YFdyh000 Simplified Chinese
Show progress bar in download notification.
This enhances the download notification to include a progress bar of the downloads progress.
(It doesn't show the actual size of the download or the number of bytes downloaded in text yet. Need to think about internationalizing this from the `AppDetails` `updateProgress` method so that it is not copy-pasted in two places.)
See merge request !261
This release allows for Android 6+ support, but we'll need to ask for
permissions at runtime too. This commit simply does one half of the work
needed to support Android 6 with all things wi-fi.
See the issue and example app commit for reference:
https://github.com/mvdan/accesspoint/issues/66284f0376b
Also add useProgard true, since minifyEnabled now refers to the new
experimental code shrinker.
I'm not removing proguard yet as we depend on it for the samsung
workaround. I also do not know how to port the rest of the config
options to the new shrinker.
New DownloaderService
This merge request is too big, but I have all this work complete, so I'm submitting it now for review. If there are bigger issues with parts, I can rebase it down to the uncontroversial bits to get things merged.
This replaces `ApkDownloader`, `AsyncDownload`, and `AsyncDownloadWrapper`, and puts the whole APK download procedure into a custom `Service` that is based on the source code for `IntentService`. It can't be a subclass of `IntentService` because it needs to be cancelable. This does not yet add back a notification for the downloading, e.g. #592. That was handled before by the Android DownloadManager stuff, and was not replaced since that was disabled.
My current implementation does not filter out duplicate requests like discussed in #601. While its possible to do, I think it'll complicate the code a lot, and I really think that should be handled elsewhere. The UI should prevent the possibility of the user being able to submit duplicate install requests. If not, even if `DownloaderService` filtered them out, it would still be a buggy UX since the user would be clicking install again or something like that even though the install was in progress.
This also moves the APK verification logic to the `Installer` side. The downloading side can check the file size to see if the whole thing is downloaded. And to be extra safe, it should not be possible to submit an APK for installation without it going through the verification procedure. So the only method for installing APKs, `Installer.install()`, is where the verification now happens. Also, the installer now always copies the APK to be installed into the safe location RE: the Cure53 audit issue. This way, the APK download and cache dirs can be merged into one, making resumable downloads and cache management easy.
ping @mvdan @pserwylo @dschuermann
more comments in the commit messages
See merge request !248
Now that we have a nice background service, let's put it to work! This
makes update APKs be automatically downloaded after the index is updated.
Then when the user goes to install the updates, they will not have to wait
for the download part.
#601https://gitlab.com/fdroid/fdroidclient/issues/601
This moves a few stray preference handling instances into Preferences, and
move the non-preference "lastUpdateCheck" state to local only to
UpdateService. This will still work with existing installs since the
String constant value is the same.
This also saves the activeDownloadUrlString per packageName. Both are
necessary so that AppDetails can accurately display the current state
of a background download. Saving this per-packageName allows there to
be multiple active downloads in the background, then when people move
around AppDetails, it'll restore the progress meter and button state
when coming back to the app that they clicked install on.
By definition, there is just one DownloaderService enforced by
Android with a single active Downloader instance enforced by the
DownloaderService. That means using a static variable maps directly
to those conditions and provides a really simple, implementation,
especially compared to what would have to happen to do it via messages
from the thread and any Activities. If this ends up blocking testing
or something, it can always be changed when someone implements those
tests.
Moving towards having the Downloader always download directly into the
cache means its really easy to support resuming downloads.
This should also work for updates and icons, since it fetches the
Content Length via HTTP first. The icon and update downloading code
needs to be adjusted to support that. For APKs, it probably makes
sense to include the file size from the index db to save the query
over the net. That would be probably more helpful on Bluetooth.
This moves the last piece of code to the DownloaderService model. Moving
the APK prep and verification to Installer.installPackage() makes it hard
to mess up and make bugs like installing from the External Storage issue
found by the Cure53 audit.
AndroidNotCompatibleException is not used for anything specific right now,
and in the process of adding verification to the start of the install
process, there will be other kinds of failures. So convert that Exception
into a general usage InstallFailedException.
No need to flood receivers with progress events since they are basically
always going to the UI, and the UI will only refresh every so often. If
the refresh rate is 50Hz, then that's every 20ms. 100ms seems to make a
smooth enough progress bar, and saves some CPU time. This becomes more
important if there are multiple downloads happening in the background, like
if we make DownloaderService support parallel downloads from different repos
When dealing with complex lifecycles like Fragments, it is important
to expose the reliance of the Fragment on the Activity, since they
have different lifecycles.
Just cast to AppDetails instead of adding complexity with unneeded
Interfaces. The actual instance and class will be the same with or
without the Interfaces, so it does not help with lifecycle issues.
The methods that implement the interfaces only hide the fact that they
rely on an active instance of AppDetails, which can lead to
lifecycle-related crashes.
This is a step along the way to streamlining AppDetails Activity so that it
only uses Fragments when they are beneficial.
DownloaderService is based on IntentService to provide queued requests that
run in a background thread via the Handler and the HandlerThread. It began
as the IntentService code, but it could not be a subclass because the
downloading needs to be cancelable. IntentServices cannot be canceled and
they provide no visibility into their queue.
DownloaderService then announces relevant events via LocalBroadcastManager
and Intents with custom "action" Strings.
https://gitlab.com/fdroid/fdroidclient/issues/601#601
Verify apk signature of privileged extension before installation
This implements a check that the signature of the extension apk is the same as the signature of F-Droid before installing the extension apk. Related issue: https://gitlab.com/fdroid/fdroidclient/issues/437
See merge request !256
More tests and bug fixes
I pulled a few commits out of the !248 saga, and added a bug fix or two. It would be easier if this was merged before !248
ping @mvdan @pserwylo
See merge request !255
Currently, UpdateService is running at default priority, which is the same
as the UI tasks, since it is a regular IntentService. That means it would
put a noticable load on the device when running, especially on older
devices. This should help with that.
#563https://gitlab.com/fdroid/fdroidclient/issues/563
Android recently switched from JUnit 3 to 4 for its base testing classes.
It doesn't seem to support the old JUnit3 methods with gradle and AS. So
all the tests need to be ported to JUnit4 to work again.
#607https://gitlab.com/fdroid/fdroidclient/issues/607
Since SSLHandshakeException is a subclass of IOException, and all that is
happening is rethrowing an Exception, instead pass this one through so it
will be handled by the central Downloader error handling. That's currently
just a Toast, but it can easily be expanded in the future.
Shared element transition for app list item v21 and above
Shared element transitions for API level v21 and above

See merge request !251
fixes and cleanups related to ongoing DownloaderService work
This includes a fix for bug that @mvdan found in the processing of `Apk.maxSdkVersion`, as well as some cleanups related to the ongoing work in !248 . Indeed a couple of these commits were pulled out of that MR.
See merge request !253
Since Downloader's outputFile variable is final, it can safely be used
as a public property variable. This makes it simple to use in
subclasses. Making it a public final variable rather than a getter
also communicates that the value does not change since there is no
getter method that could potentially change it.
http://binkley.blogspot.com/2005/01/read-only-properties-in-java.html
Having 0 mean max makes the logic confusing when maxSdkValue is used in
variable. This sanitizes the data so that maxSdkValue is always just a
plain int value that can be used to test against. It does this by
defaulting to Byte.MAX_VALUE (127) if it is not explicitly set. At the rate
of 24 SDK numbers in 8 years, that gives us about 24 years before we have
to think about setting it to Short.MAX_VALUE.
This fixes an issue created by e021eb5ca7e8f05dbce7c1b87833722542138302
gitlab-ci: use android-17 emulator for `gradle connectedCheck`
The android-10 emulator does not report test failures so it is pretty useless at the moment. After lots and lots of trying, the most recent emulator that I could get running on gitlab-ci was 17, so let's hope that turns out to be more useful. I also had to reduce the RAM that was used, it seems that gitlab-ci does not let the docker images use much RAM. This might be able to be improved by creating an pre-setup AVD image in the docker image used by this.
As you can see from the history, I tried lots of things to see if it is was possible to get a more recent emulator running on gitlab-ci.
See merge request !241
The android-10 emulator does not report test failures so it is pretty
useless at the moment. After lots and lots of trying, the most recent
emulator that I could get running on gitlab-ci was 17, so let's hope that
turns out to be more useful. I also had to reduce the RAM that was used,
it seems that gitlab-ci does not let the docker images use much RAM.
This might be able to be improved by creating an pre-setup AVD image in the
docker image used by this.
I find that the logs dumped into the gitlab-ci screens are generally
unreadable, so here, only the errors are dumped into the build log, then
the rest are uploaded to clbin, a paste bin, where the whole text can be
viewed and downloaded in a clean, raw format.
Add non-emulator tests and simplify internal API
A lot of the purely internal API is using constructs which are not needed for internal APIs. The internal API can be viewed and changed by any contributor, so its better to not cover all possible future uses. Indeed to keep the codebase simple, it should be the opposite: the app's code should reflect what is actually happening now, not what might happen in the future.
This also adds tests that run on the JVM rather than the emulator.
These commits where originally in !248 but I'm submitting them separately since !248 is too big.
See merge request !250
Since the DownloaderService's events are all based on the complete download
URLs, and RepoUpdater is where the update URLs are built, this makes the
full download URL into a read-only property of RepoUpdater so it can be
used wherever there is an instance of RepoUpdater
This is also important because having the `final` property highlights
the lifecycle of that variable: it does not change during the entire
life of a RepoUpdater Instance.
Instead of duplicate APIs, standardize on a single API, and use that
everywhere via the Downloader.LOCAL_ACTION_PROGRESS event that is already
wired in.
This is needed so that downloads can be canceled from within an
IntentService. Since the Downloader classes do not have any Thread logic in
them, they shouldn't use Thread logic within them anyway.
This also removes the unused argument to AsyncDownloader.attemptCancel().
SharedPreferences.Editor.apply() for android 8
Turns out that `SharedPreferences.Editor.apply()` was not added until `android-9`, so this is a little trick to support `android-8` still after the changes in c3b47ecd5a380678dd2df3dc2549155429d28514
See merge request !249
This is not pretty, but its the best I could think of.
Fixes this lint error:
Call requires API level 9 (current min is 8): android.content.SharedPreferences.Editor#apply [NewApi]
'src' works because we're only doing java files under that directory.
But it would be slower than needed, and in the case of PMD it would also
use the test files which wasn't intended at all.
It seems like having it as a compile dependency already works for the
tests. Having it duplicated seems to sometimes trigger errors (e.g. a
user reported a duplicate zip entry due to the duplication) and might
also be problematic if we don't keep the two versions in sync.
Random fixes from DownloaderService hacking
Here are some random fixes that I did in the process of the DownloaderService refactoring. I don't think anything should be controversial. Thanks for your rapid code reviews recently @mvdan :)
See merge request !245
"Consider using apply() instead; commit writes its data to persistent
storage immediately, whereas apply will handle it in the background"
commit() is only useful if the code actually checks the return value.
* Use a % sign that String.format() recognizes, apparently there are more
than one % signs, in Chinese, its big: %
* a string in lithuanian forgot the %s
More DownloaderService progress
As part of the incremental approach of moving downloading to an IntentService, here are a few more commits refactoring things into events instead of listerners/callbacks/etc.
ping @pserwylo
See merge request !240
An AsyncTask ties into the UI thread for things like onPostExecute(). If it
is run within an AsyncTask, then it freaks out because it can't tie into
the UI thread. We don't need it to do that here anyway, so just use a
plain Thread, and set the priority to background.
As part of the process of moving the APK downloading to an
IntentService, I'm removing and incrementally reorganizing the
existing events so that the code continues to be functional as it is
reorganized. We might want to include more detail in a download error
to expose to the user, but I think instead what will be more fruitful
is to hide details on errors where there is nothing the user can do
except retry. Then if there are errors where the user can do
something about it, then F-Droid should instead offer them the option
of doing that, and not just show an error message and walk away.
This is part of the move to standardizing all internal broadcasts to use
the Intent's Uri field as the standard place for a download URL, and then
using that in IntentFilters to do matching.
Fix version upgrade string for RTL languages
In right-to-left languages _forward_ is the direction to the left, so leftwards arrow should be used to indicate upgrade:

For left-to-right languages it remains the same:

Closes#609.
See merge request !235
Simplify Downloaders
This is some groundwork to simplify the Downloader stuff in preparation to moving it to something like an `IntentService`, as part of #601. This mostly removes unused bits that I've found in the process of writing the `DownloaderService`. Some of these events will be added back in a more consistent way, so that there is one event type for the same idea throughout the code base.
See merge request !236
This also removes all the related stuff that resulted in
EVENT_APK_DOWNLOAD_CANCELLED being sent. Since EVENT_APK_DOWNLOAD_CANCELLED
ultimately does nothing, that whole bit of plumbing is unused.
Allowing all downloads, including updates, to be canceled simplifies the
code and if the user wants to cancel an update, they should be able to. But
canceling updates is not implemented in this commit.
This makes it so gradle provides all dependencies, rather than a mix of
classes that are copied in versus imported via gradle. This library is
already used by the tests, so its not really a new dependency, and proguard
should remove all the unused stuff.
Since this is internal code and not a library for use with other projects,
it should only include the methods that are actually in use. The other
copies are just dead code, which means more stuff to read in order to
figure out.
Unfortunately, this approach did not really work out. It would have been
really nice to rely on the provided DownloadManager stuff, but it has too
many issues, like not working with Tor or other proxies, and being
difficult to tightly integrate.
start porting tests to latest Android Studio/gradle setup
To encourage more people to use and add to the test suite, I've been working on making it work nicely with Android Studio and gradle. I've also setup the possibility for JUnit tests that run on the host machine, not the emulator. Those are run with `gradle test`. I added a JUnit test of `HttpDownloader` too.
See merge request !233
"Variable explicitly initialized to 'false' (default value for its type)"
I never remember what the default init value of booleans are, so this error
is quite annoying to me, and I can't see the harm of this behavior.
checkstyle says: "So in this case, x gets initialized to 0 twice, and bar
gets initialized to null twice. So there is a minor inefficiency."
This makes it a lot easier to setup all the testing stuff. Mostly,
I'm tired of fighting Android Studio's fragility, so I want to remove
as much non-standardness as possible in the hopes of improving that
situation.
closes#534https://gitlab.com/fdroid/fdroidclient/issues/534
This is the last Android code in the whole suite of Downloader subclasses,
so now we can write JUnit tests for them all, and avoid the fragility of
tests running on the emulator.
When running tests on the host machine, android.jar contains no code at all
which is totally stupid. We can keep android.util.Log in there because it
does not affect the logic at all. Then just set that android.jar to return
generic values using:
unitTests.returnDefaultValues = true
If there is no LocalBroadcastManager, then the Downloader tests can be done
with pretty plain JUnit and then do not require the Android emulator to run
Fix crash when returning to swap after cancelling
Fixes#409. The problem was that there was some listeners being added for broadcast events when the swap view was shown. These were never removed, and so cancelling swap, then returning to it would spin up a _new_ activity with new views, while the old listeners were still around. When the old listeners received events, they would try to talk to their associated `Activity`. This no longer existed, so a crash ensued.
While I was fixing the specific bug associated with #409, I took the opportunity to make more of the event listeners well behaved in the swap process. I don't think any of them were liable to cause crashes, but were likely to cause some weirdness at some point in time if they were not fixed.
*Note:* This swap view was an exercise in moving away from `Fragment`s towards an `Activity` with individual `View`s. I'm going to call this a bit of a failure at this point, because there is so much work that needs to be invested in implementing lifecycle stuff in our custom views. `Fragment`s naturally come with lifecycle methods that are familiar to other Android dev's looking to contribute to this project (even if they are a little difficult to understand at times). Implementing our own custom Views instead still results in similar classes of bugs (i.e. talking to an `Activity` when the view no longer is part of that activity).
A classic example of this is in my usage of the `onDetachedFromWindow` function in the `View`. I have no idea if this is the best place to unregister listeners or not. In a Fragment, it would be a matter of `onPause` or one of the more well defined lifecycle methods. Empirically, `onDetachedFromWindow` seems to be well behaved. The other alternative would be for the Activity to explicitly invoke a `onRemoved` type method each view when it knows it is transitioning from one state to the next. However at this point, we are then really into reimplementing `Fragment` land.
See merge request !232
Translators:
ageru French
Ajeje Brazorf Sardinian
Mohamad Hasan Al Banna Indonesian
Paresh Chouhan Hindi
YFdyh000 Simplified Chinese
YF Simplified Chinese
Pass through a known good `Context` to `translateCategories`
All the good work to make sure that `getActivity()` actually returned
a proper context, obtaining a final reference to that known good activity
object, and then using that in the background thread is thrown away.
The reason was because the `translateCategories` method would go and
call `getActivity()` all over again. This changes the `translateCategories`
helper function so that it asks for a `Context`. This way, a known
good `Context` can be passed in, rather than having to perform a check
to see if `getActivity()` is good again.
Fixes#603 (Hopefully)
See merge request !231
The old swap code used to delegate to the `AppDetails` activity when
touching an app in the swap view. Now it shows the install button
and download feedback inline. The code which used to exist is no
longer required.
This fixes the following bugs:
* `BroadcastReceiver` was never being created due to incorrect guard
condition `if (pollForUpdatesReceiver != null)` (should have been
`== null`).
* Called `unregisterReceiver` rather than `registerReceiver`.
* Even if it did work, it didn't make an effort to unregister the receiver.
In addition, the creation and listening with the `BroadcastReceiver is
now done in a way similar to the other swap views:
* Create it as a `final` member variable.
* `registerReceiver` when view is inflated.
* `unregisterReceiver` when view is detached.
Previously the receiver was added but never removed. The result
is that once a swap session is cancelled, the receiver still
gets broadcasts.
This is what was causing the bug in #409. It was trying to access
the `Activity` once it had been closed, and another swap session started
with a new activity.
All the good work to make sure that `getActivity()` actually returned
a proper context, obtaining a final reference to that known good activity
object, and then using that in the background thread is thrown away.
The reason was because the `translateCategories` method would go and
call `getActivity()` all over again. This changes the `translateCategories`
helper function so that it asks for a `Context`. This way, a known
good `Context` can be passed in, rather than having to perform a check
to see if `getActivity()` is good again.
Update Support Library components to 23.2.1
As far as I can see from the release notes, 23.2.1 does not fix any bugs that could affect F-Droid. Anyway, it's better to be up-to-date.
See merge request !228
CI: Bump docker image
Also use a specific tag instead of latest, so that pushing latest on
Docker Hub won't break older branches and tags.
See merge request !229
gitlab-ci: enable lint, log posting, and caching
Some work to improve the CI functions, and first stab at getting parts running in parallel.
See merge request !211
* splitting out the tools script allows less things to happen on the
main job, and runs the tools script in parallel, which should speed
things up.
* `gradle check` also runs lint, and anything else we might add to
that meta-target.
* `gradle build` also runs tests, `gradle assemble` does not
This stores the Android SDK tarball and gradle caches between builds
to speed things up. Since the unpacked SDK gets unpacked very time,
updating the version is just a matter of changing the variable. Since
only the gradle caches are stored, i.e. the jars and gradle binaries,
updates there will only add more files to the cache.
Code Style : Changed variable names to be consistent with other variable names
variable names are inconsistent in other files as well. At some places it is `mVariableName` and in some places it is `variableName`.
suggestion : rename all public variables to `publicVaraible` and all private variables to `mPrivateVariable` ?
we can use http://udacity.github.io/android-nanodegree-guidelines/index.html
See merge request !225
Translators:
Adrià García-Alzórriz Catalan
Adrià García-Alzórriz Spanish
Benedikt Volkmer German
Irvan Kurniawan Indonesian
Licaon Kter Romanian
Marcelo Santana Portuguese (Brazil)
Massimiliano Caniparoli Italian
Mladen Pejaković Serbian
Mohamad Hasan Al Banna Indonesian
naofum Japanese
Pander Dutch
Phạm Nguyễn Hoàng Vietnamese
riotism Chinese (Hong Kong)
Tobias Bannert German
Verdulo Esperanto
Verdulo Polish
Ensure `getContentResolver()` is not requested from `null` object.
Should fix#554, however I couldn't reproduce this. At the very least, I fail to see how it is possible with this new change for a `null` to get passed to `AppProvider.Helper.categories`, whereas it could've before.
In the process, I also made the database query run on the background thread, then update the UI on the UI thread.
See merge request !224
Previously, it was not explicit that the `onCreate` happened to be invoked
in the UI thread. Now it is, due to passing `new Handler(Looper.getMainLooper())`.
Also, the categories are now loaded in a background task, and then the UI is
updated on the UI thread.
Translators:
ageru French
Danial Behzadi Persian
enolp Asturian
ezjerry liao Chinese (Taiwan)
Fr Translation French
Hsiu-Ming Chang Chinese (Taiwan)
Jonatan Swedish
relan Russian
riotism Chinese (Hong Kong)
Translators:
Adrià García-Alzórriz Catalan
Adrià García-Alzórriz Spanish
Ajeje Brazorf Sardinian
Alberto Moshpirit Spanish
Alex Kalles Greek
jetamkadlec Czech
Ldm Public French
Licaon Kter Romanian
Mladen Pejaković Serbian
naofum Japanese
Olexandr Nesterenko Ukrainian
Verdulo Esperanto
Verdulo Polish
Cleanup swap code when returning to start swap view.
This change cleans up some code around how the `StartSwapView` attaches and detaches listeners/broadcast receivers/etc. There are two things that occured which were undesirable, one causing observable bugs, the other having the potential to.
The first is that when the swap process is stopped (e.g. by hitting the cross in the top left of the action bar) then it would trigger an event to be broadcast. This would hit the `StartSwapView`, and then that view would change the UI widgets to match the state of the swap process (i.e. "Stopping" == Disabled and unchecked switch, "Stopped" == Enabled and unchecked switch). When the UI was updated, it was inadvertantly sending further requests to stop swap, because `Switch` widgets _always_ notify their listeners, even if they are explicitly set via `setChecked()`. Now it temporarily removes listeners while brining the UI in line with what the swap service is doing, then reattaches them afterwards.
The later is due to `BroadcastReceivers` being added each time the `StartSwapView` is shown, and never unregistered. Now they are unregistered when the view is detached. Note that because we are not using the convoluted but well documented `Fragment` API, I'm not 100% certain this is the right time to detach listeners, but it seems suitable.
FYI, here is a logcat of me starting hitting the "Cancel swap" X button on master:
```
SwapManager I Asked to stop swapping, will stop bluetooth, wifi, and move service to BG for GC.
D Moving SwapService to background so that it can be GC'ed if required.
SwapType D Sending broadcast STOPPING from WifiSwap
WifiSwap D Sending message to swap webserver to stop it.
BonjourBroadcast D Unregistering MDNS service...
WifiSwap I we've been asked to stop the webserver: Thread-622 says stop
SwapManager D Remembering that Bluetooth swap is NOT connected and WiFi swap IS connected.
StartSwapView D WiFi service is stopping (setting toggle to unchecked and disabled).
D Received onCheckChanged(false) for WiFi swap, disabling WiFi swap in background thread.
AvailableAppsFragment D Category 'What's New' selected.
OpenGLRenderer D endAllStagingAnimators on 0xb8c007e8 (RippleDrawable) with handle 0xb8cef7e8
BluetoothFinder D Stopping bluetooth discovery.
BluetoothAdapter D 235158966: getState() : mService = null. Returning STATE_OFF
BonjourFinder D Cancelling BonjourFinder, releasing multicast lock, removing jmdns service listeners
art I WaitForGcToComplete blocked for 7.385ms for cause DisableMovingGc
Manager.CallbackHandler D CM callback handler got msg 524290
D CM callback handler got msg 524290
SwapType D Sending broadcast STOPPED from BonjourBroadcast
D Sending broadcast STOPPED from WifiSwap
D Sending broadcast STOPPING from WifiSwap
SwapManager D Moving SwapService to background so that it can be GC'ed if required.
WifiSwap D Sending message to swap webserver to stop it.
MessageQueue W Handler (org.fdroid.fdroid.localrepo.type.WifiSwap$5$1$1) {29eeaaee} sending message to a Handler on a dead thread
W java.lang.IllegalStateException: Handler (org.fdroid.fdroid.localrepo.type.WifiSwap$5$1$1) {29eeaaee} sending message to a Handler on a dead
thread
W at android.os.MessageQueue.enqueueMessage(MessageQueue.java:325)
W at android.os.Handler.enqueueMessage(Handler.java:631)
W at android.os.Handler.sendMessageAtTime(Handler.java:600)
W at android.os.Handler.sendMessageDelayed(Handler.java:570)
W at android.os.Handler.sendMessage(Handler.java:507)
W at org.fdroid.fdroid.localrepo.type.WifiSwap.stop(WifiSwap.java:167)
W at org.fdroid.fdroid.localrepo.type.SwapType$3.doInBackground(SwapType.java:103)
W at org.fdroid.fdroid.localrepo.type.SwapType$3.doInBackground(SwapType.java:100)
W at android.os.AsyncTask$2.call(AsyncTask.java:288)
W at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
W at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
W at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
W at java.lang.Thread.run(Thread.java:818)
BonjourBroadcast D Unregistering MDNS service...
SwapType D Sending broadcast STOPPED from BonjourBroadcast
D Sending broadcast STOPPED from WifiSwap
SwapManager D Remembering that Bluetooth swap is NOT connected and WiFi swap is NOT connected.
StartSwapView D WiFi service has stopped (setting toggle to not-visible).
SwapManager D Remembering that Bluetooth swap is NOT connected and WiFi swap is NOT connected.
StartSwapView D WiFi service is stopping (setting toggle to unchecked and disabled).
SwapManager D Moving SwapService to background so that it can be GC'ed if required.
D Remembering that Bluetooth swap is NOT connected and WiFi swap is NOT connected.
StartSwapView D WiFi service has stopped (setting toggle to not-visible).
```
And also on this branch:
```
SwapService I Asked to stop swapping, will stop bluetooth, wifi, and move service to BG for GC.
D Moving SwapService to background so that it can be GC'ed if required.
SwapType D Sending broadcast STOPPING from WifiSwap
WifiSwap D Sending message to swap webserver to stop it.
BonjourBroadcast D Unregistering MDNS service...
WifiSwap I we've been asked to stop the webserver: Thread-646 says stop
SwapService D Remembering that Bluetooth swap is NOT connected and WiFi swap IS connected.
StartSwapView D WiFi service is stopping (setting toggle to unchecked and disabled).
AvailableAppsFragment D Category 'What's New' selected.
OpenGLRenderer D endAllStagingAnimators on 0xb8b1bb70 (RippleDrawable) with handle 0xb8cec7c0
BonjourFinder D Cancelling BonjourFinder, releasing multicast lock, removing jmdns service listeners
BluetoothFinder D Stopping bluetooth discovery.
BluetoothAdapter D 93224770: getState() : mService = null. Returning STATE_OFF
Manager.CallbackHandler D CM callback handler got msg 524290
D CM callback handler got msg 524290
SwapType D Sending broadcast STOPPED from BonjourBroadcast
D Sending broadcast STOPPED from WifiSwap
SwapService D Moving SwapService to background so that it can be GC'ed if required.
D Remembering that Bluetooth swap is NOT connected and WiFi swap is NOT connected.
StartSwapView D WiFi service has stopped (setting toggle to not-visible).
Manager.CallbackHandler D CM callback handler got msg 524290
```
One of the most notable things that can be seen is the lack of the following message in the second (fixed) logcat.
> Received onCheckChanged(false) for WiFi swap, disabling WiFi swap in background thread.
See merge request !218
Previously, they were registered, then forgotten. This means that each time
the start swap view was run, another receiver was registered. As a result,
they were being invoked multiple times.
It doesn't appear that this had any specific side effects which were terrible,
but they definitely have the potential to going forward.
Note that because we are not using `Fragments` with their convoluted, but at
least well documented API, I'm not 100% certain that I've unregistered the
receivers at the right location.
Previously, something like this would happen:
* Swap service is cancelled
* WiFi swap is asked to stop
* Event is broadcast when done
* UI listens to this event
* Upon receiving the event, it updates the UI
* Updating the UI triggers an event, causing the process to happen again
An alternative solution to this would have been for the UI to stop listening
to listeners before WiFi swap is shut down, but that is then only specific
to the case when the swap view is being destroyed/removed. This could also
happen in other situations however, such as when the swap service times out.
When the view is detached, then the listeners will be unregistered.
This will also help in the future so that they can be temporarily
unregistered when manually changing the state of the switches.
Smooth Tor setup
This is a big reworking of the Tor support to make it really easy to setup, and to make .onion addresses work automatically even when "Use Tor" is off.
I needed to make the NetCipher v1.2.1 release to support this, hence it was originally WIP:.
See merge request !216
Thread runs at normal priority by default. AsyncTasks are integrated into
Android for handling things running in the background while keeping the UI
responsive.
This reverts most of commit 828cc272ee5235f868104b009349cc7e835e144f.
Cleanup swap stuff, make more robust
While we're discussing the merits or otherwise of !208, here is some fixes to the swap workflow cherry-picked (and cleaned up) from that branch.
Although not directly reproducible, I'm confident this should fix#556 and #557.
The original assumption was that we can start the wifi local repo server, then tell the user it is connected while JmDNS is starting. The plan was for it to be very snappy, so the user could continue using the UI while bonjour was doing its stuff. However, in realtity this results in the possibility of turning swap on and off again while bonjour is still getting ready.
This now makes the user wait both when starting swap, and also when stopping swap. It will provide proper feedback to the user, do it on a background thread (properly) and update the UI when done.
Added some other misc cleanups while there.
See merge request !213
Not sure if we should be here or not in this situation, so this is
a little bit defensive. Can't bind to an IP address of `null`, so
don't bother starting LocalHTTP server unless we have an IP.
`Thread.run()` is not the correct call, changed to the correct `Thread.start()`.
Also, explicitly indicate that we want the stopping of wifi to happen in the
background.
On some devices this can take some time (i.e. a second) and the UI needs
to be disabled for that time. This should stop users quickly stopping and
starting regularly, queuing up many "start jmdns, stop jmdns, start jmdns"
calls.
These are already in its layout-v11 version. And as lint points out,
these need v11:
?android:attr/buttonBarStyle requires API level 11 (current min is 8)
?android:attr/buttonBarButtonStyle requires API level 11 (current min is 8)
Lint finds these, but it's very slow and currently we're not taking lint
errors as fatal. So for now this script will be useful, as nearly every
time I pull from weblate there are at least a couple of these.
Translators:
agilob Polish
Hsiu-Ming Chang Chinese (Taiwan)
Nathan Follens Dutch
Perry Verheij Dutch
Robin van der Vliet Dutch
Robin van der Vliet Esperanto
Verdulo Esperanto
Wathiq Qajar Arabic
We still allow them in single-line statements, like:
if (foo) bar;
for (int i : ints) bar;
Everything else should use braces to help readability and avoid silly
human mistakes that might result in bugs.
These changes were completely automated via a python script.
Search: clear focus when enter/return is pressed
Fixes#572.
Assigning to @pserwylo since he wrote the current search widget stuff.
See merge request !206
Translators:
Coucouf French
Danial Behzadi Persian
Green Lunar Hebrew
Licaon Kter Romanian
M2ck French
Marian Hanzel Slovak
Verdulo Esperanto
Verdulo Polish
Work around dead activity issue in AppDetails
It seems like install() sometimes runs when the AppDetails activity is
finished or finishing. This results in the windows (dialogs) failing to
show, and a BadTokenException to fire:
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@d6e3570 is not valid; is your activity running?
This seems to be the culprit:
at org.fdroid.fdroid.AppDetails.install(AppDetails.java:840)
at org.fdroid.fdroid.AppDetails$AppDetailsListFragment.install(AppDetails.java:1657)
at org.fdroid.fdroid.AppDetails$AppDetailsListFragment.onListItemClick(AppDetails.java:1721)
Apparently, you can check whether an activity/context is being finished:
https://stackoverflow.com/questions/7811993/error-binderproxy45d459c0-is-not-valid-is-your-activity-running
I cannot reproduce this issue, thus can't say whether this fixes it or
not. Either way, it can't hurt to try. This can be reverted if we see
ACRA reports of this in the future, and the issue reopened.
Fixes#565.
See merge request !204
It seems like install() sometimes runs when the AppDetails activity is
finished or finishing. This results in the windows (dialogs) failing to
show, and a BadTokenException to fire:
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@d6e3570 is not valid; is your activity running?
This seems to be the culprit:
at org.fdroid.fdroid.AppDetails.install(AppDetails.java:840)
at org.fdroid.fdroid.AppDetails$AppDetailsListFragment.install(AppDetails.java:1657)
at org.fdroid.fdroid.AppDetails$AppDetailsListFragment.onListItemClick(AppDetails.java:1721)
Apparently, you can check whether an activity/context is being finished:
https://stackoverflow.com/questions/7811993/error-binderproxy45d459c0-is-not-valid-is-your-activity-running
I cannot reproduce this issue, thus can't say whether this fixes it or
not. Either way, it can't hurt to try. This can be reverted if we see
ACRA reports of this in the future, and the issue reopened.
Fixes#565.
Fix 555 content provider invalid uri
Was not correctly encoding "/" characters when searching. This caused the Uri used by the Content Providers to include a slash, which makes it look like a separate segment of the path which was wrong. Now correctly encodes "/" characters. Also noticed one other place incorrectly encoding characters, where they would've been double encoded when added as query parameters to a Uri.
See merge request !203
Refactor swap "peer finders" to use ReactiveX
*NOTE: This includes the commit specified by !197.*
In the old code, there is a _lot_ of procedual style "Is this peer finder running, if so, do this". In addition, the choice to do things on background threads or not is a little ad-hoc. Finally, the `SwapService` needs to know about both bluetooth and wifi peer finders, whereas really they are both only there to emit "Peers", regardless of the type.
As such, some improvements in this change are:
* The choice to run peer finding on a background thread is made once, at a higher level when starting the peer finder.
* No longer does the UI code ask "Am I searching for peers". It instead waits to be told whether it is or isn't.
* The addition of new types of peers in the future is the job of the Peer finder itself. It quietly aggregates all of the Peer Finders it knows about into a single observable that emits different types of peers.
This code doesn't fix any particular issue, but rather it is about making the entire swap workflow easier to reason about. I plan on migrating more of this workflow to this functional style in the future, and hopefully that will have benefits in terms of stability and code understanding.
See merge request !198
Translators:
fabrizio maggi Italian
Gabriele Pau Italian
Irvan Kurniawan Indonesian
Karola Marky Latvian
Patrik Kretic Slovenian
riotism Chinese (Hong Kong)
I misread the documentation when first using the `appendEncodedPath` method,
because it expects the path to already be encoded. This causes a bug because
if you search for a '/'. The result is a malformed URI that has the path
'/search//' instead of '/search/%2F'.
Using `appendPath` will always encode the string given to it, which is desirable.
Also check for empty strings, and return a URI that gives all apps. This was
not strictly neccesary, because the code which invokes it checks for empty
strings, but if somewhere else in the future starts to use this code, they
would've had to know to check for empty strings first.
Fixes#555.
Put null check around access of `R.id.header` fragment.
Please note I haven't reproduced the specific problem. Also, the stack
traces being reported are only marginally informative, because they are
in response to a content providers firing events, and thus don't have
any context about when or where the event was fired from.
However, my looking at the code seems to indicate that this will prevent
NPE when the Activity is no longer visible but an app is finished
installing. Also, the view should still update correctly on resuming the
Activity because the `onResumeFragments()` methods will be invoked
which invokes the `refreshHeaders()` method.
Fixes#286.
See merge request !202
Please note I haven't reproduced the specific problem. Also, the stack
traces being reported are only marginally informative, because they are
in response to a content providers firing events, and thus don't have
any context about when or where the event was fired from.
However, my looking at the code seems to indicate that this will prevent
NPE when the Activity is no longer visible but an app is finished
installing. Also, the view should still update correctly on resuming the
Activity because the `onResumeFragments()` methods will be invoked
which invokes the `refreshHeaders()` method.
Fixes#286.
Add ReactiveX (rxjava + rxandroid) as dependency
This is going to be used to make the managing of async tasks in F-Droid easier to reason about. It does this by using a more functional style to performing multiple different asynchronous tasks as compared to the Android `AsyncTask` or `Service` or some other approach.
More specifically, I have some changes coming that will use this dependency.
I wanted to merge this separately so that it doesn't matter which of the changes I'm working on gets merged first.
I've never added a `dependencyVerification` to the gradle build before, and there wasn't a whole bunch of docs on the interwebs about how to do that. So I did a SHA256 sum of some other .jar files in my gradle cache and compared them to the existing dependency verification settings and they did match. So I also did a SHA256 sum of the newly added dependencies and gradle seems happy with the hashes I've chosen.
See merge request !197
The benefits of this are as follows:
No longer need to worry about how many types of `Peer`s exist.
There is a single publicly accessible `PeerFinder` which aggregates
the results of both the Bluetooth and Bonjour peer finders. In the
future if another is added, the consumer of the peer finder
(i.e. `StartSwapView`) doesn't need to be aware of this. Neither does
the `SwapService` or `SwapActivity` or any other code.
Never ask "Are we searching" but instead receive push notifications
from the peer finder when it stops searching.
Don't worry about receiving the same peer multiple times, it will
automatically get filtered out.
Less concern about doing things in `AsyncTasks` (and knowing what to
do in an `AsyncTask`). The RXJava + RXAndroid libraries deal with this
by allowing the client consuming the `PeerFinder` to specify which
thread to perform the background task on, and also that the found
`Peer`s should be emitted on the UI thread.
In the future, can play with caching the results of a particular
sequence of found peers. However right now using the `Observable.cache()`
method means we can no longer unsubscribe from the peer finders
and thus they run longer than they need to when we move on from
the initial swap screen.
This is going to be used to make the managing of async tasks in
F-Droid easier to reason about. It does this by using a more
functional style to performing multiple different asynchronous tasks
as compared to the Android `AsyncTask` or `Service` or some other approach.
fix AOSP build integration
The build isn't done from the top-level directory so the symlink needs
to use an absolute path.
Fixes#551.
See merge request !200
Fix 560 (searching only whitespace)
When no keywords to search, use an empty query selection that evaluates to "1".
This means that instead of building invalid SQL such as `WHERE (() OR ())` it
will build `WHERE((1) OR (1))` which, while non-optimal, is at least valid.
In fact, I'm not even sure that it is non optimal because I'd hope the sqlite
query optimizer is able to realise that `1 OR 1` is effectively a no-op.
Fixes issue #560.
See merge request !201
Changing the search query is quite an expensive operation, so this does some rudimentary
checking to see if the two queries are meaningfully different. At present, it trims the
strings and does a case insensitive comparison.
The query is eventually exploded based on whitespace, so leading and trailing white
space is not important. Also, sqlite `LIKE` clauses are case insensitive, so case
is unimportant. Having said that, I'm not sure how someone will be able to change
the queries case without first deleting and then adding characters (thus inducing
meaningfull changse).
This means that instead of building invalid SQL such as `WHERE (() OR ())` it
will build `WHERE((1) OR (1))` which, while non-optimal, is at least valid.
Fixes issue #560.
Its use was removed long ago, and the dependency was left behind for
some reason. This commit can be reverted if it's needed in the future
again.
This of course slightly simplifies the build thus speeding it a little,
but what's more interesting is that the output apk is also ~100KB
smaller. So something is going on.
Translators:
bd339 Danish
Danial Behzadi Persian
David Koňařík Czech
Massimiliano Caniparoli Italian
Olexandr Nesterenko Ukrainian
Sebastiano Pistore Italian
Tobias Bannert German
Tong Hui Chinese (China)
As reported by a user via ACRA:
java.util.UnknownFormatConversionException: Conversion: I
at java.util.Formatter$FormatToken.unknownFormatConversionException(Formatter.java:1399)
at java.util.Formatter$FormatToken.checkFlags(Formatter.java:1336)
at java.util.Formatter.transform(Formatter.java:1442)
at java.util.Formatter.doFormat(Formatter.java:1081)
at java.util.Formatter.format(Formatter.java:1042)
at java.util.Formatter.format(Formatter.java:1011)
at java.lang.String.format(String.java:1988)
at android.content.res.Resources.getString(Resources.java:355)
at android.content.Context.getString(Context.java:350)
at org.fdroid.fdroid.UpdateService$1.onReceive(UpdateService.java:210)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5136)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:740)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:556)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
at dalvik.system.NativeStart.main(Native Method)
Fix 265 database locked crash (I'm pretty sure)
Previously, all of the various subclasses of FDroidProvider would create
their own database open helper in their respective `onCreate()` methods.
This seemed to be the cause of the multiple database locked exceptions.
Various online articles/SO posts/etc helped come to this conclusion:
* http://stackoverflow.com/a/3689883
* http://stackoverflow.com/a/8888606
* https://web.archive.org/web/20150709074733/http://www.dmytrodanylyk.com/pages/blog/concurrent-database.html
This should fix#265.
In the process, also did away with the two `read()` and `write()` methods
that returned a "readable" and "writeable" database respectively. It turns
out that it doesn't quite do what I originally thought. There is not much
benefit to specifying to the database helper that you want a readable/writeable
database. In fact, it is often the case that a call to `read()` would most
likely have returned the same instance that is returned by `write()`. The
semantics of them were therefore broken, and they've been replaced with
`db()`.
See merge request !196
The fix for the database locking bug was to have a singleton
`DBHelper` instance. This breaks tests because multiple tests
share the same database. The solution is to:
* Hack together a static method which clears the singleton,
then invoke it in the `setUp()` method for relevant test cases.
* Ensure the mock context provided to the providers during
the tests is able to provide a context via `getApplicationContext()`.
Without this, the mock context throws an `UnsupportedOperationException`
when invoking this method.
Using whatever `Activity` as the `Context` used to construct the first content
provider means that it will be help onto in memory until the application is GC'ed.
Previously, all of the various subclasses of FDroidProvider would create
their own database open helper in their respective `onCreate()` methods.
This seemed to be the cause of the multiple database locked exceptions.
Various online articles/SO posts/etc helped come to this conclusion:
* http://stackoverflow.com/a/3689883
* http://stackoverflow.com/a/8888606
* https://web.archive.org/web/20150709074733/http://www.dmytrodanylyk.com/pages/blog/concurrent-database.html
This should fix#265.
In the process, also did away with the two `read()` and `write()` methods
that returned a "readable" and "writeable" database respectively. It turns
out that it doesn't quite do what I originally thought. There is not much
benefit to specifying to the database helper that you want a readable/writeable
database. In fact, it is often the case that a call to `read()` would most
likely have returned the same instance that is returned by `write()`. The
semantics of them were therefore broken, and they've been replaced with
`db()`.
Finish main activity after navigating to AppDetails activity.
This is what used to happen before the recent refactor to the search UI.
Finishing the main activity means that it never comes back to handle the
"View this app" intent again. Instead, the main Activity just becomes an
entry point for redirecting the UI to the correct place.
Incidentally, I don't particularly like the current solution even though
it should work, and hope to come up with a better one in the future. The
reason is because the behaviour of some methods (`onCreate()` +
`handleIntent()`) is what defines the behaviour of other methods
(`checkForAddRepoIntent()`). This means it is hard to reason about the
state of the activity at any point in time. Developers need to be careful
when making changes to the `onCreate()` method because modifying it has
unintended consequences. That is what caused the problem in issue #541.
Fixes#541.
See merge request !195
This is what used to happen before the recent refactor to the search UI.
Finishing the main activity means that it never comes back to handle the
"View this app" intent again. Instead, the main Activity just becomes an
entry point for redirecting the UI to the correct place.
Incidentally, I don't particularly like the current solution even though
it should work, and hope to come up with a better one in the future. The
reason is because the behaviour of some methods (`onCreate()` +
`handleIntent()`) is what defines the behaviour of other methods
(`checkForAddRepoIntent()`). This means it is hard to reason about the
state of the activity at any point in time. Developers need to be careful
when making changes to the `onCreate()` method because modifying it has
unintended consequences. That is what caused the problem in issue #541.
Fixes#541.
Translators:
Adam Magnier French
Adrià García-Alzórriz Catalan
Adrià García-Alzórriz Spanish
Ajeje Brazorf Sardinian
Alberto Moshpirit Spanish
Dominik george French
Dominik george German
Enol Puente Asturian
Jean-Baptiste French
M2ck French
Marcelo Santana Portuguese (Brazil)
Massimiliano Caniparoli Italian
Mladen Pejaković Serbian
Nam Mai Hoang Vietnamese
naofum Japanese
Nick Bishop Greek
riotism Chinese (Hong Kong)
Sérgio Marques Portuguese (Portugal)
tacsipacsi Hungarian
ultrapeer Turkish
Андрій Бандура Ukrainian
Дмитрий Михирев Russian
Handle fdroidrepos:// urls better.
Before, it only handled the incoming `Intent` in `onResume()`. Now, it happens in `onNewIntent()` too (but ensures to only handle the same intent once).
Note the `onResume()` only ever handles the original intent, not the new intent. See inline comments for why.
Fixes#524.
See merge request !192
Before, both the swap activity and the fdroid activity would use
the "handled" key to check if an intent had been handled. This caused
problems, because by the time we got to handling the add repo intent,
it had already been "handled" by the swap activity so we bailed.
Before, it only happened in onResume(). Now, it happens in onNewIntent()
too (but ensures to only handle the same intent once).
Note the onResume() only ever handles the original intent, not the new
intent.
Fixes#524.
Create fragments appropriately in main view.
The approach to creating fragments in the constructor of the `AppListFragmentPagerAdapter`
was incorrect. Fragments are a special kind of magic, so this commit uses the approach
documented at http://stackoverflow.com/a/15261142 to make sure that the correct
fragment instances are always retrieved.
Fixes#521.
See merge request !190
Translators:
Ajeje Brazorf Sardinian
Alberto Moshpirit Spanish
hrskrs Albanian
Kalle Lampila Finnish
Mladen Pejaković Serbian
Persian: fix format strings Daniel Martí
Tobias Bannert German
Someone introduced lots of %I and and other weird format issues like %!.
Fixed all of them.
We normally catch these via Android's lint, but it appears that %I went
under its radar.
Fixes#530.
The approach to creating fragments in the constructor of the `AppListFragmentPagerAdapter`
was incorrect. Fragments are a special kind of magic, so this commit uses the approach
documented at http://stackoverflow.com/a/15261142 to make sure that the correct
fragment instances are always retrieved.
Fixes#521.
Translators:
Adrià García-Alzórriz Catalan
ageru French
Ajeje Brazorf Sardinian
Allan Nordhøy Norwegian Bokmål
Enol Puente Asturian
Erwin Scheuch-Heilig German
Karola Marky Japanese
Ldm Public French
Marcelo Santana Portuguese (Brazil)
Massimiliano Caniparoli Italian
naofum Japanese
Reiner Herrmann German
riotism Chinese (Hong Kong)
Tobias Bannert German
ultrapeer Turkish
Use custom layout for ACRA report dialog, not default one.
Styling the default dialog was difficult and it doesn't obey some
of the guidelines provided by the Android design docs:
https://www.google.com/design/spec/components/dialogs.html#dialogs-specs
(see "Content Guidelines")
This change introduces a custom dialog extending the base ACRA reporting
activity. Specifically, it introduces a padding of 24dp around the dialog
contents.
I couldn't find design specs on the specific spacing between different elements _within_ the dialog, so I used the same spacing of 20dp suggested to use between the title and the main body of the dialog.
At the very least, this change should make it easier to update styles in the future if somebody suggests improvements to the layout of the dialog.
### Before

### After

See merge request !186
Partially revert f2212e33. Make some constructors public again.
There is some code in the Android SDK using reflection to find the constructor
of one of our custom views. As such, a previous change in f2212e33 broke this
code because it made constructors package local. This partially reverts the bit
of f2212e33 which pertains to the constructors accessed using reflection.
Fixes#519.
See merge request !187
There is some code in the Android SDK using reflection to find the constructor
of one of our custom views. As such, a previous change in f2212e33 broke this
code because it made constructors package local. This partially reverts the bit
of f2212e33 which pertains to the constructors accessed using reflection.
Styling the default dialog was difficult and it doesn't obey some
of the guidelines provided by the Android design docs:
https://www.google.com/design/spec/components/dialogs.html#dialogs-specs
(see "Content Guidelines")
This change introduces a custom dialog extending the base ACRA reporting
activity. Specifically, it introduces a padding of 24dp around the dialog
contents.
This is thinking of the future, when new AFs are added and the installed
client doens't know about them yet. Showing the name is better than not
showing anything at all.
Translators:
Adrià García-Alzórriz Catalan
Ajeje Brazorf Sardinian
Alberto Moshpirit Spanish
Allan Nordhøy Norwegian Bokmål
Enol Puente Asturian
Kalle Lampila Finnish
Marcelo Santana Portuguese (Brazil)
Mladen Pejaković Serbian
naofum Japanese
Tobias Bannert German
Андрій Бандура Ukrainian
Саша Петровић Serbian
Add opt-in crash reporting via ACRA
Fixes#398.
Screenshot: https://imgur.com/E3uNIBY
E-mails to team@, so the reports are somewhat private yet still available to developers.
Using e-mail over POST has two advantages:
* Doesn't require a specific server handling the form submits
* Makes interaction with users who reported issues easy (we can just reply to the e-mail to ask for more info, etc)
It also has a disadvantage, though:
* It requires an e-mail app to be present and set up in the device
Most phones are already logged in to user's Google accounts, though, and a large part of those have the gmail app or the stock mail app installed. Those who don't have Google installed are probably technical enough to have set up a mail client like K9 on their own.
See merge request !185
Renaming id -> packageName in local variables/method args/comments/etc.
Building on the previous MR. This finishes renaming "id" in the Java code to "package name" where appropriate. It still leaves the database symbols as "id" because they are not worth the effort to rename at this point in time.
See merge request !184
Rename id to package name
As suggested in issue #37, "id" is not the correct terminology to use in the code base. Instead, it should use "package name". I'm doing this now before I begin work on #511, where it is helpful to recoup the "id" name, as "rowid" in sqlite refers to a row's integer primary key.
This changes the following Java symbols:
* `Apk.id` -> `Apk.packageName`
* `App.id` -> `App.packageName`
* `ApkProvider.DataColumns.ID` -> `ApkProvider.DataColumns.PACKAGE_NAME`
* `AppProvider.DataColumns.ID` -> `AppProvider.DataColumns.PACKAGE_NAME`
* A small number of comments referring to `id`.
It does _not_ change (and likely will not benefit from changing):
* Any database columns, these are all still `id`.
* Anything pertaining to the index, which still uses `id`.
It does _not_ change (but future MR's will change):
* Local variables referring to `id`
* Method names referring to `id`
By not changing any string literals, only changing the name of variables and constants, and ensuring that the original variables/constants are no longer present, then in theory if this compiles is should be good to merge.
See merge request !183
Make sure to insert, rather than update apks when updating repo.
(Actually) fixes#517. I had a hunch the other bug (though problematic) wasn't actually the fix for #517. I am confident that this one _is_ the fix, although I am still struggling to reproduce specific circumstances reported by others. I tracked this down when I did an update this morning and noticed a new app without any versions - so in a way I kind of reproduced it, but I can't reproduce it at will.
The problem was that again, only the `vercode`s of apks were being
compared to see if they were the same, rather than `vercode` and `id`.
The result is that some new apks will incorrectly be asked to execute
an `UPDATE` query rather than an `INSERT`. As such, they don't end
up in our database at all because the `UPDATE` will not run against
any row at all.
See merge request !182
Fixes#517.
The problem was that again, only the `vercode`s of apks were being
compared to see if they were the same, rather than `vercode` and `id`.
The result is that some new apks will incorrectly be asked to execute
an `UPDATE` query rather than an `INSERT`. As such, they don't end
up in our database at all because the `UPDATE` will not run against
any row at all.
Correctly purge removed apks.
There were two bugs in this code that have been fixed. Both were
introduced while moving to a lower memory consumption version of
the repo updater for #324.
They were found while investigating #517, although I'm not confident
that they are to do with the specific problem in that issue. It is
possible, because it is part of the code to do with purging apks
from the database, but I'd like to do further investigations into
the code before declaring that this is actually related to that issue.
In the mean time, this includes important fixes, and should probably
go out in an alpha once merged.
The first is that for each app which is updated by a particular repo,
it was not correctly asking the question: "Which apks belonging to
this apk, and provided by this repo, are no longer provided by this
repo?". It was accidentally only comparing the version number of
existing apks in the DB and new apks from the index. This ensures
that the apks being checked to see if they need to be removed or
not are those with the same package name AND the same version code
(provided by the same repo).
The second bug is that when it was persisting a set of apps + apks
to the database, it would ask for existing apks already in the
database for these apps. In this case, there was a bug where of
the 50 apps being persisted, it would only retrieve the first of
these 50 apps from the database to decide if it needed to be
cleaned up or not. Now, it correctly retrieves data from the DB
belonging to all 50 apps.
See merge request !181
There were two bugs in this code that have been fixed. Both were
introduced while moving to a lower memory consumption version of
the repo updater.
The first is that for each app which is updated by a particular repo,
it was not correctly asking the question: "Which apks belonging to
this apk, and provided by this repo, are no longer provided by this
repo?". It was accidentally only comparing the version number of
existing apks in the DB and new apks from the index. This ensures
that the apks being checked to see if they need to be removed or
not are those with the same package name AND the same version code
(provided by the same repo).
The second bug is that when it was persisting a set of apps + apks
to the database, it would ask for existing apks already in the
database for these apps. In this case, there was a bug where of
the 50 apps being persisted, it would only retrieve the first of
these 50 apps from the database to decide if it needed to be
cleaned up or not. Now, it correctly retrieves data from the DB
belonging to all 50 apps.
Add HTTP Basic Authentication to ApkDownloader
In order to download protected files, the APKDownloader must be able to supply HTTP Basic Authentication headers to the server.
See merge request !178
Translators:
Ajeje Brazorf Sardinian
Green Lunar Hebrew
Ldm Public French
lucnsy Chinese (China)
Massimiliano Caniparoli Italian
Mladen Pejaković Serbian
naofum Japanese
Nordlenningen Norwegian Bokmål
Sérgio Marques Portuguese (Portugal)
Tobias Bannert German
Vdragon, V字龍 Chinese (Taiwan)
Search as the user types
Fixes#323.
This does away with the separate `SearchResult` and instead applies the search to the currently viewed tab on the main screen (Available, Installed, Updates). When filtering the Available list, it filters the currently selected category.
Note however that there are still times when the old style `SearchDialog` will be shown over the top of the action bar rather than the `SearchView` within the action bar. These times include:
* When a user with a hardware keyboard starts typing from the main screen.
* On older devices with a "search" hardware button.
* Probably some other cases (I think when there is not enough screen real estate, but haven't seen that happen).
In cases where this dialog is shown, filtering the lists as you type does not seem to be an option. I tried to figure out how to do that, but failed. If someone else figures it out, that would be great. However, when the search is submitted, it will hide the `SearchDialog` and populate the `SearchView`, focus it, and apply the search appropriately.
There is a script in the `F-Droid/tools/` subdirectory which will consecutively send various intents to F-Droid relating to search. This includes Play, market, Amazon search links. For good measure, I also made it send intents to do with viewing app details. This should probably be made into a proper instrumented test at some point, but I didn't have the time to figure out how to do that. Maybe a project for future @pserwylo.
One unknown is the performance implications. There is no problems on my Nexus 4 with Android 5.0. My Chinese/ebay/$30/Android 2.3.4 device seems good enough too.
See merge request !177
The system's are sometimes wrong, e.g. unexpected names. This also helps
our support across different Android versions without having to worry as
much about the system's language support.
Fixes#503.
Translators:
Dario Tordoni Italian
Elia Argentieri Italian
Enol Puente Asturian
halcyonest Korean
M2ck French
relan Russian
Tobias Bannert German
Дмитрий Михирев Russian
In addition, added a @Nullable constraint on the categorySpinner
and a null guard when resuming the fragment to handle possible
null cases (though I don't think there will be any).
This caused the entire list view to e animated when navigating
back to the Available tab. Tried switching the `animateLayoutChanged=true`
to a child view only containing the category spinner, but this is not how
the animation handling works. It needs to animate both the thing being
hidden/shown, and also the next sibling of that thing to work properly.
Thus, moving the spinner to its own child and leaving the list didn't work.
Speed up "Saving Application Details" part of repo update
Fixes 506.
This does the thing mentioned in 506 (renaming temp table, rather than copying data). In the process, I also identified that the temp tables were missing key indexes which slowed the process down. This fix took the update time from ~100 seconds to ~60 seconds on my Nexus 4.
Below are a couple of log cats from before and after the change (this logging is not part of this MR, it was just for diagnosing the problem).
*Before change:*
```
AppProvider D Calculating whether apps are compatible, based on whether any of their apks are compatible
D Update compatible flags took 20244ms
D Calculating suggested versions for all apps which specify an upstream version code.
D Update suggested from upstream took 17808ms
D Calculating suggested versions for all apps which don't specify an upstream version code.
D Update suggested from latest took 129ms
AppProvider D Update icon URLs took 7879ms
UpdateService I Updating repo(s) complete, took 104 seconds to complete.
```
*After change:*
```
AppProvider D Calculating whether apps are compatible, based on whether any of their apks are compatible
D Update compatible flags took 1047ms
D Calculating suggested versions for all apps which specify an upstream version code.
D Update suggested from upstream took 601ms
D Calculating suggested versions for all apps which don't specify an upstream version code.
D Update suggested from latest took 136ms
D Update icon URLs took 887ms
UpdateService I Updating repo(s) complete, took 63 seconds to complete.
```
See merge request !179
The idnexes which are added are for those columns which
are used to calculate information such as latest upstream version.
These queries use subqueries which seemed to be adversely
impacted by the lack of indexes.
In total, reduced update time on test device from just over 100 seconds
to just over 60 seconds.
This requires renaming the old app/apk tables to be deleted and
the temp ones to be renamed. This is done in a transaction to
ensure we always have at least `fdroid_app` and `fdroid_apk`.
Well, two transactions, one for renaming the `fdroid_app` table
and one for `fdroid_apk`.
Translators:
agilob Polish
Ajeje Brazorf Sardinian
Alberto Moshpirit Spanish
Daniil Stryukov Ukrainian
Enol Puente Asturian
Jaroslav Lichtblau Czech
Ldm Public French
lucnsy Chinese (China)
Marcelo Santana Portuguese (Brazil)
Mladen Pejaković Serbian
naofum Japanese
relan Russian
Fix 324 : Out of memory errors while updating repos.
Fixes#324, but in the process makes the updater take a lot longer. My benchmarks tell me that an update which used to take approx 30 seconds on my Nexus 4 now takes about 50-55 seconds. This is because it first inserts the apps into the database (in a temp table) and then subsequently copies that table to the actual table. This means there is a lot more disk access than before.
I'm open for discussion on whether this tradeoff is worth it - however I'll caution that there is always going to be a tradeoff between faster and more memory vs slower and less memory. This is the case with all software, and perhaps more so with memory constrained devices such as phones. Also, as the repo index grows (until perhaps we are able to extract the app descriptions in the future), this will become more of an issue.
I'd also like this to be CR'ed properly before merging, because it changes some important code around the repo updater. It is important because security, and it is also important because it is the main thing that F-Droid needs to do (get a list of apps to show the user).
See merge request !173
Translators:
ageru French
Ajeje Brazorf Sardinian
Alberto Moshpirit Spanish
enolp Asturian
Ldm Public French
lucnsy Chinese (China)
Mladen Pejaković Serbian
naofum Japanese
Will also appear as indeterminate if:
* The repo being downloaded from doesn't send a Content-Length header.
* While connecting to the HTTP server to begin downloading.
Right now it says "50%" always, will need to think whether to ditch the
percentage completely, or to have the temp app/apk providers emit progress
events some how too.
Although not used by the temp provider, it seemed strange having some of
the code always using the `DBHelper.TABLE_APP` and other code using
`getTableName()` where all of it could have used `getTableName()`.
Also moved commiting of the temp tables to the real tables into the
`RepoPersiter` instead of in `RepoUpdater`.
The repo xml handler now has a different mechanism for returning
data about the parsed xml file. This is done via a callback, rather
than storing the data in member variables. The tests now deal with
this correctly.
The update/delete operations of the TempAp[pk]Provider's didn't
work, so that has now been fixed.
At the start of a repo update, it will create a copy of the apk table.
Throughout the update, it will query the original apk table for info.
All inserts and updates happen to the temp table. After the repo has been
verified as trusted, the original apk table is emptied, and all apks are
copied from the temp table to the real one.
I realise that the work done to query the apk table for info during the update
could happen against the temp table, but it was not neccesary to move all of
the queries that are required for this task to the temp apk provider.
Refactored repo update to stream apks from network -> jar file reader ->
xml parser -> database. No longer build up large lists of app metadata
to save. Saves memory, but is MUCH slower.
Does sig verification properly, but does it at the END of the process
and DOESN'T ROLL BACK on failure.
Quick and dirty benchmarks show an increase in time from ~25 seconds
to ~30 seconds on my Nexus 4 with Android 5.0. This doesn't seem so
bad to me, for the tradeoff that people on low end devices can actually
update now.
Also, as @eighthave pointed out, if we are able to stream the download
directly from the internet, then that time will drop to essentially
the time it takes to download the index.
Improved support for HTTP Basic Authentication
Extended DownloaderFactory to support optional username & password parameters.
Extended HttpDownloader to check for HTTP 401 Authorization Required status code
and send a simple HTTP Basic Authentication header with all requests.
Extended ManageReposActivity to support repositories that use HTTP Basic
Authentication, added a dialog to prompt for username and password.
Extended RepoDetailsActivity to be able to display and modify the authentication
credentials.
See merge request !171
Instead of passing in `null` each time you don't have a username/password,
this change provides those as meaningful default values in an overloaded
version of the method. This takes care of Java's lack of default argument
support.
This is for an abundance of caution. If the guard condition checks
for the presence of both username _and_ password fields, then a crash
or some sort of force close during the update (after adding username
but before password) will mean that next time the app runs, this
condition will evaluate to false and the password field will never
get added.
As with the previous commit, there is probably not any harm doing this
in the way it was done. However it helps reason about the code if
changes are applied in the order that they were introduced. Especially
because each of them does something depending on the version of the
database at that point. With this change, you always know that at the
point that the function is run, the database version will be 51 (and
hence the structure of the database will be predictable).
This may not have caused any trouble, but the principle behind the old
behaviour is that at the point that that was required, the fdroid_repo
table had that particular structure. There is a small chance that it
_may_ have some unintended consequences when upgrading clients with very
old database versions. Probably not, but may as well leave it as is.
Extended DownloaderFactory to support optional username & password parameters.
Extended HttpDownloader to check for HTTP 401 Authorization Required status code
and send a simple HTTP Basic Authentication header with all requests.
Extended ManageReposActivity to support repositories that use HTTP Basic
Authentication, added a dialog to prompt for username and password.
Extended RepoDetailsActivity to be able to display and modify the authentication
credentials.
Replace search dialog with a search widget
SearchView is the recommended way to implement search UI. See https://developer.android.com/guide/topics/search/search-dialog.html#UsingSearchWidget
The UX is still far from ideal but looks much better now.
Before and after (Gingerbred):


Before and after (Lollipop):


See merge request !168
Enable HttpDownloader to use URL-based HTTP Basic Authentication.
This is a very small merge request that adds the possibility to use URL-based HTTP Basic Authentication in a repository URL. With this change you can for example use `https://user:password@my.repo.com` to authenticate against a private repository.
It would be great if you could merge my little feature request into the master, or let me know what I can do or have to change in order for the merge request to get accepted.
I'm of course open for discussion. My use-case is the identification of individual users. We dynamically create a signed index.jar file for each user which contains an individual set of apps depending on the permissions of the user etc.
HTTP Basic Authentication is on of the possible solutions, another solution would be to use the Android Account Manager, but this would be a much larger change.
Thank you for your consideration.
Best regards,
Christian Morgner
See merge request !167
Clean up tabs fragments
No functional changes, just refactoring. The only visual change is that empty text is now positioned at the center which was the initial design (as far as I understand):

See merge request !165
Not sure why it was added initially but now it appears to be unneeded:
the support library does everything right and the lists are themed
properly without any hacks.
Never fallback to UIL for handling image downloads, only use for displaying.
@relan picked up a bug I introduced while refactoring the icon downloading code in !139. This fixes that bug.
Our `IconDownloader` extended `BaseImageDownloader` from UIL. There was an
explicit check in the F-Droid `IconDownloader` which looks for
HTTP/HTTPS/Bluetooth schemes. If it wasn't one of these, it fell back
to the base class. This was what was happening for local cached image
files. As such, when the `getInputStream(...)` method was refactored
to only use F-Droids `DownloadFactory` and not delegate to the base class,
it failed on local "file://" URLs.
This change introduces a `LocalFileDownloader` and makes the `DownloaderFactory`
aware of it.
The `BaseImageDownloader` class only provides support for the following schemes:
* HTTP
* HTTPS
* File
* Android content providers
* Android assets
* Android drawables
F-Droid now supports HTTP, HTTPS, and File URLs. There is not currently any
need for content proiders, assets or drawables to get icons for apps in F-Droid.
If there is a need in the future (e.g. an issue currently discusses loading
icons from installed apps if possible) then that specific `Downloader` can get
introduced to solve the problem.
See merge request !164
Our `IconDownloader` extended `BaseImageDownloader` from UIL. There was an
explicit check in the F-Droid `IconDownloader` which looks for
HTTP/HTTPS/Bluetooth schemes. If it wasn't one of these, it fell back
to the base class. This was what was happening for local cached image
files. As such, when the `getInputStream(...)` method was refactored
to only use F-Droids `DownloadFactory` and not delegate to the base class,
it failed on local "file://" URLs.
This change introduces a `LocalFileDownloader` and makes the `DownloaderFactory`
aware of it.
The `BaseImageDownloader` class only provides support for the following schemes:
* HTTP
* HTTPS
* File
* Android content providers
* Android assets
* Android drawables
F-Droid now supports HTTP, HTTPS, and File URLs. There is not currently any
need for content proiders, assets or drawables to get icons for apps in F-Droid.
If there is a need in the future (e.g. an issue currently discusses loading
icons from installed apps if possible) then that specific `Downloader` can get
introduced to solve the problem.
Cache installed signature
This will later be useful for #122 and others. Also a few more fixes related to signatures and package information.
CC @pserwylo
See merge request !158
These are written manually and mostly don't contain HTML. Some html is
fine if you want to use links or markup, but <p> elements are just
pointless and very seldom used. Be consistent in not using them.
Added tests for multiple repositories providing same apks
Right now, multi repo support works, but is kinda funky. While fixing #324, I accidentally broke this support without realising it. So in the interests of making my approach to #324 more test driven, I've written some tests for multi repo support.
Initially I wrote tests for the actual correct, desirable behaviour. Then when it became apparant that we dont' do this, I commented those tests out (but left them there for hopefully future multi-repo work) and then added tests for the current behaviour to make sure we don't introduce regressions.
Android unit testing framework is nice for testing content providers. It is nice for testing file handling. However I really struggled to get it working with both. Had to do some interesting things with instrumentation and contexts in order to get it to work.
I'm sure Android has nice `Service` testing capabilities too. But given the trouble with instrumentation/contexts/files/providers/etc, it was easier for me to refactor the parts of `UpdateService` that I needed to test into a separate, testable class.
See merge request !163
Make closing of `Downloader`s more concise.
*NOTE: This is only a WIP branch in so far as I haven't tested it, as it was done on the train before my holiday. The code is final though, so if people are happy it works, please merge.*
The base `Downloader` class now wraps the `InputStream` returned by
any child classes, in order to notify the child class when that stream
is closed. This prevents each child class having to figure out a way
to be notified of this.
This helps keep the API concise, because close handling is dealt with
without the need to add any public methods to the `Downloader` class
hierarchy.
Also removed some dead code which was unused.
See merge request !139
Ensure the "no apps to display" doesn't overlap with the category spinner.
There is no layout .xml file for the updated/installed list,
but there is for the available apps list. As a result, the `TextView` containing
the empty list message is added dynamically. With the recent improvements to
the category spinner, a `RelativeLayout` was used instead of a `ListView`. To
remedy this, the layout has been augmented to ensure the dynamically added
`TextView` still displays correctly.
*Before:*

*After:*

See merge request !162
There is no layout .xml file for the updated/installed list,
but there is for the available apps list. As a result, the `TextView` containing
the empty list message is added dynamically. With the recent improvements to
the category spinner, a `RelativeLayout` was used instead of a `ListView`. To
remedy this, the layout has been augmented to ensure the dynamically added
`TextView` still displays correctly.
Came across this whibluetooth swap.
I think it is reasonable to guard against null activities here, because
we are likely not releasing observers correctly, and thus the observer
may receieve a notification when the activity is not attached.
The base `Downloader` class now wraps the `InputStream` returned by
any child classes, in order to notify the child class when that stream
is closed. This prevents each child class having to figure out a way
to be notified of this.
Also removed some dead code which was unused.
Leave only the ones which align with the current multi-repo behaviour
that F-Droid exhibits.
The commented out tests can be uncommented in the future when working
on proper multi-repo support.
(One of) the problems with F-Droid's multiple support is that there is
a primary key on the fdroid_apk table which is a composite of:
* id
* vercode
Which means that two repos providing the same version means one will
update the other, rather than ending up with two different versions.
Instead, there should be some other way to differentiate apks from
different sources. Firstly, it should take into account the signing
cert. Secondly, it may taken into account the hash, because two people
could sign different apks with the same cert and then we are back at
square one.
The new test skeletons right now update three different repos
in different configurations. They do so such that the order of updates
changes and therefore the way in which conflicts between repos are
dealt with are tested.
They should all have the same result (though I'm not sure exactly what
that should be yet).
Set category filter height to 48dp
This improves usability of the category spinner. Divider is now behind
it. This makes the layout visually more compact while keeping actual
touch target 48dp. See
http://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-touch-target-size
Before and after:


See merge request !161
New icon for Privileged Extension
Align Privileged Extension icon with current F-Droid icon. The # sign is associated with privileges because it's usually used by root shell. Lego blocks that can be plugged into each other are also known very well around the world.
"F-Droid Privileged Extension icon" by @relan is licensed under CC-BY-SA 3.0 or GPLv3+.
Smaller size:

Larger size:

Raster images are optimized using `optipng -o7 -zm1-9`.
See merge request !155
Make category filter look Material
Get rid of Gingerbred legacy.
Before and after (note the glitch below the spinner):


See merge request !159
Use darker primary colors for the Dark theme
Primary colors of the Light theme are too bright for the Dark theme. The difference should be especially noticeable on OLED screens.
Before and after:


See merge request !160
Translators:
ageru French
agilob Polish
Ajeje Brazorf Sardinian
Dario Tordoni Italian
Laura Arjona Reina Spanish
Ldm Public French
naofum Japanese
Osoitz Basque
Sérgio Marques Portuguese (Portugal)
We were fetching information on all installed packages and doing a linear
search. Which is silly and inefficient since we can directly fetch information
on a single installed package by id.
If it doesn't result in any version being marked as suggested, e.g. because it
is higher than 0 but lower than any available version, we end up with a NULL
suggestedVercode.
Run the Latest() algorithm after the Upstream() one, and have it pick up those
cases too by adding "OR suggestedVercode IS NULL". This way, we treat invalid
upstreamVercode values as if they were NULL.
Also some cleanup of comments.
Fixes#371.
Materialize app list
Improve the look of app list items. Note that compact mode is removed because it does not make much sense, see the screenshots.
Before (default layout, compact layout):


After:


Affects #471 and #467. Those bugs will still be relevant because there are other places with wrong padding and font style.
See merge request !157
TextView respects textViewStyle since appcompat-v7:22.2:
public AppCompatTextView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
+ this(context, attrs, android.R.attr.textViewStyle);
}
This change in appcompat-v7 breaks previous behaviour by applying
textColorSecondary to TextViews: text becomes grey after upgrading
appcompat-v7 to 22.2.
See https://code.google.com/p/android/issues/detail?id=170476
If getSystemService(Context.DOWNLOAD_SERVICE) returns null, we should not go
ahead with using AsyncDownloaderFromAndroid. This would result in NPE and
crashes.
Fixes#442.
Translators:
agilob Polish
AtomiKe French
Ferenc Nagy Hungarian
Mário Castanheira Portuguese (Portugal)
Sérgio Marques Portuguese (Portugal)
zmni Indonesian
It doesn't include all of the Android style checks as found in Android Studio,
but it's a start.
Bump Gradle to 2.7 because the checkstyle plugin in earlier versions is just
not good enough.
As the comment says, this should not be on until it can be opted out from. The
biggest problem is unwanted repo updates, e.g. when a user is on a mobile
connection.
Fixes#435.
The idea was good, but it applied the second-precise date format everywhere.
This broke dates on apps and apks, which have the format yyyy-MM-dd on the
index.
Fix this by having two sets of funcs in Utils, one for dates (precise to a
day) and one for times (precise to a second). We should use the latter for
dates we ourselves measure, like the last repo update time.
It is used as part of swap, but the library never caused any proguard issues
and there is no reason to think it should. The issues came from zipsigner and
its use of spongycastle.
These keeps are to keep swap working, which depends on libraries that use
reflection and other stuff that proguard breaks. libsuperuser is not used by
swap though, and doesn't have those issues.
Add night theme
[Screenshot](http://i.imgur.com/gVK5M0G.png)
The screenshot is huge, so making it an image in markdown takes up a lot of space. Hence link.
See merge request !149
Similar to the dark theme, but dropping blue in favour of very dark shades of
grey.
Removed colorEdgeEffect to simplify the sharing of the style between dark and
night themes. It should default to colorPrimary anyway, so we're good.
Fixes#345.
Refresh layout of AppDetails screen
This change primarily affects the AppDetails links section to make them easier to click. It also strips down the UI a bit to provide a cleaner interface as well as some modest Material Design tweaks.
Fixes#389.
Also fixes an unreported issue where the permissions list would be blank if the app only requested the root permission on devices which no longer support this.
The most significant change is arguably removing the expand/collapse functionality around the links and permissions UI elements. I think personally this kind of UI behavior is a bit of an anti-pattern as it clutters up the UI visually and doesn't provide much if any real utility to the user. Also, given the background and context of the app, I would argue that the links (especially to the source code and website) should have high visual priority as well as the permissions the application requests. I know I personally very commonly click on these before deciding to install any given app.
It's not perfect, and I would like to do more eventually, but I tried to refrain from making too many or too drastic of changes to the existing design for now.
| **before** | **after** |
| -------- | -------- |
| ![before] | ![after] |
**link touch target**
![touch_target]
[before]: https://gitlab.com/zaventh/fdroidclient/uploads/c45d27a19777ea345820d8753d3e290b/before_480.png
[after]: https://gitlab.com/zaventh/fdroidclient/uploads/b1f517c9de9e74c63b6957fed68b3dd3/after_480.png
[touch_target]: https://gitlab.com/zaventh/fdroidclient/uploads/6e7a38610bc792f11a76fe837fb9f28a/touch_target.png
See merge request !153
This change primarily affects the AppDetails links section to make them easier to click. It also strips down the UI a bit to provide a cleaner interface as well as some modest Material Design tweaks.
Fixes#389.
The android build process doesn't waste time running optipng and such, since
it takes minutes to run properly.
I had already ran this a few months ago. Running it again gives an apk ~15KB
smaller, which is welcome. This is because of the new images added.
Translators:
Ajeje Brazorf Sardinian
Allan Nordhøy Norwegian Bokmål
AtomiKe French
Enol Puente Asturian
jaksi Hungarian
Luis Ruiz Spanish
Marc Ringel German
Mário Castanheira Portuguese (Portugal)
Marvin W German
naofum Japanese
Olexandr Nesterenko Ukrainian
Osoitz Basque
Reg Swedish
Thomas Lü German
Ddownload index, even when not presented with a Content-Length header (Fixes 430)
This works for the index download, but still does not work for downloading .apks correctly. That is, the Android Download Manager needs one of:
* `Content-Length: ...`
* `Connection: close`
* `Transfer-Encoding: Chunked`
headers to be set to work correctly. In the absence of all three of these, problems ensue. In fact in my toy web server, even the `Connection: close` did not seem to work correctly, but I'd be happy to be corrected on that. Either way, this is an improvement on what was there before.
For reference, here is my toy PHP web server, which can be saved in the root of the F-Droid repo and invoked with:
> `php -S 10.0.0.4:8888 no-headers.php`
```
<?php
function streamFile( $file ) { $size = filesize( $file );
$contents = file_get_contents( $file );
$buffer = (int)( $size / 5 );
$bytes = 0;
while ( $bytes < $size ) {
$toStream = min( $size - $bytes, $buffer );
echo substr( $contents, $bytes, $toStream );
$bytes += $toStream;
// Sleep to allow progress to be viewed in F-Droid
sleep( 1 );
}
}
$index = "/fdroid/repo/index.jar";
$firefox = "/fdroid/repo/fennec-40.0.multi.android-arm.apk";
// Test downloading a large .apk to see how it behaves
if ( $_SERVER['REQUEST_URI'] == "/fdroid/repo/fennec-40.0.multi.android-arm.apk" ) {
$file = $firefox;
} else {
$file = $index;
}
// Android Download Manager requires this (if not using Content-Length or Transfer-Encoding
// headers, but I can't seem to get it to work as expected).
header( "Connection: Close" );
streamFile( dirname( __FILE__ ) . $file );
```
See merge request !150
Return to SearchResults on "Up" navigation from AppDetails
When arriving to the AppDetails screen from SearchResults, the Up navigation should return to SearchResults screen and not its actual parent.
Not only is this behavior intuitive, it is a commonly accepted UX paradigm on the platform.
See merge request !147
Instead, keep downloading until the `InputStream` returns -1.
Also, required updates to the UI so that when the download size is
not known, there is still a reasonable response to the user.
Note that this still fails when using the Android download manager
if the download attempts to get resumed, and the server did not
send a Connection: close, Content-Length, or Transfer-Encoding: Chunked
header.
See http://www.google.com/design/spec/style/typography.html
Remove useless attributes; add styles with proper paddings, size and color
for caption and body text. Unfortunately, line spacing attributes are
supported only since Jelly Bean.
Some were wrapped in BuildConfig.DEBUG, some weren't. Move all of them to
debugLog.
The ones that require building complex strings are left with an explicit
BuildConfig.DEBUG if so that the strings aren't constructed.
Translators:
agilob Polish
AtomiKe French
Enol Puente Asturian
Green Lunar Hebrew
Lu Ca Sardinian
Marcelo Santana Portuguese (Brazil)
naofum Japanese
Phạm Nguyễn Hoàng Esperanto
Phạm Nguyễn Hoàng Vietnamese
raspbiak Slovak
Since now we don't have problems with list item height, we can use two
different text labels for unsigned and unverified repositories
indication. Code now only switches visibility for them.
This is technical detail that should not be exposed to users, at least
not before repositories list which are the main data in the activity.
Last update label didn't work properly anyway: it was not updated on
repositories refresh, just on activity creation.
Only enable the light if it's a default. The vibration was causing issues
since we don't have the vibrate permission, and the sound is unnecessary.
Fixes#346.
Found it when trying to add a repo manually:
E/AndroidRuntime(30549): java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.replaceAll(java.lang.String, java.lang.String)' on a null object reference
E/AndroidRuntime(30549): at org.fdroid.fdroid.views.ManageReposActivity$AddRepo.normalizeUrl(ManageReposActivity.java:547)
E/AndroidRuntime(30549): at org.fdroid.fdroid.views.ManageReposActivity$AddRepo.validateRepoDetails(ManageReposActivity.java:358)
E/AndroidRuntime(30549): at org.fdroid.fdroid.views.ManageReposActivity$AddRepo.access$700(ManageReposActivity.java:218)
E/AndroidRuntime(30549): at org.fdroid.fdroid.views.ManageReposActivity$AddRepo$4.afterTextChanged(ManageReposActivity.java:342)
E/AndroidRuntime(30549): at android.widget.TextView.sendAfterTextChanged(TextView.java:7698)
Analogous to the server commit that removes it on its side. If an index
includes it, we will fail silently since unrecognised tags are ignored.
SQLite doesn't allow deleting columns, so don't do a DB version bump to try to
remove it. It will be gone on new installs, and it doesn't keep current
installs from working. Tested both cases.
This avoids problems in a few corner cases:
* The description is empty (it would probably throw an exception)
* The description doens't end in newlines (as seen in #422)
For the general case that was already working, the result is the same.
Translators:
Green Lunar Hebrew
Kiril LastName Bulgarian
Laura Arjona Reina Spanish
Lu Ca Sardinian
Marc Ringel German
Mladen Pejaković Serbian
Pablo Spanish
relan Russian
riotism Chinese (Hong Kong)
Small swap fixes
These are a few fixes I found while debugging the new swap stuff. The `repo.fingerprint` commit is quite important since that is the code that provides the core of FDroid's security model.
See merge request !142
The solution is really to get rid of the Fragment and do everything in the
Activity, especially since this nullguard will probably leave things in a
not good state. But that's better than a crash, I think.
W java.lang.NullPointerException
W at org.fdroid.fdroid.views.fragments.AvailableAppsFragment$CategoryObserver.onChange(AvailableAppsFragment.java:88)
W at org.fdroid.fdroid.views.fragments.AvailableAppsFragment$CategoryObserver.onChange(AvailableAppsFragment.java:103)
W at android.database.ContentObserver.dispatchChange(ContentObserver.java:163)
W at android.database.ContentObserver$Transport.onChange(ContentObserver.java:195)
W at android.database.IContentObserver$Stub.onTransact(IContentObserver.java:60)
W at android.os.Binder.execTransact(Binder.java:404)
W at dalvik.system.NativeStart.run(Native Method)
The repo.fingerprint logic is based on null meaning that the fingerprint
has not been set. This ensures that a "" does not sneak in somehow as a
valid fingerprint.
For Strings that are meant to be displayed to humans, using the default
Locale makes sense. But this String is meant to be parsed by code, so it
should force the Locale to make sure the digits are always rendered in the
same format.
As per !140, some languages require different translations for an app being
installed and the tab name for installed apps. Namely, one is plural while the
other isn't. Separate the two.
If they are ever useful again, they can be brought back with their
translations. Reasons to not keep them around:
* Most will not be used ever again
* Most languages don't have all of them yet
* Translators doing new languages would waste their time
Kept the local repo https ones since the setting was just temporarily
removed.
The Bonjour stuff takes a while to start, and isn't strictly neccesary
in order to swap over WiFi. Rather, it is more of a convenience. Also,
it was causing the UI to appear to lag quite a lot. This way, the WiFi
swap gets setup almost instantly, and is available to swap - therefore
the UI seems much more responsive.
While we're at it, use `Thread`s rather than `AsyncTask`s because
they are more concise. Not 100% sure, but I think `AsyncTask`s
need to wait for the UI event loop to get to them in order kick off.
Trying to pin down why the UI takes one or two seconds to catch up
after performing the "Enable WiFi" toggle. This is best done via
logging rather than using the debugger.
Also changed to Utils.DebugLog instead of Log.d, and removed some
comments and fixed a typo in one comment.
Fixes#206. Two other small fixes too:
* Units don't really translate, and they are appended to java strings anyway.
If it is to be translated, do a proper formatted string.
* Placed the light theme above the dark one, since it's the default.
qrUriString was supposed to be used. Moreover, it does look like execute()
must be called with an upper case string. Caught since qrUriString is an
unused variable.
You must, must, must close the BT sockets else you run out of them.
This code tries to handle all the places where BT sockets may not get closed.
It also tries to tweak user experience/UI integration pieces in a few areas,
and handle some NPEs that can occur when BT fails.
- don't try to start BT in the background. you can only start/stop a BT server once, else new connections don't work
- be more mindful of reading/writing bytes from the input/output streams... make sure bytes are available, because you will block forever if you do not do that
- use the device class tag to filter devices in discovery instead of the fdroid name tag
- this now successfully connects but there is an error in the certificate fingerprint verification still
My first new-swap fixes
I've been working on getting new-swap stable, these are my fixes so far. The big one is fixing swap when proxy settings are enabled. Comments in the commits.
See merge request !136
Proxying are basically always for internet access, and swap repos are by
definition only on the local network. This adds a pretty strict check for
whether a given download URL is for a swap repo, and if so, the proxy
settings are ignored.
fixes https://dev.guardianproject.info/issues/3421
It is not helpful to the user to tell them they can download from the web
when they are using swap, since a) they should use F-Droid since its safer,
and b) they are probably using swap because they can't access the web
reliably.
This required rebuilding the zipsigner.jar, since there was once issue to
be fixed in kellingwood.security.zipsigner.ZipSigner.
Here are the stacktraces:
StrictMode E A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
E java.lang.Throwable: Explicit termination method 'close' not called
E at dalvik.system.CloseGuard.open(CloseGuard.java:184)
E at java.io.FileOutputStream.<init>(FileOutputStream.java:90)
E at java.io.FileOutputStream.<init>(FileOutputStream.java:73)
E at java.io.FileWriter.<init>(FileWriter.java:42)
E at org.fdroid.fdroid.localrepo.LocalRepoManager.writeIndexJar(LocalRepoManager.java:488)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:698)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:645)
E at android.os.AsyncTask$2.call(AsyncTask.java:288)
E at java.util.concurrent.FutureTask.run(FutureTask.java:237)
E at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
E at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
E at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
E at java.lang.Thread.run(Thread.java:841)
E A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
E java.lang.Throwable: Explicit termination method 'close' not called
E at dalvik.system.CloseGuard.open(CloseGuard.java:184)
E at java.io.RandomAccessFile.<init>(RandomAccessFile.java:128)
E at kellinwood.zipio.ZipInput.<init>(ZipInput.java:57)
E at kellinwood.zipio.ZipInput.read(ZipInput.java:75)
E at kellinwood.security.zipsigner.ZipSigner.signZip(ZipSigner.java:646)
E at org.fdroid.fdroid.localrepo.LocalRepoKeyStore.signZip(LocalRepoKeyStore.java:218)
E at org.fdroid.fdroid.localrepo.LocalRepoManager.writeIndexJar(LocalRepoManager.java:515)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:698)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:645)
E at android.os.AsyncTask$2.call(AsyncTask.java:288)
E at java.util.concurrent.FutureTask.run(FutureTask.java:237)
E at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
E at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
E at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
E at java.lang.Thread.run(Thread.java:841)
E A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
E java.lang.Throwable: Explicit termination method 'end' not called
E at dalvik.system.CloseGuard.open(CloseGuard.java:184)
E at java.util.zip.Inflater.<init>(Inflater.java:82)
E at kellinwood.zipio.ZioEntry.getInputStream(ZioEntry.java:475)
E at kellinwood.zipio.ZioEntry.getInputStream(ZioEntry.java:445)
E at kellinwood.security.zipsigner.ZipSigner.addDigestsToManifest(ZipSigner.java:418)
E at kellinwood.security.zipsigner.ZipSigner.signZip(ZipSigner.java:713)
E at kellinwood.security.zipsigner.ZipSigner.signZip(ZipSigner.java:647)
E at org.fdroid.fdroid.localrepo.LocalRepoKeyStore.signZip(LocalRepoKeyStore.java:218)
E at org.fdroid.fdroid.localrepo.LocalRepoManager.writeIndexJar(LocalRepoManager.java:515)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:698)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:645)
E at android.os.AsyncTask$2.call(AsyncTask.java:288)
E at java.util.concurrent.FutureTask.run(FutureTask.java:237)
E at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
E at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
E at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
E at java.lang.Thread.run(Thread.java:841)
StrictMode E A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
E java.lang.Throwable: Explicit termination method 'close' not called
E at dalvik.system.CloseGuard.open(CloseGuard.java:184)
E at java.io.FileOutputStream.<init>(FileOutputStream.java:90)
E at java.io.FileOutputStream.<init>(FileOutputStream.java:73)
E at java.io.FileWriter.<init>(FileWriter.java:42)
E at org.fdroid.fdroid.localrepo.LocalRepoManager.writeIndexJar(LocalRepoManager.java:488)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:698)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:645)
E at android.os.AsyncTask$2.call(AsyncTask.java:288)
E at java.util.concurrent.FutureTask.run(FutureTask.java:237)
E at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
E at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
E at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
E at java.lang.Thread.run(Thread.java:841)
E A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
E java.lang.Throwable: Explicit termination method 'close' not called
E at dalvik.system.CloseGuard.open(CloseGuard.java:184)
E at java.io.RandomAccessFile.<init>(RandomAccessFile.java:128)
E at kellinwood.zipio.ZipInput.<init>(ZipInput.java:57)
E at kellinwood.zipio.ZipInput.read(ZipInput.java:75)
E at kellinwood.security.zipsigner.ZipSigner.signZip(ZipSigner.java:646)
E at org.fdroid.fdroid.localrepo.LocalRepoKeyStore.signZip(LocalRepoKeyStore.java:218)
E at org.fdroid.fdroid.localrepo.LocalRepoManager.writeIndexJar(LocalRepoManager.java:515)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:698)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:645)
E at android.os.AsyncTask$2.call(AsyncTask.java:288)
E at java.util.concurrent.FutureTask.run(FutureTask.java:237)
E at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
E at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
E at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
E at java.lang.Thread.run(Thread.java:841)
E A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
E java.lang.Throwable: Explicit termination method 'end' not called
E at dalvik.system.CloseGuard.open(CloseGuard.java:184)
E at java.util.zip.Inflater.<init>(Inflater.java:82)
E at kellinwood.zipio.ZioEntry.getInputStream(ZioEntry.java:475)
E at kellinwood.zipio.ZioEntry.getInputStream(ZioEntry.java:445)
E at kellinwood.security.zipsigner.ZipSigner.addDigestsToManifest(ZipSigner.java:418)
E at kellinwood.security.zipsigner.ZipSigner.signZip(ZipSigner.java:713)
E at kellinwood.security.zipsigner.ZipSigner.signZip(ZipSigner.java:647)
E at org.fdroid.fdroid.localrepo.LocalRepoKeyStore.signZip(LocalRepoKeyStore.java:218)
E at org.fdroid.fdroid.localrepo.LocalRepoManager.writeIndexJar(LocalRepoManager.java:515)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:698)
E at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:645)
E at android.os.AsyncTask$2.call(AsyncTask.java:288)
E at java.util.concurrent.FutureTask.run(FutureTask.java:237)
E at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
E at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
E at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
E at java.lang.Thread.run(Thread.java:841)
Its boot animation is "stopped" but on my laptop the CPU is still at 100%,
which means it's probably still working and not ready yet. The tests fail if
you run them right after wait-for-emulator is done. If you wait a few seconds
though, they do work since the CPU load drops.
Until we can figure out a better solution, have a generous 30-second sleep
after it tells us it's ready.
Resumeable, simultaneous APK downloads in the background using Android's DownloadManager (post code-review)
This is the CR'ed version of !132, ready for merging. Created a MR instead of direct merging because of CI goodness. Will merge when CI passes. Thanks for your work Toby.
See merge request !133
Renamed to Utils.copyQuietly() and Utils.symlinkOrCopyFileQuietly().
The copy(File, File) method gobbles up IOExceptions, whereas the other
copy(InputStream, OupputStream) method doesn't. This could cause confusion,
whereas developers using one may not realise it is is gobblign their
exceptions.
Android Studio by default warns about undocumented params, which makes it harder
to identify more problematic warnings to do with actual code problems. This warning could
be toned down in the IDE so that it doesn't complain, but equally, the params are
not neccesary in JavaDoc if they are undocumented, and don't end up adding any more
than the parameters themselves.
* Hard to keep both regular and source builds working and bug-free
* Keep -PsourceDeps to package jars for libs which are not yet in jcenter
* Use the libs packaged in jcenter the same way in both builds
* Remove cleanBinaryDeps, can be done via the shell easily
The NetCipher library creates instances of HttpURLConnection that are
configured to have solid TLS protocol and cipher settings, especially on
older versions of Android.
fixes#370https://gitlab.com/fdroid/fdroidclient/issues/370
This remove the magic number "1" from the network state check, and makes
explicit that it is checking the active network connection.
This is then used to check whether it is appropriate to update the index
when FDroid first starts.
Only enabled on debug builds. With only logs for now, we can move on to more
annoying penalties like dialogs or crashes once we have solved all of them.
This will make sure that all projects in the repository are built and tested.
Should come in handy for !126, which splits up the app into two apps and one
library.
This was making proguard complain and refuse to continue. This fix should not
alter how F-Droid works, it's just to make the tests function with proguard
enabled.
Spotted by Hans at fdroidserver. The 'update sdk' tool will download the
package index at each run, so installing one package at a time will be rather
inefficient.
This stops the stripping of classes required for the swap stuff. proguard
strips all sorts of stuff that is needed because of things like dynamic
loading, reflection, etc.
fixes#391https://gitlab.com/fdroid/fdroidclient/issues/391
0.96 swap fixes
This fixes the swap issues that I could find, except for #391. @mvdan feel free to beat @pserwylo to this, I just assigned it to him because its swap.
See merge request !127
Right now, turning on HTTPS really just prevents things from working. It
is not fully required, since swapping is only local connections, so not
easily susceptible to mass eavesdropping, though it would be nice. I'm
leaving the rest of the plumbing for this here intact for when we come back
to getting swap always using HTTPS.
closes#378https://gitlab.com/fdroid/fdroidclient/issues/378
When using locales that use different calendars, like Farsi, Arabic,
Hebrew, etc. there was a crash in spongycastle's X.509 generation because
it was trying to parse a Farsi date string as English.
fixes#334https://gitlab.com/fdroid/fdroidclient/issues/334
Here's the original stacktrace:
java.lang.IllegalArgumentException: invalid date string: Unparseable date: "ñõðøòñðóñõõóGMT+00:00" (at offset 0)
at org.spongycastle.asn1.ASN1UTCTime.<init>(ASN1UTCTime.java:115)
at org.spongycastle.asn1.DERUTCTime.<init>(DERUTCTime.java:23)
at org.spongycastle.asn1.x509.Time.<init>(Time.java:67)
at org.spongycastle.cert.X509v3CertificateBuilder.<init>(X509v3CertificateBuilder.java:40)
at org.fdroid.fdroid.localrepo.LocalRepoKeyStore.generateSelfSignedCertChain(LocalRepoKeyStore.java:301)
at org.fdroid.fdroid.localrepo.LocalRepoKeyStore.generateSelfSignedCertChain(LocalRepoKeyStore.java:281)
at org.fdroid.fdroid.localrepo.LocalRepoKeyStore.<init>(LocalRepoKeyStore.java:136)
at org.fdroid.fdroid.localrepo.LocalRepoKeyStore.get(LocalRepoKeyStore.java:73)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:124)
at org.fdroid.fdroid.net.WifiStateChangeService$WaitForWifiAsyncTask.doInBackground(WifiStateChangeService.java:62)
Revert "Prompt for beta updates"
This reverts commit 92f8678b3d2da18ad93bac6af0ae83984cf74714.
I read the logs on IRC and agree that we should revert this for stable.
CC @pserwylo
See merge request !122
On certain locales, F-Droid would crash at startup due to #334.
This isn't a proper fix, but rather a workaround that logs what happened and
links to the issue, instead of making F-Droid crash entirely.
We can't rely on their plugin versions to be compatible with ours. Besides,
they require tons of Android SDK targets to be installed to build.
They are already bundled with the SDK, so you can build them as part of the
SDK from source if you want to.
Include support for libaccesspoint to control the WiFiAP of a device.
Selecting wifi networks i snow possible by touching the name of the
wifi network in the "Start Swap" screen (sometimes it will say
"No network yet"). This exhibits the same behaviour as the "Join Wifi"
screen used to (and still does) do.
On emulators (is there other devices too?), Bluetooth is unavailable,
but the "Send F-Droid" is still there. I could remove it, but then
people may get confused as to why it is not there. Instead, there is
now a dialog which explains why it can't be sent (no Bluetooth).
In the future, this should be implemented with a relevant help screen,
but for now it is more than we have time for. It will require, not only
good content which is translatable, but also a generic approach so that
it can be used elsewhere in F-Droid too.
The StringBufferInputStream is deprecated, because it doesn't know the
encoding of the string it is buffering. This has been changed to use
a ByteArrayInputStream with UTF-8 encoding now.
Also made use of previously unused mimeType parameter to set the
Content-Type header of the response.
Sets a stream handler in FDroidApp which understands bluetooth://
URLs and returns a non-functioning URL handler for such URLs.
The reason it is non-funcitoning is because the openConnection()
function is not being used by the BluetoothDownloader at this point.
In the absense of this, a MalformedURLException is thrown.
The bluetooth stuff is still broken, but it is not broken in
this way any more.
The AppDetails subclass used to be used when you couldn't install apps
directly from the swap workflow. Now we don't send people to app
details, so the activity is unneeded.
The swap progress also now listens for progress broadcasts, rather than
showing an indeterminant progress bar.
Seems to work pretty-alright even when installing multiple apps.
Shows a progress (indeterminent at this stage) bar for each downloading
item, and hides the install button.
InnerView.getToolbarColour() was expecting a @ColorRes, except all views
were returning integers which were the colour value, not a pointer to
the resource as they should have been. Now only one place requires a
call to getResources().getColor() whereas before it was in each view.
Notifications on pre 4.1 devices require a pending intent to work. This
is so that when you touch the intent, it takes you somewhere meaningfull.
Without it, the update process crashes.
Add timeout for Http request
The timeout of HttpURLConnection is 0, which means the request would never return if either connection or read timeouts, and the code would never catch these exceptions.
This patch set explicit timeout value (10s for connection timeout and 40s for read timeout), so the code would catch and handle timeout exceptions as you would expect.
Best,
See merge request !117
Two views weren't hidden by default, so they were taking up space before they
were needed. The java code already takes care of showing them when necessary.
Target/Support version bumps
This is part of the (unmergeable as of now) !96. These changes are simple yet important enough for me to think that they deserve their own merge request.
I didn't push the changes directly because I haven't worked with the material stuff enough to notice whether anything is breaking or not. For all I can see, the app looks the same and the bumps just added lots more deprecation warnings (which is fine, they can be dealt with over time).
See merge request !109
Prompt for beta updates
Ready to get merged. 👍
~~This MR is ready to get merged, the last thing that needs to be fixed is [this](72ed814a73 (note_1726274)).~~
This closes#313.
See merge request !112
Integration branch containing both MR 102 and 107
Given both MR !102 and !107 both stood on eachothers feet to some extent, this branch contains all commits from both in an integration branch. If both @Nutomic and @eighthave are happy that it faithfully kept their changes during the minor merge conflict resolutions, then we can merge this instead of those two branches.
* !102 changed download progress from events + listeners to broadcasts + receivers.
* !107 made use of download progress events + listeners to show a downloading UI that was embedded in the activity, rather than shown as a modal dialog.
The conflicts which arose while I merged these branches together were in `AppDetails`. I made it so that, to the best of my ability, it uses broadcast receivers instead of progress listeners when updating the progress bar. Other than that, the only other conflict was both trying to store a reference to the main button from the UI. The only changes were in naming (mainButton vs btMain) and also in the place where the local variable was assigned (onCreate() vs setupViews() which is called during onCreate() anyway).
After merging, I did some minor cleanups. This is because in the process of checking that my conflict resolution compiled, I thought it best to remove a bunch of warnings from `AppDetails` and others. Turns out that by doing so, I found a bug due to the integration (to do with the `AppDetails` querying the downloader for status in `onResume()` rather than waiting for a broadcast event) so I'm glad I did so.
Let me know what you think, and then after the next stable, we can go ahead and merge this if people are happy.
p.s. I have no idea why GitLab is showing @eighthave's commit at the top of the list of commits, after my integration related commits.
See merge request !114
AppCompat no longer supports progress indicators in the action bar. So
this is not your everyday "Deprecated, but sure, keep using it" job.
Rather, it is "deprecated, and no, we wont even let you use it."
Also removed unused argument and extended AppCompatActivity.
To make things less confusing, this disables the main button on AppDetails
when something is running. During install, it also changes the text of the
button to "Installing..."
This removes lots of boiler plate, makes it much easier to get the info
where it is needed, and puts the code in line with rest of FDroid. The
ProgressListener pattern was forcing a lot of passing the listener
instances around through classes that never used the listener even.
That makes that repo automatically ready for use based on user actions like
adding a new repo, switching an existing repo on, etc.
This also lowers the priority of the "update" menu item since it shouldn't
be needed any more. But leave it for now, just in case.
Having a Context in Downloader means that the communications can be changed
to a LocalBroadcastManager, following the pattern that is in a lot of this
app already.
Since the repo updates are happening in an IntentService, they are already
running in a separate thread. Ironically, the dialog was showing in spite
of that. This removes the dialog entirely and instead puts up a
Notification with the same messages. Ultimately, the "refresh" button
should go away, the repos should be updated whenever someone goes to
install an app, and all APK downloads should also show up in the same
Notification.
This removes UpdateReceiver entirely and replaces it with local broadcasts,
since that is a common pattern in FDroid and Android. It also reduces the
amount of code here.
refs #103https://gitlab.com/fdroid/fdroidclient/issues/103
No need for a reusable Fragment here, its only used in one place. This
changes the structure to be a regular Activity, with all View and Menu
setup in XML files loaded in onCreate().
This also converts the URL to a TextView. Having it editable in this
Activity makes for a confusing user experience. Instead, the "Add Repo"
input should validate the URL and not allow creating repos that don't work.
This also purges the use of UpdateService.UpdateReceiver, it will be going
away in the upcoming commits.
We are not forcing an update, in the sense that we make the update
service run. Rather, we are ensuring that the next update wont return
after doing nothing, with the message "repos already up to date".
In this case, the repo metadata (and hence its etag) is the same,
but we made changes in the client to handle the metadata correctly.
Thus, we don't care that it hasn't changed, we want to update anyhow.
Did this by using the same query which is used to update the icon URLs
after updating the index. To make this same method accessible, without
causing sad database locking, had to expose the method from AppProvider
in a way which would let DBHelper access it. See comments in code for
further explainations.
While there, removed the final lint warning in my Android Studio for
the AppProvider. This was a warning because we could have ended up
iterating over a null object. Although it turns out there was a correct
guard in place which would ensure this didn't happen, it wasn't in such
a way that lint would understand. Thus, I changed the guard condition
around `for( String blah : CommaSeparatedList.make() ) {}` to let lint
relax and not be so pedantic.
Replaced `Switch` with `SwitchCompat`. In the future, should completely remove
F-Droid's `SwitchCompat` class. Fixed paddingLeft/paddingStart, except for some
places where lint complained. Apparantly that is for some Samsung tablets on
Android-16. Will have to create a layout-v17 version in the end for these.
Fixed lots of (minor) conflicts. Some due to earlier rebasing of
material stuff that was subsequently merged into master with a
different commit hash (I guess, that's what it looked like anyway).
Kept getting a message in the Bluetooth _client_, which said the
socket timed out or received an invalid response or something. Most
people seem to believe that it is due to a bug in the Bluetooth stack
on Android 4.2.1 and suggest using reflection to access a method that
actually works as intented. I couldn't get it to work correctly though.
Kept the code here because this whole branch is a WIP, and we need
to figure out how to make Bluetooth work one way or another.
`SwapService`: Remove unneccesary TODO's and unused constant. Also
added other TODO's there which will need to be done before swap is
stable. Can make them into issues in gitlab, but will leave there for
now.
`SwapWorkflowActivity`: Clarified TODO's, fixed issue with cancelling
swap when in the "connecting" state (before it would not show the
right view again, now it goes back to the first view).
`WifiQrView`: Make scanning of QR code initiated by `SwapActivity`.
This is because the activity is actually the one who is able to listen
for a response anyway.
`BluetoothFinder`: Cleaned up TODO's in but didn't actually address
them.
`BluetoothPeer`: Removed TODO about fingerprints, because TOFO is
probably the way to go with Bluetooth anyhow.
`BonjourFinder`: Remove dead code, prevent NPE by using final local
variable rather than member variable (the member variable gets set
to null in a different thread).
`BonjourPeer`: Fix bug with prompting other device to swap back.
`BluetoothDownloader`: Fix typo in comment.
`Downloader`: Removed TODO that was left over from a refactor, and
was never actually correct. The code after refactoring was broken
because it created an `InputStream` which was never closed. Now it
doesn't do that, because it was fixed in an earlier commit in this
branch.
`HttpDownloader`: Remove TODO, but clarify that the downloader does
not follow 30x redirects.
It is very hacky, and I did it through the non-swap interface, and it
only works once then the state stuffs up and it no longer accepts incomming
connections, but it worked! Now to smooth out all the things.
Apply the accent color to EditTextPreference and ListPreference. They
use android.app.AlertDialog.Builder, not
android.support.v7.app.AlertDialog.Builder, so the theme has to be
specified using "android:alertDialogTheme" attribute in addition to
AppCompat's "alertDialogTheme". For the same reason those dialogs won't
be tinted on pre-Lollipop Android versions.
Apply the accent color to alert dialog buttons. Note that without
"android:windowMinWidthMajor" and "android:windowMinWidthMinor" attributes
dialog width is calculated incorrectly resulting in visual artifacts.
Before, you could "Enable swapping" without specifying which type
of protocol to enable. Now, the two switches are clearly delimited
between bluetooth and wifi.
From the Material Design spec:
> DP unit grid
> System icons are displayed at 24dp.
See http://www.google.com/design/spec/style/icons.html#icons-system-icons
Script used to update the icons:
function download {
F-Droid/tools/download-material-icon.sh F-Droid/res $1 $2
}
download content add
download device bluetooth
download action delete
download notification do_not_disturb
download image edit
download action help
download device nfc
download av play_arrow
download navigation refresh
download action search
download action settings
download social share
download action view_headline
simplify RepoUpdater and use more streams
This is an overhaul of `RepoUpdater` to make its code match the architecture that is in use now: only download and use a signed index.jar. It also streams index.xml directly out of the index.jar and directly into the XML parser. That makes the update process quicker and more reliable because it no longer has to write out an index.xml to the filesystem, then read it in. Ultimately I hope to stream the index.jar download directly to the XML parser, so not even the index.jar needs to be written to disk. You can see that work in my git repo under the branches SKETCH-JarURLConnection and SKETCH-verify-with-JarInputStream for two different approaches.
This also changes the index parsing process to be based on bytes for now. The progress is based on the stream now, and this will still work once the full streaming mode is implemented. It also simplifies `RepoXMPHandler`.
This includes tests for index.jar signature verification. The tests all pass on my machine and our Jenkins.
See merge request !101
We don't want to depend on HTTP being active in case there is
no wifi that exists at all. In some cases, local 127.0.0.1 does not
exist if the Wifi is not connected. This commit takes the code
from LocalHTTPD and repurposes it for the BluetoothServer needs.
This drastically simplifies the very important index.jar signature
verification process by splitting out the Trust On First Use (TOFU) part of
the process into its own method, and makes the TOFU write happen separately
from the `RepoUpdateRememberer`. It requires all connections go through
the normal verification process.
Thanks to @mvdan for catching that. Turns out Java's String formatting is
not as tolerant as C's printf(). Java crashes when the format is wrong,
while C just ignores extras.
Might as well tap into the stream to get the byte counts, that's best
progress info I can think of when parsing a file.
This is a step towards a single progress bar for the whole process, instead
of showing one progress for downloading, another for parsing XML, then a
third for processing the new app info.
Now that there is only ever the index.jar, the whole flow of RepoUpdater
has changed quite a bit. This updates the logic for deciding when to store
the current repo's pubkey in the database for future reference.
This changes the flow to stop writing the unpacked index.xml and instead
stream it directly to the XML parser from the index.jar. This should speed
things up some.
refs #259https://gitlab.com/fdroid/fdroidclient/issues/259
This is also work towards running the whole thing in the background:
refs #103https://gitlab.com/fdroid/fdroidclient/issues/103
This also removes the progress stuff since it will need to change a lot to
work with the streaming mode
Before, there was an abstract RepoUpdater class with two subclasses, one
for signed and unsigned. Now there is just a single class, and it only
ever starts with the index.jar. So this removes lots of code that was
there to handle that more complicated structure. For example, there is no
longer the need to separately work on the index.xml vs index.jar.
Still need to hook up the buttons in the app list, but this change
shows the correct status and/or buttons for installable/upgradable/
incompatible/installed apps in the swap list. This change also hooks
up UIL to download icons for apps and thus display them in the list.
Involved creating another view/state for which the swap workflow can
be in. It is not explicitly stated by setting the state of the SwapService,
as is the case with other views. Rather, it is inferred based on the
presence of a `NewRepoConfig` crafted from the incoming intent in
`onResume()`.
Also gave me an idea of how to move more logic out of individual views,
and into the SwapWorkflowActivity. That is, inflateInnerView should
return the inflated view, to be cast into the specific subclass. From
there, the activity can call methods directly on the view to set it
up, rather than having the view do that stuff itself. In the future,
may consider doing this with other views too.
The reference to mini-services above are not full blown Android
services. Rather, they are utility classes which can be started,
stopped, and send broadcasts about their status.
Made the list of apps to install better, with buttons for install
or upgrade, and statuses for incompatible and installed.
Peers are shown as proper list items now, subject to feedback from Carrie.
TODO: Need to figure out how to combine bluetooth and bonjour with same
fingerprint.
Updated the README with details of how to run the tests. Also added a
minor fix to allow tests to run on pre-honeycomb devices. However their
behaviour may not be 100% defined - because the contentproviders are
not shuttind down correctly due to lack of an API to do so.
It broke with a recent support lib update that made the binary library be an
.aar instead of a .jar. Besides, the ant plugin has been buggy and unsupported
for a very long time.
Also dropping support for eclipse in the process, which lets us get rid of the
nasty symlinks.
The Bluetooth peer need only parcel up the BluetoothDevice, which
itself is parcelable. The wifi peer requires the JmDNS ServiceInfo
class to be parcelled. For this, I took the most full on looking
constructor, and parcelled up each individual property of the service
info object which is required by that constructor.
Also made the scan qr button hooked up to the swap process, and fixed
minor bugs with the "visible via wifi" TextView setup.
Implementing the bare bones of a generic "peer finder" framework. This
may or may not eventuate to something which can live in its own library
and be used by other projects. Might go hand in hand with Carries idea
of having a common UI to be shared among projects.
Got Bluetooth and Bonjour kinda working, but the UI is crud,
and it doesn't remove items and ends up with duplicates. Otherwise,
on our way to a proper "nearby peers" screen.
Implementing the bare bones of a generic "peer finder" framework. This
may or may not eventuate to something which can live in its own library
and be used by other projects. Might go hand in hand with Carries idea
of having a common UI to be shared among projects.
Fixes#240.
To make this easier, I added a script to aid in downloading icons.
Checkout F-Droid/tools/download-material-icon.sh for more details.
The icons are licensed under the CCv4 attribution license, which I
added a shout out to under "License" in the README.md.
- use 'com.android.support:appcompat-v7:22.0.0' instead of version 20.0.0
- ActionBar color: "F-Droid Blue" (also option for "F-Droid Green")
- fix invisible swap button with Material Design
- remove "Light + dark action bar" theme (as of Action Bar is always blue/green)
Fix#263 "cannot manually add repo that was swapped before"
Pretends that the swap repo never existed, by deleting it before adding
the new repo, and showing the same message that is shown when a new
repo is added. This does not change behaviour for existing non-swap
repos. They are not deleted before being added again, or else we would
lose the ability to verify the fingerprint of an existing repo is the
same as a newly added one with the same URL.
Note that this has the effect that the fingerprint/pubkey of the swap
repo is nuked when adding that repo manually.
Internationalised the string "BAD FINGERPRINT" while I was at it.
To test it out, here is some instructions to make life easier:
Firstly, go into manage repos and delete the guardian project main repo (going to pretend to use this for swapping to make life easier).
Then if you run `sqlite3 /data/data/org.fdroid.fdroid/databases/fdroid` and execute the query:
`select substr(fingerprint, 0, 10), substr(pubkey, 70, 10), address, isSwap from fdroid_repo order by fingerprint desc;`
You should see:
```
B7C2EEFD8|081ad310b3|https://guardianproject.info/fdroid/archive|0
43238D512|071310b300|https://f-droid.org/archive|0
43238D512|071310b300|https://f-droid.org/repo/|0
```
Now simulate a swap session like so:
```
adb shell am start -a android.intent.action.VIEW -d 'https://guardianproject.info/fdroid/repo?swap=1'
```
Which results in the following database:
```
B7C2EEFD8|081ad310b3|https://guardianproject.info/fdroid/archive|0
B7C2EEFD8|081ad310b3|https://guardianproject.info/fdroid/repo|1
43238D512|071310b300|https://f-droid.org/archive|0
43238D512|071310b300|https://f-droid.org/repo/|0
```
Note the last column (`isSwap`) is `1` for the newly added swap repo. Now we will add the repo (without a fingerprint) to the Manage Repo activity. If you are feeling lazy, execute:
```
adb shell am start -a android.intent.action.VIEW -d https://guardianproject.info/fdroid/repo
```
The repo will be removed, then re-added as a TOFU repo:
```
B7C2EEFD8|081ad310b3|https://guardianproject.info/fdroid/archive|0
43238D512|071310b300|https://f-droid.org/archive|0
43238D512|071310b300|https://f-droid.org/repo/|0
||https://guardianproject.info/fdroid/repo/|0
```
I noticed some bugginess with sending the same intent and it being ignored, I'll have to look at this another day (not caused by this change, it already existed in master).
See merge request !90
NFC swap now goes to confirm swap, not manage repos activity.
The NFC message now is handled by the FDroid activity, so it is treated
the same way as every other incoming repo URL. Because FDroid handles
incoming intents correctly, the NFC one just magically works when
the <intent-filter> is moved from ManageReposAcivity to FDroid without
further code changes.
The other change is that the two way swap only happens when both are
actually swapping. Otherwise, we will send a request for someone to
swap with us, when we are incapable of swapping with them.
Fixes#267.
See merge request !91
The Toolbar is the new thing from Google which acts as an ActionBar.
It is not a special view like the action bar is, it is
implemented and added to your layout the same as any view.
The InnerView classes of the swap workflow have the choice of what
colour to make the toolbar, so that they can distinguish themselves
as per the mockups (some deep blue, others bright blue).
Added icons for close, but they don't do anything yet.
Minor tweaks to layout so that it looks more like the latest mockups.
- App icon is now 72dp x 72dp
- Description is expandable
- License, categories, Website, Source Code, Issues and Donate (#232) are moved from header to summary
- Buttons to install, update and run are moved from action bar to header
- Permissions are expandable and always shown
- Add myself to copyright holders of "F-Droid/res/layout/app_details_header.xml", "F-Droid/res/layout/app_details_summary.xml" and
"F-Droid/src/org/fdroid/fdroid/AppDetails.java"
Without them, some features like proper density icons only worked after the
first update, but not on the first index update itself since the version
defaulted to 0.
"Confirm swap" background was white but should have been blue.
SwapWorkflowActivity now extends ActionBarActivity instead of
FragmentActivity so older devices have an action bar (though it is
not styled with blue action buttons on android-10 devices).
Logging to help understand when the service is started, stopped,
restarted, and when individual components (e.g. web server, bonjour)
are started/stopped.
Fixed bug where service was moved to foreground during "restartIfEnabled"
because that is unneccesary if it is already in the foreground.
There is now a private [en|dis]ableSwappingSynchronous() method, to go
along with the public [en|dis]ableSwapping() method. The public method
knows about not spinning up tasks if already enabled, and also about
pushing the invocation to a background task. The private method just
blindly does what it is asked, without checking if it should be done,
and without running on a background task.
The same goes for the restartIfEnabled() method, it is responsible for
creating a single background task which runs in order: disable, and
then enable. This is actually the reason for having the synchronous
methods, rather than having, e.g., the public [en|dis]ableSwapping()
method know about threads. If that was the case, then restarting would
consist of starting, waiting for some form of notification that the
background task has completed, and then scheduling the enable task.
Now, it is a matter of calling both *SwappingSynchronous methods in
succession.
Removed LocalRepoService, replaced with SwapService.
Still TODO:
Manage threads. Currently everything is called from the
UI thread, which is a regression from the previous behaviour.
I'd like to manage this so that the code interacting with the
SwapManager doesn't need to bother itself with whether it is calling
from the UI thread or not.
The local repo service had many different methods and properties for
dealing with starting and stopping various things (webserver, bonjour,
in the future it will also need to know about bluetooth and Wifi AP).
The SwapService handles this stuff by delegating to specific classes
that are only responsible for one of these. Hopefully this will make
the process of enabling and disabling swap repos easier to reason
about.
The local repo service was also stopped and started quite regularly.
This meant it was up to the code making use of the service to know if
it was running or not, and to enable it if required.
The new SwapService is only started once (when the singleton
SwapManager is created for the first time). It should not use any more
resources, because it is a background service most the time, and it
is responsible for moving itself to the foreground when required (the
burden is not on the code consuming this service to know when to do
this).
By having the service running more often, it doesn't need to'
continually figure out if it needs to register or unregister listeners
for various properties (e.g. https enabled) or wifi broadcasts. The
listeners can stay active, and do nothing once notified if swapping is
not enabled.
Moved the timeout timer (which cancels the swap service after 15 mins)
into the SwapService, rather than being managed by the
SwapWorkflowActivity. Seems more appropriate for the service to know to
time itself out rather than the Activity, seeing as the Activity can
die and get GC'ed while the service is still running.
Finally, although there is nothing stopping code in F-Droid from
talking to the service directly, it is now handled by the SwapManager
singleton. This means that details such as using a Messenger or Handler
object in order to communicate via arg1 and arg2 is no longer required,
and instead methods with proper type signatures can be used. This is
similar (but not exactly the same) to how Android system services work.
That is, ask for a "Manager" object using getSystemService(), and then
use that to perform functionality and query state via that object,
which delegates to the service. Then we get the best of both worlds:
* Reasonable and type safe method signatures
* Services that are not tied to activity lifecycles, which persist
beyond the closing of the swap activity.
Quick improvement for #270, but a proper solution would be more complex to
also do MiB when appropriate. It would probably involve a modified
ProgressDialog.
List views need to have their header view set _before_ an adapter is
assigned to them.
Was incorrectly passing an application context instead of an Activity
context to the progress dialog for swap.
Extend ActionBarActivity rather than FragmentActivity.
Don't persist swap workflow state to disk, only store it in the
singleton.
All the code from the activity and the fragments has been successfully
ported to the SwapWorkflowActivity + Views. Thus, the code is no longer
useful, as it was only kept over the previous WIP commits so that it
can be referred to to help re-implement fragments with views.
Currently the view to show after pressing the back button is defined
by individual InnerView classes. For example, the JoinWifiView always
says its previous step is the SelectAppsView. This works for the most
part, but doesn't work for:
* The first StartSwapView class (instead handled by a special case in
the Activity).
* WifiQrView which could either be going back to the NfcView or the
JoinWifiView, depending on a few cases, which the WifiQrView probably
shouldn't care about.
The state is saved to a preference file, but that is abstracted from
the SwapWorkflowActivity. It interacts with a SwapState object, which
is relatively safe in that you should not be able to save it into an
invalid state.
Note: At this point it is not tied to any service. I'm not sure it will
ever have to be either, as the service needs to persist state somewhere
anyway, so that will probably also end up saving to a preferences file
too.
As per the select apps list, there is quite a bit of business logic
in this class, which is now spread between the activity and the view.
The activity needs to handle some stuff, because the zxing library
routes intents to either an activity or a fragment.
There is quite a lot of business logic that was moved directly from
the fragment to the view. Before this feature is complete, that logic
should either be moved into the activity, or into some sort of
associated Presenter class for the JoinWifiView.
Not worrying about styling yet, just functionality. Added an InnerView
interface that these views can implement. Currently it asks them to
populate the menu. It may be slightly inefficient if we end up with a
popup menu, because it is called onPrepareOptionsMenu, but expects the
inner view to inflate the menu. However, for swap this shouldn't be an
issue, as all the menus pretty much fit in the action bar of most screen
sizes.
Added a SwapWorkflowActivity to re-implement the SwapActivity.
Once all the fragments have been refactored into views, then the
SwapActivity will be removed.
The multiple occurances of "if (Build.SDK_INT < ... )" statements
hint at the prospect that there are a couple of different implementations
of this class which behave differently. The new classes start with
InstallFDroidAsSystem, and then there are SDK specific subclasses
which provide the customization relevant for those subclasses.
The NFC message now is handled by the FDroid activity, so it is treated
the same way as every other incoming repo URL. Because FDroid handles
incoming intents correctly, the NFC one just magically works when
the <intent-filter> is moved from ManageReposAcivity to FDroid without
further code changes.
The other change is that the two way swap only happens when both are
actually swapping. Otherwise, we will send a request for someone to
swap with us, when we are incapable of swapping with them.
Fixes#267.
Pretends that the swap repo never existed, by deleting it before adding
the new repo, and showing the same message that is shown when a new
repo is added. This does not change behaviour for existing non-swap
repos. They are not deleted before being added again, or else we would
lose the ability to verify the fingerprint of an existing repo is the
same as a newly added one with the same URL.
Note that this has the effect that the fingerprint/pubkey of the swap
repo is nuked when adding that repo manually.
Internationalised the string "BAD FINGERPRINT" while I was at it.
Force entire swap process to be portrait.
Although this is usually regarded as poor form, it is currently better
than the alternative which is the whole swap process poohing itself
when a device is rotated. In the future, it may be worthwhile investing
in designing a proper UX for landscape swap too. However the process
of swapping can be quite complex if not presented well, and so it might
end up being too much work to maintain two different UXes for landscape
and portrait.
See merge request !89
Although this is usually regarded as poor form, it is currently better
than the alternative which is the whole swap process poohing itself
when a device is rotated. In the future, it may be worthwhile investing
in designing a proper UX for landscape swap too. However the process
of swapping can be quite complex if not presented well, and so it might
end up being too much work to maintain two different UXes for landscape
and portrait.
The code for promoting an untrusted repo with no fingerprint, to
a repo with a pubkey and a fingerprint, was still there. The problem
was that it was being executed after we verified the index.jar cert
against the pubkey stored against the repo (which is empty for TOFU
repos).
This change makes it so that if we are updating a repo without a
fingerprint, then it is a TOFU request, and we don't try to verify
the certificates.
closes#85https://gitlab.com/fdroid/fdroidclient/merge_requests/85closes#254https://gitlab.com/fdroid/fdroidclient/issues/254
fix paths in Android.mk
It turned out i only tested the build with an outdated source tree so the last commit (38c25f0ecce48ef9cfe5b0b41956ac9a4e2047fd) broke the Android.mk build. Sorry for that.
See merge request !86
Brought in an Apache 2.0 licensed file from apache commons to help
with this, which is one of many similar classes online that do the
same thing. At this stage, didn't feel like pulling in all of apache
commons as a dependency.
Devices now make themselves discoverable, and the client sends a test ping.
They UI is not styles properly though, and it doesn't handle the case where
somebody chooses to make their device not-discoverable (because the desired
peer is already paired and it is unneccesary). It also doesn't handle failure
anywhere.
I'm not 100% sure it is the right architecture yet, there wil no doubt be
things that crop up as I continue to implement it. However it seems to
be alright to work with so far.
Fixes#240.
To make this easier, I added a script to aid in downloading icons.
Checkout F-Droid/tools/download-material-icon.sh for more details.
The icons are licensed under the CCv4 attribution license, which I
added a shout out to under "License" in the README.md.
Added comment about preventing addition of the same repo multiple
times.
More specific comment about pname et al, so that people don't think
they can use the regular search feature of F-Droid to search by
package name with a pname: prefix. Instead, it is only applicable to
incoming intents.
improved, but still rudimentary, hotspot handling in swap
My previous merge request fdroid/fdroidclient!78 was based on one broken assumption: `WIFI_STATE_UNKNOWN` means that the hotspot is active. Apparently, that's not always the case. Also, sometimes when the hotspot is active, its `WIFI_STATE_DISABLED`. Even worse, there is no broadcast message sent on final config of the hotspot. There is only a `WIFI_STATE_DISABLING` from turning off the wifi, but then never a broadcast for `WIFI_STATE_UNKNOWN` and/or `WIFI_STATE_DISABLED`. But I found some tricks that seem to work for now. We'll need to use your library, @mvdan, to really get good support of hotspots.
This also includes some basic UI tweaks to represent the hotspot mode in the swap wifi screen.
See merge request !79
Removes trailing slashes from URLs, replaces multiple consecutive forward
slashes in the path with a single slash. Canonicalizes the URL.
If the URL is invalid, display a message to the user and don't let it get
added.
NOTE: This does *not* normalize existing URLs in the database.
Previously it would only compare the details of a new repo to existing
ones if the new repo came via an intent. Now it does it whenever you
change the text in the new repo dialog (shouldn't be too much of a
performance hit, it isn't doing very much).
The things it (still) doesn't do is:
* verify that the url looks like a url
* sanitize the newly input uri and compare it to sanitized saved repos
The second point means that you can end up with:
http://10.0.1.50/ and
http://10.0.1.50
both in the list. I'm going to log an issue for this, because it should
be fixed, but doesn't need to hold this up.
This is really just a placeholder, there is lots of work to be done here.
Really, this screen should have the SSID of the hotspot, but we need to use
a private API to get that. Coming soon...
The icon is free software from:
https://commons.wikimedia.org/wiki/File:Wifi.svg
hotspot mode is not well represented with the WifiState stuff. It can be
active when the WifiState is DISABLED or UNKNOWN. Also, when switching
from active wifi to hotspot mode, WIFI_STATE_DISABLING broadcasts will be
sent, but WIFI_STATE_DISABLED/WIFI_STATE_UNKNOWN will not.
When a device is setup as a WiFi Access Point aka "hotspot", the standard
API for getting the WiFi settings returns nothing. We have to use a
separate API to get the IP address of the WiFi AP. As far as I could tell,
there is no public API for getting the SSID/BSSID of the WiFi AP, so for
now that is left blank. That means the wifi screen in swap is confusing
because it will say it is not attached when the device is a hotspot
@mvdan's https://github.com/mvdan/libaccesspoint should help there
#193https://gitlab.com/fdroid/fdroidclient/issues/193
To handle hotspots, this code will become more complicated. Therefore,
first simplify things by putting all of the logic into one place, rather
than spread out across FDroidApp, the receiver, and the service
On launch, we need to get the current state of the Wifi. We only need to
start the WifiStateChangeService on WIFI_STATE_ENABLED, since any other
wifi state will be received by WifiStateChangeReceiver, which will launch
WifiStateChangeService when appropriate.
This reduces the chance that WifiStateChangeService will start when it is
not needed.
I was getting frequent crash-on-rotate NullPointerExceptions after scanning
QR Codes. This fixes it for me.
ConfirmReceiveSwapFragment is only ever used once in ConnectSwapActivity,
so it is a pointless abstraction. It makes the code a lot more complicated
and also creates very complicated situations to handle when the screen is
rotated. All of this gets much easier when everything is just included in
the Activity, since there is no problem being solved by the Fragments.
Fragments are for reusing chunks of UI in multiple places, or for showing
multiple chunks of UI in the same Activity. Both of those cases can also
be handled, arguably better, without using Fragments:
https://corner.squareup.com/2014/10/advocating-against-android-fragments.html
- use 'com.android.support:appcompat-v7:22.0.0' instead of version 20.0.0
- ActionBar color: "F-Droid Blue" (also option for "F-Droid Green")
- fix invisible swap button with Material Design
- remove "Light + dark action bar" theme (as of Action Bar is always blue/green)
On api 19 or later, writing to your own private directory on the SD doesn't
require any extra dependencies. We only ever store icons, apks and index stuff
on the private repo, so we never read/write anywhere else on the SD.
Fix issue #202 - crash due to sqlite parameter limit being hit.
*NOTE: Queuing here for merge after next stable.*
The queries which have the potential to cause crashes due to too many parameters in the `ApkProvider` are now encapsulated in `ApkProvider` and can only be accessed by safe helper methods, which alleviate the problem by breaking big requests down into many smaller requests.
This will probably have to be done for the `ApkProvider`, but leaving for now because the limit is twice as big.
See merge request !70
Cache files to SD card again (if preference set).
**NOTE: Queueing here to be merged after next stable**
A previous security fix meant we no longer stored apk files on the
SD card. However, this should still be a feature that people can opt
for if they want, without being insecure. As such the process is now:
* First download: put in internal storage (to ensure it can't be
modified before installing)
* After download: also copy to SD card for caching.
* On starting F-Droid:
+ Always delete internal storage apks.
+ Only delete other, cached apks if cache preference is false.
To make the code simpler and less prone to bugs, I had to consider
the fact that if people did not have an accessible SD card, then the
path to a cached apk and a "downloaded but transient" apk cannot be
the same. While possible, it means many checks to see if they are
the same, thorough permission management to prevent security issues,
and makes it harder to clear transient apks when F-Droid starts.
See merge request !71
A previous security fix meant we no longer stored apk files on the
SD card. However, this should still be a feature that people can opt
for if they want, without being insecure. As such the process is now:
* First download: put in internal storage (to ensure it can't be
modified before installing)
* After download: also copy to SD card for caching.
* On starting F-Droid:
+ Always delete internal storage apks.
+ Only delete other, cached apks if cache preference is false.
To make the code simpler and less prone to bugs, I had to consider
the fact that if people did not have an accessible SD card, then the
path to a cached apk and a "downloaded but transient" apk cannot be
the same. While possible, it means many checks to see if they are
the same, thorough permission management to prevent security issues,
and makes it harder to clear transient apks when F-Droid starts.
Now the other content provider functions which can result in broken SQL
due to the number of arguments is private, and can only be accessed
from a public helper method which ensures that limit is never hit.
The static delete helper function in ApkProvider now supports
recursively reducing the size of the list of apks to delete, until
it is under the threshold that sqlite can handle.
Fixes#231 - category translations were reverting to english.
Minor fix - feel free to hold off before stable (and then we can do a 0.88-test pretty soon after stable with my other change, as we did last time), or feel free to merge beforehand and do another -test.
There are two places where translations get populated. One of them (when the fragment is initialized) translates the categories. The other place (when the "installed app cache" background process completes) did not translate the category names. They are now both translated.
See merge request !69
There are two places where translations get populated. One of them (when
the fragment is initialized) translates the categories. The other place
(when the "installed app cache" background process completes) did not
translate the category names. They are now both translated.
There was some confusion about the "SD Card" portion of the string.
Sometimes it will not store on the SD card, e.g. if the SD card is
not mounted, or inaccessible to F-Droid.
Change some "defaultValue"s
Key "theme": I also use the dark version of F-Droid but I think the most users would prefer the light version. This is also standard in the
most used apps on Android, I think, see GApps, Flym, K-9, Telegram, ...
Key "localRepoBonjour": I never used this function and I think it's better, if the user manually activates it when he wants to use it.
Key "localRepoHttps": I see no reason why it should not use https over http, it's better of security view, I think.
Key "cacheDownloaded": The most smartphones today have enough storage available to allow to enable this function. This would decrease the
traffic on both sides, as the user does not have to download a app and the F-Droid server does not have to send it multiple
times.
This is somehow related to #221, I think.
See merge request !67
The error below was happening on my crappy slow phone, I suspect because
the database upgrade check was locking the database, while this was
scheduled and started running on a different thread. Given it is a very
low priority task (it notifies F-Droid if apps were installed by a method
other than F-Droid) it is now delayed by 10 seconds.
E FATAL EXCEPTION: AsyncTask #1
E java.lang.RuntimeException: An error occured while executing doInBackground()
E at android.os.AsyncTask$3.done(AsyncTask.java:200)
E at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
E at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
E at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
E at java.util.concurrent.FutureTask.run(FutureTask.java:138)
E at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
E at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
E at java.lang.Thread.run(Thread.java:1019)
E Caused by: android.database.sqlite.SQLiteException: database is locked: BEGIN EXCLUSIVE;
E at android.database.sqlite.SQLiteDatabase.native_execSQL(Native Method)
E at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1763)
E at android.database.sqlite.SQLiteDatabase.beginTransactionWithListener(SQLiteDatabase.java:527)
E at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:481)
E at org.fdroid.fdroid.data.FDroidProvider.applyBatch(FDroidProvider.java:58)
E at android.content.ContentProvider$Transport.applyBatch(ContentProvider.java:217)
E at android.content.ContentProviderClient.applyBatch(ContentProviderClient.java:95)
E at android.content.ContentResolver.applyBatch(ContentResolver.java:639)
E at org.fdroid.fdroid.data.InstalledAppCacheUpdater.updateCache(InstalledAppCacheUpdater.java:98)
E at org.fdroid.fdroid.data.InstalledAppCacheUpdater.update(InstalledAppCacheUpdater.java:64)
E at org.fdroid.fdroid.data.InstalledAppCacheUpdater$Worker.doInBackground(InstalledAppCacheUpdater.java:163)
E at org.fdroid.fdroid.data.InstalledAppCacheUpdater$Worker.doInBackground(InstalledAppCacheUpdater.java:159)
E at android.os.AsyncTask$2.call(AsyncTask.java:185)
E at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
E ... 4 more
Use custom code rather than "Locale.forLanguageTag" (which is android-21
specific). Also fixed the actual setting of the language, by modifying
the code in FDroidApp to respect the country code (rather than just the
language code).
Android's lint will give warnings for all strings that are not translatable
or translated. Some strings like repo name/description and language codes
should never be translated. Here's how to officially mark as "do not
translate":
* for individual strings, include: translatable="false"
* for the entire file, name it: donottranslate.xml
http://tools.android.com/recent/non-translatablestringshttps://code.google.com/p/android/issues/detail?id=75163
Key "theme": I also use the dark version of F-Droid but I think the most users would prefer the light version. This is also standard in the
most used apps on Android, I think, see GApps, Flym, K-9, Telegram, ...
Key "localRepoBonjour": I never used this function and I think it's better, if the user manually activates it when he wants to use it.
Key "localRepoHttps": I see no reason why it should not use https over http, it's better of security view, I think.
Key "cacheDownloaded": The most smartphones today have enough storage available to allow to enable this function. This would decrease the
traffic on both sides, as the user does not have to download a app and the F-Droid server does not have to send it multiple
times.
Also forceably hid the "update all" button in the update tab.
This is because on my crapy LG Tracphone android-10 device, it is slow
enough that I can see that button appear and then disappear.
Also changed the way locales are loaded. Now it deals well with language
codes that are followed by country codes (e.g. "en-US" rather than just
"en").
Change client language from the app options (#155)
Bug: ActionBar Language does not change.
Edit: To make it more clear, you have to restart the app manually for the action bar language to be changed.
See merge request !65
It does this by sending a HTTP POST request to /request-swap on the
swap server. That listens for a POST to this path, and responds by
popping open a confirmation message to the user on the server device.
The categories were previously selected in the Spinner, then passed
directly to the database. Once the categories became translated, this
meant we were sending the translated category to the database.
However, the database only knows about English categories, and so we
instead need to look up the English translation for the selected
category before passing to the database. This is done by keeping
a list of original category names which is indexed the same as the
translated ones.
Previously, there was a bug with an off-by-one error, as android-11
or later was treating the headerView different to android-10 and
earlier. They now both have the same understanding about the header
view.
For some reasono specify different layout-v* directories to inflate
views based on the android version is not working as desired.
Previously there was a "layout", "layout-land" and "layout-v11" dir.
Only "layout" and "layout-v11" had the "select_local_apps_list_item.xml"
layout in them, the "layout-v11" version did _not_ have a checkbox,
whereas the other vanilla one did.
This worked on a android-16 emulator. It would correctly pick up the
view from "layout-v11". However, on a android-19 device, android-19
emulator, android-21 device and android-21 emulator, they all picked
up the view from the "layout" dir - with checkbox and all.
I couldn't figure out for the life of my why this was happening, so
I started to figure out which one it _would_ work with. I added
a layout-v* for every single version, and in each, put a text view
telling me which version it was. That way, viewing the list of apps to
swap, the list would inflate a view, and tell me which layout-v* dir it
inflated it from. It worked for layout-v17 and higher, but was unable
to inflate layouts from layout-v11 to layout-v16. So I deleted all
except layout-v17, and it now works for android-16, 19 and 21 as per my
tests.
Now that we controll all lib build.gradle files, we can finally do it.
If we want to build support-v7 from source again with gradle, we'll import the
build.gradle and "fix" it like the rest.
The update count was broken because I added the join onto the
apk table, and in the process, forced a GROUP BY on the AppProvider
queries. This group by made the COUNT(*) actually count the number
of apks for each app, not the total rows.
When viewing app details from a swap list, we need to return to the
swap list when pressing "up". Previously it would go to the main list
of apps, and only return to the swap list when pressing "back".
Now, a subclass of AppDetails is used when in swap mode, which knows
how to navigate up to the correct task.
They were all due to the addition of "application label" to the
installed app cache. This commit adds a mock ApplicationInfo
to the mock package manager and also specifies the label while
inserting into the test content provider.
This also makes AndroidStudio integration work better, which makes
running and debugging tests much nicer than the CLI.
Also cleaned up imports in one test, and made the symlink tests not
fail on older devices below API 19.
I had trouble wrapping my head around which point in time the fdroid/repo
directories are created, when they are populated with .html files, and
when the index.xml is put there. I did some minor cleaning up to make
it a bit easier to manage this in the future.
Althought the construction of the XML document was fine witn Android 7,
the actual serialization of it was limited to 8 or higher. Try as I might,
I couldn't find a way to figure out how to serialize a DOM tree on API 7.
Turns out that the "PullParser" API is able to build and serialize XML
trees on API 7. It's a little clunkier than the DOM alternative, so I
refactored out the generation into a subclass to make it clearer what
it is doing and when.
* Selecting apps to swap fixed
Before the checking of a list item would not actually register it to
be included in the swap. This has been rectified.
* Added a new property to repos for "isSwap"
Repositories with this property are not shown in the Manage Repos
activity, as there is not much benefit to having this happen.
* More robust error handling when symlinking files
Before it would check for stdout or stderr and then throw an exception.
This happened even on successful symlinks on my 2.3.3 device. As such,
I've put the error checking after the shell command has completely finished
(just in case there were any race conditions), and more importantly, checked
for the presence of the file being linked - rather than just stdout or
stderr.
* More code cleanup
Generics <> operator, Nullable annotations, removal of dead code.
* Removed dead code
* Added some Nullable/NonNull annotations to prevent future misuses of variables.
* More verbose errors when an error occurs creating directories/files.
* Provide CheckBox for selected items
Newer API's highlight the background using the "activated" state. Older
APIs need this to be implemented differently, so there are now checkboxes
on the left of the list view items to provide this functionality.
* Clean up IDE warnings
Diamond operator for generics, remove unused imports and unused method.
* Adapter class created for installed apps
Cleaned up the code to do with binding views to the adapter in this view.
Previously it made quite a few assumptions about the structure of the layout,
e.g. "layout.getParent().getParent() is a LinearLayout", which would cause
crashes if the layout changed slightly.
* Cleaned up text alignment styles for API < 17.
API v17 has a textAlignment style, wherease previous verisons rely on
the "gravity" property. This change includes gravity="center" where there
was previously only textAlignment="center".
* Fragments get added properly on 2.3 device.
For some reason, when adding the fragment to android.R.id.content, it
wouldn't work on my 2.3 device. This change includes a (almost) empty
activity layout with a single FrameLayout. The fragments are added to
this rather than "content", and it works better. It is not perfect - it
still adds the fargments behind the action bar, and so the action bar
appears blue. But at least they are there :)
* Added translatable strings where constants were used before.
Not related to v2.3 support, but stil important for a stable release,
that is fully translated.
Bug in the code which decides which apps to select for swapping.
Due to the way in which Adapters and ListViews work together to provide
"header" and "footer" functionalities for lists, there is a mismatch between
the index in our original adapter, and the actual index on the list. It is
up to us to maintain this correctly, which was not done, hence the off by
one error.
This was present in the old local repo implementation, and the skeleton
code for implementing it was copied to the swap fragment. The only change
neccesary was to add a search button to the menu and make it have a
SearchView as its action view.
Shows an expandable notification on devices that support it (4.1+
I believe). The support library does most of the job of handling
incompatibilities between platforms.
We were using different methods and violating the correctness error
"Log tags are only allowed to be at most 23 tag characters long." as reported
by lint.
Changes:
* Always start tags by "fdroid." (even though it's a bit redundant)
* Replace "org.fdroid.fdroid." by "fdroid."
* Don't include "compat.", "data." "views.fragments." et al (these didn't and
shouldn't be namespaces really, only for file/code organisation)
* Do include parent classes for subclasses, e.g. "fdroid.RepoProvider.Helper"
since these can conflict
If the download process is interrupted, a "dl-" file hangs around in
F-Droid's cache directory. If the download succeeds, extracts an xml
file from the downloaded .jar file, and then the rest of the process
is interrupted, then a "index-*.xml" file hangs around in F-Droids
files directory.
This fix removes these two types of left over files when F-Droid
is started. In addition, it also makes it so that the downloaded
repo indexes are now more clearly named "index-*-downloaded" (with no
extension, because the code which does it doesn't know whether it is
a .jar or .xml file, and it doesn't matter anyway). Also, it extracts
the .xml files to the cache directory too (instead of the files dir)
and names them "index-*-extracted.xml".
There is some code which removes the old "dl-" files that will become
redundant after the first time it is executed on a users device after
upgrading F-Droid. It is a very small amount of code run on startup,
so I am not concerned about the performance implications of leaving it
there, but I put a comment explaining that it can be removed in the
future.
Includes minor formatting changes, and a few annotations.
Previously, a few people have been confused by an empty list when they first
open F-Droid (e.g. if they are not connected to the internet, and repos didn't
update). This provides some feedback so that there is never a blank screen.
Fixes Issue #34.
Refresh AppDetails header on package state change (fix#161)
Update the application details to display the correct state on
application install/uninstall.
Should fix issue #161https://gitlab.com/fdroid/fdroidclient/issues/161
See merge request !56
To refresh the header, we retrieve the fragment from its id.
But the landscape layout used another id for the same fragment, so it
could not be retrieved, leading to a NullPointerException in landscape.
Therefore, use the same fragment id as in the portrait layout.
When adding repositories using the Manage Repos activity, firstly look
for an /index.jar appended on the URL provided by the user. If that
doesn't work (HTTP status code other than 200) then it will try
/fdroid/repo/index.jar, then /repo/index.jar. If it can't establish a
connection to the server, or if none of the above attempts results
in a 200, then the path provided by the user is kept (even though we
have a hunch it might be wrong).
This is to cover for the case where people arn't connected to the net.
Another way to deal with no internet connectivity is provided by a
"Skip" button on the dialog while searching for the index.jar.
The searching for index.jar is done by doing a HTTP HEAD request, so
the entire jar needn't be downloaded.
Finally, to make this happen in a clean sort of way, I refactored the
ManageReposActivity a little bit to encapsulate all of the add repo
dialog handling into a subclass. This way, the outer class doesn't
need to know things like: Is the dialog showing, what state is it in,
is the background task to search for index.jar files running, how and
when to cancel that task, etc.
Do not manually call onChange() (fix NPE)
If the AppDetails activity has been destroyed by the system during an
application installation/remove, it is recreated once it should be
displayed again (this behavior can be forced by enabling "Don't keep
activities" in Android developer options).
In onCreate(), it passes its instance of myInstallerCallback to an
Installer. In onActivityResult() (which is called before onResume()),
this installer calls a method (onSuccess() or onError()) on this
callback. The implementation of these methods (the anonymous inner class
assigned to myInstallerCallback) dereference myAppObserver, which is
still null, because it will be initialized in onResume(), so a
NullPointerException is thrown.
However, the problem is not only that myAppObserver.onChange() is called
when myAppObserver is null, but that it should not be called manually at
all: it is a ContentObserver, so it is automatically called when
registered to the content resolver. As a consequence, this callback was
called twice.
Removing these calls fix both problems.
Should fix issue #135https://gitlab.com/fdroid/fdroidclient/issues/135
See merge request !58
When we receive notifications indicating that the app has changed, the
App object needs to be changed and the view updated.
These notifications can be received from two sources:
- the ContentObserver;
- onActivityResult().
Thus, the implementation should not be related to the ContentObserver
(in theory, we might want to keep only the onActivityResult()
notification). Therefore, move it to a separate method in AppDetails.
This also preventively avoids bugs when the ContentObserver is null.
This reverts commit 47e065442edc108d4bb38f9daaa7cdb3fff26b49.
Now that the ContentObserver is created when activity is started (even if not
resumed), then it will be non-null during onActivityResult(). Therefore,
the calls to onChange() will not lead to NullPointerException anymore.
The reason why we want to manually call onChange() is that the
ContentObserver notifications may happen several seconds later:
https://gitlab.com/fdroid/fdroidclient/merge_requests/58#note_948719
The ContentObserver was registered only when the activity was in resumed
state. However, in started but paused state (when the activity is
visible but not in focus), we still want to receive these notifications
to update the view.
Therefore, register it on start and unregister it on stop.
As a consequence, myAppObserver will be non-null during
onActivityResult().
If the AppDetails activity has been destroyed by the system during an
application installation/remove, it is recreated once it should be
displayed again (this behavior can be forced by enabling "Don't keep
activities" in Android developer options).
In onCreate(), it passes its instance of myInstallerCallback to an
Installer. In onActivityResult() (which is called before onResume()),
this installer calls a method (onSuccess() or onError()) on this
callback. The implementation of these methods (the anonymous inner class
assigned to myInstallerCallback) dereference myAppObserver, which is
still null, because it will be initialized in onResume(), so a
NullPointerException is thrown.
However, the problem is not only that myAppObserver.onChange() is called
when myAppObserver is null, but that it should not be called manually at
all: it is a ContentObserver, so it is automatically called when
registered to the content resolver. As a consequence, this callback was
called twice.
Removing these calls fix both problems.
Should fix issue #135https://gitlab.com/fdroid/fdroidclient/issues/135
Immediately after an app uninstall, the associated App will be updated
by a call to reset() in the AppObserver.onChange().
But before receiving this event, the activity and the fragments resume,
leading to a call to getInstalledStatus(…). At this stage, we don't know
that the app has been removed yet, but the package manager already
removed it. Therefore, PackageManager.getInstallerPackageName(…) throws
an IllegalArgumentException.
In that case, consider that the application has been uninstalled.
Should fix issue #167https://gitlab.com/fdroid/fdroidclient/issues/167
Default to building F-Droid with precompiled dependencies, with option for source dependencies.
I hope this isn't to controversial, I've tried to very clearly articulate my thinking behind it in the commit message for 6594357c and also document the feature in detail in F-Droid/libs/README.md.
When I first contributed to F-Droid, it had zero dependencies, and so was a matter of checking it out and running `ant debug`. I'd like future contributors to be able to experience this too, by checking out the code and then running `gradle assembleDebug`.
I've ensured that the premise of building libraries from source is still front and centre though, and building from source is a matter of running `gradle -PsourceDeps assembleDebug` as documented in F-Droid/libs/README.md.
I'd appreciate if somebody (hopefully with all of the dependencies already checked out and building) would be able to test this for me, and provide feedback. Happy to answer any questions which remain unanswered after reading the commit message and the F-Droid/libs/README.md file.
See merge request !49
Binary deps were not getting build and cleaned from the binaryDeps
subdirectory of "libs". Also, reverted the android support appcompat
library target to android-19, as there was no need to change it.
The support libraries expect to be using the gradle plugin version 0.10.0.
We are currently on version 1.0.0. They use APIs in their build script which
have moved or been removed, and so the build just breaks when we run it with
the 1.0.0 plugin. I tried some magic to make it work in various ways, but
kept failing. As such, I've reverted the `gradle -PsourceDeps` build to not
build the support libraries from source. In the future, we should be able to
change this if they change the plugin version to a more recent one.
Note that the ant build script still hasn't been modified, and so will be
using the binary support-v4 library, but should build appcompat-v7 from source.
Was going to bump to Support v21, however there is some behaviour change which
causes a crash. They have removed the progress view from the toolbar/actionbar.
This breaks the AppDetails activity. As such, I'll leave that for the future.
For now, there will be a slight difference between building with
ant (which uses support v-almost-21) and gradle (which uses v20).
This will stay the case until we get around to completely porting
the app to v21, and fixing any bugs or UI sadness that arises.
NOTE: This commit does not touch the ant build system at all,
only gradle.
There are currently 23 gradle projects which require configuration,
let alone building, in order to build F-Droid. This takes a non-trivial
amount of time/memory/cpu. Additionally, it also provides difficulties
when importing the project into Android Studio - which is the IDE that
many potential contributors will be using. Finally, I have over 100mb
of data in the extern/ folder, and the support libraries require almost
every single Android SDK to be installed, which is several GB. This is
not a friendly environment to encourage people to submit merge requests.
However, I'm very mindful of the need for an open source project such
as F-Droid to be able to be built from source. So to make sure we have
the best of both worlds, I've ensured that building all dependencies
from source is still possible.
The F-Droid/libs/README.md file explains in greater detail how to
do this (i.e. "gradle -PsourceDeps build").
As much as possible, I've tried to make the binary dependencies fetched
from jcenter. However there are still libraries which either haven't
integrated required changes for F-Droid back upstream, or don't have
mavenCentral/jcenter binaries available.
Android preference fragment has been changed to the original
upstream repository. The one we had before was because upstream
hadn't merged a MR for gradfle support yet. However, that has
now been merged. This version still doesn't exist in jcenter though.
In order for libsuperuser to build from upstream, using
`gradle -PsourceDeps`, we need to include a few gradle plugins
from jcenter which are never actually used (used by upstream to
release to jcenter).
Even though support-v4 is included through jcenter, it is kept in
the libs directory, so that ./ant-prepare.sh can use it.
Update support preference fragment to newer version. There has been
bugfixes commited, so lets include them in the version we are using.
Repository update interval (#158)
This is issue #158https://gitlab.com/fdroid/fdroidclient/issues/158
I have modified the update interval to include "weekly" and "Every 2 Weeks" and removed "Hourly" as update frequencies. THe maximum interval was daily which was still too often for me. I have removed "Hourly" as you probably have to pay for your bandwidth and that is insanely often :-). Every 4 hours should still be plenty often.
2 Languages did not have the right amount of options there anyway, and I fixed those.
Please check, test, judge and ... hopefully ... merge.
See merge request !52
This makes this script more likely to run on various setups, since it does
three checks for finding where the `android` utility is:
1. is it in the PATH already?
2. is ANDROID_HOME set?
3. does ~/.android/bashrc exist?
This also copies ~/.android/ant.properties into the project for anyone who
wants to setup automated tests of `ant release` builds.
The maximum interval to check repositories was "Daily". Allow to also
update weekly and bi-weekly and remove the "hourly" option (someone has
to pay for all that bandwidth after all). I was considering to even remove
the "Every 4 hours" option, but did not dare do it.
Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Classes which contain calls to platform specific methods cause
problems, because the dexer will go looking for that method even
if you put a guard condition checking the build number. However,
if you lazily load a class depdending on the version number, then
older API devices wont try and load it, and no VerifyError occurs.
* Android-21 introduced an API for symlinking.
* Android-19 has an API which can be used via reflection.
* Earlier versions use Runtime.exec('/system/bin/ln')
This also extends the SanitizedFile stuff so that the android < 19 can
safely use Runtime.exec() with less fear of command injection vulnerabilities.
Finally, some tests for the SanitizedFile and symlink stuff was added.
This prevents an app with "write external storage" permission from
being able to switch the legit app with a dodgey one between F-Droid
requesting an install, and the package manager actually showing the
install dialog to the user.
In order to make the file in private internal storage readable by
the package manager, its parent directories need to be world-executable,
and the file itself needs to be world-readable. It seems that the
"/data/data/org.fdroid.fdroid/cache" dir provided by the Context is
already world executable, but the "apks" subdirectory does not default
to this.
Also, to be compatible with android-8, a Runtime.getRuntime().exec()
call was added for such devices, which invokes /system/bin/chmod.
The effect of this was to require some level of file sanitization to
be made available using the Java type system to prevent command injection
attacks from weird apk names (as people are free to download metadata
from random internet people).
Superfdroid fixes
Package names and apk file names should only contain letters, numbers, dots,
and underscores. This is now checked in RootInstaller before executing 'pm
install' or 'pm uninstall'.
See merge request !48https://gitlab.com/fdroid/fdroidclient/merge_requests/48
Also, start using String[] like Android's SUPPORTED_ABIS instead of
Set<String>. Said list of ABIs will always be very short, at most containing a
handful of elements.
* Don't apply android plugin in root project
This results in the root project being treated like and Android project.
That is, gradle will expect an AndroidManifest, a targetSdk property, and
all sorts of stuff that is not relevant to the root project.
Perhaps more importantly, this breaks integration with Android Studio,
which is the tool that many potential contributors will be using.
Finally, it also allows runing gradle tasks in the root project, rather
than having to cd into the F-Droid directory, which is a minor nicety.
The reason it was there in the first place was to make it so that we could
find the location of the Android SDK using the same mechanism that the
plugin used. To deal with this, this commit adapts the SDK finding code
from the gradle plugin.
* Make gradle error out when missing depenencies.
The support v4 library requires some obsolte SDKs that are likely
not installed. It caused non-intuitive errors to come up for me,
so I've made gradle tell the user when this occurs.
* Documented the main build.gradle file
This is primarily to explain the hacks we use in order to build the
Android support libraries.
The specific reason for this is that it provides @Null and @NotNull
annotations which should increase the safety of our code. Many of the
bugs which get filed are due to NullPointerExceptions, which could be
avoided by tooling using these annotations. The goal is to statically
catch this specific class of errors in as many situations as possible,
rather than waiting for them to occur at runtime.
The README documentation was not updated after the move of the f-droid client
code to the F-Droid directory. The instruction where therefore wrong
The troubleshooting documentation about determining the used Android versions
in the project was also outdated. While the script was made functional the tip
to install the various sdk from the command line did does not work for me.
This script should be in the root of the git repo so that the config in
Jenkins' web interface is always just:
./jenkins-build
This commit also includes a little code to make it easier for people to run
this script on their own machines, if they want to reproduce the Jenkins
build setup.
This now figures out the root of the fdroidclient project and works there,
rather than just working in the root on the project. So it can be run like
./tools/zip-build.sh or (cd tools && ./zip-build.sh) or whatever.
I also removed the pointless function, since it is only called once in the
script.
This is a basic set of tests. These tests should be extended in three ways:
* make sure all of the apps are parsed
* make sure all of the APKs are parsed
* make sure the ProgressListener is called
As described on https://f-droid.org/forums/topic/gradle-directory-structure/.
This will keep things a bit weird with git for a bit, but you can always use
--follow to make it take renames into account for the commit history.
I tweaked both the ant and gradle build setups and both of them work in the
new structure. And gradle is much faster since e.g. now an 'assemble' inside
F-Droid assembles only the client, without including all the subprojects as it
happens in master.
Was experiencing some problems with local repo keystore on Android 2.3.4.
Previously, the local repo keystore could fail to create, but would still
return a LocalRepoKeyStore instance to be used. Now it throws a checked
exception, and is dealt with in the relveant places.
Also cleaned up some calls to "e.printStackTrace()" and replaced with
more informative logging messages.
Fixes issue #111. This is a workaround rather than a full solution,
but it solves the problem. There is a bug in Lollipop, whereby including
spongycastle as a security provider causes problems verifying .jar files.
As a result, it is now disabled when actually performing verification,
so that the libraries provided by Android do the work, and then re-enabled
afterwards.
There is an ever so slight chance that the period when this is disabled
may align with the period when spongycastle may actually be required
(i.e. for signing a local repo index). This is a risk which I cnanot
see how to avoid, and will likel7 cause either the signing to fail
due to the unavailability of the relevant security classes. However
this is very minimal, hard to reproduce (I couldn't get it to happen)
and also has the effect of the local repo failing, rather than the updating
of apps failing (which is arguably more important) and so is worth it in
my opinion.
See comments at https://gitlab.com/fdroid/fdroidclient/issues/111
for much greater detail.
Previously the update silently failed, and even showed "No Repository has any package updates". The commit relays one error message per repo and adds a localized message when only a part of the repo updates failed. Also, the error message for more severe errors is made more verbose.
Also removed the "errmsg" variable.
Initial "swap" UI implementation
This is a major rejigging of the "Local Repo" user interface, to make it easier for lay people to use F-Droid to swap their apps with their peers. I wont mention too much here, because the individual commits have more detailed information. However, it is worth noting that major things that probably deserve testing are:
* Going through the proccess of creating a swap
* Scanning the QR code from another users F-Droid "swap" screen
* Scanning the QR code from their barcode scanner
* Entering the URL shown below the QR code into the browser and following the steps
* Installing an app from the "Swap" success screen
* Using NFC to add swap (I couldn't test this as only have 1 NFC device in the house)
I've actually had trouble installing from the swap screen after upgrading to Android 4.3.1 (CM) on my Nexus 4. However that issue seems to be present on the current stable "Local Repo" implementation too, so will stop banging my head against the desk in the interest of getting this initial swap implementation out.
Subsequent work will go towards refining edge cases. I'm cataloging these in my fork of fdroidclient on github. The type of things which I still need to work on include :
* i18n all of the strings
* Enabling NFC
* Dealing with WIFI disconnects
* Support WIFI AP
* Adding back the "search" functionality in the "create swap" screen
* ... and no doubt others will arise ...
A major feature which is missing is Bluetooth, but some earlier work has been done on that which will make it less of a burden to implement. As this is getting reviewed for merging, I will start working through these other issues.
See merge request !28
When electing to "Swap Apps" from the main menu, and a LocalRepoService
is already running, then it jumps straight to the wifi QR fragment.
However, it didn't play nice when pressing the "back" button. This is
now fixed, by manually recreating the backstack in this situation.
It also fires up the NFC push message if NFC exists.
Added a "Cancel" button to the swap QR code screen.
Also changed how the LocalRepoService deals with onDestroy(). Previously, it
would invoke `stopNetworkServices()` on the UI thread, blocking for a
significant portion of time (enough to cause ANR messages on my devices).
Now, it does this on a new thread.
As for the QR code size, it was getting quite small on my nexus 4,
which I think has a large screen (even though there is much larger about).
As a result, it couldn't be scanned properly using barcode scanner.
This is the first major diversion from carries mockups, in that the qr
view is now in a scroller, which means the "Open QR Code Scanner" button
may well not be visible on the main screen on small devices. Not sure about
how to manage this tradeoff between biggish qr code but everything
viewable on one screen.
Previously it would direct to the LocalRepo screen, then when that code
was ported to the swap workflow, it did nothing :( Now it does as one
might expect, and actually directs the user to the swap screen.
I had a couple of months of changes which I rebased over master.
It made me sad. After doign what I thought was required to resolve
conflicts, I've now gone and fixed a few bits here and there.
This arose because I didn't try and compile after each merge conflict.
I'll take the lesson onboard and try to remember to do this in the
future :)
Previously, we would redirect to the main list of apps. Now, the swap
thing will show the specific apps in the "LocalRepo" category. It also
shows the "Swap complete" message with a nice icon carrie designed.
Instead of always showing "swap", sometimes it shouldn't be shown
at all (i.e. on the first and last screen) and on the NFC screen,
it says "skip".
There is a translated string called "skip_button_label" available
in the AndroidPreferenceFragment library, but I don't want to rely
on that. Prefer to have our awesome translators be in charge of
that label.
Previously, swap would only enable this if the user hadn't previously
said "Don't show NFC message again". However, we really want people
to be able to swap regardless of whether the actual UI message is shown
or not.
Refactored NfcBeamManager to NfcHelper, to allow extra utility methods
to live there. Specifically, the process of sending a URI over NFC.
Removed some superfluous debugging calls to Log.i().
Took a while to figure out, but the problem was that the support
libraries ListFragment would only ever use getActivity() when
creating widgets. What we really needed was the ability to use
an alternative context, namely a ContextThemeWrapper. If this is
passed to the constructor of a widget, it is themed appropriately.
To make it work, we now create our own list view in onCreateView()
(which is really a copy of the code from the android list_content.xml
file anyway). There are also some workarounds for a bug in the
support library with regards to using your own view.
I did the same as some previous code which copied code from the zxing
library and put it in the com.google.zxing namespace (but left the license
in tact - is this okay for Apache 2.0 stuff?). The reason was that they
encourage copying the relevant two files into your project and modifying
as neccesary. In this case, it wasn't about modifying for functionalities
sake, but rather for android support libraries sake. The upstream version
doesn't support android.support.v4.app.Fragments, only android.app.Fragments.
Hooking up the intent integrator from zxing almost removes the need for
the refactoring which made FDroid.java responsible for handling "new repo"
intents. However, there is still the one case of the user not using
the QR code, but rather a web browser to connect to a swap. That is, they
will click a link on the other phones web browser, which directs them to
initiate the swap. Thus, we still need FDroid.java to be able to distinguish
between a "swap" new repo request, and a regular "add repo" new repo request.
In the process, I also commented out the "It's not working" button.
These have been temoprarily moved to an unused layout. The reason
it wasn't removed completely was because the layout still adheres
to the spec from carrie (and I couldn't be bothered looking through
the git history to find it later on when it is time to re-implement
it :)
I've also commented out the "Learn more about wifi" and "use
bluetooth instead" buttons for now.
Listen for a new intent, show a screen to the user mentioning
they are about to start a swap. Make FDroid receive repo intents,
then dispatch to relevant Activity.
Previously manage repo always got the intents. Now FDroid does, and
chooses whether to give to ManageRepos or Client connect.
Not sure if it is required to do it this way or not, but it seems to
work.
I had a bit of an issue getting the "Welcome to F-Droid" string to
fit on one line, because it was breaking on the hyphen. That is still
not resolved in this commit.
Still need to:
* Show error messages instead of the "connect" description
* Jar signing seems not to work when connecting to other repo.
In order to handle returning to F-Droid after connecting (or saying no)
I tagged the intent with a "handled" extra value. That way, I can ignore
trying to connect to a repo if we've already handled that event.
Finally, I also fixed an issue regarding downloading of signed
index.jar files with an uppercase fingerprint.
The fingerprint from the jar differed from that in the swap url,
in that one was upper case and the other was lower case.
This uses an .equalsIgnoreCase check instead. It also adds an
extra guard in case the repo doesn't have a fingerprint. Although
it may not even use the signed repo updater if both the pubkey
and fingerprint are null, it is nice to have the extra assurance.
Fixes issue #19.
I also left some more TODO's around. I should put them in issues,
but I'm in a bit of a hurry.
By starting the repo in the activity, both the NFC and wifi fragments
have access to it. In the future, it will make it easier to move some
of the static stuff from FDroidApp (to do with "selected apps") into
the SwapActivity, as it will not really be needed anywhere else.
The back button will take you back through each step of the swap
process now, and remove the swap Activity completely if you press
back from the first screen. Also, when the WiFi QR code is shown,
the local repo manager actually starts the relevant service.
The website now also has icons which it needs to worry about copying
across to the webroot, so I refactored the "symlink webroot to other
folders - such as /fdroid and /fdroid/repo".
Along with a bunch of networking stuff, a lot of UI to do with selecting
apps to swap was also moved. The background on the list is transparent,
which allows blue to shine through. Also, the text on the list items is
white, which will not work with a white background.
I've temporarily dropped support for searching this list too, until
I get some feedback from carrie et al.
NOTE: This stuff was written before hans fixed apcompat problems with
LocalRepoActivity, but then rebased over it later. As such, it doesn't
contain his fixes. Will need to do that before a stable release. i.e.
Still has a bit of a dependency on API 11 which needs to be resolved.
The Fragments and an Activity which tie all of the swap views together
has begun. The first bit of implementation is to get the current
wifi network displayed, which worked out alright.
This commit contains a lot of theme related stuff, particularly
involving taking assets from carries mockups and making them suitable
to use as drawables. The process for doing this is a story for another
day, but I'll document it and put it on the wiki in the future. carrie
showed me a script that a mate of hers used on another project, and
I've adapted it a little to make it work nicely here (note - it isn't
in this commit).
The button is blue, and always shown with associated text in the
ActionBar. This required a custom drawable which was set as the
background in the styles.xml.
fix crasher bug in 0.75
fix divide-by-zero crash when a repo has less than 25 apps in it
This was introduced in e4401ed22c0f65db5d4f1a1f0e1222b061e471af
See merge request !39
Fixes this crash:
dalvikvm W threadid=1: thread exiting with uncaught exception (group=0xb68df4f0)
AndroidRuntime E FATAL EXCEPTION: main
E java.lang.RuntimeException: Unable to resume activity {org.fdroid.fdroid/org.fdroid.fdroid.views.ManageReposActivity}: java.lang.NullPointerException
E at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2120)
E at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2135)
E at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1668)
E at android.app.ActivityThread.access$1500(ActivityThread.java:117)
E at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
E at android.os.Handler.dispatchMessage(Handler.java:99)
E at android.os.Looper.loop(Looper.java:130)
E at android.app.ActivityThread.main(ActivityThread.java:3683)
E at java.lang.reflect.Method.invokeNative(Native Method)
E at java.lang.reflect.Method.invoke(Method.java:507)
E at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
E at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
E at dalvik.system.NativeStart.main(Native Method)
E Caused by: java.lang.NullPointerException
E at org.fdroid.fdroid.views.ManageReposActivity.checkIfNewRepoOnSameWifi(ManageReposActivity.java:466)
E at org.fdroid.fdroid.views.ManageReposActivity.addRepoFromIntent(ManageReposActivity.java:455)
E at org.fdroid.fdroid.views.ManageReposActivity.onResume(ManageReposActivity.java:144)
E at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1150)
E at android.app.Activity.performResume(Activity.java:3832)
E at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2110)
E ... 12 more
README: more on installation and troubleshooting
android SDK commands to compare locally installed targets with git fetched libraries and additional code snippets to install missing targets.
See merge request !38
do not run proguard in `ant debug` builds, it breaks the tests
The whole Android ant build system was built around the assumption that proguard is only run on the release builds. Running proguard on the debug builds breaks the test instrumentation, which does something like insert the emma methods into each class. In theory, this could be fixed, but I think its a bad idea to heavily customize the ant build system since its basically deprecated in favor of gradle.
Here's the proguard warning message:
`[proguard] Warning: can't find referenced class com.vladium.emma.rt.RT`
Instead, there is probably a way to test the release build. For example, in the Jenkins build, I added `ant clean release`, then it installs the APK, and runs the "monkey tester" on it.
See merge request !36
Rather, only show 25 progress events. I went with "25 events" rather
than "every X apps" because then it will be nice for both large repos
(e.g. F-Droid will update every ~50 apps) and small repos (something
with 20 apps will update for every one, but not add much overhead).
On my testing with an API 11 emulator here, it went from ~32s to
process ~1100 apps to ~20s. When no progress events are sent, then it
also takes ~20s, so this essentially is a 50% improvement for this part
of the update process.
The whole ant build system was built around the assumption that proguard is
only run on the release builds. Running proguard on the debug builds
breaks the test instrumentation, which does something like insert the emma
methods into each class. In theory, this could be fixed, but I think its a
bad idea to heavily customize the ant build system since its basically
deprecated in favor of gradle.
Here's the proguard warning message:
[proguard] Warning: can't find referenced class com.vladium.emma.rt.RT
Saves the new data for the repo at the end of the update process to enable the user to re-trigger the update in case the update exited prematurely.
Fixes#84.
There is a bug where the android.support.widget.SearchView gets
removed by proguard which causes breakage. This prevents that
from occuring by a bit of a brute force approach. While it would
be possible to keep only the class in question, this may leave
us open to other bugs in the future. Better to be safe than sorry.
http://stackoverflow.com/questions/22136032/action-bar-search-view-android-null-error
When the user updates the repo and all packages are recent, the update dialog exited so fast, that some users might have thought that the repo didn't update. To notify them that no new updates were found, the user now gets a toast.
Proguard removed the method org.fdroid.fdroid.installer.SystemInstaller$PackageInstallObserver$packageInstalled,
resulting in an AbstractMethodError crash.
Fixes issue [79](https://gitlab.com/fdroid/fdroidclient/issues/79).
This increases the size of the .apk by ~800kb, which is clearly
undesirable. However, the nature of the Java security suite
implementations are that they use a lot of reflection to instantiate
classes. The end result is that proguard excludes classes which
may be required, depending on the security algorithms required
by certain certificates.
Fixes issue #88
revert to plain CA-based HTTPS verification
This is to address #80 so we can get a stable release out. Then we should revisit #80 and actually get the pinning and TOFU working properly.
See merge request !30
This reverts to only using standard HTTPS verification based on Certificate
Authorities. This means that self-signed certificates will not work at all
since that is what MemorizingTrustManager was providing. It seems to me
that this was originally working because MemorizingTrustManager was not
correctly validating. I couldn't figure out why PinningTrustManager alone
was not working. But we need to get good stable release out!
I'm leaving all the plumbing in place because this stuff should be included
once its all fixed and working properly. You can see where it was added
here: 254327f9a7700c8196e61f53801f1f12ac825806
refs #80https://gitlab.com/fdroid/fdroidclient/issues/80
The problem arose when we start with no categories other than the
three defaults, then add a repo with multiple categories. The exact
issue was that although the category spinner itself was updated,
the listener for onChange was referencing the list of categories
from before (with only three categories). Changed it to use data
from the category spinner adapter, that way it is always up to
date.
Also fixed some warnings in the file. Instance access of a static
object, and deprecated method warning.
Fix issue #56 -- update.zip
Creates a script create a update.zip file that installs F-Droid to `/system/app` or `/system/priv-app` depending on the android version. Also creates a remove.zip to remove F-Droid. Generates a zip file for every apk in the bin directory, to work with both signed and unsigned apks. Fixes issue #56.
See merge request !29
Fix issues #74 and #75
The indeterminent progress indicator in the `AppDetails` view is now hidden by default on Android 4.0.3 (which is the only reproducing device I had access to). Had to replace a bunch of calls with those provided by appcompat.
As for the "(De-)Installation Error" message when using the root installer, it was due to the timeout being set to 5 seconds for the superuser shell. Note that just accepting the super user prompt takes a minimum of 3 seconds, plus the install time. I changed the timeout to 30 seconds, for reasons explained in the commit.
NOTE: @mvdan, I noted that you mentioned you were not interested in client development any more, so I'm not quite sure if it is worthwhile me submitting this as a MR (FYI - thanks for all the help on the client, also happy that your able to spend more time on build recipies and fdroidserver stuff. Great to see all that moving along). Perhaps @CiaranG, if you had the time to review it you could give it the thumbs up or down? Otherwise, I'll merge it myself.
See merge request !27
The superuser shell has a timeout that can be specified. The timeout
was set to 5 seconds, thus the exit code from the shell is -1 (a
reserved exit code used by libsuperuse to indicate timeout).
By my estimate, it is more likely that a user will hit this error
message and get annoyed, compared to actually hitting a genuine timeout
because of some issue with superuser. Especially when we factor in slow
devices and large apps to install. Thus, the timeout has been bumped
to a more generous 30 seconds.
Replaced progress indicator methods with their "support" equivalents.
Also, it seems that on Android 4.0.4 and 4.0.3 if you request a
progress indicator in your ActionBarActivity (from the support
library), that it is set to visible to begin with. At least, that is
the conclusion I have come to, seeing as the only places it is set to
visible is on installApk() and removeApk(). Setting it to hidden in
onCreate seemed to do the trick (Couldn't use onResume, because we come
back from the "Request super user permissions" dialog, which causes
onResume to be invoked).
This commit uses alpha to make the category menu button appear darker to
match the rest of the dark theme. Since the background is black, the alpha
makes it darker. It is only used on the dark theme since alpha would
lighten the menu button on the light themes, and that would make it a worse
match.
If there is an unknown HTTPS certificate, MemorizingTrustManager puts up a
prompt to ask whether the user wants to trust the certificate. It comes at
a weird time in the lifecycle of the dialogs, so the previous dialog might
be null. Therefore add a null check.
This situation should probably be improved and better integrated.
The current HTTPS trust model is to first check if a site's key is TOFUed,
then check if it is pinned and check the CA, then prompt the user. There
is currently no way to only check the CA for validity. Ultimately, that
should probably not be needed if the repo URLs can include the HTTPS pin
info in the same way that the repo fingerprint is specified. Then it can
be added to the TOFU/POP keystore when the user accepts the Add Repo dialog
Since that idea does not exist yet, this commit adds the sites that are
likely to run their own repos in the near future:
https://f-droid.orghttps://guardianproject.infohttps://s3.amazonaws.com # multiple orgs use this
https://panicbutton.io # Amnesty International's app
https://psiphon.ca # circumvention tool
findbugs tells us:
Incorrect lazy initialization and update of static field org.fdroid.fdroid.
FDroidCertPins.PINLIST in org.fdroid.fdroid.FDroidCertPins.getPinList().
This method contains an unsynchronized lazy initialization of a static
field. After the field is set, the object stored into that location is
further updated or accessed. The setting of the field is visible to other
threads as soon as it is set. If the futher accesses in the method that set
the field serve to initialize the object, then you have a very serious
multithreading bug, unless something else prevents any other thread from
accessing the stored object until it is fully initialized.
Even if you feel confident that the method is never called by multiple
threads, it might be better to not set the static field until the value you
are setting it to is fully populated/initialized.
PRNGFixes.apply() is run in FDroidApp.onCreate(). This is enough,
according to Google, and their instructions say to disable this lint
warning once the workaround is included since lint cannot detech whether
the workaround is applied.
This the code format was also automatically corrected by the Eclipse plugin
The third parameter in the MemorizingTrustManager constructor was not good
apparently. Here's the email from Ge0rg, the MemorizingTrustManager author:
As you added MTM into the f-droid client, I'm writing to inform you that
the MTM constructor API was incorrect, and has been changed in current
git master:
When using the three-parameter constructor, the second parameter, a
trustmanager, was only used until the user stored a certificate into
MTM, and was overwritten after that.
Please use the new MTM constructor, and pass it the pinMgr as the only
trust manager parameter.
bug fixes for 0.71
I fixed the compact layout to make more sense. There seems to be something about the appcompat stuff that is preventing it from being short as it was before. There are some other bug fixes here, and I also included the Guardian Project repo by default, but disabled. @CiaranG proposed doing that a while back, now I finally did it!.
See merge request !25
This should help is there is ever multithreaded access to this variable.
This is an unlikely scenario, but the fix is easy.
findbugs reported this issue like this:
Incorrect lazy initialization and update of static field org.fdroid.fdroid.
FDroidApp.selectedApps in org.fdroid.fdroid.views.fragments.
SelectLocalAppsFragment.onActivityCreated(Bundle)
This method contains an unsynchronized lazy initialization of a static
field. After the field is set, the object stored into that location is
further updated or accessed. The setting of the field is visible to other
threads as soon as it is set. If the futher accesses in the method that set
the field serve to initialize the object, then you have a very serious
multithreading bug, unless something else prevents any other thread from
accessing the stored object until it is fully initialized.
findbugs found this problem and reported it like this:
Nullcheck of org.fdroid.fdroid.data.App.installedApk at line 191 of value
previously dereferenced in org.fdroid.fdroid.localrepo.LocalRepoManager.copyApksToRepo(List)
A value is checked here to see whether it is null, but this value can't be
null because it was previously dereferenced and if it were null a null
pointer exception would have occurred at the earlier dereference.
Essentially, this code and the previous dereference disagree as to whether
this value is allowed to be null. Either the check is redundant or the
previous dereference is erroneous.
This restores the Compact Layout being smaller than the normal layout, and
properly centers the icon in both. This stuff could use some refactoring
to work better with all the appcompat styles.
fixes#61https://gitlab.com/fdroid/fdroidclient/issues/61
default_repo_count is not used at all, and the numbering scheme is just a
vestige of that. This switches all the variables to have clear names of
what they are representing.
On Android 4.x, the category menu is showing up as pure black, and looks
very much like an app list item. I've personally witnessed many new users
struggle to find an app because the category is on "What's New" by default
and the app they are looking for is not new. Some even had troubles
remembering about the category menu after I told them. This small change
makes the category dropdown look the same on all Android versions, and
makes it a lot more apparent on newer Android releases.
This is a temporary usability fix until we can do something much better
than the category dropdown menu.
This merge request is a lot of porting code to use Android Support
appcompat-v7, now that it is in place. There are places where old custom
compat layers are replaced by appcompat, and other places like the local repo
stuff, where appcompat allows for full support on platforms older than 11.
onNewIntent() is called because ManageReposActivity is set to "singleTask"
launchMode, but it is only called if ManageReposActivity is already
running. onResume() is always called, and called after onNewIntent() if it
is called, so use onNewIntent() only to set the current Intent, then parse
the Intent only in onResume().
Here is how to reproduce the original bug:
1. Close F-Droid properly and start it again.
2. Click on https://guardianproject.info/fdroid/repo in a browser (and tell
it to open with F-Droid)
3. Hit cancel on the add repo dialog
4. Leave F-Droid open and switch back to the browser
5. Open that link again.
This should result in two dialogs on top of one another. Happened from both
Firefox, Chrome, and Android browsers.
When someone clicks on a URL that F-Droid can accept, i.e. a repo URL, then
Android puts up a chooser where the user can select which app to VIEW the
URL with. That was showing up with the title "Repositories", which is the
title used for that Activity when viewing it. This keeps the Activity
title the same while changing the title in the chooser.
Instead of just sticking whatever URL is in the clipboard into the "Add
Repo" dialog, this attempts to sanitize the URL in case it has some garbage
or came from a QR Code, and therefore was all uppercase (that makes for
smaller QR Codes). It also checks if there is a fingerprint in the query
string of the URL, and sticks that into the fingerprint box.
fixes#50https://gitlab.com/fdroid/fdroidclient/issues/50
The swap stuff will also need to handle incoming Intents that represent
new repos, so the parsing logic is now its own class NewRepoConfig, which
is something like the Repo class, but using getters instead of properties.
Since the new repo data does not change once FDroid receives it, the only
way to set the values of a NewRepoConfig is via the constructor.
This is based on some incomplete work from @pserwylo:
71cb12ef5c (diff-6)
and
71cb12ef5c (diff-7)
AppListFragmentPageAdapter is a subclass of FragmentPagerAdapter, so it
should include the same spelling to make that clear and easy to trace,
grep for, etc.
Now that the Fragment is embedded in the Activity, and the menu has been
moved to the Activity in ActionBar style, most of the utility functions and
the Dialogs can be based out of the Activity, which is how they are
designed to work. This makes things work a lot easier.
fixes#3https://gitlab.com/fdroid/fdroidclient/issues/3
This allows the main menu to act like a proper ActionBar using appcompat.
It also allows for making the search happen live on the ListView, rather
than having to launch a separate Activity to show the results.
I went through all of the source code replacing anything that is now
possible using appcompat-v7. appcompat-v7 is the official way to handle
backwards compatibility, and it is supported by Google and others. Using it
as much as possible should make the code more maintainable and readable by
others since they'll be used to seeing the appcompat-v7 patterns from other
projects.
fixes#51https://gitlab.com/fdroid/fdroidclient/issues/51fixes#42https://gitlab.com/fdroid/fdroidclient/issues/42
Previously, it was using the native android.widget.SearchView.
Now it uses the widget from appcompat. For good measure, I also
made it so that the search button is always in the action bar,
rather than being hidden behind a menu sometimes.
Do so by:
- Making RepoDetailsFragment/RepoListFragment retain their instances
- Move the management of the update repo dialog to UpdateReceiver
- Let the fragments tell the UpdateReceiver when to show/hide
HTTP Proxy preference for Tor, I2P, etc; add jenkins script
This adds a preference to set the HTTP Proxy so that FDroid can funnel all traffic through Tor, I2P, psiphon, or any other HTTP proxy.
Also, there is a script for jenkins to run before calling `ant clean debug` for the whole setup process. This script sets the `versionCode` to the UNIX time in seconds, and adds the date/time to the `versionName` so that the debug builds are very clearly marked from real builds, and they will automatically update when included in a debug repo:
https://dev.guardianproject.info/issues/2601
Jenkins will then call ant directly after running this script. This then sets
the debug builds made by Jenkins to have a versionCode of the UNIX time in
seconds and adds the data to the versionName. This clearly marks these as
debug builds and also makes it possible to have an fdroid repo of debug builds
that will automatically update after each build.
Taken from the gradle plugin user guide:
With Android KitKat (buildToolsVersion 19) you can use the diamond operator,
multi-catch, strings in switches, try with resources, etc.
Note that you can use minSdkVersion with a value earlier than 19, for all
language features except try with resources. If you want to use try with
resources, you will need to also use a minSdkVersion of 19.
You also need to make sure that Gradle is using version 1.7 or later of the
JDK. (And version 0.6.1 or later of the Android Gradle plugin.)
Appcompat on top of pserwylo
This is a refactor of @pserwylo's !19 to get appcompat building with `ant` and Eclipse. I reviewed @pserwylo's commits and they are ready to go as they are in this merge request.
Both preferencefragment and appcompat-v7 submodules need the
android-support-v4.jar to be included in their respective libs/ dirs in
order for ant to build those projects.
This was a bit more complex than all the other views, because it supports
rotation, and different views for when it is rotated. The end result is
that the way in which the views were constructed needed to be completely
redone.
In the process, I also moved the layout of the app summary to a Relative
Layout. This adds more flexibility, and is also the suggested layout
for complex views (as apposed to nested linear layouts). I believe this
is due to the performance of relative vs linear layotus.
It was aprticularly hard to figure out what was going on
when rotating an Activity which had a list fragment
that had another fragment as a header. I don't think fragments
were designed to work like this, but I believe it is all working
as expected now.
Conflicts:
src/org/fdroid/fdroid/Preferences.java
NOTE: I don't know how android will go with adding a new property
to a string-array resource, but not having it translated everywhere.
Will it struggle because the EN version has three values for "theme",
but other translations only have two?
The only remaining activity is the AppDetails acvitity, which will require
a little more than just making it extend ActionBarActivity. Currently,
it extends ListActivity. To support appcompat-v7, it really should have
two fragments - the details one and the list one. Then, when the orientation
is changed, it should load a different layout with the fragments side by side.
Although Google is encouraging people to make old devices run apps
with the action bar (via appcompat-v7), they haven't provided a way
for people to create preference/setting screens with an action bar.
There are plenty of issues in the Android issue tracker relating
to this, but it doesn't yet seem to be on the radar of the Android
devs.
Until there is a native implementation of PreferenceFragment in
the appcompat-v7 support library, this submodule provides is a 3rd
party solution. It is actually a fork of the first repo in github,
though that was a bit of an upload and dump, without accepting MR's.
This fork includes gradle support.
Although Google is encouraging people to make old devices run apps
with the action bar (via appcompat-v7), they haven't provided a way
for people to create preference/setting screens with an action bar.
There are plenty of issues in the Android issue tracker relating
to this, but it doesn't yet seem to be on the radar of the Android
devs.
Until there is a native implementation of PreferenceFragment in
the appcompat-v7 support library, this submodule provides is a 3rd
party solution. It is actually a fork of the first repo in github,
though that was a bit of an upload and dump, without accepting MR's.
This fork includes gradle support.
Thanks to the awesome work of mvdan, this was mostly ready to roll.
However, I had to wrestle for a while for two reasons:
1) I forgot to add the dependency in the build.gradle file (it was
already present in settings.gradle)
2) My IDE was unable to read the ANDROID_HOME env variable, and
despite my internet-search-fu, I couldn't figure out how to
make IntelliJ specify env variables for a gradle build. It took
a while to figure out, because it was failing silently in weird
ways.
After slaving away on a nice method to parse both the ANDROID_HOME
and the local.properties file (looking for sdk.dir), and then emmiting
nice error messages if neither were found or pointed to an invalid
location, I discovered it had already be done:
android.plugin.sdkDirectory ends up here:
https://android.googlesource.com/platform/tools/build/+/master/gradle/src/main/groovy/com/android/build/gradle/internal/Sdk.groovy#161
Which does exactly all that and more. So now sdkLoc is initialized to
the value of android.plugin.sdkDirectory.
Jmdns fixes and tor onion support
There are three groups of work in this collection of commits:
* improvements to the `WifiStateChangeService` and related activities like JmDNS to eliminate problems that happen when there are a lot of wifi change events.
* add rework the `.net.Downloader` stuff to add Tor support and lay the groundwork for Bluetooth support
* add support for repos on Tor Hidden Service .onion addresses
Tor Hidden Services are on domain names that always end in .onion, so there
is a URL pattern matcher that chooses which Downloader subclass to use
based on testing for .onion. This is a quick, dumb implementation. It
does make any attempt to see if Tor is running or even installed. That
can come once NetCipher is easy to handle in the context of FDroid.
refs #2367https://dev.guardianproject.info/issues/2367
This will ultimately be used to create the right Downloader subclass
instance based on the URL of the file to download (i.e. rfcomm://, .onion
address, ssh://, new socket protocols, etc).
Also delete unused constructors, they can trivially be readded if they are
ever used, and they are currently just clutter.
If a new "wifi connected" event comes in while a previous one is still
being processed, then cancel the current one as soon as possible. This
prevents the events from being processed in an interleaved manner, causing
chaos and crashes. Hopefully this will fix the jmdns crashes, since that
is triggered by onPostExecute() via FDroidApp.restartLocalRepoService().
java.lang.IllegalStateException: A service information can only be registered with a single instamce of JmDNS.
at javax.jmdns.impl.JmDNSImpl.registerService(JmDNSImpl.java:1005)
at org.fdroid.fdroid.localrepo.LocalRepoService$5.run(LocalRepoService.java:239)
at java.lang.Thread.run(Thread.java:856)
There is only ever a single service to advertise via mDNS, so when a new
registration is requested, remove any existing ones. This should eliminate
these stacktraces:
java.lang.IllegalStateException: A service information can only be registered with a single instamce of JmDNS.
at javax.jmdns.impl.JmDNSImpl.registerService(JmDNSImpl.java:1005)
at org.fdroid.fdroid.localrepo.LocalRepoService$5.run(LocalRepoService.java:239)
at java.lang.Thread.run(Thread.java:856)
WifiStateChangeService handles updating lots of IP-related things, then
things that depend on it listen to the broadcast from that Service. The
most straightforward way to update HTTPS or HTTP throughout the app is to
trigger this Service. It runs its stuff in an AsyncTask so it is all low
priority.
We only ever want a single LocalRepoService. Use the values returned by
the standard methods for start/stop and bind/unbind as the test for whether
the Service is indeed running.
There are about 4000 warnings from all the included submodules included as
symlinks. This hides them all so Eclipse only shows the warnings for
FDroid itself.
getApplicationContext() returns the Context of the application, which is
guaranteed to have the same life as the app itself. Other Contexts, like
an Activity, might go away during runtime.
As far as I can tell, the 'url' metadata in index.xml is not used at all by
the client. In order to keep it up-to-date in the local repo, it would
have to regenerate index.xml and index.jar each time the IP address
changed. That would mean a decent amount of work happening in the
background, all the update an unused field in index.xml.
There is no longer a reason to expose writeIndexXML() since FDroid should
always generate a signed repo. So make writeIndexXML() be called as part
of writeIndexJar().
Since the HTTPS certificate includes the current IP address in it, it needs
to be regenerated each time that the IP address changes. It also can take
a long time to run, especially on the first time, since it had to do things
like create a key pair and make the certificate. Therefore it should be in
a Service/AsyncTask.
Many of the classes in spongycastle are entirely unused in FDroid and
dependencies. So remove them from the Eclipse/Ant build to speed things up
and make the binaries smaller.
Allow the local repo to use HTTPS:// instead of HTTP://. This is currently
default off since handling the self-signed certificate is not currently
graceful. In the future, the SPKI that AndroidPinning uses should be
included in the repo meta data, then when someone marks a repo as trusted,
that local repo's SPKI should be added to the list of trusted keys in
AndroidPinning.
fixes#2960https://dev.guardianproject.info/issues/2960
This makes it so the local repo is always signed by a locally generated and
stored key. That key will become the unique ID that represents a given
local repo. It should seamlessly upgrade any existing unsigned local repo
next time that the user makes any changes to their local repo.
fixes#3380https://dev.guardianproject.info/issues/3380
Before, it didn't seem to find anything unless I ran this on my laptop:
`avahi-browse -a -v`
So added two recommended practices from other jmdns code for Android:
* force full resolution on receiving serviceAdded()
* feed the WiFi's IP address to jmdns when creating an instance
fixes#3379https://dev.guardianproject.info/issues/3379
ApkDownloaders keep track of a unique id. This id is passed through with
each event coming from the downloader. When progress events are received,
if they don't have the same id as the current downloader, they are ignored.
When returning to the app details screen after leaving, if a download
completed in the mean time, automatically show the install screen. Otherwise,
when you, e.g. return to your devices home screen during a download, the
download keeps occuring in the background, but we are unable to make use of
the resulting downloaded file.
The ApkDownloader now returns true or false depending on whether it
is using a cached version on disk, or hitting the network.
I was also a little more defensive about checking for if downloadHandler
is null before deciding to show a progress dialog.
Previously it was in the org.fdroid.fdroid package, along with
most other things. As such, it only need a package-local constructor.
However, now it has moved to the .net subpackage, and needs to be
made public.
Keep track of the downloader after an orientation change, and
make sure that there is always UI feedback beeing shown.
Removed resetRequired after merge conflict (the content observer
now deals with this for us in a much nicer way).
Conflicts:
src/org/fdroid/fdroid/AppDetails.java
One thing that still annoys me is that indeterminate progress dialog
still shows "0/100" due to the number formatter being used to display
the values in the TextView. Solution (from http://stackoverflow.com/a/6784180)
is to either make a custom dialog, or at the very least, in API 11 or higher
we can set the number formatter to display nothing.
Don't show the full download path of repo.
The full path includes the path to the index.jar, as well as ?clientVersion=*.
This is undesirable, the main purpose of even showing where we are downloading
from is to differentiate between multiple repos being updated at once.
This has a very different interface to the Downloader class, because
a handful of operations need to be run off the main thread (not just
download, but also totalDownloadSize and perhaps cache tag related
stuff). The AsyncDownloadWrapper provides its own listener, more
specific than just the progress listener, which invokes functions
at different points in time (e.g. download failed, completed, cancelled).
Refactored progress events to use string types instead of ints.
This way, it is less likely that two different events from
different processes will clash with each other.
Conflicts:
src/org/fdroid/fdroid/AppDetails.java
It still executes the download on another thread, and receives progress
events on that thread. However it now posts those events to the main UI
thread for them to get handled.
Also refactored Downloader/HttpDownloader a tad further. Some caching
stuff was pushed down from HttpDownloader -> Downloader (renamed from
eTag to just cacheTag). HttpDownloader.download() was recursively calling
itself, so I made the base class download() method abstract, and instead
the stream downloading is done in "protected void downloadFromStream()"
Other minor stuff:
* Prefixed some log TAGs with package names, to make them easier to grep.
* Removed a little bit of unused code (parameter to DownloadHandler).
This is the beginning of moving to a Downloader class and subclasses that
are used everywhere for downloading APKs, indexes, and icons.
I think that the ETag value can probably be used everywhere as the
unique ID for testing whether to download content, so I moved that to the
super class Downloader. But that's just a guess as of now...
This will allow the more general, non HTTP related stuff (progress events,
stream copying) to occur in a separate base class. HTTP specific stuff
(HTTP status codes, etag cache checking) is done in the HTTPDownloader
base class.
Find local repos with jmdns
This enables users to find local repos on local wifi using mDNS. Local repos can be advertized according to a preference, and you can find local repos by browsing via mDNS in `ManageRepos` under the "Find Local Repos".
This also includes fixes for the `SignedRepoUpdater` tests, and comments out other tests that have not yet worked.
The local repo generation code will take the description from the manifest
and include it in the repo meta data. So FDroid itself should also include
this description. Indeed every app should... perhaps this should also be
an `fdroid lint` item.
The tests of the jar signatures require working on files, but some
emulators make that very difficult. So try all possible paths for writing
to, and otherwise just skip the tests.
FDroid repos are advertised via Bonjour as plain HTTP or HTTPS services,
since they are browseable with a standard Web Browser. The "Find Local
Repos" browser in FDroid should only show FDroid repos, not any website,
so that is detected using a "type" TXT record in the FDroid broadcasts.
Multicast transmission is subject to heavy power management on Android,
because it apparently can be a battery drain. mDNS/Bonjour is based
entirely on multicast, so in order to have good Bonjour performance, there
needs to be good multicast performance. MulticastLock provides that.
fixes#3381https://dev.guardianproject.info/issues/3381
This name is used in the RepoList, the local repo website title, the
Bonjour broadcast, etc. By default, a name is generated using the make and
model of the phone plus a random number.
This adds support for registering the local repo with Bonjour/mDNS so that
it is broadcast out to all devices on the local network. This makes it
easy to discover and add local repos on the same wifi.
refs #2900https://dev.guardianproject.info/issues/2900
* The repo instructions are just a duplicate of the simple git submodules
* The Android.mk build instructions don't work and will never support what
e.g. gradle does
Anyone wanting to bundle F-Droid in a ROM can build it with git and gradle,
and then including the resulting apk.
UI bug fixes
This fixes a couple of crasher issues with the UI. And also a small change to allow Eclipse to find the reference files for Android Support.
I tried lots of things to make it more general, but the implementation of
this jar properties file is just too simple, so no variables can be used.
I renamed the other files related to android-support to match the naming
scheme.
When the "Add Repo" dialog was showing and the screen was rotated, it would
first leak the AlertDialog because it was not dismissed, then it would
crash after rotation, because the AlertDialog was trying to be restored but
no longer existed. That's what I think was happening at least... the
solution surprised me a bit here...
Otherwise, it gets confusing what is the action the user should do. Perhaps
the Action Mode "Done" button should always trigger the "Update Repo"
action, right now it means do nothing and return.
Since we have the packageName, we can just fetch the Drawables directly.
This uses some shortcuts to try to make things run faster. For example,
the ImageView does not have an ID, instead it is references by the index
number within the LinearLayout.
When you visit LocalRepoActivity, the swapping webserver is automatically
turned on, since it is required for any swapping to happen. When it was
automatically turned on, it will automatically turn itself off after 15
minutes to make sure that it doesn't stay running forever. If the user
manually turns it off, that cancels the automatic stop.
This forces the use of the Application's Context, so we can be sure the
webserver will run as long as FDroid is running. It also checks to make
sure whether the webserver is running before trying to start it.
This implements live filtering in a SearchView so that it is easy to search
for the apps you want to include in your Local Repo. This requires some
newer stuff, so I switched it to the android-11 Activity until appcompat-v7
is included. All this functionality will work fine with appcompat-v7.
This allows for searching installed app names for ones which match
a string. It searches based on the "label" as declared in the
manifest in the <application> tag as "android:label".
Most people are going to know the "label" i.e. the display name rather than
the packageName/id. So also store the label in the database and make it
accessible via InstalledAppsProvider so SelectLocalAppsFragment can show a
list of friendly names rather than packageNames.
The zxing library includes some Java 7 features, so it will only work on
Android SDK 8 (2.1) or above. FDroid supports SDK 7 (2.0), so on 7, do not
try to generate QR Codes.
If an application was installed via ADB, then it won't have an installer
package/name set. Therefore, looking up the label will cause a
NameNotFoundException, which should be ignored and not passed to the method
that called App() since it does not refer to the packageName that the
method is trying to look up, but instead the packageName of the missing
installer app.
This then serves to represent the APK that is installed for this app. It
needs to be filled out by the Providers then also. This then becomes a
place for data specific to the an installed App, like installedVersionCode
and installedVersionName, instead of having it both stored in an App
instance and a related Apk instance.
First try to get the description from APK, which is seems that no one sets
so it is usually null. Then get info about how it was installed, like
which app store, when it was installed and upgraded.
This automatically creates a repo with only FDroid in it the first time the
user goes to the Local Repo view. Having an empty repo is useless if the
user is trying to swap with someone. Having FDroid in there is not a
privacy leak since FDroid is needed for the swap process, and it will then
enable people to automatically get updates from each other, and do the
bootstrap process from the web browser.
fixes#2954https://dev.guardianproject.info/issues/2954
When FDroid has been started, this checks the symlinked APKs in the local
repo and uses those package names to build up the list of selected local
apps. This gives the SelectLocalApps experience continuity across app
restarts.
refs #3204https://dev.guardianproject.info/issues/3204
This gets the data about which apps are installed from the ContentProvider
that pserwylo recently added for data about "Installed Apps" and presents
a list view for the user to select from by touching each line. Then if the
user chooses "Update Repo", it will regenerate the local repo based on the
current selection. It will always include FDroid in the local repo.
fixes#3232https://dev.guardianproject.info/issues/3232
refs #3204https://dev.guardianproject.info/issues/3204
In order to use the data from InstalledAppProvider in a lot of the high
level classes provided by Android, like CursorLoader, SimpleCursorLoader,
etc, there must be an _ID row.
This is a little helper to direct people to get a new device to download
FDroid from another device that already has it. It first prompts them to
join the same wifi network, and offers a QR Code to associate to the same
wifi. The next step is a QR Code for getting the URL to the local repo.
The index.html on that local repo includes a download link for FDroid and
a repo link to the local repo.
refs #3204https://dev.guardianproject.info/issues/3204
Wire up the "Setup Repo" button on the Local Repo view to generate an
FDroid repo on the device that is hosted with the local webserver. This
also generates an index.html for when people navigate to the local repo via
a browser. This index.html will allow them to both download FDroid and to
setup the repo on the device running the webserver on the local FDroid.
refs #3204https://dev.guardianproject.info/issues/3204
refs #2668https://dev.guardianproject.info/issues/2668
This is a skeleton for the upcoming local repo (aka swap aka Kerplapp).
Right now, it just provides an Activity for controlling a Service which
manages a local webserver (nanohttpd). Next, it will be wired up to the
local repo created via a dedicated Activity for managing the list of apps
included in the local repo.
refs #3204https://dev.guardianproject.info/issues/3204
The local repo will need to both have current wifi settings in order to
send the correct IP address, SSID, etc. Also, this could be used to handle
interrupted downloads and updates, but that is not included in this commit.
refs #3204https://dev.guardianproject.info/issues/3204
This webserver is the core of the kerplapp swap local repo when used over
IP connections (WiFi). It is the smallest Java webserver we could find. It
is included as a git submodule, but then only the actual source files that
are needed are included. They are symlinked in src/.
The git repo used is the one that we submitted upstream as a pull request.
The pull request contains changes required to support https://. It has not
yet been accepted, so we cannot yet use the official repo. Once the pull
request is included, this should be switched to the latest release in the
official git repo.
https://github.com/eighthave/nanohttpdhttps://github.com/NanoHttpd/nanohttpd/pull/107
refs #3204https://dev.guardianproject.info/issues/3204
This app needs to be able to generate QR Codes regardless of what other
app might be installed, so zxing's core.jar needs to be embedded in this
app.
This also includes two classes which are modified versions of ZXing classes
that allow the generation of QR Codes without the Barcode Scanner app being
installed:
https://stackoverflow.com/questions/4782543/integration-zxing-library-directly-into-my-android-application
The classes are src/com/google/zxing/encode/Contents.java which is a copy
of zxing/android/src/com/google/zxing/client/android/Contents.java; and
src/com/google/zxing/encode/QRCodeEncoder.java which is a heavily stripped
and modified version of
zxing/android/src/com/google/zxing/client/android/encode/QRCodeEncoder.java
refs #3204https://dev.guardianproject.info/issues/3204
refs #2470https://dev.guardianproject.info/issues/2470
The receiver of this can at least make some sense of it, versus null or a
blank string. This prevents crashes where FDroid.repo.address is not yet
set since the wifi was never enabled or it does not have an IP address yet.
For some reason, on some emulators, it is failing to find a place to write
the test index files. But on most setups, it works fine. So instead try
writing the files to the cacheDir of FDroid itself rather than the test app
It seems that sometimes checking dialog.isShowing() is not enough. I got a
crash on dialog.dismiss(). But since the dialog is already gone in that
case, just catch the Exception and move on. Here's the stacktrace:
31760 FDroid D Update took 45 seconds.
31760 FDroid D Invalidating preference 'lastUpdateCheck'.
31760 AndroidRuntime D Shutting down VM
31760 AndroidRuntime E FATAL EXCEPTION: main
31760 AndroidRuntime E java.lang.IllegalArgumentException: View not attached to window manager
31760 AndroidRuntime E at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:378)
31760 AndroidRuntime E at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:223)
31760 AndroidRuntime E at android.view.Window$LocalWindowManager.removeView(Window.java:432)
31760 AndroidRuntime E at android.app.Dialog.dismissDialog(Dialog.java:278)
31760 AndroidRuntime E at android.app.Dialog.access$000(Dialog.java:71)
31760 AndroidRuntime E at android.app.Dialog$1.run(Dialog.java:111)
31760 AndroidRuntime E at android.app.Dialog.dismiss(Dialog.java:268)
31760 AndroidRuntime E at org.fdroid.fdroid.UpdateService$UpdateReceiver.onReceiveResult(UpdateService.java:124)
31760 AndroidRuntime E at android.os.ResultReceiver$MyRunnable.run(ResultReceiver.java:43)
31760 AndroidRuntime E at android.os.Handler.handleCallback(Handler.java:587)
31760 AndroidRuntime E at android.os.Handler.dispatchMessage(Handler.java:92)
31760 AndroidRuntime E at android.os.Looper.loop(Looper.java:130)
31760 AndroidRuntime E at android.app.ActivityThread.main(ActivityThread.java:3687)
31760 AndroidRuntime E at java.lang.reflect.Method.invokeNative(Native Method)
31760 AndroidRuntime E at java.lang.reflect.Method.invoke(Method.java:507)
31760 AndroidRuntime E at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
31760 AndroidRuntime E at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
31760 AndroidRuntime E at dalvik.system.NativeStart.main(Native Method)
119 E Dumpstate > /data/log/dumpstate_app_error
119 ActivityManager W Force finishing activity org.fdroid.fdroid/.views.RepoDetailsActivity
more info:
https://stackoverflow.com/questions/19538282/view-not-attached-to-window-manager-dialog-dismiss
This helps with dealing with listFragment being null after a rotation. That
was happening when an Intent was coming in from a QR Code scanner, and
the screen was rotated during the process. This snippet was taken from
SearchResults.java
This is Peter Serwylo's idea, I just typed it up and tested it :)
Fix/update notification
In addition to making update notifications work again (fixing issue #20) I also removed an unused class, and made the update count easily accessible from the AppProvider.Helper class.
security updates for added repos
These commits fix a couple of security issues with adding repos, they should be included in the 0.65 release. Here is the bug report from Adam Pritchard, these issues should be fixed:
2.
But wait, you say? Where's the "EF" at the start? F-Droid actually shows
(and takes) a version of the fingerprint with the first byte (first two
hex) dropped. Bwah?
You can see this with Guardian's fingerprint here:
https://guardianproject.info/2012/03/15/our-new-f-droid-app-repository/
len('050C8155DCA377F23D5A15B77D3713400CDBD8B42FBFBE0E3F38096E68CECE') / 2 *
8 == 248
...But it should be 256.
On purpose?
3.
And it seems like there's a bug in F-Droid. If you enter the fingerprint
when adding the repo, the repo gets flagged with "Unsigned", but if you add
the repo without entering the fingerprint it doesn't.
Reproduction:
- Add https://guardianproject.info/repo/ and enter
050C8155DCA377F23D5A15B77D3713400CDBD8B42FBFBE0E3F38096E68CECE
- Refresh
- It's say "Unsigned" in red text under the repo name
- Delete the repo
- Add it again, but without the fingerprint
- It won't have any red text
This is surely unintended?
If a repo was configured with a fingerprint, but it has not yet updated and
gotten the pubkey from the index.jar, then it will be in an "unverified"
state, i.e. the signing key fingerprint is stored, but it has not yet been
used to check against the pubkey in the index.jar
The logic here is crufty, so I slapped a flag in there to make sure that
the pubkey gets stored when someone configures a repo and includes the
fingerprint. When the fingerprint is set, it will first download the
index.jar and verify it against that fingerprint. The logic for storing
the pubkey permanently happens later in the XML parsing, so there needs to
be a flag to signal to store the pubkey in this case.
Before the flow was always index.xml -> get pubkey -> index.jar. Really,
there should no longer be support for unsigned repos, then all of this
stuff can be dramatically simplified.
fixes#2924https://dev.guardianproject.info/issues/2924
refs #2960https://dev.guardianproject.info/issues/2960
This was causing the first byte of the signature to be chopped off, so
therefore it would not validate since the fingerprint of the cert from
the net connection had the right fingerprint, but it was compared to the
stored, truncated version.
This also means that the database version needs to be bumped to trigger an
upgrade so that the bad 62 char fingerprints are removed from the database.
Since before, incoming repo Intents where handled in the Fragment's
onCreate(), an Intent that was received while the Fragment was visible was
just ignored. Activities have onNewIntent() for that, but Fragments don't
so the repo Intent handling had to be moved to the ManageRepo Activity.
That makes for a more direct relationship anyway, since ManageRepo is what
is configured as receiving all those Intents in AndroidManifest.xml.
Doesn't work because:
* In this installer, we are not a system app
* Our intent action is of type ACTION_VIEW, not ACTION_INSTALL_PACKAGE
The equivalent of "we are not an unknown source" would be the other
installers, such as the Root and System ones.
There were some weird edge cases that couldn't quite be pinned down,
whereby installing an app would result in a unique key violation being
hit. One example was when somebody was installing an apk from a file
manager. It seems that this doesn't trigger a PACKAGE_CHANGED, but
rather a PACKAGE_INSTALLED. The end result is that it attempts to insert
a record that already exists in the installed apps table. Because we
have a unique key constraing on the appId, it breaks.
This commit changes the way that we insert installed app details.
Instead of inserting some times, and updating other times, we always
insert. If we hit a unique key violation, the row is deleted, and then
the new values are reinserted.
send any installed app via NFC/Beam or Bluetooth
Building upon the NFC+Bluetooth sending of the FDroid.apk, these two commits allow the user to send any installed app via Bluetooth or NFC/Android Beam.
This takes the code used for sending the FDroid.apk and applies it to any
installed app. So the user can go to the AppDetails for any installed app
and select "Send via Bluetooth" from the menu, and send the app to another
phone.
If you are viewing the AppDetails screen for an installed app, this code
configures Android Beam to send the APK for that installed app if the you
initiate via NFC.
Also move the SDK checks into each method so that they are easier to use
without doing the wrong thing.
If a new repo comes in via Intent, like from clicking a link, scanning a QR
Code, etc., then stay in FDroid once the add dialog is complete.
Previously, it would sometimes stay in FDroid and sometimes go back to the
sending Activity, depending on the sending Activity. It was confusing and
annoying behavior.
Previously the data was not stored anywhere, and each time we wanted
to know about all installed apps, we built a ridiculously long SQL
query. The query had essentially one "OR" clause for each installed
app. To make matters worse, it also required one parameter for each
of these, so we could bind the installed app name to a "?" in the query.
SQL has a limit of (usually) 999 parameters which can be provided to
a query, which meant it would fall over if the user had more than
1000 apps installed.
This change introduces a new table called "fdroid_installedApps".
It is initialized on first run, by iterating over the installed apps
as given by the PackageManager. It is subsequenty kept up to date
by a set of BroadcastReceivers, which listen for apps being
uninstalled/installed/upgraded.
It also includes tests to verify that queries of installed apps,
when there are more than 1000 apps installed, don't break.
Finally, tests are also now able to to insert into providers other
than the one under test. This is due to the fact that the providers
often join onto tables managed by other providers.
This allows you to specify the Uri of a single apk, and
it will return it. Right now it is just used in a test, but
hopefully it will be useful in other situations too.
I forgot to commit this last time, and didn't review my patch
well enough before submitting.
This was explicitly not-allowed previously, and so there was a
test that ensured it threw an exception when attempted on the
ApkProvider. However I implemented it for another feature, but
forgot to change the tests. Now the test no longer tests for
an exception. Rather, it properly tests for the correct execution
of the method.
run JUnit tests
It turns out that Jenkins was running the JUnit tests all along, but it just never reported on them. This adds a jar to the test project that makes JUnit reports that Jenkins can parse, and the report on the results. So now if the JUnit tests fail, people will be emailed just like build failures.
Also, I added a quick `ant javadoc` target to the main project in case anyone likes that kind of thing.
Jenkins needs some kind of report from the JUnit tests in order to tell
whether the tests succeeded or not. android-junit-report is a library to do
exactly that. With this setup, Jenkins should now successfully understand the
status of the JUnit tests, where before it just ran them and ignored the
results
fix two bugs
First bug is with lots of QR Code readers, they don't respect custom URI schemes like `fdroidrepo://` and just force all URIs to be `http://`. A slight tweak makes it possible to use a QRCode to configure a repo using all of the major ones I could find (I tested with 8 different apps).
The second bug is a simple crasher bug.
Some QR Code scanners don't respect custom schemes like fdroidrepo://, so
this is a workaround, since the local repo URI is all uppercase in the QR
Code for sending the local repo to another device. This way, the QR Code
can still be all uppercase and use HTTP:// and Android will still route it
to FDroid, but via the Just Once/Always chooser (fdroidrepo:// goes
directly to FDroid with no prompt, when it works)
* Don't hard-code ellipsis in the code
* Separate the two rows into two linear layouts
* Don't abuse relative layouts
* Use ellipsize with weights to achieve best results
Fix Multi Repo Updating Only One Repo
Java update logic has been moved to SQL, to prevent having to pull out apps fromt he database, and then iterate over them in Java.
Previously, I accidentally made the repo updater presume that it
had access to all apps in a big fat list. This meant that I was iterating
over that list, performing calculations, etc, rather than actually
querying the entire database.
The solution was to bundled all update-service related processing to one
process in AppProvider (I didn't want to have to pull every single app/apk
out of the database in order to perform the update, because that will become
more and more burdensom as the repo grows).
There is a method calcDetailsFromIndex() in the AppProvider.Helper class.
It now does three things:
* updates compatibility flag.
* updates suggested version (outstanding issue is documented in gitlab issue #1)
* updates iconsUrl (fixed in this commit)
Icons from old repos will just have icons in an "icons" dir
in the same folder as the index.jar. New repos have density
specific icon dirs (e.g. "icons-240") which depend on the
device F-Droid is running on.
The archive repo was getting updated after the regular repo.
In these situations, we didn't have every single app/apk in
memory in order to calculate the suggested version. As a
result, F-Droid ended up choosing a suggested version from
the archived versions, when terhere was actually a newer version
in the database.
This change does all of the calculations in two database queries
now. Although the implementation of the query is not hackey,
they way I get to the code in order to execute the query
is a bit hacky, so most of the implementation is private.
It adds an extra 600ms on my Nexus 4 with ~2000 apks from the
F-Droid index. But I think it is the only way, as we really need
to iterate over every single installed apk, to see if it is still
wanted. The up side is that we can query for a large amount of
them, rather than quering individually for each apk.
NOTE: I haven't added a new status message yet, because we are
about to do a stable release. After the stable release, I'll
add a new status message to cover for this > half a second
(on my relatively fast device). This will probably be part of
an overhaul of the update process in general, including a
proper progress dialog.
This can later be removed again if the user still has a way to easily update
repos manually without having to enter "Manage Repos" and exit again. A good
option would be a pull-to-refresh action.
The problem was that they defaulted to 0 if not specified, however
the code checking for current version was looking for -1 for a "no upstream version".
The idea was good: reduce the amount of copied/pasted code
where ContentValues were initialized, populated, then inserted.
I've kept the idea, by putting it in its own method which is
called twice. But the resources are not loaded dynamically any
more. This is so that the compiler will be able to pick up if
we reference a missing resource. Also, I took the opportunity
to replace the field name string literals with references to
RepoProvider.DataColumns.* constants.
Finally, changed the tests around because now we need to
have the "getInteger()" call mocked in resources correctly
(for priority/inUse).
From before content providers, where we rolled our own update notification
system for when data changed in the database. I also removed the property
"ctx", because it is availble in getApplicationContext(). As a general rule,
it is usually safer to not use a member field if not neccesary. That way,
there doesn't need to be any assumptions about when it is set and what value
it has. In this case, it was only set half way through onCreate, and therefore
usage before then would break.
Safer and less error-prone because:
* Always checks for null
* Checks for sizes
* Inits App/Apk lists at known capacity
* Properly closes all cursors
There are still one or two cursors that are not closed correctly and show
things like these:
W/CursorWrapperInner(19973): Cursor finalized without prior close()
The Activity.getActionBar() method can only be called after
setContentView() has been invoked, as described here:
http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html
I couldn't think of any way to enforce this safely
(i.e. make the compiler kick up a stink if we didn't do it). As such,
I just put a comment above each usage of the ActionBarCompat class.
Another outstanding issue is a duplicate of 474, where it crashes when you
press the "Up" button from ManageRepos, but I'll create a different issue
for that.
The cryptographically secure random number generator exposed to Android
through the Java Cryptography Architecture is not properly initialized
on some older unpatched versions of Android. Google provides
a PRNGFixes.java class to force secure seeding of the CSRNG on all
platform versions. This comment adds the PRNGFixes class & and a call to
invoke the fixes from the FDroidApp class.
More detail is available from the Google Android Developers blogpost on
the subject:
http://android-developers.blogspot.ca/2013/08/some-securerandom-thoughts.html
When adding "default_repo_priority", it was copy/pasted from
"default_repo_pubkey" without changing the name, and so
tried to cast the string value into an int and failed.
Related to the last bug with the update notify count. This one is
also due to the fact we didn't ask for the right data from the
provider. If these bugs keep coming in over time, I will seriously
consider guarding access to each variable with a check, and throwing
an exception if the variable hasn't been initialized. For now I'll
see if it was a once off. Hopefullly tests will catch these issues
in the future.
This property will be ignored if f-droid is not installed as priv-app,
but it _will_ skip the "you have to enable unknown sources" dialog if
f-droid is installed as priv-app.
There is thus no gain in keeping it as is (false).
This patch iterates over the configured list of repos and adds them to
the db on create. This means that the initial list of repositories is
now fully configurable. Added the guardian project repo (disabled) as a
testcase.
Android.mk is needed to build F-Droid as part of other ROMs. A ROM would
have to emulate the .gitmodules with repo.
Note: the build will fail until AndroidPinning pulls a trivial fix for
super(null). There is also a layout bug that is fixed by the next
commit.
Inconsistent formatting types for argument #1 in format string
searchres_napps ('%s'): Found both 'd' and 's' (in values/strings.xml)
This lint check ensures the following: (1) If there are multiple
translations of the format string, then all translations use the same type
for the same numbered arguments (2) The usage of the format string in Java
consistent with the format string, meaning that the parameter types passed
to String.format matches those in the format string.
<string name="searchres_napps">Sa gasit o aplicatie potrivita cu %s\'</string>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The update notification was not taking ignored apps into account.
This is the first manifestation of a class of bug I feared whereby
the properties of an App object are not initialized, but no error
is thrown. It occured because we were iterating over apps that were
created from the index file, rather than our database. Hence, they
had no knowledge about whether they should be ignored or not.
Also took the chance to perform a minor refactor of UpdateService
class. The onHandleIntent method was getting huge, so I extracted
two methods: verifyIsTimeForScheduledRun() and
performUpdateNotification(), as well as removing the unused "success"
flag.
The two methods should theoretically make the class more testable
later, as we can test the scheduled run code, and the update notification
code in separate tests, but we'll wait and see if that eventuates.
Not sure that the "parent" activity of ManageRepos is required in
the manifest any more, due to the fact that the main use seems
to be to direct the "NavUtils.navigateUpSameTask" method uses it,
but this change switches to "NavUtils.navigateUpTo" and specifies
the activity explicitly.
In order to support suggested version, I didn't want to have both
suggested version + versionCode in the App table. Rather, just the
code, and then use that (and the apps id) to join onto the apk table.
This is something we wanted to do elsewhere, so I refactored the
QueryBuilder class from the ApkProvider so that it can also be used
by the AppProvider.
This is just a cosmetic fix to make the RTL layout look like the normal LTR
one. It is, effectively, making non-RTL text be aligned to the right. I
suppose that's fine, for the sake of making it readable since we don't want it
aligned to the left, breaking the layout.
Following the Android 4.2 changes, which explain how to add native support for
RTL, I've replaced Right for End and Left for Start. Enabling RTL to see the
results.
Following the Android 4.2 changes, which explain how to add native support for
RTL, I've replaced Right for End and Left for Start. Enabling RTL to see the
results.
Removed unused code from ApkProvider.Helper, made it throw proper
exceptions when trying unsupported operations. Refactored tests
a little bit to facilitate separate test cases for the provider
and its helper.
The bug is explained in detail in the issue tracker.
This change added guard condition to check for existence of the field
before adding.
While I was at it, I also guarded a bunch of other ALTER statements
with the if (!columnExists()) check. It turns out that many of them
break, but we only saw the first one because it threw an exception
before getting to the others.
This marks a method as overriding another method, and makes sure that it
matches the signature of the method it is supposed to be overriding,
otherwise it gives a warning.
Its a bit verbose, but can catch mistakes and save time. And the default
Android profile for Eclipse always adds them automatically...
Here's the crash dump:
java.lang.NullPointerException
at org.fdroid.fdroid.data.ValueObject.checkCursorPosition(ValueObject.java:13)
at org.fdroid.fdroid.data.Repo.<init>(Repo.java:37)
at org.fdroid.fdroid.views.fragments.RepoListFragment.onListItemClick(RepoListFragment.java:269)
at android.support.v4.app.ListFragment$2.onItemClick(ListFragment.java:58)
at android.widget.AdapterView.performItemClick(AdapterView.java:299)
at android.widget.AbsListView.performItemClick(AbsListView.java:1113)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:2904)
at android.widget.AbsListView$3.run(AbsListView.java:3638)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
Categories can technically be null, so need to guard for that
when getting them from the ContentProvider. This occurred because
I had an old index from before the change from "category" to
"categories", so it indeed was null.
The other one was from when I added a new repo without a fingerprint.
The fingerprint gets changed to upper case, but if null, causes
a NPE.
If the device supports API level 16 (Android 4.1) then add a menu item
on the repository management screen to "Find Local Repos". Activating
this menu item will initiate NSD service discovery with the NsdHelper
class looking for 'fdroidrepo' and 'fdroidrepos' service types on the
local network. When one is found, the service is resolved and the name
& IP are populated into a list of discovered repositories. Clicking an
NSD discovered repo will prompt the user to add the repo.
For now it's enforced like minSdkVersion. It is possible to try and install
incompatible apks by enabling "Incompatible Versions" and agreeing to the
warning shown when clicking on such a version.
For now, the UpdateService ignores these fields when updating from
the index. There is no time that the index should specify what
versions to be ignored.
In the future, this will be done with a join table that stores
info about what to ignore. Another future improvement should also be
to make "App.toContentValues()" smarter. That is, make it only return
values which have been set since the object was created. However this
will add an overhead which may or may not be noticable.
Huge improvements! Amongst them:
* Pressing Up is just as fast as pressing Back
* Like Back, it keeps the scroll position and everything
* Now FDroid behaves like the other activities that an user may navigate up to
Inconsistent formatting types for argument #1 in format string
searchres_napps ('%s'): Found both 'd' and 's' (in values/strings.xml)
This lint check ensures the following: (1) If there are multiple
translations of the format string, then all translations use the same type
for the same numbered arguments (2) The usage of the format string in Java
consistent with the format string, meaning that the parameter types passed
to String.format matches those in the format string.
<string name="searchres_napps">Sa gasit o aplicatie potrivita cu %s\'</string>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Having the pre-configured Eclipse files in git will make it easier for
other people to work with FDroid in Eclipse, and should not affect anything
else. The key files are .classpath and .project. The .settings/ folder is
for user-specific settings, so its ignored.
This is another easy method to send FDroid to a device that doesn't have it
yet. Unfortunately, stock Android blocks the receiving of APKs, but many
ROMs and even some Samsung devices do not have this block.
You can find the lengthy backstory on this work here:
https://dev.guardianproject.info/issues/2084
Setting android.permission.ACCESS_WIFI_STATE automatically sets up
uses-feature to require wifi. Therefore, we have to manually say that
wifi is not actually required.
This adds the command to update the embedded Android Test Project, so that
it can be run using `cd test/; ant clean emma debug install test`
It also changes -p to --path just to make things a little easier to read.
This pre-configures a file:// URI that points to the installed location of
the FDroid.apk. When users put two devices together, and touch the screen
on the device with FDroid on it, it will "beam" over the APK, and prompt
the user to install it.
To reproduce the crash:
0. click a fdroidrepo:// URI to bring up the "app repo" dialog
1. rotate the device
2. click back to make the keyboard go away
3. click back to make the dialog go away
4. click back on Manage Repos screen
5. boom!
Otherwise we get errors like this upon rotation:
"android.support.v4.app.Fragment$InstantiationException: Unable to
instantiate fragment org.fdroid.fdroid.RepoListFragment: make sure class
name exists, is public, and has an empty constructor that is public"
It is now possible to beam a repo config via NFC but just selecting the
repo in FDroid, then touching two NFC devices together, and clicking on the
FDroid one. There is no indication that NFC is off, so this commit adds a
menu item that makes it easy to enable the required NFC settings for
sending a repo to another device via NFC.
This is the framework for easily swapping repos. The idea is that a user
can send the URL with the fingerprint for trusted bootstrapping of the repo
on a new user's device. This will be essential for p2p repos provided
by Bazaar/Kerplapp.
The required NFC APIs were introduced in android-14. So android-14 and below
skip the NFC stuff.
this makes sure that the repo fingerprints are always going to have the
same case, no matter how they were added. Repo.fingerprint probably should
be converted to a BigInteger so that the comparison can be numeric rather
than String. Then when the fingerprint needs to be displayed, it can be
formatted appropriately.
This saves the currently selected category in the Available apps view, and
restores that category when the user returns to the Available screen. It
drives me totally nuts that it always forgets the category when I nav away
from that screen, always returning to What's New.
* If the info is taller than the icon, grow larger to fit it in
* Center icon vertically
* Move padding out of the header
* Revert some font sizes to how they were some time ago, a bit smaller
Yay!
As expected, a lot of the stuff in DB class is due to UpdateService
requiring it to process the downloaded indexes and insert data into
the database. Thus, this change is about removing that stuff from
the DB class and migrating to ContentProviders.
This required a bit of a change to the way that UpdateService decides
what to do with the data from indexes, but I hope it will make
understanding and changing UpdateService easier in the long term.
For example, it used to read the app details from database, then
if a repo wasn't updated (due to unchanged index) then it would take
the app details for that repo from the list of apps, and re-update
the database (or something like that).
Now, it has been refactored into the following methods:
* updateOrInsertApps(appsToUpdate);
* updateOrInsertApks(apksToUpdate);
* removeApksFromRepos(disabledRepos);
* removeApksNoLongerInRepo(appsToUpdate, updatedRepos);
* removeAppsWithoutApks();
* and probably some others...
Which hopefully are self-explanitory.
The recent change to implement single repo updates was re-implemented
with in light of the methods above. The interface to UpdateService for
scheduling a single repo update is the same as it was before, but
the implementation is completely different. Still works though.
Using batch content provider operations for repo updates,
but they suffer from the problem of not all being under the same
transaction, so if an insert/update stuffs up half way through, we
are left with only half of the update being complete. In the future,
if there is some way to implement notifications from the content provider's
applyBatch method, then we can do it all in the one transaction, and
still have notifications. Currently we break it into several calls
to applyBatch (and hence several transactions) to inform the user
of the progress.
Also adding the beginnings of some tests for AppProvider. In the future, I'll
work on adding better coverage, including instrumentation to test UI features.
==========================
Below is a list of many of the minor changes that also happened along the way
==========================
Make "Can update" tab stay up to date using content observer, rather
than manually deciding when to refresh the tab label as before.
The installed app list is now cached in Utils, because it is invoked
quite a few times, especially when rendering the app lists. The cache is
invalidated when PackageReceiver is notified of new apps.
The content providers don't notify changes if we are in batch mode.
I've left the notification at the end of the batch updates as the
responsibility of the UpdateService. However, it would be nice if this
was somehow handled by the content, as they are really the ones who
should worry about it.
Made curVersion, curVercode and curApk work with providers.
This was done by removing curApk (otherwise we'd need to query the db each
time we fetched one app to get a reference to that apk (resulting in hundreds
of queries). Instead, UpdateService now calculates curVercode and curVersion
and saves them to the database. We then use these where possible. If we really
need curApk (because we want info other than its version and code) we still have
the option of ApkProvider.Helper.find(app.id, app.curVercode). I considered
putting this inside the app value object, e.g. in getCurApk() but thought
better of it as it will likely result in people invoking it all the time,
without realising it causes a DB query.
incompatibleReasons required a minor UI tweak, removing the "min sdk"
ui element from the Apk list. It is replaced by the "Requires: %s" view
(which only appears when the app is incompatible). In the process, and in
response to some feedback from mvdan, I left the min sdk in there, but
only made it show when in "expert mode", just like the architecture.
In order to make the "installed apps" query work under test conditions,
needed to change the way the InstalledApkCache be replaceable with a
mock object.
Pause UIL loading on fast scroll of list, as the list was very choppy for
some reason.
Re-added "Last repo scan" info to the Manage Repo list view.
Fixed up some misc TODO's, removed some unused/empty functions.
Fixes issue #452. It turns out that recent versions of android do
this automatically, but my gingerbread emulator didn't.
In the process, I also refactored the getQuery method. It was previously
a void method that modified the state of the search view, which is a
bit counter-intuitive given it's name. Instead, I made it just a getter,
which calculates the query and returns it. That way, I was able to remove
mQuery from the fields in the search view. Generally, the less state we
need to worry about (e.g. fields in an object), the less assumptions we
need to make about whether that field has been set or not.
Solves issue # 453. Previously, it was reading one line at
a time of the index file. Turns out that the entire index is
only on about 5 lines, thus the 5th line is about 2mb. The
solution here is to switch to reading a fixed length of 4096
characters at a time rather than entire lines. This ends up
a bit more complex (because I wrote a custom tokenizer rather
than being able to use Java String methods such as indexOf).
There are still other memory issues on low memory devices,
to do with trying to parse ~1000 app value objects into memory,
each with numerous string objects associated with them. But
that particular issue will be solved in the future, with
the ContentProvider stuff.
A new repo can be added with only the fingerprint of the signing key, while
the regular tests are based on the entire public key (repo.pubkey). This
checks for the case when a repo only has the fingerprint and no pubkey yet.
In that case, it the pubkey presented by the index.jar file against the
stored fingerprint. If they match, then the whole pubkey in the index.jar
is stored.
The stored fingerprint is checked when a repo URI is received by FDroid to
prevent bad actors from overriding repo configs with other keys. So if the
fingerprint is not stored yet, calculate it and store it. If the fingerprint
is stored, then check it against the calculated fingerprint just to make sure
it is correct. If the fingerprint is empty, then store the calculated one.
This was in place before, but it needed to be updated for the new Repo
ContentProvider.
A repo URI can include info such as the current wifi SSID and BSSID that
the repo is hosted on. This is for when local repos are transmitted via
QRCode, NFC, etc. The receiver of this URI then checks to make sure it is
on the same wifi access point, and warns the user if not.
In the future, it should do more than just warn the user, but instead give
concrete actions for the user to take, like associating to that wifi.
Instead of ramming the fingerprint in the user field of the URI, use the
query string. Having the fingerprint in the user field was confusing some
browsers because it was trying to log in. The query string is the standard
place for such meta data, and has lots of room for expansion including
things like wifi network names. This will be useful later to determine if
both devices are currently on the same wifi network, and if they are local
repos, they should try syncing.
Its quite annoying to have the URI EditText in focus and the soft keyboard
pop up when viewing the RepoDetails since the vast majority of the time,
the user will be viewing the info there, not editing the URL.
This commit just moves the focus to the frame, and prevents the soft
keyboard from showing up by default. The user can still click on the URI
EditText to edit it and the soft keyboard will pop up.
URIs can come from clicking a web page, NFC transmission, QR Code scan, and
more. This code stops badly formed Uri strings from crashing F-Droid. It
then shows a Toast error message that it can't understand the incoming URI.
Android's scheme matcher is case-sensitive, so include
ALL CAPS versions to support ALL CAPS URLs in QR Codes.
QR Codes have a special ALL CAPS mode that uses a reduced
character set, making for more compact QR Codes.
This reverts:
We should not encourage all caps urls
2651b81792bdeed0db3c3960d0b7283536611012
The performance improvement from this will not be noticable (perhaps
there isn't one), however it is part of the bigger plan to move all of
the DB access to ContentProviders. This will make a big improvement to
the startup time of the app, given we are currently loading all of the
apps to populate the list of apps.
It will come at the cost of some apparantly weird code convensions. Most
notably, when loading data from a content provder, you only ask
for the fields that you intend to use. As a result of my Helper class which
converts results from the content providers cursor into Repo value objects,
there is no guarantee that certain attributes will be available on the
value object. E.g. if I load repos and only ask for "_ID" and "ADDRESS",
then it is meaningless to ask the resulting Repo object for its "VERSION"
(it wont be there), despite it being a perfectly legal attribute from
the Java compilers perspective.
Repo.id field has also been made private (sqlite is the only
entity should be able to set id's), and made id a long (sqlite stores
identifiers as longs rather than ints).
Now, clicking Update in "Repo Detail" only updates that repo, not all of
the repos. This will also be very useful for more transitory repos, like
p2p repos that are reached via bluetooth, local wifi, etc.
This keeps the same logic as was in place before, but splits out code from
onHandleIntent() so that it can be used to update specific repos, rather
than always updating all repos. This is needed for transient repos, like
p2p repos connected via bluetooth or local wifi.
Before, "Repo Details" always had a title of "Repositories". Now
it shows the name of the repo as the title. This also enables the
ActionBar back button (aka "display home as up") to return to
"Manage Repos".
In the new Repo Details screen, there is some elements that are labels as
the "signature". This is not quite right, it is actually referring to the
fingerprint of the repo signing key. Since a repo will also usually have a
HTTPS certificate fingerprint, there will also be a fingerprint for that
certificate.
In order to support F-droid repositories hosted with HTTPS using
a self-signed certificate the f-droid client should prompt the user to
trust or 'memorize' the certificate presented by a repository. The
MemorizingTrustManager[0] project enables easy integration of
a prompting activity and corresponding trust manager implementation.
This behaviour is useful to projects such as Kerplapp[1] that boostrap
an F-droid repository on a user's device where it isn't possible to
acquire a long lived CA vetted TLS certificate.
In addition to Trust-on-First-Use (TOFU) behaviour, this patch
integrates the PinningTrustManager [2] project by Moxie Marlinspike to
allow the FDroid client to ship a hardcoded set of Subject Public Key
Identifier pins [3] for the official FDroid repository TLS certificate,
and the Guardian Project TLS certificate. Additional pins can be added
to the FDroidPins.java class.
The upstream release of AndroidPinning by moxie0 uses a minsdk value of
8. The Fdroid client has a minsdk of 5, presenting compatibility issues
using the AndroidPinning lib as a submodule. Fortunately it seems there
is no technical reason preventing using a minSDK of 5 with
AndroidPinning. I have created a fork with this change and submitted
a pull req upstream. Until this pull is merged we can use my fork of
AndroidPinning as the submodule.
The new 'flow' for deciding if a repositories presented TLS certificate
should be trusted is as follows:
1) If the certificate was previously trusted by a TOFU action, then the
certificate is accepted as trusted
2) If the certificate wasn't previously trusted by a TOFU action but
there is a matching SPKI pin then the certificate is accepted as
trusted
3) If the certificate wasn't previously trusted by a TOFU action and
there is no SPKI pin but the certificate is signed by a trusted
Certificate Authority it is accepted as trusted (This is the
behaviour of the FDroid client prior to this patch with all other
conditions being a hard-fail).
4) If the certificate wasn't previously trusted by a TOFU action and
there is no SPKI pin and the certificate is not signed by a trusted
CA (i.e. self signed or otherwise) then the user is prompted to TOFU
the certificate. The user may choose to trust the certificate for the
current connection or forever. If the user chooses an option other
than "deny" the certificate is accepted as trusted for the specified
duration.
Users currently using a TLS protected repository will see *no
difference* in user experience after this patch is merged as the only
TLS protected repositories that would function prior to this patch were
providing certificates that match condition #3.
[0] https://github.com/ge0rg/MemorizingTrustManager/wiki/Integration
[1] https://github.com/guardianproject/kerplapp
[2] https://github.com/moxie0/AndroidPinning
[3] https://www.imperialviolet.org/2011/05/04/pinning.html
This will be required for ContentProviders, because the need
SQLiteOpenHelpers (or whatever they are called) in order to work,
and having it as a subclass of DB wouldn't quite work for that.
It's handier to have just the one resource version_name, but lint gets very
angry at this (and there must be a reason for that). We must not forget to
update the resource in res/values/no_trans.xml, since it's still used in the
code.
Rebased several months of work, and attempted to resolve any conflicts.
The conflicts were a tad more difficult than usual to resolve because they
were in files where large blocks of code were refactored into different
files, and git didn't realise.
Conflicts:
src/org/fdroid/fdroid/RepoXMLHandler.java
src/org/fdroid/fdroid/UpdateService.java
* Move it to the top right corner
* Don't let the app name overlap it
* Ellipsize version names to leave space for name
* Summary can now take two lines if it needs them
* Installed versions are now bold
When the repository is updated, it will check if the "name" or "description"
have been modified (or learnt for the first time) and if so, update the DB and UI.
Phew, monster merge. Going to commit after *seemingly* resolving
conflicts, but it will no doubt take a few compile and runs to sort out
any funny stuff.
Conflicts:
AndroidManifest.xml
res/layout/addrepo.xml
res/layout/appdetails.xml
res/layout/repolisticons.xml
res/values/strings.xml
src/org/fdroid/fdroid/DB.java
src/org/fdroid/fdroid/FDroid.java
src/org/fdroid/fdroid/ManageRepo.java
src/org/fdroid/fdroid/UpdateService.java
SwitchCompat will return a Switch or a ToggleButton depending on the
platform (doesn't matter, both are CompoundButtons) and this will be
added to the repo_item view programatically.
I'm using some pretty specific listeners
to communicate between the details fragment and the repo list activity.
I've also split the functionality (e.g. for deleting) between the repo
list and the details view. In the future, when we have content providers
for repos, it will be easier to take care of everything from the details
screen, and automatically notify the repo list of changes.
Refactored update service.
Now has a static update method that can be called which
will setup the required intent to begin the update. It also deals with
progress listeners and dialogs for the user, so all of this is moved out
of FDroid. This was so that RepoDetailsFragment can now invoke the same
functionality.
When a new repo is being added, whether manually or via an incoming Intent,
check the address and fingerprint against repos in the DB. If the repo is
not in the DB, offer to add it. If the repo address is in the DB, then do
more checks:
* If that address has no fingerprint in the DB, then offer to add the new
repo including that fingerprint. This might happen when upgrading a repo
from unsigned to signed.
* if the incoming info matches a repo in the DB, offer to enable that repo
* if the address matches a repo in the DB but the incoming fingerprint does
not match the fingerprint in the DB, warn the user, and tell them to
delete the existing repo if they truly want to override the existing info
the Eclipse Android mode loves to remove all trailing whitespaces quite
religiously. This commit just removes trailing whitespace. It was done
by running this:
sed -i 's/[[:space:]]*$//' *.java */*.java
With so many patterns being matched, it is highly likely that there will be
false positives, i.e. random URLs will trigger the prompt of whether to use
F-Droid or not. The updated set of patterns narrowly tailors the matches
so that it is highly unlikely to match URLs that are not fdroid repos, yet
still makes it useful both as a generic repo and a peer-to-peer
bootstrapping repo.
This set of patterns only matches URLs like this:
https://foo.org/fdroid/repohttps://foo.org/fdroid/repo/https://foo.org/fdroid/repo/////
It does not match URLs like this:
https://f-droid.org/repohttps://myblog.com/thoughts-about-my-lovely-fdroid-repohttps://news.com/tag/repohttps://somesite.com/repo/this-is-my-stuff
It matches multiple slashes to since those are in effect the same URL, and
they sometimes show up as typos. This does not include mvdan's proposal
for the 'fdroid-bootstrap' tag on the end because its not something that I
would use in this current project, so I don't know how best to apply it. I
have no objection to the 'fdroid-bootstrap' proposal.
The stored fingerprint is needed for comparing new, incoming repos that are
in the Add Repo dialog. This is to prevent malicious use of the automated
adding of repos via QR Codes, NFC, etc. The only other option that I could
think of for handling this situation is for the Add Repo dialog to open a
socket to the proposed repo to get its pubkey. That seems much less
desirable than just storing the fingerprints in the database.
When the fingerprint is generated to be stored in the database in the repo
table, make it a single String that is a hex number. This is a natural
format for working with the fingerprints programmatically. The display
formatting can then be handled by the display code, and can freely change
without affecting the underlying function of the code.
* a number of sources have said to avoid SHA-1 in new implementations
* nothing currently depends on the SHA-1 fingerprint in the code, it is
only used to display on the repo list.
* Java 7 requires SHA-256 to be included
* keytool -list -v shows the SHA-256 fingerprint
Everytime I save a java file with Android ADT, it adds @Override decorators
and throws errors if a method is called that is not supported in API 4. My
setup might be more sensitive since its setup with the official Android
style plugin for Eclipse. But the decorators are "correct" Java style, so
it would be nice to have them in F-Droid.
Previously, anything added via the Add New Repository dialog would just
overwrite any existing repo config that was there. This has become a
bigger issue with the QR Code scanning since it could become an attack
vector. This is the first step towards making this Add Repo dialog give
more info to the user about the state of things, and what the user might
replace by clicking OK.
This patch makes F-Droid register with Android that it accepts the URI
schemes of fdroidrepo (HTTP) and fdroidrepos(HTTPS). When F-Droid receives
one of these URIs, it launches the ManageRepo Activity and then launches
the New Repository dialog.
refs #2454
This allows us to add new ones without making a mess in the client.
Prior to this change it would add empty lines, and also if the only
antifeature was an unrecognised one, would enable the antifeature view
box but with nothing in it. It should now ignore them completely.
finish() may be called from reset() which sets app to null. This issues
hits for example when clicking on wiki-link of an app which doesn't
currenty exist in client index, throws:
E/AndroidRuntime(17630): java.lang.RuntimeException: Unable to start activity ComponentInfo{org.fdroid.fdroid/org.fdroid.fdroid.AppDetails}: java.lang.NullPointerException
E/AndroidRuntime(17630): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1651)
E/AndroidRuntime(17630): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1667)
E/AndroidRuntime(17630): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
E/AndroidRuntime(17630): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935)
E/AndroidRuntime(17630): at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(17630): at android.os.Looper.loop(Looper.java:130)
E/AndroidRuntime(17630): at android.app.ActivityThread.main(ActivityThread.java:3691)
E/AndroidRuntime(17630): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(17630): at java.lang.reflect.Method.invoke(Method.java:507)
E/AndroidRuntime(17630): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:907)
E/AndroidRuntime(17630): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:665)
E/AndroidRuntime(17630): at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(17630): Caused by: java.lang.NullPointerException
E/AndroidRuntime(17630): at org.fdroid.fdroid.AppDetails.finish(AppDetails.java:1012)
E/AndroidRuntime(17630): at org.fdroid.fdroid.AppDetails.reset(AppDetails.java:353)
E/AndroidRuntime(17630): at org.fdroid.fdroid.AppDetails.onCreate(AppDetails.java:243)
E/AndroidRuntime(17630): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
E/AndroidRuntime(17630): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1615)
Rather large rewrite, basically doing:
* Always show incompatible apps
* Don't fetch incompatible apks if the new setting is off
* Start using result codes when returning from PreferencesActivity
* Only change compact layout stuff when really needed
* No need to use StringBuilders in some cases
* No need to keep setting status and license to "" when in compact
They are more consistent now. Transitioning to RTL languages will also be
easier since more of them can be mirrored now. Also fixes some spacing issues
with icons and others.
Also, move visibility stuff from onResume to onCreate (they are only affected
by preferences, i.e. onCreate will always be run since the preferences button
is only in our main activity).
*@see<ahref="https://artemzin.com/blog/easiest-way-to-give-set_animation_scale-permission-for-your-ui-tests-on-android/>EASIEST WAY TO GIVE SET_ANIMATION_SCALE PERMISSION FOR YOUR UI TESTS ON ANDROID</a>
*@see<ahref="https://gist.github.com/xrigau/11284124>Disable animations for Espresso tests</a>
thrownewIllegalStateException(String.format(Locale.ENGLISH,"EOF reached while copying %s with %d bytes left to go",filename,compressedSize-totalCount));
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.