fdroid-client/app/src/main/java/org/fdroid/fdroid/data/App.java

1460 lines
57 KiB
Java
Raw Normal View History

Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
package org.fdroid.fdroid.data;
import android.content.ContentValues;
import android.content.Context;
2015-04-06 22:16:51 +02:00
import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
import android.database.Cursor;
2022-01-19 19:07:46 +01:00
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
2022-01-19 19:07:46 +01:00
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestBuilder;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.io.filefilter.RegexFileFilter;
2022-01-19 19:07:46 +01:00
import org.fdroid.download.DownloadRequest;
import org.fdroid.fdroid.Preferences;
2021-10-30 14:09:37 +02:00
import org.fdroid.fdroid.R;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
2015-04-06 22:16:51 +02:00
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.os.ConfigurationCompat;
import androidx.core.os.LocaleListCompat;
/**
* Represents an application, its availability, and its current installed state.
* This represents the app in general, for a specific version of this app, see
* {@link Apk}.
* <p>
* <b>Do not rename these instance variables without careful consideration!</b>
* They are mapped to JSON field names, the {@code fdroidserver} internal variable
* names, and the {@code fdroiddata} YAML field names. Only the instance variables
* decorated with {@code @JsonIgnore} are not directly mapped.
* <p>
* <b>NOTE:</b>If an instance variable is only meant for internal state, and not for
* representing data coming from the server, then it must also be decorated with
* {@code @JsonIgnore} to prevent abuse! The tests for
* {@link org.fdroid.fdroid.IndexV1Updater} will also have to be updated.
*
* @see <a href="https://gitlab.com/fdroid/fdroiddata">fdroiddata</a>
* @see <a href="https://gitlab.com/fdroid/fdroidserver">fdroidserver</a>
*/
public class App extends ValueObject implements Comparable<App>, Parcelable {
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
@JsonIgnore
private static final String TAG = "App";
/**
* {@link LocaleListCompat} for finding the right app description material.
* It is set globally static to a) cache this value, since there are thousands
* of {@link App} entries, and b) make it easy to test {@link #setLocalized(Map)} )}
*/
@JsonIgnore
public static LocaleListCompat systemLocaleList;
// these properties are not from the index metadata, but represent the state on the device
/**
* True if compatible with the device (i.e. if at least one apk is)
*/
@JsonIgnore
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public boolean compatible;
@JsonIgnore
public Apk installedApk; // might be null if not installed
@JsonIgnore
public String installedSig;
@JsonIgnore
public int installedVersionCode;
@JsonIgnore
public String installedVersionName;
@JsonIgnore
private long id;
@JsonIgnore
private AppPrefs prefs;
@JsonIgnore
@NonNull
public String preferredSigner;
@JsonIgnore
public boolean isApk;
/**
* Has this {@code App} been localized into one of the user's current locales.
*/
@JsonIgnore
boolean isLocalized;
2017-07-05 09:18:51 +02:00
/**
* This is primarily for the purpose of saving app metadata when parsing an index.xml file.
* At most other times, we don't particularly care which repo an {@link App} object came from.
* It is pretty much transparent, because the metadata will be populated from the repo with
* the highest priority. The UI doesn't care normally _which_ repo provided the metadata.
* This is required for getting the full URL to the various graphics and screenshots.
*/
@JacksonInject("repoId")
public long repoId;
// the remaining properties are set directly from the index metadata
public String packageName = "unknown";
public String name = "Unknown";
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public String summary = "Unknown application";
2020-02-21 17:16:07 +01:00
@JsonProperty("icon")
public String iconFromApk;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public String description;
/**
* A descriptive text for what has changed in this version.
*/
public String whatsNew;
public String featureGraphic;
public String promoGraphic;
public String tvBanner;
public String[] phoneScreenshots = new String[0];
public String[] sevenInchScreenshots = new String[0];
public String[] tenInchScreenshots = new String[0];
public String[] tvScreenshots = new String[0];
public String[] wearScreenshots = new String[0];
public String license;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public String authorName;
public String authorEmail;
public String webSite;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public String issueTracker;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public String sourceCode;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public String translation;
public String video;
public String changelog;
2015-06-04 11:57:16 +02:00
public String donate;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public String bitcoin;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public String litecoin;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public String flattrID;
public String liberapay;
public String openCollective;
/**
* This matches {@code CurrentVersion} in build metadata files.
*
* @see <a href="https://f-droid.org/docs/Build_Metadata_Reference/#CurrentVersion">CurrentVersion</a>
*/
public String suggestedVersionName;
/**
* This matches {@code CurrentVersionCode} in build metadata files. Java
* inits {@code int}s to 0. Since it is valid to have a negative Version
* Code, this is inited to {@link Integer#MIN_VALUE};
*
* @see <a href="https://f-droid.org/docs/Build_Metadata_Reference/#CurrentVersionCode">CurrentVersionCode</a>
*/
public int suggestedVersionCode = Integer.MIN_VALUE;
/**
* Unlike other public fields, this is only accessible via a getter, to
* emphasise that setting it wont do anything. In order to change this,
* you need to change {@link #autoInstallVersionCode} to an APK which is
* in the {@link Schema.ApkTable} table.
*/
private String autoInstallVersionName;
/**
* The version that will be automatically installed if the user does not
* choose a specific version.
* TODO this should probably be converted to init to {@link Integer#MIN_VALUE} like {@link #suggestedVersionCode}
*/
public int autoInstallVersionCode;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public Date added;
public Date lastUpdated;
/**
* List of categories (as defined in the metadata documentation) or null if there aren't any.
* This is only populated when parsing a repository. If you need to know about the categories
* an app is in any other part of F-Droid, use the {@link CategoryProvider}.
*/
public String[] categories;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
/**
* List of anti-features (as defined in the metadata documentation) or null if there aren't any.
*/
public String[] antiFeatures;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
/**
* Requires root access (only ever used for root)
*/
@Deprecated
public String[] requirements;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
/**
* URL to download the app's icon. (Set only from localized block, see also
* {@link #iconFromApk} and {@link #getIconUrl(Context)}
*/
private String iconUrl;
public static String getIconName(String packageName, int versionCode) {
return packageName + "_" + versionCode + ".png";
}
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
@Override
public int compareTo(@NonNull App app) {
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
return name.compareToIgnoreCase(app.name);
}
public App() {
}
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public App(final Cursor cursor) {
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
checkCursorPosition(cursor);
final int cursorColumnCount = cursor.getColumnCount();
for (int i = 0; i < cursorColumnCount; i++) {
final String n = cursor.getColumnName(i);
switch (n) {
Change join from `app` to `apk` table to auto increment integers rather than Android package name strings. 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.
2016-06-23 03:30:01 +02:00
case Cols.ROW_ID:
id = cursor.getLong(i);
break;
case Cols.REPO_ID:
repoId = cursor.getLong(i);
break;
case Cols.IS_COMPATIBLE:
2015-10-08 21:41:38 +02:00
compatible = cursor.getInt(i) == 1;
break;
case Cols.Package.PACKAGE_NAME:
2015-12-20 21:07:59 +01:00
packageName = cursor.getString(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.NAME:
2015-10-08 21:41:38 +02:00
name = cursor.getString(i);
break;
case Cols.SUMMARY:
2015-10-08 21:41:38 +02:00
summary = cursor.getString(i);
break;
case Cols.ICON:
iconFromApk = cursor.getString(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.DESCRIPTION:
2015-10-08 21:41:38 +02:00
description = cursor.getString(i);
break;
case Cols.WHATSNEW:
whatsNew = cursor.getString(i);
break;
case Cols.LICENSE:
2015-10-08 21:41:38 +02:00
license = cursor.getString(i);
break;
case Cols.AUTHOR_NAME:
authorName = cursor.getString(i);
break;
case Cols.AUTHOR_EMAIL:
authorEmail = cursor.getString(i);
break;
case Cols.WEBSITE:
webSite = cursor.getString(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.ISSUE_TRACKER:
issueTracker = cursor.getString(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.SOURCE_CODE:
sourceCode = cursor.getString(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.TRANSLATION:
translation = cursor.getString(i);
break;
case Cols.VIDEO:
video = cursor.getString(i);
break;
case Cols.CHANGELOG:
changelog = cursor.getString(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.DONATE:
donate = cursor.getString(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.BITCOIN:
bitcoin = cursor.getString(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.LITECOIN:
litecoin = cursor.getString(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.FLATTR_ID:
2015-10-08 21:41:38 +02:00
flattrID = cursor.getString(i);
break;
case Cols.LIBERAPAY:
liberapay = cursor.getString(i);
break;
case Cols.OPEN_COLLECTIVE:
openCollective = cursor.getString(i);
break;
case Cols.AutoInstallApk.VERSION_NAME:
autoInstallVersionName = cursor.getString(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.PREFERRED_SIGNER:
preferredSigner = cursor.getString(i);
break;
case Cols.AUTO_INSTALL_VERSION_CODE:
autoInstallVersionCode = cursor.getInt(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.SUGGESTED_VERSION_CODE:
suggestedVersionCode = cursor.getInt(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.SUGGESTED_VERSION_NAME:
suggestedVersionName = cursor.getString(i);
2015-10-08 21:41:38 +02:00
break;
case Cols.ADDED:
2015-10-08 21:41:38 +02:00
added = Utils.parseDate(cursor.getString(i), null);
break;
case Cols.LAST_UPDATED:
2015-10-08 21:41:38 +02:00
lastUpdated = Utils.parseDate(cursor.getString(i), null);
break;
case Cols.ANTI_FEATURES:
antiFeatures = Utils.parseCommaSeparatedString(cursor.getString(i));
2015-10-08 21:41:38 +02:00
break;
case Cols.REQUIREMENTS:
requirements = Utils.parseCommaSeparatedString(cursor.getString(i));
2015-10-08 21:41:38 +02:00
break;
case Cols.ICON_URL:
2015-10-08 21:41:38 +02:00
iconUrl = cursor.getString(i);
break;
case Cols.FEATURE_GRAPHIC:
featureGraphic = cursor.getString(i);
break;
case Cols.PROMO_GRAPHIC:
promoGraphic = cursor.getString(i);
break;
case Cols.TV_BANNER:
tvBanner = cursor.getString(i);
break;
case Cols.PHONE_SCREENSHOTS:
phoneScreenshots = Utils.parseCommaSeparatedString(cursor.getString(i));
break;
case Cols.SEVEN_INCH_SCREENSHOTS:
sevenInchScreenshots = Utils.parseCommaSeparatedString(cursor.getString(i));
break;
case Cols.TEN_INCH_SCREENSHOTS:
tenInchScreenshots = Utils.parseCommaSeparatedString(cursor.getString(i));
break;
case Cols.TV_SCREENSHOTS:
tvScreenshots = Utils.parseCommaSeparatedString(cursor.getString(i));
break;
case Cols.WEAR_SCREENSHOTS:
wearScreenshots = Utils.parseCommaSeparatedString(cursor.getString(i));
break;
case Cols.IS_APK:
isApk = cursor.getInt(i) == 1;
break;
case Cols.IS_LOCALIZED:
isLocalized = cursor.getInt(i) == 1;
break;
case Cols.InstalledApp.VERSION_CODE:
2015-10-08 21:41:38 +02:00
installedVersionCode = cursor.getInt(i);
break;
case Cols.InstalledApp.VERSION_NAME:
2015-10-08 21:41:38 +02:00
installedVersionName = cursor.getString(i);
break;
case Cols.InstalledApp.SIGNATURE:
installedSig = cursor.getString(i);
break;
2015-10-08 21:41:38 +02:00
case "_id":
break;
default:
Log.e(TAG, "Unknown column name " + n);
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
}
}
}
/**
* Instantiate from a locally installed package.
* <p>
* Initializes an {@link App} instances from an APK file. Since the file
* could in the cache, and files can disappear from the cache at any time,
* this needs to be quite defensive ensuring that {@code apkFile} still
* exists.
*/
@Nullable
public static App getInstance(Context context, PackageManager pm, InstalledApp installedApp, String packageName)
2015-04-06 22:16:51 +02:00
throws CertificateEncodingException, IOException, PackageManager.NameNotFoundException {
App app = new App();
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
SanitizedFile apkFile = SanitizedFile.knownSanitized(packageInfo.applicationInfo.publicSourceDir);
app.installedApk = new Apk();
if (installedApp != null) {
app.installedApk.hashType = installedApp.getHashType();
app.installedApk.hash = installedApp.getHash();
} else if (apkFile.canRead()) {
String hashType = "sha256";
String hash = Utils.getFileHexDigest(apkFile, hashType);
if (TextUtils.isEmpty(hash)) {
return null;
}
app.installedApk.hashType = hashType;
app.installedApk.hash = hash;
}
app.setFromPackageInfo(pm, packageInfo);
app.initInstalledApk(context, app.installedApk, packageInfo, apkFile);
return app;
}
/**
* In order to format all in coming descriptions before they are written
* out to the database and used elsewhere, this is needed to intercept
* the setting of {@link App#description} to insert the format method.
*/
@JsonProperty("description")
private void setDescription(String description) { // NOPMD
this.description = formatDescription(description);
}
/**
* Set the Package Name property while ensuring it is sanitized.
*/
@JsonProperty("packageName")
void setPackageName(String packageName) {
if (Utils.isSafePackageName(packageName)) {
this.packageName = packageName;
} else {
throw new IllegalArgumentException("Repo index app entry includes unsafe packageName: '"
+ packageName + "'");
}
}
/**
* {@link #liberapay} was originally included using a numeric ID, now it is a
* username. This should not override {@link #liberapay} if that is already set.
*/
@JsonProperty("liberapayID")
void setLiberapayID(String liberapayId) { // NOPMD
if (TextUtils.isEmpty(liberapayId) || !TextUtils.isEmpty(liberapay)) {
return;
}
try {
int id = Integer.parseInt(liberapayId);
if (id > 0) {
liberapay = "~" + liberapayId;
}
} catch (NumberFormatException e) {
// ignored
}
}
/**
* Parses the {@code localized} block in the incoming index metadata,
* choosing the best match in terms of locale/language while filling as
* many fields as possible. It first sets up a locale list based on user
* preference and the locales available for this app, then picks the texts
* based on that list. One thing that makes this tricky is that any given
* locale block in the index might not have all the fields. So when filling
* out each value, it needs to go through the whole preference list each time,
* rather than just taking the whole block for a specific locale. This is to
* ensure that there is something to show, as often as possible.
* <p>
* It is still possible that the fields will be loaded directly by Jackson
* without any locale info. This comes from the old-style, inline app metadata
* fields that do not have locale info. They should not be used if the
* {@code localized} block is included in the index. Also, null strings in
* the {@code localized} block should not overwrite Name/Summary/Description
* strings with empty/null if they were set directly by Jackson.
* <ol>
* <li>the country variant {@code de-AT} from the user locale list
* <li>only the language {@code de} from the above locale
* <li>next locale in the user's preference list ({@code >= android-24})
* <li>{@code en-US} since its the most common English for software
* <li>the first available {@code en} locale
* </ol>
* <p>
* The system-wide language preference list was added in {@code android-24}.
*
* @see <a href="https://developer.android.com/guide/topics/resources/multilingual-support">Android language and locale resolution overview</a>
*/
@JsonProperty("localized")
void setLocalized(Map<String, Map<String, Object>> localized) { // NOPMD
if (systemLocaleList == null) {
systemLocaleList = ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration());
}
Set<String> supportedLocales = localized.keySet();
setIsLocalized(supportedLocales);
String value = getLocalizedEntry(localized, supportedLocales, "whatsNew");
if (!TextUtils.isEmpty(value)) {
whatsNew = value;
}
value = getLocalizedEntry(localized, supportedLocales, "video");
if (!TextUtils.isEmpty(value)) {
video = value.trim();
}
value = getLocalizedEntry(localized, supportedLocales, "name");
if (!TextUtils.isEmpty(value)) {
name = value.trim();
}
value = getLocalizedEntry(localized, supportedLocales, "summary");
if (!TextUtils.isEmpty(value)) {
summary = value.trim();
}
value = getLocalizedEntry(localized, supportedLocales, "description");
if (!TextUtils.isEmpty(value)) {
description = formatDescription(value);
}
value = getLocalizedGraphicsEntry(localized, supportedLocales, "icon");
if (!TextUtils.isEmpty(value)) {
iconUrl = value;
}
featureGraphic = getLocalizedGraphicsEntry(localized, supportedLocales, "featureGraphic");
promoGraphic = getLocalizedGraphicsEntry(localized, supportedLocales, "promoGraphic");
tvBanner = getLocalizedGraphicsEntry(localized, supportedLocales, "tvBanner");
wearScreenshots = getLocalizedListEntry(localized, supportedLocales, "wearScreenshots");
phoneScreenshots = getLocalizedListEntry(localized, supportedLocales, "phoneScreenshots");
sevenInchScreenshots = getLocalizedListEntry(localized, supportedLocales, "sevenInchScreenshots");
tenInchScreenshots = getLocalizedListEntry(localized, supportedLocales, "tenInchScreenshots");
tvScreenshots = getLocalizedListEntry(localized, supportedLocales, "tvScreenshots");
}
/**
* Sets the boolean flag {@link #isLocalized} if this app entry has an localized
* entry in one of the user's current locales.
*
* @see org.fdroid.fdroid.views.main.WhatsNewViewBinder#onCreateLoader(int, android.os.Bundle)
*/
private void setIsLocalized(Set<String> supportedLocales) {
isLocalized = false;
for (int i = 0; i < systemLocaleList.size(); i++) {
String language = systemLocaleList.get(i).getLanguage();
for (String supportedLocale : supportedLocales) {
if (language.equals(supportedLocale.split("-")[0])) {
isLocalized = true;
return;
}
}
}
}
/**
* Returns the right localized version of this entry, based on an imitation of
* the logic that Android uses.
*
* @see LocaleList
*/
private String getLocalizedEntry(Map<String, Map<String, Object>> localized,
Set<String> supportedLocales, @NonNull String key) {
Map<String, Object> localizedLocaleMap = getLocalizedLocaleMap(localized, supportedLocales, key);
if (localizedLocaleMap != null && !localizedLocaleMap.isEmpty()) {
for (Object entry : localizedLocaleMap.values()) {
return (String) entry; // NOPMD
}
}
return null;
}
private String getLocalizedGraphicsEntry(Map<String, Map<String, Object>> localized,
Set<String> supportedLocales, @NonNull String key) {
Map<String, Object> localizedLocaleMap = getLocalizedLocaleMap(localized, supportedLocales, key);
if (localizedLocaleMap != null && !localizedLocaleMap.isEmpty()) {
for (String locale : localizedLocaleMap.keySet()) {
return locale + "/" + localizedLocaleMap.get(locale); // NOPMD
}
}
return null;
}
private String[] getLocalizedListEntry(Map<String, Map<String, Object>> localized,
Set<String> supportedLocales, @NonNull String key) {
Map<String, Object> localizedLocaleMap = getLocalizedLocaleMap(localized, supportedLocales, key);
if (localizedLocaleMap != null && !localizedLocaleMap.isEmpty()) {
for (String locale : localizedLocaleMap.keySet()) {
ArrayList<String> entry = (ArrayList<String>) localizedLocaleMap.get(locale);
if (entry != null && entry.size() > 0) {
String[] result = new String[entry.size()];
int i = 0;
for (String e : entry) {
result[i] = locale + "/" + key + "/" + e;
i++;
}
return result;
}
}
}
return new String[0];
}
/**
* Return one matching entry from the {@code localized} block in the app entry
* in the index JSON.
*/
private Map<String, Object> getLocalizedLocaleMap(Map<String, Map<String, Object>> localized,
Set<String> supportedLocales, @NonNull String key) {
String[] localesToUse = getLocalesForKey(localized, supportedLocales, key);
if (localesToUse.length > 0) {
Locale firstMatch = systemLocaleList.getFirstMatch(localesToUse);
if (firstMatch != null) {
for (String languageTag : new String[]{toLanguageTag(firstMatch), null}) {
if (languageTag == null) {
languageTag = getFallbackLanguageTag(firstMatch, localesToUse); // NOPMD
}
Map<String, Object> localeEntry = localized.get(languageTag);
if (localeEntry != null && localeEntry.containsKey(key)) {
Object value = localeEntry.get(key);
if (value != null) {
Map<String, Object> localizedLocaleMap = new HashMap<>();
localizedLocaleMap.put(languageTag, value);
return localizedLocaleMap;
}
}
}
}
}
return null;
}
/**
* Replace with {@link Locale#toLanguageTag()} once
* {@link android.os.Build.VERSION_CODES#LOLLIPOP} is {@code minSdkVersion}
*/
private String toLanguageTag(Locale firstMatch) {
if (Build.VERSION.SDK_INT < 21) {
return firstMatch.toString().replace("_", "-");
} else {
return firstMatch.toLanguageTag();
}
}
/**
* Get all locales that have an entry for {@code key}.
*/
private String[] getLocalesForKey(Map<String, Map<String, Object>> localized,
Set<String> supportedLocales, @NonNull String key) {
Set<String> localesToUse = new HashSet<>();
for (String locale : supportedLocales) {
Map<String, Object> localeEntry = localized.get(locale);
if (localeEntry != null && localeEntry.get(key) != null) {
localesToUse.add(locale);
}
}
return localesToUse.toArray(new String[0]);
}
/**
* Look for the first language-country match for languages with multiple scripts.
* Then look for a language-only match, for when there is no exact
* {@link Locale} match. Then try a locale with the same language, but
* different country. If there are still no matches, return the {@code en-US}
* entry. If all else fails, try to return the first existing English locale.
*/
private String getFallbackLanguageTag(Locale firstMatch, String[] localesToUse) {
final String firstMatchLanguageCountry = firstMatch.getLanguage() + "-" + firstMatch.getCountry();
for (String languageTag : localesToUse) {
if (languageTag.equals(firstMatchLanguageCountry)) {
return languageTag;
}
}
final String firstMatchLanguage = firstMatch.getLanguage();
String englishLastResort = null;
for (String languageTag : localesToUse) {
if (languageTag.equals(firstMatchLanguage)) {
return languageTag;
} else if ("en-US".equals(languageTag)) {
englishLastResort = languageTag;
}
}
for (String languageTag : localesToUse) {
String languageToUse = languageTag.split("-")[0];
if (firstMatchLanguage.equals(languageToUse)) {
return languageTag;
} else if (englishLastResort == null && "en".equals(languageToUse)) {
englishLastResort = languageTag;
}
}
return englishLastResort;
}
/**
* Returns the app description text with all newlines replaced by {@code <br>}
*/
public static String formatDescription(String description) {
return description.replace("\n", "<br>");
}
/**
* Get the URL with the standard path for displaying in a browser.
*/
@NonNull
public Uri getShareUri(Context context) {
Repo repo = RepoProvider.Helper.findById(context, repoId);
return Uri.parse(repo.address).buildUpon()
.path(String.format("/packages/%s/", packageName))
.build();
}
2022-01-19 19:07:46 +01:00
public RequestBuilder<Drawable> loadWithGlide(Context context) {
Repo repo = RepoProvider.Helper.findById(context, repoId);
2022-01-19 19:07:46 +01:00
if (repo.address.startsWith("content://")) {
return Glide.with(context).load(getIconUrl(context, repo));
} else {
return Glide.with(context).load(getDownloadRequest(context, repo));
}
}
@Nullable
@Deprecated // not taking mirrors into account
public String getIconUrl(Context context, Repo repo) {
if (TextUtils.isEmpty(iconUrl)) {
if (TextUtils.isEmpty(iconFromApk)) {
return null;
}
if (iconFromApk.endsWith(".xml")) {
// We cannot use xml ressources as icons. F-Droid server should not include them
// https://gitlab.com/fdroid/fdroidserver/issues/344
return null;
}
String iconsDir;
if (repo.version >= Repo.VERSION_DENSITY_SPECIFIC_ICONS) {
iconsDir = Utils.getIconsDir(context, 1.0);
} else {
iconsDir = Utils.FALLBACK_ICONS_DIR;
}
return repo.getFileUrl(iconsDir, iconFromApk);
}
return repo.getFileUrl(packageName, iconUrl);
}
2022-01-19 19:07:46 +01:00
@Nullable
@Deprecated // not taking mirrors into account
public String getIconUrl(Context context) {
Repo repo = RepoProvider.Helper.findById(context, repoId);
return getIconUrl(context, repo);
}
@Nullable
public DownloadRequest getDownloadRequest(Context context, Repo repo) {
String path;
if (TextUtils.isEmpty(iconUrl)) {
if (TextUtils.isEmpty(iconFromApk)) {
return null;
}
if (iconFromApk.endsWith(".xml")) {
// We cannot use xml resources as icons. F-Droid server should not include them
// https://gitlab.com/fdroid/fdroidserver/issues/344
return null;
}
String iconsDir;
if (repo.version >= Repo.VERSION_DENSITY_SPECIFIC_ICONS) {
iconsDir = Utils.getIconsDir(context, 1.0);
} else {
iconsDir = Utils.FALLBACK_ICONS_DIR;
}
path = repo.getPath(iconsDir, iconFromApk);
} else {
path = repo.getPath(packageName, iconUrl);
}
return repo.getDownloadRequest(path);
}
@Nullable
public DownloadRequest getDownloadRequest(Context context) {
Repo repo = RepoProvider.Helper.findById(context, repoId);
return getDownloadRequest(context, repo);
}
public String getFeatureGraphicUrl(Context context) {
if (TextUtils.isEmpty(featureGraphic)) {
return null;
}
Repo repo = RepoProvider.Helper.findById(context, repoId);
return repo.getFileUrl(packageName, featureGraphic);
}
public String getPromoGraphic(Context context) {
if (TextUtils.isEmpty(promoGraphic)) {
return null;
}
Repo repo = RepoProvider.Helper.findById(context, repoId);
return repo.getFileUrl(packageName, promoGraphic);
}
public String getTvBanner(Context context) {
if (TextUtils.isEmpty(tvBanner)) {
return null;
}
Repo repo = RepoProvider.Helper.findById(context, repoId);
return repo.getFileUrl(packageName, tvBanner);
}
public String[] getAllScreenshots(Context context) {
Repo repo = RepoProvider.Helper.findById(context, repoId);
ArrayList<String> list = new ArrayList<>();
if (phoneScreenshots != null) {
Collections.addAll(list, phoneScreenshots);
}
if (sevenInchScreenshots != null) {
Collections.addAll(list, sevenInchScreenshots);
}
if (tenInchScreenshots != null) {
Collections.addAll(list, tenInchScreenshots);
}
if (tvScreenshots != null) {
Collections.addAll(list, tvScreenshots);
}
if (wearScreenshots != null) {
Collections.addAll(list, wearScreenshots);
}
String[] result = new String[list.size()];
int i = 0;
for (String url : list) {
result[i] = repo.getFileUrl(packageName, url);
i++;
}
return result;
}
/**
* Get the directory where APK Expansion Files aka OBB files are stored for the app as
* specified by {@code packageName}.
*
* @see <a href="https://developer.android.com/google/play/expansion-files.html">APK Expansion Files</a>
*/
public static File getObbDir(String packageName) {
return new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/Android/obb/" + packageName);
}
private void setFromPackageInfo(PackageManager pm, PackageInfo packageInfo) {
this.packageName = packageInfo.packageName;
final String installerPackageName = pm.getInstallerPackageName(packageName);
CharSequence installerPackageLabel = null;
if (!TextUtils.isEmpty(installerPackageName)) {
try {
ApplicationInfo installerAppInfo = pm.getApplicationInfo(installerPackageName,
PackageManager.GET_META_DATA);
installerPackageLabel = installerAppInfo.loadLabel(pm);
2015-04-06 22:16:51 +02:00
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Could not get app info: " + installerPackageName, e);
}
}
if (TextUtils.isEmpty(installerPackageLabel)) {
installerPackageLabel = installerPackageName;
}
ApplicationInfo appInfo = packageInfo.applicationInfo;
final CharSequence appDescription = appInfo.loadDescription(pm);
if (TextUtils.isEmpty(appDescription)) {
this.summary = "(installed by " + installerPackageLabel + ")";
} else if (appDescription.length() > 40) {
this.summary = (String) appDescription.subSequence(0, 40);
} else {
this.summary = (String) appDescription;
}
this.added = new Date(packageInfo.firstInstallTime);
this.lastUpdated = new Date(packageInfo.lastUpdateTime);
this.description = "<p>";
if (!TextUtils.isEmpty(appDescription)) {
this.description += appDescription + "\n";
}
this.description += "(installed by " + installerPackageLabel
+ ", first installed on " + this.added
+ ", last updated on " + this.lastUpdated + ")</p>";
this.name = (String) appInfo.loadLabel(pm);
this.iconFromApk = getIconName(packageName, packageInfo.versionCode);
this.installedVersionName = packageInfo.versionName;
this.installedVersionCode = packageInfo.versionCode;
this.compatible = true;
}
public static void initInstalledObbFiles(Apk apk) {
File obbdir = getObbDir(apk.packageName);
FileFilter filter = new RegexFileFilter("(main|patch)\\.[0-9-][0-9]*\\." + apk.packageName + "\\.obb");
File[] files = obbdir.listFiles(filter);
if (files == null) {
return;
}
Arrays.sort(files);
for (File f : files) {
String filename = f.getName();
String[] segments = filename.split("\\.");
if (Integer.parseInt(segments[1]) <= apk.versionCode) {
if ("main".equals(segments[0])) {
apk.obbMainFile = filename;
apk.obbMainFileSha256 = Utils.getFileHexDigest(f, apk.hashType);
} else if ("patch".equals(segments[0])) {
apk.obbPatchFile = filename;
apk.obbPatchFileSha256 = Utils.getFileHexDigest(f, apk.hashType);
}
}
}
}
@SuppressWarnings("EmptyForIteratorPad")
private void initInstalledApk(Context context, Apk apk, PackageInfo packageInfo, SanitizedFile apkFile)
throws IOException, CertificateEncodingException {
apk.compatible = true;
apk.versionName = packageInfo.versionName;
apk.versionCode = packageInfo.versionCode;
apk.added = this.added;
int[] minTargetMax = getMinTargetMaxSdkVersions(context, packageName);
apk.minSdkVersion = minTargetMax[0];
apk.targetSdkVersion = minTargetMax[1];
apk.maxSdkVersion = minTargetMax[2];
2015-12-21 11:07:45 +01:00
apk.packageName = this.packageName;
apk.requestedPermissions = packageInfo.requestedPermissions;
apk.apkName = apk.packageName + "_" + apk.versionCode + ".apk";
initInstalledObbFiles(apk);
final FeatureInfo[] features = packageInfo.reqFeatures;
if (features != null && features.length > 0) {
apk.features = new String[features.length];
for (int i = 0; i < features.length; i++) {
apk.features[i] = features[i].name;
}
}
if (!apkFile.canRead()) {
return;
}
apk.installedFile = apkFile;
JarFile apkJar = new JarFile(apkFile);
HashSet<String> abis = new HashSet<>(3);
Pattern pattern = Pattern.compile("^lib/([a-z0-9-]+)/.*");
for (Enumeration<JarEntry> jarEntries = apkJar.entries(); jarEntries.hasMoreElements(); ) {
JarEntry jarEntry = jarEntries.nextElement();
Matcher matcher = pattern.matcher(jarEntry.getName());
if (matcher.matches()) {
abis.add(matcher.group(1));
}
}
apk.nativecode = abis.toArray(new String[abis.size()]);
2015-04-30 20:40:19 +02:00
final JarEntry aSignedEntry = (JarEntry) apkJar.getEntry("AndroidManifest.xml");
if (aSignedEntry == null) {
apkJar.close();
throw new CertificateEncodingException("null signed entry!");
}
final InputStream tmpIn = apkJar.getInputStream(aSignedEntry);
byte[] buff = new byte[2048];
//noinspection StatementWithEmptyBody
while (tmpIn.read(buff, 0, buff.length) != -1) {
/*
* NOP - apparently have to READ from the JarEntry before you can
* call getCerficates() and have it return != null. Yay Java.
*/
}
tmpIn.close();
if (aSignedEntry.getCertificates() == null
|| aSignedEntry.getCertificates().length == 0) {
apkJar.close();
throw new CertificateEncodingException("No Certificates found!");
}
final Certificate signer = aSignedEntry.getCertificates()[0];
byte[] rawCertBytes = signer.getEncoded();
apkJar.close();
apk.sig = Utils.getsig(rawCertBytes);
}
/**
* Attempts to find the installed {@link Apk} from the database. If not found, will lookup the
* {@link InstalledAppProvider} to find the details of the installed app and use that to
* instantiate an {@link Apk} to be returned.
* <p>
* Cases where an {@link Apk} will not be found in the database and for which we fall back to
* the {@link InstalledAppProvider} include:
* <li>System apps which are provided by a repository, but for which the version code bundled
* with the system is not included in the repository.</li>
* <li>Regular apps from a repository, where the installed version is old enough that it is no
* longer available in the repository.</li>
*/
@Nullable
public Apk getInstalledApk(Context context) {
try {
PackageInfo pi = context.getPackageManager().getPackageInfo(this.packageName, 0);
Apk apk = ApkProvider.Helper.findApkFromAnyRepo(context, pi.packageName, pi.versionCode);
if (apk == null) {
InstalledApp installedApp = InstalledAppProvider.Helper.findByPackageName(context, pi.packageName);
if (installedApp == null) {
throw new IllegalStateException("No installed app found when trying to uninstall");
}
apk = new Apk(installedApp);
}
return apk;
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
public boolean isValid() {
if (TextUtils.isEmpty(this.name)
|| TextUtils.isEmpty(this.packageName)) {
return false;
}
if (this.installedApk == null) {
return false;
}
if (TextUtils.isEmpty(this.installedApk.sig)) {
return false;
}
2015-04-30 20:40:19 +02:00
final File apkFile = this.installedApk.installedFile;
return !(apkFile == null || !apkFile.canRead());
}
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
public ContentValues toContentValues() {
2015-04-30 20:40:19 +02:00
final ContentValues values = new ContentValues();
Change join from `app` to `apk` table to auto increment integers rather than Android package name strings. 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.
2016-06-23 03:30:01 +02:00
// Intentionally don't put "ROW_ID" in here, because we don't ever want to change that
// primary key generated by sqlite.
values.put(Cols.Package.PACKAGE_NAME, packageName);
values.put(Cols.NAME, name);
values.put(Cols.REPO_ID, repoId);
values.put(Cols.SUMMARY, summary);
values.put(Cols.ICON, iconFromApk);
values.put(Cols.ICON_URL, iconUrl);
values.put(Cols.DESCRIPTION, description);
values.put(Cols.WHATSNEW, whatsNew);
values.put(Cols.LICENSE, license);
values.put(Cols.AUTHOR_NAME, authorName);
values.put(Cols.AUTHOR_EMAIL, authorEmail);
values.put(Cols.WEBSITE, webSite);
values.put(Cols.ISSUE_TRACKER, issueTracker);
values.put(Cols.SOURCE_CODE, sourceCode);
values.put(Cols.TRANSLATION, translation);
values.put(Cols.VIDEO, video);
values.put(Cols.CHANGELOG, changelog);
values.put(Cols.DONATE, donate);
values.put(Cols.BITCOIN, bitcoin);
values.put(Cols.LITECOIN, litecoin);
values.put(Cols.FLATTR_ID, flattrID);
values.put(Cols.LIBERAPAY, liberapay);
values.put(Cols.OPEN_COLLECTIVE, openCollective);
values.put(Cols.ADDED, Utils.formatDate(added, ""));
values.put(Cols.LAST_UPDATED, Utils.formatDate(lastUpdated, ""));
values.put(Cols.PREFERRED_SIGNER, preferredSigner);
values.put(Cols.AUTO_INSTALL_VERSION_CODE, autoInstallVersionCode);
values.put(Cols.SUGGESTED_VERSION_NAME, suggestedVersionName);
values.put(Cols.SUGGESTED_VERSION_CODE, suggestedVersionCode);
values.put(Cols.ForWriting.Categories.CATEGORIES, Utils.serializeCommaSeparatedString(categories));
values.put(Cols.ANTI_FEATURES, Utils.serializeCommaSeparatedString(antiFeatures));
values.put(Cols.REQUIREMENTS, Utils.serializeCommaSeparatedString(requirements));
values.put(Cols.FEATURE_GRAPHIC, featureGraphic);
values.put(Cols.PROMO_GRAPHIC, promoGraphic);
values.put(Cols.TV_BANNER, tvBanner);
values.put(Cols.PHONE_SCREENSHOTS, Utils.serializeCommaSeparatedString(phoneScreenshots));
values.put(Cols.SEVEN_INCH_SCREENSHOTS, Utils.serializeCommaSeparatedString(sevenInchScreenshots));
values.put(Cols.TEN_INCH_SCREENSHOTS, Utils.serializeCommaSeparatedString(tenInchScreenshots));
values.put(Cols.TV_SCREENSHOTS, Utils.serializeCommaSeparatedString(tvScreenshots));
values.put(Cols.WEAR_SCREENSHOTS, Utils.serializeCommaSeparatedString(wearScreenshots));
values.put(Cols.IS_COMPATIBLE, compatible ? 1 : 0);
values.put(Cols.IS_APK, isApk ? 1 : 0);
values.put(Cols.IS_LOCALIZED, isLocalized ? 1 : 0);
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
return values;
}
public boolean isInstalled(Context context) {
// First check isApk() before isMediaInstalled() because the latter is quite expensive,
// hitting the database for each apk version, then the disk to check for installed media.
return installedVersionCode > 0 || (!isApk() && isMediaInstalled(context));
}
private boolean isApk() {
return isApk;
}
public boolean isMediaInstalled(Context context) {
return getMediaApkifInstalled(context) != null;
}
/**
* Gets the installed media apk from all the apks of this {@link App}, if any.
*
* @return The installed media {@link Apk} if it exists, null otherwise.
*/
public Apk getMediaApkifInstalled(Context context) {
// This is always null for media files. We could skip the code below completely if it wasn't
if (this.installedApk != null && !this.installedApk.isApk() && this.installedApk.isMediaInstalled(context)) {
return this.installedApk;
}
// This code comes from AppDetailsRecyclerViewAdapter
final List<Apk> apks = ApkProvider.Helper.findByPackageName(context, this.packageName);
for (final Apk apk : apks) {
boolean allowByCompatability = apk.compatible || Preferences.get().showIncompatibleVersions();
boolean allowBySig = this.installedSig == null || TextUtils.equals(this.installedSig, apk.sig);
if (allowByCompatability && allowBySig) {
if (!apk.isApk()) {
if (apk.isMediaInstalled(context)) {
return apk;
}
}
}
}
return null;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
}
/**
* True if there are new versions (apks) available
*/
public boolean hasUpdates() {
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
boolean updates = false;
if (autoInstallVersionCode > 0) {
updates = installedVersionCode > 0 && installedVersionCode < autoInstallVersionCode;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
}
return updates;
}
public AppPrefs getPrefs(Context context) {
if (prefs == null) {
prefs = AppPrefsProvider.Helper.getPrefsOrDefault(context, this);
}
return prefs;
}
/**
* True if there are new versions (apks) available and the user wants
* to be notified about them
*/
public boolean canAndWantToUpdate(Context context) {
boolean canUpdate = hasUpdates();
AppPrefs prefs = getPrefs(context);
boolean wantsUpdate = !prefs.ignoreAllUpdates && prefs.ignoreThisUpdate < autoInstallVersionCode;
return canUpdate && wantsUpdate;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
}
/**
* @return if the given app should be filtered out based on the
* {@link Preferences#PREF_SHOW_ANTI_FEATURES Show Anti-Features Setting}
*/
2021-10-30 14:09:37 +02:00
public boolean isDisabledByAntiFeatures(Context context) {
if (this.antiFeatures == null) {
return false;
}
2021-10-30 22:44:46 +02:00
List<String> chooseableAntiFeatures = Arrays.asList(
2021-10-30 14:09:37 +02:00
context.getResources().getStringArray(R.array.antifeaturesValues)
);
Set<String> shownAntiFeatures = Preferences.get().showAppsWithAntiFeatures();
for (String antiFeature : this.antiFeatures) {
2021-10-30 22:44:46 +02:00
if (chooseableAntiFeatures.contains(antiFeature)) {
if (!shownAntiFeatures.contains(antiFeature)) {
return true;
}
} else {
if (!shownAntiFeatures.contains(context.getResources().getString(R.string.antiothers_key))) {
return true;
}
}
}
return false;
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
}
@Nullable
public String getBitcoinUri() {
return TextUtils.isEmpty(bitcoin) ? null : "bitcoin:" + bitcoin;
}
@Nullable
public String getLitecoinUri() {
return TextUtils.isEmpty(bitcoin) ? null : "litecoin:" + bitcoin;
}
@Nullable
public String getOpenCollectiveUri() {
return TextUtils.isEmpty(openCollective) ? null : "https://opencollective.com/"
+ openCollective + "/donate/";
}
@Nullable
public String getFlattrUri() {
return TextUtils.isEmpty(flattrID) ? null : "https://flattr.com/thing/" + flattrID;
}
@Nullable
public String getLiberapayUri() {
return TextUtils.isEmpty(liberapay) ? null : "https://liberapay.com/" + liberapay;
}
/**
* @see App#autoInstallVersionName for why this uses a getter while other member variables are
* publicly accessible.
*/
public String getAutoInstallVersionName() {
return autoInstallVersionName;
2015-10-09 11:40:50 +02:00
}
/**
* {@link PackageManager} doesn't give us {@code minSdkVersion}, {@code targetSdkVersion},
* and {@code maxSdkVersion}, so we have to parse it straight from {@code <uses-sdk>} in
* {@code AndroidManifest.xml}. If {@code targetSdkVersion} is not set, then it is
* equal to {@code minSdkVersion}
*
* @see <a href="https://developer.android.com/guide/topics/manifest/uses-sdk-element.html">&lt;uses-sdk&gt;</a>
*/
private static int[] getMinTargetMaxSdkVersions(Context context, String packageName) {
int minSdkVersion = Apk.SDK_VERSION_MIN_VALUE;
int targetSdkVersion = Apk.SDK_VERSION_MIN_VALUE;
int maxSdkVersion = Apk.SDK_VERSION_MAX_VALUE;
try {
AssetManager am = context.createPackageContext(packageName, 0).getAssets();
XmlResourceParser xml = am.openXmlResourceParser("AndroidManifest.xml");
int eventType = xml.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && "uses-sdk".equals(xml.getName())) {
for (int j = 0; j < xml.getAttributeCount(); j++) {
if (xml.getAttributeName(j).equals("minSdkVersion")) {
minSdkVersion = Integer.parseInt(xml.getAttributeValue(j));
} else if (xml.getAttributeName(j).equals("targetSdkVersion")) {
targetSdkVersion = Integer.parseInt(xml.getAttributeValue(j));
} else if (xml.getAttributeName(j).equals("maxSdkVersion")) {
maxSdkVersion = Integer.parseInt(xml.getAttributeValue(j));
}
}
break;
}
eventType = xml.nextToken();
}
} catch (PackageManager.NameNotFoundException
| IOException
| XmlPullParserException
| NumberFormatException e) {
Log.e(TAG, "Could not get min/max sdk version", e);
}
if (targetSdkVersion < minSdkVersion) {
targetSdkVersion = minSdkVersion;
}
return new int[]{minSdkVersion, targetSdkVersion, maxSdkVersion};
}
Change join from `app` to `apk` table to auto increment integers rather than Android package name strings. 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.
2016-06-23 03:30:01 +02:00
public long getId() {
return id;
}
@Override
public int describeContents() {
return 0;
}
public boolean isUninstallable(Context context) {
if (this.isMediaInstalled(context)) {
return true;
} else if (this.isInstalled(context)) {
PackageManager pm = context.getPackageManager();
ApplicationInfo appInfo;
try {
appInfo = pm.getApplicationInfo(this.packageName,
PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
// System apps aren't uninstallable.
final boolean isSystem = (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
return !isSystem && this.isInstalled(context);
} else {
return false;
}
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte(this.compatible ? (byte) 1 : (byte) 0);
dest.writeString(this.packageName);
dest.writeString(this.name);
dest.writeLong(this.repoId);
dest.writeString(this.summary);
dest.writeString(this.iconFromApk);
dest.writeString(this.description);
dest.writeString(this.whatsNew);
dest.writeString(this.license);
dest.writeString(this.authorName);
dest.writeString(this.authorEmail);
dest.writeString(this.webSite);
dest.writeString(this.issueTracker);
dest.writeString(this.sourceCode);
dest.writeString(this.translation);
dest.writeString(this.video);
dest.writeString(this.changelog);
dest.writeString(this.donate);
dest.writeString(this.bitcoin);
dest.writeString(this.litecoin);
dest.writeString(this.flattrID);
dest.writeString(this.liberapay);
dest.writeString(this.openCollective);
dest.writeString(this.preferredSigner);
dest.writeString(this.suggestedVersionName);
dest.writeInt(this.suggestedVersionCode);
dest.writeString(this.autoInstallVersionName);
dest.writeInt(this.autoInstallVersionCode);
dest.writeLong(this.added != null ? this.added.getTime() : -1);
dest.writeLong(this.lastUpdated != null ? this.lastUpdated.getTime() : -1);
dest.writeStringArray(this.categories);
dest.writeStringArray(this.antiFeatures);
dest.writeStringArray(this.requirements);
dest.writeString(this.iconUrl);
dest.writeString(this.featureGraphic);
dest.writeString(this.promoGraphic);
dest.writeString(this.tvBanner);
dest.writeStringArray(this.phoneScreenshots);
dest.writeStringArray(this.sevenInchScreenshots);
dest.writeStringArray(this.tenInchScreenshots);
dest.writeStringArray(this.tvScreenshots);
dest.writeStringArray(this.wearScreenshots);
dest.writeByte(this.isApk ? (byte) 1 : (byte) 0);
dest.writeByte(this.isLocalized ? (byte) 1 : (byte) 0);
dest.writeString(this.installedVersionName);
dest.writeInt(this.installedVersionCode);
dest.writeParcelable(this.installedApk, flags);
dest.writeString(this.installedSig);
dest.writeLong(this.id);
}
protected App(Parcel in) {
this.compatible = in.readByte() != 0;
this.packageName = in.readString();
this.name = in.readString();
this.repoId = in.readLong();
this.summary = in.readString();
this.iconFromApk = in.readString();
this.description = in.readString();
this.whatsNew = in.readString();
this.license = in.readString();
this.authorName = in.readString();
this.authorEmail = in.readString();
this.webSite = in.readString();
this.issueTracker = in.readString();
this.sourceCode = in.readString();
this.translation = in.readString();
this.video = in.readString();
this.changelog = in.readString();
this.donate = in.readString();
this.bitcoin = in.readString();
this.litecoin = in.readString();
this.flattrID = in.readString();
this.liberapay = in.readString();
this.openCollective = in.readString();
this.preferredSigner = in.readString();
this.suggestedVersionName = in.readString();
this.suggestedVersionCode = in.readInt();
this.autoInstallVersionName = in.readString();
this.autoInstallVersionCode = in.readInt();
long tmpAdded = in.readLong();
this.added = tmpAdded == -1 ? null : new Date(tmpAdded);
long tmpLastUpdated = in.readLong();
this.lastUpdated = tmpLastUpdated == -1 ? null : new Date(tmpLastUpdated);
this.categories = in.createStringArray();
this.antiFeatures = in.createStringArray();
this.requirements = in.createStringArray();
this.iconUrl = in.readString();
this.featureGraphic = in.readString();
this.promoGraphic = in.readString();
this.tvBanner = in.readString();
this.phoneScreenshots = in.createStringArray();
this.sevenInchScreenshots = in.createStringArray();
this.tenInchScreenshots = in.createStringArray();
this.tvScreenshots = in.createStringArray();
this.wearScreenshots = in.createStringArray();
this.isApk = in.readByte() != 0;
this.isLocalized = in.readByte() != 0;
this.installedVersionName = in.readString();
this.installedVersionCode = in.readInt();
this.installedApk = in.readParcelable(Apk.class.getClassLoader());
this.installedSig = in.readString();
this.id = in.readLong();
}
@JsonIgnore
public static final Parcelable.Creator<App> CREATOR = new Parcelable.Creator<App>() {
@Override
public App createFromParcel(Parcel source) {
return new App(source);
}
@Override
public App[] newArray(int size) {
return new App[size];
}
};
/**
* Choose the signature which we should encourage the user to install.
* Usually, we want the {@link #preferredSigner} rather than any random signature.
* However, if the app is installed, then we override this and instead want to only encourage
* the user to try and install versions with that signature (because thats all the OS will let
* them do).
* <p>
* Will return null for any {@link App} which represents media (instead of an apk) and thus
* doesn't have a signer.
*/
@Nullable
public String getMostAppropriateSignature() {
if (!TextUtils.isEmpty(installedSig)) {
return installedSig;
} else if (!TextUtils.isEmpty(preferredSigner)) {
return preferredSigner;
}
return null;
}
Removed DB, implemented AppProvider. 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.
2014-02-02 09:38:36 +01:00
}