diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 09eeaf944..605135467 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -70,8 +70,7 @@ errorprone: - ./gradlew connectedCheck || (adb -e logcat -d '*:E' > logcat.txt; exit 1) connected24: - only: - - fdroid/fdroidclient@master + retry: 1 <<: *test-template variables: AVD_SDK: "24" @@ -79,10 +78,6 @@ connected24: AVD_PACKAGE: "system-images;android-${AVD_SDK};${AVD_TAG};armeabi-v7a" <<: *connected-template -connected25: - <<: *test-template - <<: *connected-template - deploy_nightly: stage: deploy only: diff --git a/app/src/full/java/org/fdroid/fdroid/views/main/WhatsNewViewBinder.java b/app/src/full/java/org/fdroid/fdroid/views/main/WhatsNewViewBinder.java index acb91388a..c53932349 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/main/WhatsNewViewBinder.java +++ b/app/src/full/java/org/fdroid/fdroid/views/main/WhatsNewViewBinder.java @@ -13,8 +13,9 @@ import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ProgressBar; import android.widget.TextView; - import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; import org.fdroid.fdroid.UpdateService; @@ -40,6 +41,8 @@ class WhatsNewViewBinder implements LoaderManager.LoaderCallbacks { private final TextView emptyState; private final RecyclerView appList; + private ProgressBar progressBar; + WhatsNewViewBinder(final AppCompatActivity activity, FrameLayout parent) { this.activity = activity; @@ -124,6 +127,19 @@ class WhatsNewViewBinder implements LoaderManager.LoaderCallbacks { } private void explainEmptyStateToUser() { + if (Preferences.get().isIndexNeverUpdated() && UpdateService.isUpdating()) { + if (progressBar != null) { + return; + } + LinearLayout linearLayout = (LinearLayout) appList.getParent(); + progressBar = new ProgressBar(activity, null, android.R.attr.progressBarStyleLarge); + progressBar.setId(R.id.progress_bar); + linearLayout.addView(progressBar); + emptyState.setVisibility(View.GONE); + appList.setVisibility(View.GONE); + return; + } + StringBuilder emptyStateText = new StringBuilder(); emptyStateText.append(activity.getString(R.string.latest__empty_state__no_recent_apps)); emptyStateText.append("\n\n"); diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java index 19c097646..640d3f6f1 100644 --- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java +++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java @@ -349,8 +349,9 @@ public class FDroidApp extends Application { } Preferences.setup(this); Languages.setLanguage(this); + Preferences preferences = Preferences.get(); - if (Preferences.get().promptToSendCrashReports()) { + if (preferences.promptToSendCrashReports()) { ACRA.init(this); if (isAcraProcess() || HidingManager.isHidden(this)) { return; @@ -359,16 +360,15 @@ public class FDroidApp extends Application { PRNGFixes.apply(); - curTheme = Preferences.get().getTheme(); - Preferences.get().configureProxy(); + curTheme = preferences.getTheme(); + preferences.configureProxy(); // bug specific to exactly 5.0 makes it only work with the old index // which includes an ugly, hacky workaround // https://gitlab.com/fdroid/fdroidclient/issues/1014 if (Build.VERSION.SDK_INT == 21) { - Preferences p = Preferences.get(); - p.setExpertMode(true); - p.setForceOldIndex(true); + preferences.setExpertMode(true); + preferences.setForceOldIndex(true); } InstalledAppProviderService.compareToPackageManager(this); @@ -376,7 +376,7 @@ public class FDroidApp extends Application { // If the user changes the preference to do with filtering rooted apps, // it is easier to just notify a change in the app provider, // so that the newly updated list will correctly filter relevant apps. - Preferences.get().registerAppsRequiringRootChangeListener(new Preferences.ChangeListener() { + preferences.registerAppsRequiringRootChangeListener(new Preferences.ChangeListener() { @Override public void onPreferenceChange() { getContentResolver().notifyChange(AppProvider.getContentUri(), null); @@ -386,14 +386,14 @@ public class FDroidApp extends Application { // If the user changes the preference to do with filtering anti-feature apps, // it is easier to just notify a change in the app provider, // so that the newly updated list will correctly filter relevant apps. - Preferences.get().registerAppsRequiringAntiFeaturesChangeListener(new Preferences.ChangeListener() { + preferences.registerAppsRequiringAntiFeaturesChangeListener(new Preferences.ChangeListener() { @Override public void onPreferenceChange() { getContentResolver().notifyChange(AppProvider.getContentUri(), null); } }); - Preferences.get().registerUnstableUpdatesChangeListener(new Preferences.ChangeListener() { + preferences.registerUnstableUpdatesChangeListener(new Preferences.ChangeListener() { @Override public void onPreferenceChange() { AppProvider.Helper.calcSuggestedApks(FDroidApp.this); @@ -403,7 +403,6 @@ public class FDroidApp extends Application { CleanCacheService.schedule(this); notificationHelper = new NotificationHelper(getApplicationContext()); - UpdateService.schedule(getApplicationContext()); bluetoothAdapter = getBluetoothAdapter(); // There are a couple things to pay attention to with this config: memory usage, @@ -452,21 +451,26 @@ public class FDroidApp extends Application { .build(); ImageLoader.getInstance().init(config); + if (preferences.isIndexNeverUpdated()) { + // force this check to ensure it starts fetching the index on initial runs + networkState = ConnectivityMonitorService.getNetworkState(this); + } ConnectivityMonitorService.registerAndStart(this); + UpdateService.schedule(getApplicationContext()); FDroidApp.initWifiSettings(); WifiStateChangeService.start(this, null); // if the HTTPS pref changes, then update all affected things - Preferences.get().registerLocalRepoHttpsListeners(new ChangeListener() { + preferences.registerLocalRepoHttpsListeners(new ChangeListener() { @Override public void onPreferenceChange() { WifiStateChangeService.start(getApplicationContext(), null); } }); - configureTor(Preferences.get().isTorEnabled()); + configureTor(preferences.isTorEnabled()); - if (Preferences.get().isKeepingInstallHistory()) { + if (preferences.isKeepingInstallHistory()) { InstallHistoryService.register(this); } @@ -492,7 +496,7 @@ public class FDroidApp extends Application { atStartTime.edit().putInt("build-version", Build.VERSION.SDK_INT).apply(); final String queryStringKey = "http-downloader-query-string"; - if (Preferences.get().sendVersionAndUUIDToServers()) { + if (preferences.sendVersionAndUUIDToServers()) { HttpDownloader.queryString = atStartTime.getString(queryStringKey, null); if (HttpDownloader.queryString == null) { UUID uuid = UUID.randomUUID(); diff --git a/app/src/main/java/org/fdroid/fdroid/Preferences.java b/app/src/main/java/org/fdroid/fdroid/Preferences.java index 68def793a..ce0413c7e 100644 --- a/app/src/main/java/org/fdroid/fdroid/Preferences.java +++ b/app/src/main/java/org/fdroid/fdroid/Preferences.java @@ -101,7 +101,6 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh public static final String PREF_PROXY_PORT = "proxyPort"; public static final String PREF_SHOW_NFC_DURING_SWAP = "showNfcDuringSwap"; public static final String PREF_POST_PRIVILEGED_INSTALL = "postPrivilegedInstall"; - public static final String PREF_TRIED_EMPTY_UPDATE = "triedEmptyUpdate"; public static final String PREF_PREVENT_SCREENSHOTS = "preventScreenshots"; public static final String PREF_PANIC_EXIT = "pref_panic_exit"; public static final String PREF_PANIC_HIDE = "pref_panic_hide"; @@ -114,10 +113,14 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh public static final int OVER_NETWORK_ON_DEMAND = 1; public static final int OVER_NETWORK_ALWAYS = 2; + // not shown in Settings + private static final String PREF_LAST_UPDATE_CHECK = "lastUpdateCheck"; + // these preferences are not listed in preferences.xml so the defaults are set here @SuppressWarnings("PMD.AvoidUsingHardCodedIP") public static final String DEFAULT_PROXY_HOST = "127.0.0.1"; // TODO move to preferences.xml public static final int DEFAULT_PROXY_PORT = 8118; // TODO move to preferences.xml + private static final int DEFAULT_LAST_UPDATE_CHECK = -1; private static final boolean DEFAULT_SHOW_NFC_DURING_SWAP = true; private static final boolean DEFAULT_POST_PRIVILEGED_INSTALL = false; private static final boolean DEFAULT_PANIC_EXIT = true; @@ -321,18 +324,23 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh } } - /** - * Used the first time F-Droid is installed to flag whether or not we have tried to request - * apps from the repo. This is used so that when there is no apps available, we can differentiate - * between whether the repos actually have no apps (in which case we don't need to continue - * asking), or whether there is no apps because we have never actually asked to update the repos. - */ - public boolean hasTriedEmptyUpdate() { - return preferences.getBoolean(PREF_TRIED_EMPTY_UPDATE, IGNORED_B); + public long getLastUpdateCheck() { + return preferences.getLong(PREF_LAST_UPDATE_CHECK, DEFAULT_LAST_UPDATE_CHECK); } - public void setTriedEmptyUpdate(boolean value) { - preferences.edit().putBoolean(PREF_TRIED_EMPTY_UPDATE, value).apply(); + public void setLastUpdateCheck(long lastUpdateCheck) { + preferences.edit().putLong(PREF_LAST_UPDATE_CHECK, lastUpdateCheck).apply(); + } + + public void resetLastUpdateCheck() { + setLastUpdateCheck(DEFAULT_LAST_UPDATE_CHECK); + } + + /** + * The first time the app has been run since fresh install or clearing all data. + */ + public boolean isIndexNeverUpdated() { + return getLastUpdateCheck() == DEFAULT_LAST_UPDATE_CHECK; } public boolean getUnstableUpdates() { diff --git a/app/src/main/java/org/fdroid/fdroid/UpdateService.java b/app/src/main/java/org/fdroid/fdroid/UpdateService.java index e87b7e725..2e41694fe 100644 --- a/app/src/main/java/org/fdroid/fdroid/UpdateService.java +++ b/app/src/main/java/org/fdroid/fdroid/UpdateService.java @@ -29,7 +29,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; @@ -41,7 +40,6 @@ import android.support.annotation.NonNull; import android.support.v4.app.JobIntentService; import android.support.v4.app.NotificationCompat; import android.support.v4.content.LocalBroadcastManager; -import android.support.v7.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; @@ -82,11 +80,11 @@ public class UpdateService extends JobIntentService { public static final int STATUS_ERROR_LOCAL_SMALL = 4; public static final int STATUS_INFO = 5; - private static final String STATE_LAST_UPDATED = "lastUpdateCheck"; private static final int JOB_ID = 0xfedcba; private static final int NOTIFY_ID_UPDATING = 0; + private static UpdateService updateService; private static Handler toastHandler; private NotificationManager notificationManager; @@ -118,11 +116,19 @@ public class UpdateService extends JobIntentService { } /** - * Add work to the queue for processing now + * Add work to the queue for processing now. + *

+ * This also shows a {@link Toast} if the Data/WiFi Settings make it so the + * update process is not allowed to run and the device is attached to a + * network (e.g. is not offline or in Airplane Mode). * * @see JobIntentService#enqueueWork(Context, Class, int, Intent) */ private static void enqueueWork(Context context, @NonNull Intent intent) { + if (FDroidApp.networkState > 0 && !Preferences.get().isOnDemandDownloadAllowed()) { + Toast.makeText(context, R.string.updates_disabled_by_settings, Toast.LENGTH_LONG).show(); + } + enqueueWork(context, UpdateService.class, JOB_ID, intent); } @@ -188,6 +194,8 @@ public class UpdateService extends JobIntentService { /** * Whether or not a repo update is currently in progress. Used to show feedback throughout * the app to users, so they know something is happening. + * + * @see set a global variable when it is running that your client can check */ public static boolean isUpdating() { return updateService != null; @@ -239,8 +247,6 @@ public class UpdateService extends JobIntentService { } - private static UpdateService updateService; - public static void stopNow(Context context) { if (updateService != null) { updateService.stopSelf(JOB_ID); @@ -494,10 +500,7 @@ public class UpdateService extends JobIntentService { } } - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); - SharedPreferences.Editor e = prefs.edit(); - e.putLong(STATE_LAST_UPDATED, System.currentTimeMillis()); - e.apply(); + fdroidPrefs.setLastUpdateCheck(System.currentTimeMillis()); if (errorRepos == 0) { if (changes) { diff --git a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java index ee39e383e..7e60661b9 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -1095,7 +1095,7 @@ public class DBHelper extends SQLiteOpenHelper { private static void resetTransient(SQLiteDatabase db) { Utils.debugLog(TAG, "Removing all index tables, they will be recreated next time F-Droid updates."); - Preferences.get().setTriedEmptyUpdate(false); + Preferences.get().resetLastUpdateCheck(); db.beginTransaction(); try { @@ -1147,7 +1147,7 @@ public class DBHelper extends SQLiteOpenHelper { return; } - Preferences.get().setTriedEmptyUpdate(false); + Preferences.get().resetLastUpdateCheck(); db.execSQL("drop table " + AppMetadataTable.NAME); db.execSQL("drop table " + ApkTable.NAME); diff --git a/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java b/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java index fcf90e1c2..888dd0aec 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java @@ -158,16 +158,9 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB bottomNavigation.selectTab(adapter.adapterPositionFromItemId(selectedMenuId)); } - /** - * The first time the app is run, we will have an empty app list. To deal with this, we will - * attempt to update with the default repo. However, if we have tried this at least once, then - * don't try to do it automatically again. - */ private void initialRepoUpdateIfRequired() { - Preferences prefs = Preferences.get(); - if (!prefs.hasTriedEmptyUpdate()) { + if (Preferences.get().isIndexNeverUpdated() && !UpdateService.isUpdating()) { Utils.debugLog(TAG, "We haven't done an update yet. Forcing repo update."); - prefs.setTriedEmptyUpdate(true); UpdateService.updateNow(this); } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4169e1415..60ff065fe 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -129,6 +129,7 @@ This often occurs with apps installed via Google Play or other sources, if they Download update for %1$d app. Download updates for %1$d apps. + All updates disabled by Data/WiFi Settings OK