There were some weird edge cases that couldn't quite be pinned down,
whereby installing an app would result in a unique key violation being
hit. One example was when somebody was installing an apk from a file
manager. It seems that this doesn't trigger a PACKAGE_CHANGED, but
rather a PACKAGE_INSTALLED. The end result is that it attempts to insert
a record that already exists in the installed apps table. Because we
have a unique key constraing on the appId, it breaks.
This commit changes the way that we insert installed app details.
Instead of inserting some times, and updating other times, we always
insert. If we hit a unique key violation, the row is deleted, and then
the new values are reinserted.
Previously the data was not stored anywhere, and each time we wanted
to know about all installed apps, we built a ridiculously long SQL
query. The query had essentially one "OR" clause for each installed
app. To make matters worse, it also required one parameter for each
of these, so we could bind the installed app name to a "?" in the query.
SQL has a limit of (usually) 999 parameters which can be provided to
a query, which meant it would fall over if the user had more than
1000 apps installed.
This change introduces a new table called "fdroid_installedApps".
It is initialized on first run, by iterating over the installed apps
as given by the PackageManager. It is subsequenty kept up to date
by a set of BroadcastReceivers, which listen for apps being
uninstalled/installed/upgraded.
It also includes tests to verify that queries of installed apps,
when there are more than 1000 apps installed, don't break.
Finally, tests are also now able to to insert into providers other
than the one under test. This is due to the fact that the providers
often join onto tables managed by other providers.
This was explicitly not-allowed previously, and so there was a
test that ensured it threw an exception when attempted on the
ApkProvider. However I implemented it for another feature, but
forgot to change the tests. Now the test no longer tests for
an exception. Rather, it properly tests for the correct execution
of the method.
Jenkins needs some kind of report from the JUnit tests in order to tell
whether the tests succeeded or not. android-junit-report is a library to do
exactly that. With this setup, Jenkins should now successfully understand the
status of the JUnit tests, where before it just ran them and ignored the
results
The idea was good: reduce the amount of copied/pasted code
where ContentValues were initialized, populated, then inserted.
I've kept the idea, by putting it in its own method which is
called twice. But the resources are not loaded dynamically any
more. This is so that the compiler will be able to pick up if
we reference a missing resource. Also, I took the opportunity
to replace the field name string literals with references to
RepoProvider.DataColumns.* constants.
Finally, changed the tests around because now we need to
have the "getInteger()" call mocked in resources correctly
(for priority/inUse).
Removed unused code from ApkProvider.Helper, made it throw proper
exceptions when trying unsupported operations. Refactored tests
a little bit to facilitate separate test cases for the provider
and its helper.
This marks a method as overriding another method, and makes sure that it
matches the signature of the method it is supposed to be overriding,
otherwise it gives a warning.
Its a bit verbose, but can catch mistakes and save time. And the default
Android profile for Eclipse always adds them automatically...
Having the pre-configured Eclipse files in git will make it easier for
other people to work with FDroid in Eclipse, and should not affect anything
else. The key files are .classpath and .project. The .settings/ folder is
for user-specific settings, so its ignored.
This adds the command to update the embedded Android Test Project, so that
it can be run using `cd test/; ant clean emma debug install test`
It also changes -p to --path just to make things a little easier to read.
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.