diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index 479d53e49..b6121dd08 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -79,6 +79,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.TimeZone; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; @@ -96,6 +97,8 @@ public final class Utils { private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss", Locale.ENGLISH); + private static final TimeZone UTC = TimeZone.getTimeZone("Etc/GMT"); + private static final String[] FRIENDLY_SIZE_FORMAT = { "%.0f B", "%.0f KiB", "%.1f MiB", "%.2f GiB", }; @@ -583,6 +586,7 @@ public final class Utils { } Date result; try { + format.setTimeZone(UTC); result = format.parse(str); } catch (ArrayIndexOutOfBoundsException | NumberFormatException | ParseException e) { e.printStackTrace(); @@ -595,21 +599,34 @@ public final class Utils { if (date == null) { return fallback; } + format.setTimeZone(UTC); return format.format(date); } + /** + * Parses a date string into UTC time + */ public static Date parseDate(String str, Date fallback) { return parseDateFormat(DATE_FORMAT, str, fallback); } + /** + * Formats UTC time into a date string + */ public static String formatDate(Date date, String fallback) { return formatDateFormat(DATE_FORMAT, date, fallback); } + /** + * Parses a date/time string into UTC time + */ public static Date parseTime(String str, Date fallback) { return parseDateFormat(TIME_FORMAT, str, fallback); } + /** + * Formats UTC time into a date/time string + */ public static String formatTime(Date date, String fallback) { return formatDateFormat(TIME_FORMAT, date, fallback); } diff --git a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java index 18ac2d62d..32edd6e47 100644 --- a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java +++ b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java @@ -10,6 +10,8 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import java.io.File; +import java.util.Date; +import java.util.TimeZone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -77,9 +79,9 @@ public class UtilsTest { assertEquals("three", tripleValue[2]); assertNull(Utils.serializeCommaSeparatedString(null)); - assertNull(Utils.serializeCommaSeparatedString(new String[] {})); - assertEquals("Single", Utils.serializeCommaSeparatedString(new String[] {"Single"})); - assertEquals("One,TWO,three", Utils.serializeCommaSeparatedString(new String[] {"One", "TWO", "three"})); + assertNull(Utils.serializeCommaSeparatedString(new String[]{})); + assertEquals("Single", Utils.serializeCommaSeparatedString(new String[]{"Single"})); + assertEquals("One,TWO,three", Utils.serializeCommaSeparatedString(new String[]{"One", "TWO", "three"})); } @Test @@ -192,4 +194,25 @@ public class UtilsTest { } // TODO write tests that work with a Certificate + @Test + public void testIndexDatesWithTimeZones() { + for (int h = 0; h < 12; h++) { + for (int m = 0; m < 60; m = m + 15) { + TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT+%d%02d", h, m))); + + String timeString = "2017-11-27_20:13:24"; + Date time = Utils.parseTime(timeString, null); + assertEquals("The String representation must match", timeString, Utils.formatTime(time, null)); + assertEquals(timeString + " failed to parse", 1511813604000L, time.getTime()); + assertEquals("time zones should match", -((h * 60) + m), time.getTimezoneOffset()); + + TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT+%d%02d", h, m))); + String dateString = "2017-11-27"; + Date date = Utils.parseDate(dateString, null); + assertEquals("The String representation must match", dateString, Utils.formatDate(date, null)); + assertEquals(dateString + " failed to parse", 1511740800000L, date.getTime()); + assertEquals("time zones should match", -((h * 60) + m), date.getTimezoneOffset()); + } + } + } } diff --git a/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java index e012cc079..e063e409b 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java @@ -7,10 +7,12 @@ import android.net.Uri; import org.fdroid.fdroid.Assert; import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.TestUtils; +import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Schema.ApkTable.Cols; import org.fdroid.fdroid.data.Schema.RepoTable; import org.fdroid.fdroid.mock.MockApk; import org.fdroid.fdroid.mock.MockRepo; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -18,6 +20,7 @@ import org.robolectric.annotation.Config; import java.util.Date; import java.util.List; +import java.util.TimeZone; import static org.fdroid.fdroid.Assert.assertCantDelete; import static org.fdroid.fdroid.Assert.assertResultCount; @@ -34,6 +37,13 @@ public class ApkProviderTest extends FDroidProviderTest { private static final String[] PROJ = Cols.ALL; + @BeforeClass + public static void setRandomTimeZone() { + TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d", + System.currentTimeMillis() % 12, System.currentTimeMillis() % 60))); + System.out.println("TIME ZONE for this test: " + TimeZone.getDefault()); + } + @Test public void testAppApks() { App fdroidApp = insertApp(context, "org.fdroid.fdroid", "F-Droid"); @@ -153,7 +163,7 @@ public class ApkProviderTest extends FDroidProviderTest { @Test public void testCount() { - String[] projectionCount = new String[] {Cols._COUNT}; + String[] projectionCount = new String[]{Cols._COUNT}; for (int i = 0; i < 13; i++) { Assert.insertApk(context, "com.example", i); @@ -315,12 +325,13 @@ public class ApkProviderTest extends FDroidProviderTest { assertNull(apk.added); assertNull(apk.hashType); - apk.antiFeatures = new String[] {"KnownVuln", "Other anti feature"}; - apk.features = new String[] {"one", "two", "three" }; - long dateTimestamp = System.currentTimeMillis(); - apk.added = new Date(dateTimestamp); + apk.antiFeatures = new String[]{"KnownVuln", "Other anti feature"}; + apk.features = new String[]{"one", "two", "three"}; apk.hashType = "i'm a hash type"; + Date testTime = Utils.parseDate(Utils.formatTime(new Date(System.currentTimeMillis()), null), null); + apk.added = testTime; + ApkProvider.Helper.update(context, apk); // Should not have inserted anything else, just updated the already existing apk. @@ -340,9 +351,10 @@ public class ApkProviderTest extends FDroidProviderTest { assertArrayEquals(new String[]{"KnownVuln", "Other anti feature"}, updatedApk.antiFeatures); assertArrayEquals(new String[]{"one", "two", "three"}, updatedApk.features); - assertEquals(new Date(dateTimestamp).getYear(), updatedApk.added.getYear()); - assertEquals(new Date(dateTimestamp).getMonth(), updatedApk.added.getMonth()); - assertEquals(new Date(dateTimestamp).getDay(), updatedApk.added.getDay()); + assertEquals(testTime.getYear(), updatedApk.added.getYear()); + assertEquals(testTime.getYear(), updatedApk.added.getYear()); + assertEquals(testTime.getMonth(), updatedApk.added.getMonth()); + assertEquals(testTime.getDay(), updatedApk.added.getDay()); assertEquals("i'm a hash type", updatedApk.hashType); } @@ -381,8 +393,8 @@ public class ApkProviderTest extends FDroidProviderTest { assertEquals("a hash type", apk.hashType); String[] projection = { - Cols.Package.PACKAGE_NAME, - Cols.HASH, + Cols.Package.PACKAGE_NAME, + Cols.HASH, }; Apk apkLessFields = ApkProvider.Helper.findApkFromAnyRepo(context, "com.example", 11, null, projection); diff --git a/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java index 113d27920..5c5495d7e 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java @@ -11,6 +11,7 @@ import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.TestUtils; import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -19,6 +20,7 @@ import org.robolectric.shadows.ShadowContentResolver; import java.util.ArrayList; import java.util.List; +import java.util.TimeZone; import static org.fdroid.fdroid.Assert.assertContainsOnly; import static org.fdroid.fdroid.Assert.assertResultCount; @@ -36,6 +38,13 @@ public class AppProviderTest extends FDroidProviderTest { private static final String[] PROJ = Cols.ALL; + @BeforeClass + public static void setRandomTimeZone() { + TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d", + System.currentTimeMillis() % 12, System.currentTimeMillis() % 60))); + System.out.println("TIME ZONE for this test: " + TimeZone.getDefault()); + } + @Before public void setup() { TestUtils.registerContentProvider(AppProvider.getAuthority(), AppProvider.class); diff --git a/app/src/test/java/org/fdroid/fdroid/data/RepoProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/RepoProviderTest.java index 8ddefdfaa..de6326e4f 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/RepoProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/RepoProviderTest.java @@ -30,6 +30,7 @@ import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Schema.RepoTable; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -37,6 +38,7 @@ import org.robolectric.annotation.Config; import java.util.Date; import java.util.List; +import java.util.TimeZone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -48,6 +50,16 @@ public class RepoProviderTest extends FDroidProviderTest { private static final String[] COLS = RepoTable.Cols.ALL; + /** + * Set to random time zone to make sure that the dates are properly parsed. + */ + @BeforeClass + public static void setRandomTimeZone() { + TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d", + System.currentTimeMillis() % 12, System.currentTimeMillis() % 60))); + System.out.println("TIME ZONE for this test: " + TimeZone.getDefault()); + } + @Test public void countEnabledRepos() { diff --git a/app/src/test/java/org/fdroid/fdroid/data/RepoXMLHandlerTest.java b/app/src/test/java/org/fdroid/fdroid/data/RepoXMLHandlerTest.java index 1dda8e339..2754014d0 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/RepoXMLHandlerTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/RepoXMLHandlerTest.java @@ -29,6 +29,7 @@ import org.apache.commons.io.FileUtils; import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.mock.MockRepo; import org.fdroid.fdroid.mock.RepoDetails; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -48,6 +49,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TimeZone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -63,6 +65,16 @@ public class RepoXMLHandlerTest { private static final String FAKE_SIGNING_CERT = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"; // NOCHECKSTYLE LineLength + /** + * Set to random time zone to make sure that the dates are properly parsed. + */ + @BeforeClass + public static void setRandomTimeZone() { + TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d", + System.currentTimeMillis() % 12, System.currentTimeMillis() % 60))); + System.out.println("TIME ZONE for this test: " + TimeZone.getDefault()); + } + @Test public void testExtendedPerms() throws IOException { Repo expectedRepo = new Repo(); @@ -129,6 +141,12 @@ public class RepoXMLHandlerTest { "org.gege.caldavsyncadapter", "info.guardianproject.checkey", }); + for (App app : actualDetails.apps) { + if ("org.mozilla.firefox".equals(app.packageName)) { + assertEquals(1411776000000L, app.added.getTime()); + assertEquals(1411862400000L, app.lastUpdated.getTime()); + } + } } @Test(expected = IllegalArgumentException.class) @@ -897,7 +915,7 @@ public class RepoXMLHandlerTest { List apps = actualDetails.apps; assertNotNull(apps); assertEquals(apps.size(), appCount); - for (App app: apps) { + for (App app : apps) { assertTrue("Added should have been set", app.added.getTime() > 0); assertTrue("Last Updated should have been set", app.lastUpdated.getTime() > 0); } diff --git a/app/src/test/resources/smallRepo.xml b/app/src/test/resources/smallRepo.xml index 431de6335..d8f7593bd 100644 --- a/app/src/test/resources/smallRepo.xml +++ b/app/src/test/resources/smallRepo.xml @@ -1,4 +1,4 @@ -A local FDroid repo generated from apps installed on Android-Nexus-7-20139453org.mozilla.firefox2014-09-272014-09-27Firefox(installed by null)<p>(installed by null, first installed on Sat Sep 27 23:47:05 EDT 2014, last updated on Sat Sep 27 23:47:05 EDT 2014)</p>org.mozilla.firefox_2014092317.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-2013945332.0.3201409231732.0.32014092317org.mozilla.firefox_2014092317.apk4b4e642b71acfe217758bb12ae7dec7fe46027ee732f4a9775ef7a4107deb5fb20e61aee1b748061ec3b0ab1bbe5bac43083163392014-09-27RECEIVE_BOOT_COMPLETED,org.mozilla.firefox.permission.PER_ANDROID_PACKAGE,GET_ACCOUNTS,ACCESS_NETWORK_STATE,MANAGE_ACCOUNTS,USE_CREDENTIALS,AUTHENTICATE_ACCOUNTS,WRITE_SYNC_SETTINGS,WRITE_SETTINGS,READ_SYNC_STATS,READ_SYNC_SETTINGS,org.mozilla.firefox_fxaccount.permission.PER_ACCOUNT_TYPE,org.mozilla.firefox_sync.permission.PER_ACCOUNT_TYPE,ACCESS_FINE_LOCATION,INTERNET,WRITE_EXTERNAL_STORAGE,com.android.launcher.permission.INSTALL_SHORTCUT,com.android.launcher.permission.UNINSTALL_SHORTCUT,com.android.browser.permission.READ_HISTORY_BOOKMARKS,WAKE_LOCK,VIBRATE,org.mozilla.firefox.permissions.PASSWORD_PROVIDER,org.mozilla.firefox.permissions.BROWSER_PROVIDER,org.mozilla.firefox.permissions.FORMHISTORY_PROVIDER,NFC,RECORD_AUDIO,CAMERA,READ_EXTERNAL_STORAGEcom.koushikdutta.superuser2014-09-272014-09-27Superuser(installed by null)<p>(installed by null, first installed on Sat Sep 27 23:23:22 EDT 2014, last updated on Sat Sep 27 23:23:22 EDT 2014)</p>com.koushikdutta.superuser_1030.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394531.0.3.010301.0.3.01030com.koushikdutta.superuser_1030.apk8bbe1c0aa307a0c689d0b97ea5123f6d74d42bb9f91cbeaac2583b23de3a77ab9af2c721bb87900f7e386d7a3716a7a6294460282014-09-27ACCESS_SUPERUSER,RECEIVE_BOOT_COMPLETEDinfo.guardianproject.courier2014-10-032014-10-03CourierCourier is a mobile RSS news reader with<p>Courier is a mobile RSS news reader with enhanced security features, offline reading and sharing capability. +A local FDroid repo generated from apps installed on Android-Nexus-7-20139453org.mozilla.firefox2014-09-272014-09-28Firefox(installed by null)<p>(installed by null, first installed on Sat Sep 27 23:47:05 EDT 2014, last updated on Sat Sep 27 23:47:05 EDT 2014)</p>org.mozilla.firefox_2014092317.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-2013945332.0.3201409231732.0.32014092317org.mozilla.firefox_2014092317.apk4b4e642b71acfe217758bb12ae7dec7fe46027ee732f4a9775ef7a4107deb5fb20e61aee1b748061ec3b0ab1bbe5bac43083163392014-09-27RECEIVE_BOOT_COMPLETED,org.mozilla.firefox.permission.PER_ANDROID_PACKAGE,GET_ACCOUNTS,ACCESS_NETWORK_STATE,MANAGE_ACCOUNTS,USE_CREDENTIALS,AUTHENTICATE_ACCOUNTS,WRITE_SYNC_SETTINGS,WRITE_SETTINGS,READ_SYNC_STATS,READ_SYNC_SETTINGS,org.mozilla.firefox_fxaccount.permission.PER_ACCOUNT_TYPE,org.mozilla.firefox_sync.permission.PER_ACCOUNT_TYPE,ACCESS_FINE_LOCATION,INTERNET,WRITE_EXTERNAL_STORAGE,com.android.launcher.permission.INSTALL_SHORTCUT,com.android.launcher.permission.UNINSTALL_SHORTCUT,com.android.browser.permission.READ_HISTORY_BOOKMARKS,WAKE_LOCK,VIBRATE,org.mozilla.firefox.permissions.PASSWORD_PROVIDER,org.mozilla.firefox.permissions.BROWSER_PROVIDER,org.mozilla.firefox.permissions.FORMHISTORY_PROVIDER,NFC,RECORD_AUDIO,CAMERA,READ_EXTERNAL_STORAGEcom.koushikdutta.superuser2014-09-272014-09-27Superuser(installed by null)<p>(installed by null, first installed on Sat Sep 27 23:23:22 EDT 2014, last updated on Sat Sep 27 23:23:22 EDT 2014)</p>com.koushikdutta.superuser_1030.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394531.0.3.010301.0.3.01030com.koushikdutta.superuser_1030.apk8bbe1c0aa307a0c689d0b97ea5123f6d74d42bb9f91cbeaac2583b23de3a77ab9af2c721bb87900f7e386d7a3716a7a6294460282014-09-27ACCESS_SUPERUSER,RECEIVE_BOOT_COMPLETEDinfo.guardianproject.courier2014-10-032014-10-03CourierCourier is a mobile RSS news reader with<p>Courier is a mobile RSS news reader with enhanced security features, offline reading and sharing capability. (installed by F-Droid, first installed on Fri Oct 03 09:12:04 EDT 2014, last updated on Fri Oct 03 09:12:04 EDT 2014)</p>info.guardianproject.courier_15.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394530.1.9150.1.915info.guardianproject.courier_15.apkbf6566da1f90831887f5bf5605f8d816b1f7f694969459dec599b8bc01a827d3d70ac6a02b53ebdd1354ea7af7b9ceee1648475392014-10-03INTERNET,ACCESS_NETWORK_STATE,WRITE_EXTERNAL_STORAGE,ACCESS_WIFI_STATE,BLUETOOTH,BLUETOOTH_ADMIN,VIBRATE,READ_EXTERNAL_STORAGEorg.adaway2014-09-292014-09-29AdAway(installed by F-Droid)<p>(installed by F-Droid, first installed on Mon Sep 29 08:31:13 EDT 2014, last updated on Mon Sep 29 08:31:13 EDT 2014)</p>org.adaway_50.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394532.9.1502.9.150org.adaway_50.apkc9f4fcdca5e47abecfe8bc529ba6299f242fce1d5c87e50b8a36ace82d4fc2595292cd7cf993d06d6f6741117b8400fc281687772014-09-29INTERNET,ACCESS_NETWORK_STATE,RECEIVE_BOOT_COMPLETED,WRITE_EXTERNAL_STORAGE,WAKE_LOCK,ACCESS_SUPERUSER,READ_EXTERNAL_STORAGEinfo.guardianproject.gilga2014-10-042014-10-07Gilgamesh(installed by F-Droid)<p>(installed by F-Droid, first installed on Sat Oct 04 14:59:10 EDT 2014, last updated on Tue Oct 07 09:49:14 EDT 2014)</p>info.guardianproject.gilga_2.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394530.0.222info.guardianproject.gilga_2.apk1eac2e0043977dc8f31947f19852803207205887a5f01a1cbb8a5e477b3d82a96de574cd61a684bda38ba8e895dece7c30761142014-10-04BLUETOOTH_ADMIN,BLUETOOTH,ACCESS_WIFI_STATE,CHANGE_WIFI_STATE,INTERNETcom.google.zxing.client.android2014-09-272014-09-27Barcode Scanner(installed by null)<p>(installed by null, first installed on Sat Sep 27 23:36:20 EDT 2014, last updated on Sat Sep 27 23:36:20 EDT 2014)</p>com.google.zxing.client.android_100.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394534.7.01004.7.0100com.google.zxing.client.android_100.apk62dea7cf201d2e36725ad76b1525decb58b05dd9e4a032ed084c7a6c6d38da2cc3bb49636af03e7c23a63724eb529ea9735678152014-09-27CAMERA,INTERNET,VIBRATE,FLASHLIGHT,READ_CONTACTS,com.android.browser.permission.READ_HISTORY_BOOKMARKS,WRITE_EXTERNAL_STORAGE,CHANGE_WIFI_STATE,ACCESS_WIFI_STATE,READ_EXTERNAL_STORAGEinfo.guardianproject.lildebi2014-09-282014-09-28Lil' DebiWant an easy Debian chroot running that <p>Want an easy Debian chroot running that you can trust? Install Lil’ Debi, and you can have a Debian install running with a single click of a button. It builds up a whole Debian chroot on your phone entirely using debootstrap. You choose the release, mirror, and size of the disk image, and away it goes. It could take up to an hour on a slow device, then its done. The entire package is built from source using publicly available, repeatable builds. It even validates every package using the official Debian repository keys, which are included in the app. (installed by null, first installed on Sun Sep 28 00:39:09 EDT 2014, last updated on Sun Sep 28 00:39:09 EDT 2014)</p>info.guardianproject.lildebi_5100.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394530.5.151000.5.15100info.guardianproject.lildebi_5100.apk7af667881af6107f3fe60d6973712506dbaf83710d383eb1c312fc75dae2e46ab4964fd759edaa54e65bb476d0276880156948082014-09-28INTERNET,ACCESS_NETWORK_STATE,ACCESS_SUPERUSER,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE,WAKE_LOCK,jackpal.androidterm.permission.RUN_SCRIPTde.danoeh.antennapod2014-10-032014-10-03AntennaPod(installed by F-Droid)<p>(installed by F-Droid, first installed on Fri Oct 03 09:08:31 EDT 2014, last updated on Fri Oct 03 09:08:31 EDT 2014)</p>de.danoeh.antennapod_40.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394530.9.9.3400.9.9.340de.danoeh.antennapod_40.apkea83cb81c03bc8b6c1fb6e61e7d0915abef8ca175789a5ac5add45c12f633ebd3870fa89bfb5e13509ce5888800bc6432815685102014-10-03INTERNET,WRITE_EXTERNAL_STORAGE,WAKE_LOCK,ACCESS_NETWORK_STATE,ACCESS_WIFI_STATE,RECEIVE_BOOT_COMPLETED,READ_EXTERNAL_STORAGEinfo.guardianproject.otr.app.im2014-09-272014-09-27ChatSecure(installed by F-Droid)<p>(installed by F-Droid, first installed on Sat Sep 27 23:42:00 EDT 2014, last updated on Sat Sep 27 23:42:00 EDT 2014)</p>info.guardianproject.otr.app.im_1403000.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-2013945314.0.3140300014.0.31403000info.guardianproject.otr.app.im_1403000.apk46b0b26e2122a8bf937be259d6219ffe5bace7b2eda692b43a5f55fff2322e13a0eeebb161f946e3516945fae8a92a3e1123585292014-09-27info.guardianproject.otr.app.providers.imps.permission.READ_ONLY,info.guardianproject.otr.app.providers.imps.permission.WRITE_ONLY,WAKE_LOCK,VIBRATE,INTERNET,CHANGE_WIFI_MULTICAST_STATE,ACCESS_WIFI_STATE,ACCESS_NETWORK_STATE,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE,RECEIVE_BOOT_COMPLETED,info.guardianproject.otr.app.im.permission.IM_SERVICE,UPDATE_APP_OPS_STATS,GET_ACCOUNTS,MANAGE_ACCOUNTS,USE_CREDENTIALS,com.google.android.googleapps.permission.GOOGLE_AUTHorg.torproject.android2014-09-272014-09-27OrbotOrbot is a free proxy app that empowers <p>Orbot is a free proxy app that empowers other apps to use the internet more securely. Orbot uses Tor to encrypt your Internet traffic and then hides it by bouncing through a series of computers around the world. Tor is free software and an open network that helps you defend against a form of network surveillance that threatens personal freedom and privacy, confidential business activities and relationships, and state security known as traffic analysis. (installed by null, first installed on Sat Sep 27 23:39:38 EDT 2014, last updated on Sat Sep 27 23:39:38 EDT 2014)</p>org.torproject.android_124.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-2013945314.0.8.112414.0.8.1124org.torproject.android_124.apk74daa523cd9e85722a9190dd95f3b839d69b7457ffcb9647ca6745824f6ef66d8bd7e51b479aeba908ff46ada3305a29554834792014-09-27INTERNET,RECEIVE_BOOT_COMPLETED,ACCESS_NETWORK_STATE,ACCESS_SUPERUSER,org.torproject.android.MANAGE_TORorg.gege.caldavsyncadapter2014-10-032014-10-03CalDAV Sync Adapter(installed by F-Droid)<p>(installed by F-Droid, first installed on Fri Oct 03 09:07:38 EDT 2014, last updated on Fri Oct 03 09:07:38 EDT 2014)</p>org.gege.caldavsyncadapter_18.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394531.8.1181.8.118org.gege.caldavsyncadapter_18.apk3fba34579260dd8f4290a0f68c7526e12385901d52d81267751336e81e2f654a9767040018038872ff3f651a32969d77796278142014-10-03AUTHENTICATE_ACCOUNTS,READ_CALENDAR,WRITE_CALENDAR,INTERNETinfo.guardianproject.checkey2014-09-272014-09-27Checkey(installed by F-Droid)<p>(installed by F-Droid, first installed on Sat Sep 27 23:44:13 EDT 2014, last updated on Sat Sep 27 23:44:21 EDT 2014)</p>info.guardianproject.checkey_1.pngUnknownLocalRepo,Android-Nexus-7-20139453LocalRepo,Android-Nexus-7-201394530.110.11info.guardianproject.checkey_1.apka8e3c102d5279a3029d0eebdeda2ffdbe1f8a3493ea7dbdc31a11affc708ee57d70ac6a02b53ebdd1354ea7af7b9ceee87867982014-09-27INTERNET \ No newline at end of file