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;
|
2014-05-06 22:24:05 +02:00
|
|
|
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;
|
2016-05-23 14:26:52 +02:00
|
|
|
import android.content.res.AssetManager;
|
2017-04-19 00:12:29 +02:00
|
|
|
import android.content.res.Resources;
|
2016-05-23 14:26:52 +02:00
|
|
|
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;
|
2021-09-10 10:08:17 +02:00
|
|
|
import android.net.Uri;
|
2021-02-06 01:23:53 +01:00
|
|
|
import android.os.Build;
|
2016-06-28 00:07:50 +02:00
|
|
|
import android.os.Environment;
|
2017-04-19 00:12:29 +02:00
|
|
|
import android.os.LocaleList;
|
2016-07-25 16:52:39 +02:00
|
|
|
import android.os.Parcel;
|
2016-05-19 20:30:17 +02:00
|
|
|
import android.os.Parcelable;
|
2014-05-06 22:24:05 +02:00
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.util.Log;
|
2021-04-13 10:23:22 +02:00
|
|
|
|
2022-01-19 19:07:46 +01:00
|
|
|
import com.bumptech.glide.Glide;
|
|
|
|
import com.bumptech.glide.RequestBuilder;
|
2017-03-22 13:58:27 +01:00
|
|
|
import com.fasterxml.jackson.annotation.JacksonInject;
|
2017-03-22 16:14:43 +01:00
|
|
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
2016-12-06 11:04:22 +01:00
|
|
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
2021-04-13 10:23:22 +02:00
|
|
|
|
2016-06-28 00:07:50 +02:00
|
|
|
import org.apache.commons.io.filefilter.RegexFileFilter;
|
2022-01-19 19:07:46 +01:00
|
|
|
import org.fdroid.download.DownloadRequest;
|
2017-06-20 12:31:56 +02:00
|
|
|
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;
|
2016-06-28 00:07:50 +02:00
|
|
|
import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols;
|
2016-05-23 14:26:52 +02:00
|
|
|
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
|
|
|
|
2014-05-06 22:24:05 +02:00
|
|
|
import java.io.File;
|
2016-06-28 00:07:50 +02:00
|
|
|
import java.io.FileFilter;
|
2014-05-06 22:24:05 +02:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.security.cert.Certificate;
|
|
|
|
import java.security.cert.CertificateEncodingException;
|
2016-12-06 11:04:22 +01:00
|
|
|
import java.util.ArrayList;
|
2016-06-28 00:07:50 +02:00
|
|
|
import java.util.Arrays;
|
2016-12-06 11:04:22 +01:00
|
|
|
import java.util.Collections;
|
2015-04-06 22:16:51 +02:00
|
|
|
import java.util.Date;
|
2016-05-17 23:49:33 +02:00
|
|
|
import java.util.Enumeration;
|
2021-02-05 16:36:47 +01:00
|
|
|
import java.util.HashMap;
|
2016-05-17 23:49:33 +02:00
|
|
|
import java.util.HashSet;
|
2017-06-20 12:31:56 +02:00
|
|
|
import java.util.List;
|
2016-12-06 11:04:22 +01:00
|
|
|
import java.util.Locale;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
2014-05-06 22:24:05 +02:00
|
|
|
import java.util.jar.JarEntry;
|
|
|
|
import java.util.jar.JarFile;
|
2016-05-17 23:49:33 +02:00
|
|
|
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
|
|
|
|
2021-04-13 10:23:22 +02:00
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import androidx.core.os.ConfigurationCompat;
|
|
|
|
import androidx.core.os.LocaleListCompat;
|
|
|
|
|
2017-03-22 21:38:33 +01:00
|
|
|
/**
|
|
|
|
* 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
|
2017-03-22 16:14:43 +01:00
|
|
|
* 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.
|
2017-03-22 21:38:33 +01:00
|
|
|
*
|
|
|
|
* @see <a href="https://gitlab.com/fdroid/fdroiddata">fdroiddata</a>
|
|
|
|
* @see <a href="https://gitlab.com/fdroid/fdroidserver">fdroidserver</a>
|
|
|
|
*/
|
2016-07-25 16:59:53 +02:00
|
|
|
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
|
|
|
|
2017-03-22 16:14:43 +01:00
|
|
|
@JsonIgnore
|
2015-05-08 23:28:32 +02:00
|
|
|
private static final String TAG = "App";
|
2015-04-10 13:58:26 +02:00
|
|
|
|
2021-02-05 16:36:47 +01:00
|
|
|
/**
|
|
|
|
* {@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;
|
|
|
|
|
2017-03-22 16:14:43 +01:00
|
|
|
// these properties are not from the index metadata, but represent the state on the device
|
2016-05-27 20:48:09 +02:00
|
|
|
/**
|
|
|
|
* True if compatible with the device (i.e. if at least one apk is)
|
|
|
|
*/
|
2017-03-22 16:14:43 +01:00
|
|
|
@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;
|
2017-03-22 16:14:43 +01:00
|
|
|
@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;
|
2017-06-21 07:48:21 +02:00
|
|
|
@JsonIgnore
|
2017-09-02 22:23:51 +02:00
|
|
|
@NonNull
|
2017-06-21 07:48:21 +02:00
|
|
|
public String preferredSigner;
|
2017-09-12 16:48:14 +02:00
|
|
|
@JsonIgnore
|
|
|
|
public boolean isApk;
|
2021-02-05 16:36:47 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Has this {@code App} been localized into one of the user's current locales.
|
|
|
|
*/
|
2019-03-22 13:36:53 +01:00
|
|
|
@JsonIgnore
|
2021-02-05 16:36:47 +01:00
|
|
|
boolean isLocalized;
|
2017-03-22 16:14:43 +01:00
|
|
|
|
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.
|
|
|
|
*/
|
2017-03-22 21:45:59 +01:00
|
|
|
@JacksonInject("repoId")
|
2017-03-22 13:58:27 +01:00
|
|
|
public long repoId;
|
|
|
|
|
2017-03-22 16:14:43 +01:00
|
|
|
// 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")
|
2020-02-21 16:08:08 +01:00
|
|
|
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;
|
|
|
|
|
2017-04-14 01:31:33 +02:00
|
|
|
/**
|
|
|
|
* A descriptive text for what has changed in this version.
|
|
|
|
*/
|
|
|
|
public String whatsNew;
|
2016-12-06 11:04:22 +01:00
|
|
|
|
|
|
|
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];
|
|
|
|
|
2017-05-01 20:57:12 +02:00
|
|
|
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
|
|
|
|
2016-11-25 23:36:37 +01:00
|
|
|
public String authorName;
|
|
|
|
public String authorEmail;
|
2016-01-04 23:53:28 +01:00
|
|
|
|
2016-11-25 23:36:37 +01:00
|
|
|
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
|
|
|
|
2016-11-25 23:36:37 +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
|
|
|
|
2016-11-25 23:36:37 +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
|
|
|
|
2019-03-22 15:22:11 +01:00
|
|
|
public String translation;
|
|
|
|
|
2017-04-14 01:31:33 +02:00
|
|
|
public String video;
|
|
|
|
|
2016-11-25 23:36:37 +01:00
|
|
|
public String changelog;
|
2015-06-04 11:57:16 +02:00
|
|
|
|
2016-11-25 23:36:37 +01: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
|
|
|
|
2016-11-25 23:36:37 +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
|
|
|
|
2016-11-25 23:36:37 +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;
|
|
|
|
|
2020-06-16 10:12:24 +02:00
|
|
|
public String liberapay;
|
2018-02-14 22:48:00 +01:00
|
|
|
|
2020-06-10 23:53:16 +02:00
|
|
|
public String openCollective;
|
|
|
|
|
2020-01-10 16:59:21 +01:00
|
|
|
/**
|
|
|
|
* 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;
|
2017-06-16 22:56:57 +02:00
|
|
|
|
|
|
|
/**
|
2020-01-10 17:18:28 +01:00
|
|
|
* 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};
|
2017-09-02 22:23:51 +02:00
|
|
|
*
|
2020-01-10 17:06:58 +01:00
|
|
|
* @see <a href="https://f-droid.org/docs/Build_Metadata_Reference/#CurrentVersionCode">CurrentVersionCode</a>
|
2017-06-16 22:56:57 +02:00
|
|
|
*/
|
2020-01-10 17:18:28 +01:00
|
|
|
public int suggestedVersionCode = Integer.MIN_VALUE;
|
2014-02-15 10:57:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Unlike other public fields, this is only accessible via a getter, to
|
|
|
|
* emphasise that setting it wont do anything. In order to change this,
|
2020-01-10 17:41:29 +01:00
|
|
|
* you need to change {@link #autoInstallVersionCode} to an APK which is
|
|
|
|
* in the {@link Schema.ApkTable} table.
|
2014-02-15 10:57:18 +01:00
|
|
|
*/
|
2020-01-10 17:41:29 +01:00
|
|
|
private String autoInstallVersionName;
|
2014-12-30 23:48:36 +01:00
|
|
|
|
2020-01-10 16:46:07 +01:00
|
|
|
/**
|
|
|
|
* The version that will be automatically installed if the user does not
|
|
|
|
* choose a specific version.
|
2020-01-10 17:18:28 +01:00
|
|
|
* TODO this should probably be converted to init to {@link Integer#MIN_VALUE} like {@link #suggestedVersionCode}
|
2020-01-10 16:46:07 +01:00
|
|
|
*/
|
|
|
|
public int autoInstallVersionCode;
|
2014-02-15 10:57:18 +01:00
|
|
|
|
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;
|
|
|
|
|
2016-05-27 20:48:09 +02:00
|
|
|
/**
|
|
|
|
* List of categories (as defined in the metadata documentation) or null if there aren't any.
|
2016-10-13 01:17:54 +02:00
|
|
|
* 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}.
|
2016-05-27 20:48:09 +02:00
|
|
|
*/
|
2016-06-20 14:24:52 +02:00
|
|
|
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
|
|
|
|
2016-05-27 20:48:09 +02:00
|
|
|
/**
|
|
|
|
* List of anti-features (as defined in the metadata documentation) or null if there aren't any.
|
|
|
|
*/
|
2016-06-20 14:24:52 +02:00
|
|
|
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
|
|
|
|
2016-05-27 20:48:09 +02:00
|
|
|
/**
|
2016-12-01 20:19:54 +01:00
|
|
|
* Requires root access (only ever used for root)
|
2016-05-27 20:48:09 +02:00
|
|
|
*/
|
2016-12-01 20:19:54 +01:00
|
|
|
@Deprecated
|
2016-06-20 14:24:52 +02:00
|
|
|
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
|
|
|
|
2016-05-27 20:48:09 +02:00
|
|
|
/**
|
2020-02-21 16:08:08 +01:00
|
|
|
* URL to download the app's icon. (Set only from localized block, see also
|
|
|
|
* {@link #iconFromApk} and {@link #getIconUrl(Context)}
|
2016-05-27 20:48:09 +02:00
|
|
|
*/
|
2020-02-21 14:45:46 +01:00
|
|
|
private String iconUrl;
|
2016-05-27 20:48:09 +02:00
|
|
|
|
2016-05-17 16:23:59 +02:00
|
|
|
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
|
2017-09-02 22:23:51 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-05-17 23:49:33 +02:00
|
|
|
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
|
|
|
|
2018-04-03 23:59:50 +02: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);
|
|
|
|
|
2018-04-03 23:59:50 +02:00
|
|
|
final int cursorColumnCount = cursor.getColumnCount();
|
|
|
|
for (int i = 0; i < cursorColumnCount; i++) {
|
|
|
|
final String n = cursor.getColumnName(i);
|
2015-06-22 23:01:39 +02:00
|
|
|
switch (n) {
|
2016-06-23 03:30:01 +02:00
|
|
|
case Cols.ROW_ID:
|
|
|
|
id = cursor.getLong(i);
|
|
|
|
break;
|
2016-07-28 04:25:17 +02:00
|
|
|
case Cols.REPO_ID:
|
|
|
|
repoId = cursor.getLong(i);
|
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.IS_COMPATIBLE:
|
2015-10-08 21:41:38 +02:00
|
|
|
compatible = cursor.getInt(i) == 1;
|
|
|
|
break;
|
2016-07-28 04:25:17 +02:00
|
|
|
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;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.NAME:
|
2015-10-08 21:41:38 +02:00
|
|
|
name = cursor.getString(i);
|
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.SUMMARY:
|
2015-10-08 21:41:38 +02:00
|
|
|
summary = cursor.getString(i);
|
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.ICON:
|
2020-02-21 16:08:08 +01:00
|
|
|
iconFromApk = cursor.getString(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.DESCRIPTION:
|
2015-10-08 21:41:38 +02:00
|
|
|
description = cursor.getString(i);
|
|
|
|
break;
|
2017-04-14 01:31:33 +02:00
|
|
|
case Cols.WHATSNEW:
|
|
|
|
whatsNew = cursor.getString(i);
|
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.LICENSE:
|
2015-10-08 21:41:38 +02:00
|
|
|
license = cursor.getString(i);
|
|
|
|
break;
|
2016-11-25 23:36:37 +01:00
|
|
|
case Cols.AUTHOR_NAME:
|
|
|
|
authorName = cursor.getString(i);
|
2016-01-04 23:53:28 +01:00
|
|
|
break;
|
2016-11-25 23:36:37 +01:00
|
|
|
case Cols.AUTHOR_EMAIL:
|
|
|
|
authorEmail = cursor.getString(i);
|
2016-01-04 23:53:28 +01:00
|
|
|
break;
|
2016-11-25 23:36:37 +01:00
|
|
|
case Cols.WEBSITE:
|
|
|
|
webSite = cursor.getString(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2016-11-25 23:36:37 +01:00
|
|
|
case Cols.ISSUE_TRACKER:
|
|
|
|
issueTracker = cursor.getString(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2016-11-25 23:36:37 +01:00
|
|
|
case Cols.SOURCE_CODE:
|
|
|
|
sourceCode = cursor.getString(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2019-03-22 15:22:11 +01:00
|
|
|
case Cols.TRANSLATION:
|
|
|
|
translation = cursor.getString(i);
|
|
|
|
break;
|
2017-04-14 01:31:33 +02:00
|
|
|
case Cols.VIDEO:
|
|
|
|
video = cursor.getString(i);
|
|
|
|
break;
|
2016-11-25 23:36:37 +01:00
|
|
|
case Cols.CHANGELOG:
|
|
|
|
changelog = cursor.getString(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2016-11-25 23:36:37 +01:00
|
|
|
case Cols.DONATE:
|
|
|
|
donate = cursor.getString(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2016-11-25 23:36:37 +01:00
|
|
|
case Cols.BITCOIN:
|
|
|
|
bitcoin = cursor.getString(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2016-11-25 23:36:37 +01:00
|
|
|
case Cols.LITECOIN:
|
|
|
|
litecoin = cursor.getString(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.FLATTR_ID:
|
2015-10-08 21:41:38 +02:00
|
|
|
flattrID = cursor.getString(i);
|
|
|
|
break;
|
2020-06-16 10:12:24 +02:00
|
|
|
case Cols.LIBERAPAY:
|
|
|
|
liberapay = cursor.getString(i);
|
2018-02-14 22:48:00 +01:00
|
|
|
break;
|
2020-06-10 23:53:16 +02:00
|
|
|
case Cols.OPEN_COLLECTIVE:
|
|
|
|
openCollective = cursor.getString(i);
|
|
|
|
break;
|
2020-01-10 16:54:09 +01:00
|
|
|
case Cols.AutoInstallApk.VERSION_NAME:
|
2020-01-10 17:41:29 +01:00
|
|
|
autoInstallVersionName = cursor.getString(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2017-06-21 07:48:21 +02:00
|
|
|
case Cols.PREFERRED_SIGNER:
|
|
|
|
preferredSigner = cursor.getString(i);
|
|
|
|
break;
|
2020-01-10 16:46:07 +01:00
|
|
|
case Cols.AUTO_INSTALL_VERSION_CODE:
|
|
|
|
autoInstallVersionCode = cursor.getInt(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2020-01-10 17:06:58 +01:00
|
|
|
case Cols.SUGGESTED_VERSION_CODE:
|
|
|
|
suggestedVersionCode = cursor.getInt(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2020-01-10 16:59:21 +01:00
|
|
|
case Cols.SUGGESTED_VERSION_NAME:
|
|
|
|
suggestedVersionName = cursor.getString(i);
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.ADDED:
|
2015-10-08 21:41:38 +02:00
|
|
|
added = Utils.parseDate(cursor.getString(i), null);
|
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.LAST_UPDATED:
|
2015-10-08 21:41:38 +02:00
|
|
|
lastUpdated = Utils.parseDate(cursor.getString(i), null);
|
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.ANTI_FEATURES:
|
2016-06-20 16:17:11 +02:00
|
|
|
antiFeatures = Utils.parseCommaSeparatedString(cursor.getString(i));
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.REQUIREMENTS:
|
2016-06-20 16:17:11 +02:00
|
|
|
requirements = Utils.parseCommaSeparatedString(cursor.getString(i));
|
2015-10-08 21:41:38 +02:00
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.ICON_URL:
|
2015-10-08 21:41:38 +02:00
|
|
|
iconUrl = cursor.getString(i);
|
|
|
|
break;
|
2016-12-06 11:04:22 +01:00
|
|
|
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;
|
2017-09-12 16:48:14 +02:00
|
|
|
case Cols.IS_APK:
|
|
|
|
isApk = cursor.getInt(i) == 1;
|
|
|
|
break;
|
2019-03-22 13:36:53 +01:00
|
|
|
case Cols.IS_LOCALIZED:
|
|
|
|
isLocalized = cursor.getInt(i) == 1;
|
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.InstalledApp.VERSION_CODE:
|
2015-10-08 21:41:38 +02:00
|
|
|
installedVersionCode = cursor.getInt(i);
|
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.InstalledApp.VERSION_NAME:
|
2015-10-08 21:41:38 +02:00
|
|
|
installedVersionName = cursor.getString(i);
|
|
|
|
break;
|
2016-06-30 05:14:01 +02:00
|
|
|
case Cols.InstalledApp.SIGNATURE:
|
2015-10-22 19:42:39 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-06 22:24:05 +02:00
|
|
|
/**
|
|
|
|
* Instantiate from a locally installed package.
|
2018-04-25 17:10:03 +02:00
|
|
|
* <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.
|
2014-05-06 22:24:05 +02:00
|
|
|
*/
|
2018-04-25 17:10:03 +02:00
|
|
|
@Nullable
|
2019-05-14 23:47:24 +02:00
|
|
|
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 {
|
2018-04-25 17:10:03 +02:00
|
|
|
App app = new App();
|
2016-05-25 10:41:37 +02:00
|
|
|
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
|
|
|
|
SanitizedFile apkFile = SanitizedFile.knownSanitized(packageInfo.applicationInfo.publicSourceDir);
|
2018-07-12 10:22:51 +02:00
|
|
|
app.installedApk = new Apk();
|
2019-05-14 23:47:24 +02:00
|
|
|
if (installedApp != null) {
|
|
|
|
app.installedApk.hashType = installedApp.getHashType();
|
|
|
|
app.installedApk.hash = installedApp.getHash();
|
|
|
|
} else if (apkFile.canRead()) {
|
2018-08-06 23:20:32 +02:00
|
|
|
String hashType = "sha256";
|
2022-01-26 10:45:34 +01:00
|
|
|
String hash = Utils.getFileHexDigest(apkFile, hashType);
|
2018-04-25 17:10:03 +02:00
|
|
|
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;
|
2016-05-25 10:41:37 +02:00
|
|
|
}
|
|
|
|
|
2018-12-09 08:37:18 +01:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2019-01-02 17:14:34 +01:00
|
|
|
/**
|
|
|
|
* 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 + "'");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-16 10:12:24 +02:00
|
|
|
/**
|
|
|
|
* {@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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-06 11:04:22 +01:00
|
|
|
/**
|
|
|
|
* Parses the {@code localized} block in the incoming index metadata,
|
|
|
|
* choosing the best match in terms of locale/language while filling as
|
2017-04-28 10:17:20 +02:00
|
|
|
* 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.
|
2016-12-06 11:04:22 +01:00
|
|
|
* <p>
|
2017-04-28 10:17:20 +02:00
|
|
|
* 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
|
2016-12-06 11:04:22 +01:00
|
|
|
* fields that do not have locale info. They should not be used if the
|
2017-04-28 10:17:20 +02:00
|
|
|
* {@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.
|
2021-02-05 16:36:47 +01:00
|
|
|
* <ol>
|
2017-04-28 10:17:20 +02:00
|
|
|
* <li>the country variant {@code de-AT} from the user locale list
|
|
|
|
* <li>only the language {@code de} from the above locale
|
2021-02-05 16:36:47 +01:00
|
|
|
* <li>next locale in the user's preference list ({@code >= android-24})
|
2017-04-28 10:17:20 +02:00
|
|
|
* <li>{@code en-US} since its the most common English for software
|
|
|
|
* <li>the first available {@code en} locale
|
2021-02-05 16:36:47 +01:00
|
|
|
* </ol>
|
2017-04-28 10:17:20 +02:00
|
|
|
* <p>
|
2021-02-05 16:36:47 +01:00
|
|
|
* 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>
|
2016-12-06 11:04:22 +01:00
|
|
|
*/
|
|
|
|
@JsonProperty("localized")
|
2020-07-14 19:28:46 +02:00
|
|
|
void setLocalized(Map<String, Map<String, Object>> localized) { // NOPMD
|
2021-02-05 16:36:47 +01:00
|
|
|
if (systemLocaleList == null) {
|
|
|
|
systemLocaleList = ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration());
|
2017-10-04 22:32:55 +02:00
|
|
|
}
|
2021-02-06 00:37:57 +01:00
|
|
|
Set<String> supportedLocales = localized.keySet();
|
|
|
|
setIsLocalized(supportedLocales);
|
|
|
|
String value = getLocalizedEntry(localized, supportedLocales, "whatsNew");
|
2018-10-16 19:53:30 +02:00
|
|
|
if (!TextUtils.isEmpty(value)) {
|
|
|
|
whatsNew = value;
|
|
|
|
}
|
2021-02-06 00:31:31 +01:00
|
|
|
|
2021-02-06 00:37:57 +01:00
|
|
|
value = getLocalizedEntry(localized, supportedLocales, "video");
|
2017-04-17 12:12:27 +02:00
|
|
|
if (!TextUtils.isEmpty(value)) {
|
2021-02-06 00:31:31 +01:00
|
|
|
video = value.trim();
|
2017-04-17 12:12:27 +02:00
|
|
|
}
|
2021-02-06 00:37:57 +01:00
|
|
|
value = getLocalizedEntry(localized, supportedLocales, "name");
|
2016-12-06 11:04:22 +01:00
|
|
|
if (!TextUtils.isEmpty(value)) {
|
2021-02-06 00:31:31 +01:00
|
|
|
name = value.trim();
|
2016-12-06 11:04:22 +01:00
|
|
|
}
|
2021-02-06 00:37:57 +01:00
|
|
|
value = getLocalizedEntry(localized, supportedLocales, "summary");
|
2016-12-06 11:04:22 +01:00
|
|
|
if (!TextUtils.isEmpty(value)) {
|
2021-02-06 00:31:31 +01:00
|
|
|
summary = value.trim();
|
2016-12-06 11:04:22 +01:00
|
|
|
}
|
2021-02-06 00:37:57 +01:00
|
|
|
value = getLocalizedEntry(localized, supportedLocales, "description");
|
2016-12-06 11:04:22 +01:00
|
|
|
if (!TextUtils.isEmpty(value)) {
|
2017-12-01 12:40:39 +01:00
|
|
|
description = formatDescription(value);
|
2016-12-06 11:04:22 +01:00
|
|
|
}
|
2021-02-06 00:37:57 +01:00
|
|
|
value = getLocalizedGraphicsEntry(localized, supportedLocales, "icon");
|
2020-02-21 14:45:46 +01:00
|
|
|
if (!TextUtils.isEmpty(value)) {
|
|
|
|
iconUrl = value;
|
|
|
|
}
|
2016-12-06 11:04:22 +01:00
|
|
|
|
2021-02-06 00:37:57 +01:00
|
|
|
featureGraphic = getLocalizedGraphicsEntry(localized, supportedLocales, "featureGraphic");
|
|
|
|
promoGraphic = getLocalizedGraphicsEntry(localized, supportedLocales, "promoGraphic");
|
|
|
|
tvBanner = getLocalizedGraphicsEntry(localized, supportedLocales, "tvBanner");
|
2016-12-06 11:04:22 +01:00
|
|
|
|
2021-02-06 00:37:57 +01:00
|
|
|
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");
|
2016-12-06 11:04:22 +01:00
|
|
|
}
|
|
|
|
|
2017-10-04 22:32:55 +02:00
|
|
|
/**
|
2021-02-05 16:36:47 +01:00
|
|
|
* 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.
|
2017-10-04 22:32:55 +02:00
|
|
|
*
|
|
|
|
* @see LocaleList
|
|
|
|
*/
|
2016-12-06 11:04:22 +01:00
|
|
|
private String getLocalizedEntry(Map<String, Map<String, Object>> localized,
|
2021-02-06 00:37:57 +01:00
|
|
|
Set<String> supportedLocales, @NonNull String key) {
|
|
|
|
Map<String, Object> localizedLocaleMap = getLocalizedLocaleMap(localized, supportedLocales, key);
|
2021-02-05 16:36:47 +01:00
|
|
|
if (localizedLocaleMap != null && !localizedLocaleMap.isEmpty()) {
|
|
|
|
for (Object entry : localizedLocaleMap.values()) {
|
|
|
|
return (String) entry; // NOPMD
|
2016-12-06 11:04:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private String getLocalizedGraphicsEntry(Map<String, Map<String, Object>> localized,
|
2021-02-06 00:37:57 +01:00
|
|
|
Set<String> supportedLocales, @NonNull String key) {
|
|
|
|
Map<String, Object> localizedLocaleMap = getLocalizedLocaleMap(localized, supportedLocales, key);
|
2021-02-05 16:36:47 +01:00
|
|
|
if (localizedLocaleMap != null && !localizedLocaleMap.isEmpty()) {
|
|
|
|
for (String locale : localizedLocaleMap.keySet()) {
|
|
|
|
return locale + "/" + localizedLocaleMap.get(locale); // NOPMD
|
2016-12-06 11:04:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-06-02 13:20:33 +02:00
|
|
|
private String[] getLocalizedListEntry(Map<String, Map<String, Object>> localized,
|
2021-02-06 00:37:57 +01:00
|
|
|
Set<String> supportedLocales, @NonNull String key) {
|
|
|
|
Map<String, Object> localizedLocaleMap = getLocalizedLocaleMap(localized, supportedLocales, key);
|
2021-02-05 16:36:47 +01:00
|
|
|
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++;
|
2016-12-06 11:04:22 +01:00
|
|
|
}
|
2021-02-05 16:36:47 +01:00
|
|
|
return result;
|
2016-12-06 11:04:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new String[0];
|
|
|
|
}
|
|
|
|
|
2021-02-05 16:36:47 +01:00
|
|
|
/**
|
|
|
|
* 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,
|
2021-02-06 00:37:57 +01:00
|
|
|
Set<String> supportedLocales, @NonNull String key) {
|
|
|
|
String[] localesToUse = getLocalesForKey(localized, supportedLocales, key);
|
2021-02-05 16:36:47 +01:00
|
|
|
if (localesToUse.length > 0) {
|
|
|
|
Locale firstMatch = systemLocaleList.getFirstMatch(localesToUse);
|
|
|
|
if (firstMatch != null) {
|
2021-02-06 01:23:53 +01:00
|
|
|
for (String languageTag : new String[]{toLanguageTag(firstMatch), null}) {
|
2021-02-05 16:36:47 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-02-06 01:23:53 +01:00
|
|
|
/**
|
|
|
|
* 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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-05 16:36:47 +01:00
|
|
|
/**
|
|
|
|
* Get all locales that have an entry for {@code key}.
|
|
|
|
*/
|
|
|
|
private String[] getLocalesForKey(Map<String, Map<String, Object>> localized,
|
2021-02-06 00:37:57 +01:00
|
|
|
Set<String> supportedLocales, @NonNull String key) {
|
2021-02-05 16:36:47 +01:00
|
|
|
Set<String> localesToUse = new HashSet<>();
|
2021-02-06 00:37:57 +01:00
|
|
|
for (String locale : supportedLocales) {
|
2021-02-05 16:36:47 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-12-01 12:40:39 +01:00
|
|
|
/**
|
|
|
|
* Returns the app description text with all newlines replaced by {@code <br>}
|
|
|
|
*/
|
|
|
|
public static String formatDescription(String description) {
|
|
|
|
return description.replace("\n", "<br>");
|
|
|
|
}
|
|
|
|
|
2021-09-10 10:08:17 +02:00
|
|
|
/**
|
|
|
|
* 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) {
|
2020-02-21 14:45:46 +01:00
|
|
|
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) {
|
2020-02-21 14:45:46 +01:00
|
|
|
if (TextUtils.isEmpty(iconUrl)) {
|
2020-06-16 10:12:24 +02:00
|
|
|
if (TextUtils.isEmpty(iconFromApk)) {
|
2020-02-21 14:45:46 +01:00
|
|
|
return null;
|
|
|
|
}
|
2020-06-16 10:12:24 +02:00
|
|
|
if (iconFromApk.endsWith(".xml")) {
|
2020-02-24 16:44:44 +01:00
|
|
|
// We cannot use xml ressources as icons. F-Droid server should not include them
|
|
|
|
// https://gitlab.com/fdroid/fdroidserver/issues/344
|
|
|
|
return null;
|
|
|
|
}
|
2020-02-21 14:45:46 +01:00
|
|
|
String iconsDir;
|
|
|
|
if (repo.version >= Repo.VERSION_DENSITY_SPECIFIC_ICONS) {
|
|
|
|
iconsDir = Utils.getIconsDir(context, 1.0);
|
|
|
|
} else {
|
|
|
|
iconsDir = Utils.FALLBACK_ICONS_DIR;
|
|
|
|
}
|
2020-11-08 09:00:28 +01:00
|
|
|
return repo.getFileUrl(iconsDir, iconFromApk);
|
2020-02-21 14:45:46 +01:00
|
|
|
}
|
2020-11-08 09:00:28 +01:00
|
|
|
return repo.getFileUrl(packageName, iconUrl);
|
2020-02-21 14:45:46 +01:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-12-06 11:04:22 +01:00
|
|
|
public String getFeatureGraphicUrl(Context context) {
|
|
|
|
if (TextUtils.isEmpty(featureGraphic)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
Repo repo = RepoProvider.Helper.findById(context, repoId);
|
2020-11-08 09:00:28 +01:00
|
|
|
return repo.getFileUrl(packageName, featureGraphic);
|
2016-12-06 11:04:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public String getPromoGraphic(Context context) {
|
|
|
|
if (TextUtils.isEmpty(promoGraphic)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
Repo repo = RepoProvider.Helper.findById(context, repoId);
|
2020-11-08 09:00:28 +01:00
|
|
|
return repo.getFileUrl(packageName, promoGraphic);
|
2016-12-06 11:04:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public String getTvBanner(Context context) {
|
|
|
|
if (TextUtils.isEmpty(tvBanner)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
Repo repo = RepoProvider.Helper.findById(context, repoId);
|
2020-11-08 09:00:28 +01:00
|
|
|
return repo.getFileUrl(packageName, tvBanner);
|
2016-12-06 11:04:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2020-11-08 09:00:28 +01:00
|
|
|
result[i] = repo.getFileUrl(packageName, url);
|
2016-12-06 11:04:22 +01:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-06-28 00:07:50 +02:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2016-06-07 23:24:27 +02:00
|
|
|
private void setFromPackageInfo(PackageManager pm, PackageInfo packageInfo) {
|
2016-05-25 10:41:37 +02:00
|
|
|
|
|
|
|
this.packageName = packageInfo.packageName;
|
2015-04-01 16:11:56 +02:00
|
|
|
final String installerPackageName = pm.getInstallerPackageName(packageName);
|
2014-05-07 05:15:51 +02:00
|
|
|
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) {
|
2015-10-08 19:44:31 +02:00
|
|
|
Log.w(TAG, "Could not get app info: " + installerPackageName, e);
|
2014-05-07 05:15:51 +02:00
|
|
|
}
|
|
|
|
}
|
2016-02-17 12:58:35 +01:00
|
|
|
if (TextUtils.isEmpty(installerPackageLabel)) {
|
2014-05-06 23:09:19 +02:00
|
|
|
installerPackageLabel = installerPackageName;
|
2016-02-17 12:58:35 +01:00
|
|
|
}
|
2014-05-07 05:15:51 +02:00
|
|
|
|
2016-05-25 10:41:37 +02:00
|
|
|
ApplicationInfo appInfo = packageInfo.applicationInfo;
|
2015-04-01 16:11:56 +02:00
|
|
|
final CharSequence appDescription = appInfo.loadDescription(pm);
|
2016-02-17 12:58:35 +01:00
|
|
|
if (TextUtils.isEmpty(appDescription)) {
|
2014-05-06 23:09:19 +02:00
|
|
|
this.summary = "(installed by " + installerPackageLabel + ")";
|
2016-06-01 22:44:03 +02:00
|
|
|
} else if (appDescription.length() > 40) {
|
2014-05-06 23:09:19 +02:00
|
|
|
this.summary = (String) appDescription.subSequence(0, 40);
|
2016-06-01 22:44:03 +02:00
|
|
|
} else {
|
|
|
|
this.summary = (String) appDescription;
|
2016-02-17 12:58:35 +01:00
|
|
|
}
|
2015-05-19 13:08:55 +02:00
|
|
|
this.added = new Date(packageInfo.firstInstallTime);
|
|
|
|
this.lastUpdated = new Date(packageInfo.lastUpdateTime);
|
2014-05-06 23:09:19 +02:00
|
|
|
this.description = "<p>";
|
2016-02-17 12:58:35 +01:00
|
|
|
if (!TextUtils.isEmpty(appDescription)) {
|
2014-05-06 23:09:19 +02:00
|
|
|
this.description += appDescription + "\n";
|
2016-02-17 12:58:35 +01:00
|
|
|
}
|
2014-05-06 23:09:19 +02:00
|
|
|
this.description += "(installed by " + installerPackageLabel
|
|
|
|
+ ", first installed on " + this.added
|
|
|
|
+ ", last updated on " + this.lastUpdated + ")</p>";
|
|
|
|
|
|
|
|
this.name = (String) appInfo.loadLabel(pm);
|
2020-02-21 16:08:08 +01:00
|
|
|
this.iconFromApk = getIconName(packageName, packageInfo.versionCode);
|
2016-05-27 23:02:54 +02:00
|
|
|
this.installedVersionName = packageInfo.versionName;
|
|
|
|
this.installedVersionCode = packageInfo.versionCode;
|
2016-05-18 14:46:07 +02:00
|
|
|
this.compatible = true;
|
2016-05-25 10:41:37 +02:00
|
|
|
}
|
2014-05-06 22:24:05 +02:00
|
|
|
|
2016-06-28 00:07:50 +02:00
|
|
|
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;
|
2022-01-26 10:45:34 +01:00
|
|
|
apk.obbMainFileSha256 = Utils.getFileHexDigest(f, apk.hashType);
|
2016-06-28 00:07:50 +02:00
|
|
|
} else if ("patch".equals(segments[0])) {
|
|
|
|
apk.obbPatchFile = filename;
|
2022-01-26 10:45:34 +01:00
|
|
|
apk.obbPatchFileSha256 = Utils.getFileHexDigest(f, apk.hashType);
|
2016-06-28 00:07:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-25 17:10:03 +02:00
|
|
|
@SuppressWarnings("EmptyForIteratorPad")
|
2016-05-25 10:41:37 +02:00
|
|
|
private void initInstalledApk(Context context, Apk apk, PackageInfo packageInfo, SanitizedFile apkFile)
|
|
|
|
throws IOException, CertificateEncodingException {
|
2016-05-18 14:46:07 +02:00
|
|
|
apk.compatible = true;
|
2016-04-19 13:00:17 +02:00
|
|
|
apk.versionName = packageInfo.versionName;
|
2016-04-19 12:49:00 +02:00
|
|
|
apk.versionCode = packageInfo.versionCode;
|
2014-05-06 22:24:05 +02:00
|
|
|
apk.added = this.added;
|
2016-06-10 10:28:25 +02:00
|
|
|
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;
|
2016-10-07 09:42:43 +02:00
|
|
|
apk.requestedPermissions = packageInfo.requestedPermissions;
|
2016-04-19 12:49:00 +02:00
|
|
|
apk.apkName = apk.packageName + "_" + apk.versionCode + ".apk";
|
2016-05-17 23:49:33 +02:00
|
|
|
|
2016-06-28 00:07:50 +02:00
|
|
|
initInstalledObbFiles(apk);
|
|
|
|
|
2018-02-08 23:52:23 +01:00
|
|
|
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;
|
2016-05-25 10:41:37 +02:00
|
|
|
JarFile apkJar = new JarFile(apkFile);
|
2016-05-17 23:49:33 +02:00
|
|
|
HashSet<String> abis = new HashSet<>(3);
|
|
|
|
Pattern pattern = Pattern.compile("^lib/([a-z0-9-]+)/.*");
|
2018-04-25 17:10:03 +02:00
|
|
|
for (Enumeration<JarEntry> jarEntries = apkJar.entries(); jarEntries.hasMoreElements(); ) {
|
2016-05-17 23:49:33 +02:00
|
|
|
JarEntry jarEntry = jarEntries.nextElement();
|
|
|
|
Matcher matcher = pattern.matcher(jarEntry.getName());
|
|
|
|
if (matcher.matches()) {
|
|
|
|
abis.add(matcher.group(1));
|
|
|
|
}
|
|
|
|
}
|
2016-06-20 16:17:11 +02:00
|
|
|
apk.nativecode = abis.toArray(new String[abis.size()]);
|
2016-05-17 23:49:33 +02:00
|
|
|
|
2015-04-30 20:40:19 +02:00
|
|
|
final JarEntry aSignedEntry = (JarEntry) apkJar.getEntry("AndroidManifest.xml");
|
2014-05-06 22:24:05 +02:00
|
|
|
|
|
|
|
if (aSignedEntry == null) {
|
|
|
|
apkJar.close();
|
|
|
|
throw new CertificateEncodingException("null signed entry!");
|
|
|
|
}
|
|
|
|
|
2022-01-17 09:51:22 +01:00
|
|
|
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();
|
2014-05-06 22:24:05 +02:00
|
|
|
|
2022-01-17 09:51:22 +01:00
|
|
|
if (aSignedEntry.getCertificates() == null
|
|
|
|
|| aSignedEntry.getCertificates().length == 0) {
|
|
|
|
apkJar.close();
|
|
|
|
throw new CertificateEncodingException("No Certificates found!");
|
2014-11-17 12:21:18 +01:00
|
|
|
}
|
2022-01-17 09:51:22 +01:00
|
|
|
|
|
|
|
final Certificate signer = aSignedEntry.getCertificates()[0];
|
|
|
|
byte[] rawCertBytes = signer.getEncoded();
|
2014-05-06 22:24:05 +02:00
|
|
|
apkJar.close();
|
|
|
|
|
2021-01-05 15:26:38 +01:00
|
|
|
apk.sig = Utils.getsig(rawCertBytes);
|
2014-05-06 22:24:05 +02:00
|
|
|
}
|
|
|
|
|
2017-07-05 09:28:46 +02:00
|
|
|
/**
|
|
|
|
* 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.
|
2017-10-04 22:32:55 +02:00
|
|
|
* <p>
|
2017-07-05 09:28:46 +02:00
|
|
|
* Cases where an {@link Apk} will not be found in the database and for which we fall back to
|
|
|
|
* the {@link InstalledAppProvider} include:
|
2017-10-04 22:32:55 +02:00
|
|
|
* <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>
|
2017-07-05 09:28:46 +02:00
|
|
|
*/
|
|
|
|
@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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-06 22:24:05 +02:00
|
|
|
public boolean isValid() {
|
|
|
|
if (TextUtils.isEmpty(this.name)
|
2016-02-17 12:58:35 +01:00
|
|
|
|| TextUtils.isEmpty(this.packageName)) {
|
2014-05-06 22:24:05 +02:00
|
|
|
return false;
|
2016-02-17 12:58:35 +01:00
|
|
|
}
|
2014-05-06 22:24:05 +02:00
|
|
|
|
2016-02-17 12:58:35 +01:00
|
|
|
if (this.installedApk == null) {
|
2014-05-06 22:24:05 +02:00
|
|
|
return false;
|
2016-02-17 12:58:35 +01:00
|
|
|
}
|
2014-05-06 22:24:05 +02:00
|
|
|
|
2016-02-17 12:58:35 +01:00
|
|
|
if (TextUtils.isEmpty(this.installedApk.sig)) {
|
2014-05-06 22:24:05 +02:00
|
|
|
return false;
|
2016-02-17 12:58:35 +01:00
|
|
|
}
|
2014-05-06 22:24:05 +02:00
|
|
|
|
2015-04-30 20:40:19 +02:00
|
|
|
final File apkFile = this.installedApk.installedFile;
|
2015-05-04 00:06:32 +02:00
|
|
|
return !(apkFile == null || !apkFile.canRead());
|
2014-05-06 22:24:05 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
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();
|
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.
|
2016-07-28 04:25:17 +02:00
|
|
|
values.put(Cols.Package.PACKAGE_NAME, packageName);
|
2016-06-30 05:14:01 +02:00
|
|
|
values.put(Cols.NAME, name);
|
2016-07-28 04:25:17 +02:00
|
|
|
values.put(Cols.REPO_ID, repoId);
|
2016-06-30 05:14:01 +02:00
|
|
|
values.put(Cols.SUMMARY, summary);
|
2020-02-21 16:08:08 +01:00
|
|
|
values.put(Cols.ICON, iconFromApk);
|
2016-06-30 05:14:01 +02:00
|
|
|
values.put(Cols.ICON_URL, iconUrl);
|
|
|
|
values.put(Cols.DESCRIPTION, description);
|
2017-04-14 01:31:33 +02:00
|
|
|
values.put(Cols.WHATSNEW, whatsNew);
|
2016-06-30 05:14:01 +02:00
|
|
|
values.put(Cols.LICENSE, license);
|
2016-11-25 23:36:37 +01:00
|
|
|
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);
|
2019-03-22 15:22:11 +01:00
|
|
|
values.put(Cols.TRANSLATION, translation);
|
2017-04-14 01:31:33 +02:00
|
|
|
values.put(Cols.VIDEO, video);
|
2016-11-25 23:36:37 +01:00
|
|
|
values.put(Cols.CHANGELOG, changelog);
|
|
|
|
values.put(Cols.DONATE, donate);
|
|
|
|
values.put(Cols.BITCOIN, bitcoin);
|
|
|
|
values.put(Cols.LITECOIN, litecoin);
|
2016-06-30 05:14:01 +02:00
|
|
|
values.put(Cols.FLATTR_ID, flattrID);
|
2020-06-16 10:12:24 +02:00
|
|
|
values.put(Cols.LIBERAPAY, liberapay);
|
2020-06-10 23:53:16 +02:00
|
|
|
values.put(Cols.OPEN_COLLECTIVE, openCollective);
|
2016-06-30 05:14:01 +02:00
|
|
|
values.put(Cols.ADDED, Utils.formatDate(added, ""));
|
|
|
|
values.put(Cols.LAST_UPDATED, Utils.formatDate(lastUpdated, ""));
|
2017-06-21 07:48:21 +02:00
|
|
|
values.put(Cols.PREFERRED_SIGNER, preferredSigner);
|
2020-01-10 16:46:07 +01:00
|
|
|
values.put(Cols.AUTO_INSTALL_VERSION_CODE, autoInstallVersionCode);
|
2020-01-10 16:59:21 +01:00
|
|
|
values.put(Cols.SUGGESTED_VERSION_NAME, suggestedVersionName);
|
2020-01-10 17:06:58 +01:00
|
|
|
values.put(Cols.SUGGESTED_VERSION_CODE, suggestedVersionCode);
|
2016-10-20 04:03:03 +02:00
|
|
|
values.put(Cols.ForWriting.Categories.CATEGORIES, Utils.serializeCommaSeparatedString(categories));
|
2016-06-30 05:14:01 +02:00
|
|
|
values.put(Cols.ANTI_FEATURES, Utils.serializeCommaSeparatedString(antiFeatures));
|
|
|
|
values.put(Cols.REQUIREMENTS, Utils.serializeCommaSeparatedString(requirements));
|
2016-12-06 11:04:22 +01:00
|
|
|
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));
|
2016-06-30 05:14:01 +02:00
|
|
|
values.put(Cols.IS_COMPATIBLE, compatible ? 1 : 0);
|
2017-09-12 16:48:14 +02:00
|
|
|
values.put(Cols.IS_APK, isApk ? 1 : 0);
|
2019-03-22 13:36:53 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-06-20 12:31:56 +02:00
|
|
|
public boolean isInstalled(Context context) {
|
2017-09-12 16:48:14 +02:00
|
|
|
// 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;
|
2017-06-20 12:31:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
Adding our own cache of currently installed apks in the database.
Previously the data was not stored anywhere, and each time we wanted
to know about all installed apps, we built a ridiculously long SQL
query. The query had essentially one "OR" clause for each installed
app. To make matters worse, it also required one parameter for each
of these, so we could bind the installed app name to a "?" in the query.
SQL has a limit of (usually) 999 parameters which can be provided to
a query, which meant it would fall over if the user had more than
1000 apps installed.
This change introduces a new table called "fdroid_installedApps".
It is initialized on first run, by iterating over the installed apps
as given by the PackageManager. It is subsequenty kept up to date
by a set of BroadcastReceivers, which listen for apps being
uninstalled/installed/upgraded.
It also includes tests to verify that queries of installed apps,
when there are more than 1000 apps installed, don't break.
Finally, tests are also now able to to insert into providers other
than the one under test. This is due to the fact that the providers
often join onto tables managed by other providers.
2014-04-13 16:08:31 +02:00
|
|
|
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;
|
2020-01-10 16:46:07 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-07-25 10:11:31 +02:00
|
|
|
public AppPrefs getPrefs(Context context) {
|
|
|
|
if (prefs == null) {
|
|
|
|
prefs = AppPrefsProvider.Helper.getPrefsOrDefault(context, this);
|
|
|
|
}
|
|
|
|
return prefs;
|
|
|
|
}
|
|
|
|
|
2016-05-27 20:48:09 +02:00
|
|
|
/**
|
|
|
|
* True if there are new versions (apks) available and the user wants
|
|
|
|
* to be notified about them
|
|
|
|
*/
|
2016-07-25 10:11:31 +02:00
|
|
|
public boolean canAndWantToUpdate(Context context) {
|
Adding our own cache of currently installed apks in the database.
Previously the data was not stored anywhere, and each time we wanted
to know about all installed apps, we built a ridiculously long SQL
query. The query had essentially one "OR" clause for each installed
app. To make matters worse, it also required one parameter for each
of these, so we could bind the installed app name to a "?" in the query.
SQL has a limit of (usually) 999 parameters which can be provided to
a query, which meant it would fall over if the user had more than
1000 apps installed.
This change introduces a new table called "fdroid_installedApps".
It is initialized on first run, by iterating over the installed apps
as given by the PackageManager. It is subsequenty kept up to date
by a set of BroadcastReceivers, which listen for apps being
uninstalled/installed/upgraded.
It also includes tests to verify that queries of installed apps,
when there are more than 1000 apps installed, don't break.
Finally, tests are also now able to to insert into providers other
than the one under test. This is due to the fact that the providers
often join onto tables managed by other providers.
2014-04-13 16:08:31 +02:00
|
|
|
boolean canUpdate = hasUpdates();
|
2016-07-25 10:11:31 +02:00
|
|
|
AppPrefs prefs = getPrefs(context);
|
2020-01-10 16:46:07 +01:00
|
|
|
boolean wantsUpdate = !prefs.ignoreAllUpdates && prefs.ignoreThisUpdate < autoInstallVersionCode;
|
2020-02-25 15:47:33 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-05-27 20:48:09 +02:00
|
|
|
/**
|
2018-07-30 18:03:12 +02:00
|
|
|
* @return if the given app should be filtered out based on the
|
2021-10-14 11:25:25 +02:00
|
|
|
* {@link Preferences#PREF_SHOW_ANTI_FEATURES Show Anti-Features Setting}
|
2016-05-27 20:48:09 +02:00
|
|
|
*/
|
2021-10-30 14:09:37 +02:00
|
|
|
public boolean isDisabledByAntiFeatures(Context context) {
|
2021-10-14 11:25:25 +02:00
|
|
|
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)
|
|
|
|
);
|
|
|
|
|
2021-10-14 11:25:25 +02:00
|
|
|
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;
|
|
|
|
}
|
2021-10-14 11:25:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
2014-02-15 10:57:18 +01:00
|
|
|
|
2016-11-30 12:28:12 +01:00
|
|
|
@Nullable
|
|
|
|
public String getBitcoinUri() {
|
2016-11-25 23:36:37 +01:00
|
|
|
return TextUtils.isEmpty(bitcoin) ? null : "bitcoin:" + bitcoin;
|
2016-11-30 12:28:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
public String getLitecoinUri() {
|
2016-11-25 23:36:37 +01:00
|
|
|
return TextUtils.isEmpty(bitcoin) ? null : "litecoin:" + bitcoin;
|
2016-11-30 12:28:12 +01:00
|
|
|
}
|
|
|
|
|
2020-06-10 23:53:16 +02:00
|
|
|
@Nullable
|
|
|
|
public String getOpenCollectiveUri() {
|
|
|
|
return TextUtils.isEmpty(openCollective) ? null : "https://opencollective.com/"
|
|
|
|
+ openCollective + "/donate/";
|
|
|
|
}
|
|
|
|
|
2016-11-30 12:28:12 +01:00
|
|
|
@Nullable
|
|
|
|
public String getFlattrUri() {
|
|
|
|
return TextUtils.isEmpty(flattrID) ? null : "https://flattr.com/thing/" + flattrID;
|
|
|
|
}
|
|
|
|
|
2018-02-14 22:48:00 +01:00
|
|
|
@Nullable
|
|
|
|
public String getLiberapayUri() {
|
2020-06-16 10:12:24 +02:00
|
|
|
return TextUtils.isEmpty(liberapay) ? null : "https://liberapay.com/" + liberapay;
|
2018-02-14 22:48:00 +01:00
|
|
|
}
|
|
|
|
|
2017-02-13 23:07:00 +01:00
|
|
|
/**
|
2020-01-10 17:41:29 +01:00
|
|
|
* @see App#autoInstallVersionName for why this uses a getter while other member variables are
|
2017-02-13 23:07:00 +01:00
|
|
|
* publicly accessible.
|
|
|
|
*/
|
2020-01-10 17:41:29 +01:00
|
|
|
public String getAutoInstallVersionName() {
|
|
|
|
return autoInstallVersionName;
|
2015-10-09 11:40:50 +02:00
|
|
|
}
|
2016-05-23 14:26:52 +02:00
|
|
|
|
|
|
|
/**
|
2016-06-10 10:28:25 +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}
|
|
|
|
*
|
2017-06-06 16:11:46 +02:00
|
|
|
* @see <a href="https://developer.android.com/guide/topics/manifest/uses-sdk-element.html"><uses-sdk></a>
|
2016-05-23 14:26:52 +02:00
|
|
|
*/
|
2016-06-10 10:28:25 +02:00
|
|
|
private static int[] getMinTargetMaxSdkVersions(Context context, String packageName) {
|
2016-05-23 14:26:52 +02:00
|
|
|
int minSdkVersion = Apk.SDK_VERSION_MIN_VALUE;
|
2016-06-10 10:28:25 +02:00
|
|
|
int targetSdkVersion = Apk.SDK_VERSION_MIN_VALUE;
|
2016-05-23 14:26:52 +02:00
|
|
|
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));
|
2016-06-10 10:28:25 +02:00
|
|
|
} else if (xml.getAttributeName(j).equals("targetSdkVersion")) {
|
|
|
|
targetSdkVersion = Integer.parseInt(xml.getAttributeValue(j));
|
2016-05-23 14:26:52 +02:00
|
|
|
} else if (xml.getAttributeName(j).equals("maxSdkVersion")) {
|
|
|
|
maxSdkVersion = Integer.parseInt(xml.getAttributeValue(j));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
eventType = xml.nextToken();
|
|
|
|
}
|
2019-03-28 19:52:21 +01:00
|
|
|
} catch (PackageManager.NameNotFoundException
|
|
|
|
| IOException
|
|
|
|
| XmlPullParserException
|
|
|
|
| NumberFormatException e) {
|
2016-05-23 14:26:52 +02:00
|
|
|
Log.e(TAG, "Could not get min/max sdk version", e);
|
|
|
|
}
|
2016-06-10 10:28:25 +02:00
|
|
|
if (targetSdkVersion < minSdkVersion) {
|
|
|
|
targetSdkVersion = minSdkVersion;
|
|
|
|
}
|
|
|
|
return new int[]{minSdkVersion, targetSdkVersion, maxSdkVersion};
|
2016-05-23 14:26:52 +02:00
|
|
|
}
|
2016-06-23 03:30:01 +02:00
|
|
|
|
|
|
|
public long getId() {
|
|
|
|
return id;
|
|
|
|
}
|
2016-07-25 16:52:39 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public int describeContents() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-18 13:51:18 +02:00
|
|
|
public boolean isUninstallable(Context context) {
|
2017-06-20 12:31:56 +02:00
|
|
|
if (this.isMediaInstalled(context)) {
|
|
|
|
return true;
|
|
|
|
} else if (this.isInstalled(context)) {
|
2017-04-18 13:51:18 +02:00
|
|
|
PackageManager pm = context.getPackageManager();
|
|
|
|
ApplicationInfo appInfo;
|
|
|
|
try {
|
|
|
|
appInfo = pm.getApplicationInfo(this.packageName,
|
|
|
|
PackageManager.GET_UNINSTALLED_PACKAGES);
|
|
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-06-20 12:31:56 +02:00
|
|
|
// System apps aren't uninstallable.
|
2017-04-18 13:51:18 +02:00
|
|
|
final boolean isSystem = (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
|
2017-06-20 12:31:56 +02:00
|
|
|
return !isSystem && this.isInstalled(context);
|
2017-04-18 13:51:18 +02:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-25 16:52:39 +02:00
|
|
|
@Override
|
|
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
|
|
dest.writeByte(this.compatible ? (byte) 1 : (byte) 0);
|
|
|
|
dest.writeString(this.packageName);
|
|
|
|
dest.writeString(this.name);
|
2016-07-28 04:25:17 +02:00
|
|
|
dest.writeLong(this.repoId);
|
2016-07-25 16:52:39 +02:00
|
|
|
dest.writeString(this.summary);
|
2020-02-21 16:08:08 +01:00
|
|
|
dest.writeString(this.iconFromApk);
|
2016-07-25 16:52:39 +02:00
|
|
|
dest.writeString(this.description);
|
2017-04-14 01:31:33 +02:00
|
|
|
dest.writeString(this.whatsNew);
|
2016-07-25 16:52:39 +02:00
|
|
|
dest.writeString(this.license);
|
2016-11-25 23:36:37 +01:00
|
|
|
dest.writeString(this.authorName);
|
|
|
|
dest.writeString(this.authorEmail);
|
|
|
|
dest.writeString(this.webSite);
|
|
|
|
dest.writeString(this.issueTracker);
|
|
|
|
dest.writeString(this.sourceCode);
|
2019-03-22 15:22:11 +01:00
|
|
|
dest.writeString(this.translation);
|
2017-04-14 01:31:33 +02:00
|
|
|
dest.writeString(this.video);
|
2016-11-25 23:36:37 +01:00
|
|
|
dest.writeString(this.changelog);
|
|
|
|
dest.writeString(this.donate);
|
|
|
|
dest.writeString(this.bitcoin);
|
|
|
|
dest.writeString(this.litecoin);
|
2016-07-25 16:52:39 +02:00
|
|
|
dest.writeString(this.flattrID);
|
2020-06-16 10:12:24 +02:00
|
|
|
dest.writeString(this.liberapay);
|
2020-06-10 23:53:16 +02:00
|
|
|
dest.writeString(this.openCollective);
|
2017-06-21 07:48:21 +02:00
|
|
|
dest.writeString(this.preferredSigner);
|
2020-01-10 16:59:21 +01:00
|
|
|
dest.writeString(this.suggestedVersionName);
|
2020-01-10 17:06:58 +01:00
|
|
|
dest.writeInt(this.suggestedVersionCode);
|
2020-01-10 17:41:29 +01:00
|
|
|
dest.writeString(this.autoInstallVersionName);
|
2020-01-10 16:46:07 +01:00
|
|
|
dest.writeInt(this.autoInstallVersionCode);
|
2016-07-25 16:52:39 +02:00
|
|
|
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);
|
2016-12-06 11:04:22 +01:00
|
|
|
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);
|
2017-09-12 16:48:14 +02:00
|
|
|
dest.writeByte(this.isApk ? (byte) 1 : (byte) 0);
|
2019-03-22 13:36:53 +01:00
|
|
|
dest.writeByte(this.isLocalized ? (byte) 1 : (byte) 0);
|
2016-07-25 16:52:39 +02:00
|
|
|
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();
|
2016-07-28 04:25:17 +02:00
|
|
|
this.repoId = in.readLong();
|
2016-07-25 16:52:39 +02:00
|
|
|
this.summary = in.readString();
|
2020-02-21 16:08:08 +01:00
|
|
|
this.iconFromApk = in.readString();
|
2016-07-25 16:52:39 +02:00
|
|
|
this.description = in.readString();
|
2017-04-14 01:31:33 +02:00
|
|
|
this.whatsNew = in.readString();
|
2016-07-25 16:52:39 +02:00
|
|
|
this.license = in.readString();
|
2016-11-25 23:36:37 +01:00
|
|
|
this.authorName = in.readString();
|
|
|
|
this.authorEmail = in.readString();
|
|
|
|
this.webSite = in.readString();
|
|
|
|
this.issueTracker = in.readString();
|
|
|
|
this.sourceCode = in.readString();
|
2019-03-22 15:22:11 +01:00
|
|
|
this.translation = in.readString();
|
2017-04-14 01:31:33 +02:00
|
|
|
this.video = in.readString();
|
2016-11-25 23:36:37 +01:00
|
|
|
this.changelog = in.readString();
|
|
|
|
this.donate = in.readString();
|
|
|
|
this.bitcoin = in.readString();
|
|
|
|
this.litecoin = in.readString();
|
2016-07-25 16:52:39 +02:00
|
|
|
this.flattrID = in.readString();
|
2020-06-16 10:12:24 +02:00
|
|
|
this.liberapay = in.readString();
|
2020-06-10 23:53:16 +02:00
|
|
|
this.openCollective = in.readString();
|
2017-06-21 07:48:21 +02:00
|
|
|
this.preferredSigner = in.readString();
|
2020-01-10 16:59:21 +01:00
|
|
|
this.suggestedVersionName = in.readString();
|
2020-01-10 17:06:58 +01:00
|
|
|
this.suggestedVersionCode = in.readInt();
|
2020-01-10 17:41:29 +01:00
|
|
|
this.autoInstallVersionName = in.readString();
|
2020-01-10 16:46:07 +01:00
|
|
|
this.autoInstallVersionCode = in.readInt();
|
2016-07-25 16:52:39 +02:00
|
|
|
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();
|
2016-12-06 11:04:22 +01:00
|
|
|
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();
|
2017-09-12 16:48:14 +02:00
|
|
|
this.isApk = in.readByte() != 0;
|
2019-03-22 13:36:53 +01:00
|
|
|
this.isLocalized = in.readByte() != 0;
|
2016-07-25 16:52:39 +02:00
|
|
|
this.installedVersionName = in.readString();
|
|
|
|
this.installedVersionCode = in.readInt();
|
|
|
|
this.installedApk = in.readParcelable(Apk.class.getClassLoader());
|
|
|
|
this.installedSig = in.readString();
|
|
|
|
this.id = in.readLong();
|
|
|
|
}
|
|
|
|
|
2017-03-22 16:14:43 +01:00
|
|
|
@JsonIgnore
|
2016-07-25 16:52:39 +02:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
};
|
2017-06-30 04:14:15 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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).
|
2017-10-04 22:32:55 +02:00
|
|
|
* <p>
|
2017-09-12 00:43:17 +02:00
|
|
|
* Will return null for any {@link App} which represents media (instead of an apk) and thus
|
|
|
|
* doesn't have a signer.
|
2017-06-30 04:14:15 +02:00
|
|
|
*/
|
2017-09-05 04:56:05 +02:00
|
|
|
@Nullable
|
2017-06-30 04:14:15 +02:00
|
|
|
public String getMostAppropriateSignature() {
|
|
|
|
if (!TextUtils.isEmpty(installedSig)) {
|
|
|
|
return installedSig;
|
|
|
|
} else if (!TextUtils.isEmpty(preferredSigner)) {
|
|
|
|
return preferredSigner;
|
|
|
|
}
|
2017-09-05 04:56:05 +02:00
|
|
|
|
|
|
|
return null;
|
2017-06-30 04:14:15 +02:00
|
|
|
}
|
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
|
|
|
}
|