From df6a37a705d895a5d09e17f16e9cf927959841e1 Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Tue, 4 Jan 2011 23:52:04 +0000 Subject: [PATCH 1/7] Expert mode - currently just displays md5 of signature for installed apps --- res/layout/appdetails.xml | 4 +++ res/values/strings.xml | 3 +++ res/xml/preferences.xml | 17 ++++++------ src/org/fdroid/fdroid/AppDetails.java | 39 +++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/res/layout/appdetails.xml b/res/layout/appdetails.xml index 057645189..ee88f8e19 100644 --- a/res/layout/appdetails.xml +++ b/res/layout/appdetails.xml @@ -28,6 +28,10 @@ android:textSize="12sp" android:layout_height="wrap_content" android:layout_width="fill_parent" /> + + diff --git a/res/values/strings.xml b/res/values/strings.xml index ddc4145c6..4afc6faec 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -115,4 +115,7 @@ Network Services Show apps that promote non-free network services + Expert + Enabled expert mode + diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index 7baff4fe6..6ad6b66fb 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -7,9 +7,9 @@ + android:summary="@string/update_apps_list" android:key="updateInterval" + android:defaultValue="0" android:entries="@array/updateIntervalNames" + android:entryValues="@array/updateIntervalValues" /> @@ -29,9 +29,10 @@ android:key="cacheDownloaded" /> - - + + + diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index 551e02320..f0cb9a332 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -22,7 +22,10 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; +import java.math.BigInteger; import java.net.URL; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; @@ -43,6 +46,7 @@ import android.widget.TextView; import android.widget.Toast; import android.content.pm.PackageManager; import android.content.pm.PackageInfo; +import android.content.pm.Signature; import android.content.pm.PackageManager.NameNotFoundException; import android.content.Context; import android.content.DialogInterface; @@ -162,8 +166,12 @@ public class AppDetails extends ListActivity { } private boolean pref_cacheDownloaded; + private boolean pref_expert; private boolean viewResetRequired; + // The signature of the installed version. + private Signature mInstalledSignature; + @Override protected void onStart() { super.onStart(); @@ -174,6 +182,7 @@ public class AppDetails extends ListActivity { SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(getBaseContext()); pref_cacheDownloaded = prefs.getBoolean("cacheDownloaded", true); + pref_expert = prefs.getBoolean("expert", false); viewResetRequired = true; } @@ -204,6 +213,19 @@ public class AppDetails extends ListActivity { DB.Apk curver = app.getCurrentVersion(); app_currentvercode = curver == null ? 0 : curver.vercode; + // Get the signature of the installed package... + mInstalledSignature = null; + if (curver != null) { + PackageManager pm = getBaseContext().getPackageManager(); + try { + PackageInfo pi = pm.getPackageInfo(appid, + PackageManager.GET_SIGNATURES); + mInstalledSignature = pi.signatures[0]; + } catch (NameNotFoundException e) { + Log.d("FDroid", "Failed to get installed signature"); + } + } + // Set the icon... ImageView iv = (ImageView) findViewById(R.id.icon); String icon_path = DB.getIconsPath() + app.icon; @@ -229,6 +251,23 @@ public class AppDetails extends ListActivity { app.installedVersion)); tv = (TextView) findViewById(R.id.description); tv.setText(app.description); + if (pref_expert && mInstalledSignature != null) { + try { + tv = (TextView) findViewById(R.id.signature); + byte[] sig = mInstalledSignature.toByteArray(); + MessageDigest md; + md = MessageDigest.getInstance("MD5"); + byte[] md5sum = new byte[32]; + md.update(sig, 0, sig.length); + md5sum = md.digest(); + BigInteger bigInt = new BigInteger(1, md5sum); + String md5hash = bigInt.toString(16); + while (md5hash.length() < 32) + md5hash = "0" + md5hash; + tv.setText("Signed: " + md5hash); + } catch (NoSuchAlgorithmException e) { + } + } // Set up the list... ApkListAdapter la = new ApkListAdapter(this); From 0881142023ef52aeb748d10b6da2ce4bf41536df Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Thu, 13 Jan 2011 22:55:03 +0000 Subject: [PATCH 2/7] Might be better to show that dialog before the thread starts - possible FC on gingerbread --- src/org/fdroid/fdroid/AppDetails.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index f0cb9a332..7995bd065 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -414,6 +414,7 @@ public class AppDetails extends ListActivity { pd = new ProgressDialog(this); pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pd.setMessage(getString(R.string.download_server)); + pd.show(); new Thread() { public void run() { @@ -527,8 +528,6 @@ public class AppDetails extends ListActivity { } }.start(); - pd.show(); - } // Handler used to update the progress dialog while downloading. The From c629a6884fa5a9610aa6d5334f42619283dcee1f Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Fri, 14 Jan 2011 23:08:34 +0000 Subject: [PATCH 3/7] Slight adjustment signature md5 calculation, to match server --- src/org/fdroid/fdroid/AppDetails.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index 7995bd065..bd31e7681 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -254,11 +254,10 @@ public class AppDetails extends ListActivity { if (pref_expert && mInstalledSignature != null) { try { tv = (TextView) findViewById(R.id.signature); - byte[] sig = mInstalledSignature.toByteArray(); MessageDigest md; md = MessageDigest.getInstance("MD5"); byte[] md5sum = new byte[32]; - md.update(sig, 0, sig.length); + md.update(mInstalledSignature.toCharsString().getBytes()); md5sum = md.digest(); BigInteger bigInt = new BigInteger(1, md5sum); String md5hash = bigInt.toString(16); From 3aa6d5a7a9829dce2c60b010a08d8783d7ab3070 Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Sun, 16 Jan 2011 12:21:35 +0000 Subject: [PATCH 4/7] Formatting fixes only - seems I had the wrong settings on one of my machines at some point --- src/org/fdroid/fdroid/DB.java | 1164 ++++++++++----------- src/org/fdroid/fdroid/FDroid.java | 826 +++++++-------- src/org/fdroid/fdroid/FDroidApp.java | 2 +- src/org/fdroid/fdroid/RepoXMLHandler.java | 340 +++--- src/org/fdroid/fdroid/UpdateService.java | 210 ++-- 5 files changed, 1271 insertions(+), 1271 deletions(-) diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java index dbefca5b7..f9db98971 100644 --- a/src/org/fdroid/fdroid/DB.java +++ b/src/org/fdroid/fdroid/DB.java @@ -35,586 +35,586 @@ import android.util.Log; public class DB { - private static final String DATABASE_NAME = "fdroid"; - - private SQLiteDatabase db; - - // The TABLE_APP table stores details of all the applications we know about. - // This information is retrieved from the repositories. - private static final String TABLE_APP = "fdroid_app"; - private static final String CREATE_TABLE_APP = "create table " + TABLE_APP - + " ( " + "id text not null, " + "name text not null, " - + "summary text not null, " + "icon text, " - + "description text not null, " + "license text not null, " - + "webURL text, " + "trackerURL text, " + "sourceURL text, " - + "installedVersion text," + "hasUpdates int not null," - + "primary key(id));"; - - public static class App { - - public App() { - name = "Unknown"; - summary = "Unknown application"; - icon = "noicon.png"; - id = "unknown"; - license = "Unknown"; - trackerURL = ""; - sourceURL = ""; - webURL = ""; - antiFeatures = null; - hasUpdates = false; - updated = false; - apks = new Vector(); - } - - public String id; - public String name; - public String summary; - public String icon; - public String description; - public String license; - public String webURL; - public String trackerURL; - public String sourceURL; - public String installedVersion; - public int installedVerCode; - public String marketVersion; - public int marketVercode; - - // Comma-separated list of anti-features (as defined in the metadata - // documentation) or null if there aren't any. - public String antiFeatures; - - // True if there are new versions (apks) that the user hasn't - // explicitly ignored. (We're currently not using the database - // field for this - we make the decision on the fly in getApps(). - public boolean hasUpdates; - - // Used internally for tracking during repo updates. - public boolean updated; - - public Vector apks; - - // Get the current version - this will be one of the Apks from 'apks'. - // Can return null if there are no available versions. - // This should be the 'current' version, as in the most recent stable - // one, that most users would want by default. It might not be the - // most recent, if for example there are betas etc. - public Apk getCurrentVersion() { - - // Try and return the version that's in Google's market first... - if (marketVersion != null && marketVercode > 0) { - for (Apk apk : apks) { - if (apk.vercode == marketVercode) - return apk; - } - } - - // If we don't know the market version, or we don't have it, we - // return the most recent version we have... - int latestcode = -1; - Apk latestapk = null; - for (Apk apk : apks) { - if (apk.vercode > latestcode) { - latestapk = apk; - latestcode = apk.vercode; - } - } - return latestapk; - } - - } - - // The TABLE_APK table stores details of all the application versions we - // know about. Each relates directly back to an entry in TABLE_APP. - // This information is retrieved from the repositories. - private static final String TABLE_APK = "fdroid_apk"; - private static final String CREATE_TABLE_APK = "create table " + TABLE_APK - + " ( " + "id text not null, " + "version text not null, " - + "server text not null, " + "hash text not null, " - + "vercode int not null," + "apkName text not null, " - + "size int not null," + "primary key(id,version));"; - - public static class Apk { - - public Apk() { - updated = false; - size = 0; - apkSource = null; - } - - public String id; - public String version; - public int vercode; - public int size; // Size in bytes - 0 means we don't know! - public String server; - public String hash; - public String apkName; - - // If null, the apk comes from the same server as the repo index. - // Otherwise - // this is the complete URL to download the apk from. - public String apkSource; - - // Used internally for tracking during repo updates. - public boolean updated; - - public String getURL() { - String path = apkName.replace(" ", "%20"); - return server + "/" + path; - } - } - - // The TABLE_REPO table stores the details of the repositories in use. - private static final String TABLE_REPO = "fdroid_repo"; - private static final String CREATE_TABLE_REPO = "create table " - + TABLE_REPO + " (" + "address text primary key, " - + "inuse integer not null, " + "priority integer not null);"; - - public static class Repo { - public String address; - public boolean inuse; - public int priority; - } - - // SQL to update the database to versions beyond the first. Here is - // how the database works: - // - // * The SQL to create the database tables always creates version - // 1. This SQL will never be altered. - // * In the array below there is SQL for each subsequent version - // from 2 onwards. - // * For a new install, the database is always initialised to version - // 1. - // * Then, whether it's a new install or not, all the upgrade SQL in - // the array below is executed in order to bring the database up to - // the latest version. - // * The current version is tracked by an entry in the TABLE_VERSION - // table. - // - private static final String[][] DB_UPGRADES = { - - // Version 2... - { "alter table " + TABLE_APP + " add marketVersion text", - "alter table " + TABLE_APP + " add marketVercode integer" }, - - // Version 3... - { "alter table " + TABLE_APK + " add apkSource text" }, - - // Version 4... - { "alter table " + TABLE_APP + " add installedVerCode integer" }, - - // Version 5... - { "alter table " + TABLE_APP + " add antiFeatures string" } - - }; - - private class DBHelper extends SQLiteOpenHelper { - - public DBHelper(Context context) { - super(context, DATABASE_NAME, null, DB_UPGRADES.length + 1); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(CREATE_TABLE_REPO); - db.execSQL(CREATE_TABLE_APP); - db.execSQL(CREATE_TABLE_APK); - ContentValues values = new ContentValues(); - values.put("address", "http://f-droid.org/repo"); - values.put("inuse", 1); - values.put("priority", 10); - db.insert(TABLE_REPO, null, values); - onUpgrade(db, 1, DB_UPGRADES.length + 1); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - for (int v = oldVersion + 1; v <= newVersion; v++) - for (int i = 0; i < DB_UPGRADES[v - 2].length; i++) - db.execSQL(DB_UPGRADES[v - 2][i]); - } - - } - - public static String getIconsPath() { - return "/sdcard/.fdroid/icons/"; - } - - private PackageManager mPm; - - public DB(Context ctx) { - - DBHelper h = new DBHelper(ctx); - db = h.getWritableDatabase(); - mPm = ctx.getPackageManager(); - } - - public void close() { - db.close(); - db = null; - } - - // Delete the database, which should cause it to be re-created next time - // it's used. - public static void delete(Context ctx) { - try { - ctx.deleteDatabase(DATABASE_NAME); - // Also try and delete the old one, from versions 0.13 and earlier. - ctx.deleteDatabase("fdroid_db"); - } catch (Exception ex) { - Log.d("FDroid", "Exception in DB.delete: " + ex.getMessage()); - } - } - - // Get the number of apps that have updates available. - public int getNumUpdates() { - Vector apps = getApps(null, null, false); - int count = 0; - for (App app : apps) { - if (app.hasUpdates) - count++; - } - return count; - } - - // Return a list of apps matching the given criteria. - // 'appid' - specific app id to retrieve, or null - // 'filter' - search text to filter on, or null - // 'update' - update installed version information from device, rather than - // simply using values cached in the database. Slower. - public Vector getApps(String appid, String filter, boolean update) { - Vector result = new Vector(); - Cursor c = null; - Cursor c2 = null; - try { - - String query = "select * from " + TABLE_APP; - if (appid != null) { - query += " where id = '" + appid + "'"; - } else if (filter != null) { - query += " where name like '%" + filter + "%'" - + " or description like '%" + filter + "%'"; - } - query += " order by name collate nocase"; - - c = db.rawQuery(query, null); - c.moveToFirst(); - while (!c.isAfterLast()) { - - App app = new App(); - app.id = c.getString(c.getColumnIndex("id")); - app.name = c.getString(c.getColumnIndex("name")); - app.summary = c.getString(c.getColumnIndex("summary")); - app.icon = c.getString(c.getColumnIndex("icon")); - app.description = c.getString(c.getColumnIndex("description")); - app.license = c.getString(c.getColumnIndex("license")); - app.webURL = c.getString(c.getColumnIndex("webURL")); - app.trackerURL = c.getString(c.getColumnIndex("trackerURL")); - app.sourceURL = c.getString(c.getColumnIndex("sourceURL")); - app.installedVersion = c.getString(c - .getColumnIndex("installedVersion")); - app.installedVerCode = c.getInt(c - .getColumnIndex("installedVerCode")); - app.marketVersion = c.getString(c - .getColumnIndex("marketVersion")); - app.marketVercode = c.getInt(c.getColumnIndex("marketVercode")); - app.antiFeatures = c - .getString(c.getColumnIndex("antiFeatures")); - app.hasUpdates = false; - - c2 = db.rawQuery("select * from " + TABLE_APK - + " where id = ? order by vercode desc", - new String[] { app.id }); - c2.moveToFirst(); - while (!c2.isAfterLast()) { - Apk apk = new Apk(); - apk.id = app.id; - apk.version = c2.getString(c2.getColumnIndex("version")); - apk.vercode = c2.getInt(c2.getColumnIndex("vercode")); - apk.server = c2.getString(c2.getColumnIndex("server")); - apk.hash = c2.getString(c2.getColumnIndex("hash")); - apk.size = c2.getInt(c2.getColumnIndex("size")); - apk.apkName = c2.getString(c2.getColumnIndex("apkName")); - apk.apkSource = c2 - .getString(c2.getColumnIndex("apkSource")); - app.apks.add(apk); - c2.moveToNext(); - } - c2.close(); - - result.add(app); - c.moveToNext(); - } - - } catch (Exception e) { - Log.d("FDroid", "Exception during database reading - " - + e.getMessage() + " ... " + e.toString()); - } finally { - if (c != null) { - c.close(); - } - if (c2 != null) { - c2.close(); - } - } - - if (update) { - getUpdates(result); - } - - // We'll say an application has updates if it's installed AND the - // installed version is not the 'current' one AND the installed - // version is older than the current one. - for (App app : result) { - Apk curver = app.getCurrentVersion(); - if (curver != null && app.installedVersion != null - && !app.installedVersion.equals(curver.version)) { - if (app.installedVerCode < curver.vercode) - app.hasUpdates = true; - } - } - - return result; - } - - // Verify installed status against the system's package list. - private void getUpdates(Vector apps) { - List installedPackages = mPm.getInstalledPackages(0); - Map systemApks = new HashMap(); - Log.d("FDroid", "Reading installed packages"); - for (PackageInfo appInfo : installedPackages) { - systemApks.put(appInfo.packageName, appInfo); - } - - for (DB.App app : apps) { - if (systemApks.containsKey(app.id)) { - PackageInfo sysapk = systemApks.get(app.id); - String version = sysapk.versionName; - int vercode = sysapk.versionCode; - if (app.installedVersion == null - || !app.installedVersion.equals(version)) { - setInstalledVersion(app.id, version, vercode); - app.installedVersion = version; - } - } else { - if (app.installedVersion != null) { - setInstalledVersion(app.id, null, 0); - app.installedVersion = null; - } - } - } - } - - private Vector updateApps = null; - - // Called before a repo update starts. - public void beginUpdate() { - // Get a list of all apps. All the apps and apks in this list will - // have 'updated' set to false at this point, and we will only set - // it to true when we see the app/apk in a repository. Thus, at the - // end, any that are still false can be removed. - // TODO: Need to ensure that UI and UpdateService can't both be doing - // an update at the same time. - updateApps = getApps(null, null, true); - Log.d("FDroid", "AppUpdate: " + updateApps.size() - + " apps before starting."); - } - - // Called when a repo update ends. Any applications that have not been - // updated (by a call to updateApplication) are assumed to be no longer - // in the repos. - public void endUpdate() { - if (updateApps == null) - return; - for (App app : updateApps) { - if (!app.updated) { - // The application hasn't been updated, so it's no longer - // in the repos. - Log.d("FDroid", "AppUpdate: " + app.name - + " is no longer in any repository - removing"); - db.delete(TABLE_APP, "id = ?", new String[] { app.id }); - db.delete(TABLE_APK, "id = ?", new String[] { app.id }); - } else { - for (Apk apk : app.apks) { - if (!apk.updated) { - // The package hasn't been updated, so this is a - // version that's no longer available. - Log.d("FDroid", "AppUpdate: Package " + apk.id + "/" - + apk.version - + " is no longer in any repository - removing"); - db.delete(TABLE_APK, "id = ? and version = ?", - new String[] { app.id, apk.version }); - } - } - } - } - Log.d("FDroid", "AppUpdate: " + updateApps.size() - + " apps on completion."); - updateApps = null; - return; - } - - // Called during update to supply new details for an application (or - // details of a completely new one). Calls to this must be wrapped by - // a call to beginUpdate and a call to endUpdate. - public void updateApplication(App upapp) { - - if (updateApps == null) { - return; - } - - boolean found = false; - for (App app : updateApps) { - if (app.id.equals(upapp.id)) { - Log.d("FDroid", "AppUpdate: " + app.id - + " is already in the database."); - updateAppIfDifferent(app, upapp); - app.updated = true; - found = true; - for (Apk upapk : upapp.apks) { - boolean afound = false; - for (Apk apk : app.apks) { - if (apk.version.equals(upapk.version)) { - Log.d("FDroid", "AppUpdate: " + apk.version - + " is a known version."); - updateApkIfDifferent(apk, upapk); - apk.updated = true; - afound = true; - break; - } - } - if (!afound) { - // A new version of this application. - Log.d("FDroid", "AppUpdate: " + upapk.version - + " is a new version."); - updateApkIfDifferent(null, upapk); - upapk.updated = true; - app.apks.add(upapk); - } - } - break; - } - } - if (!found) { - // It's a brand new application... - Log - .d("FDroid", "AppUpdate: " + upapp.id - + " is a new application."); - updateAppIfDifferent(null, upapp); - for (Apk upapk : upapp.apks) { - updateApkIfDifferent(null, upapk); - upapk.updated = true; - } - upapp.updated = true; - updateApps.add(upapp); - } - - } - - // Update application details in the database, if different to the - // previous ones. - // 'oldapp' - previous details - i.e. what's in the database. - // If null, this app is not in the database at all and - // should be added. - // 'upapp' - updated details - private void updateAppIfDifferent(App oldapp, App upapp) { - ContentValues values = new ContentValues(); - values.put("id", upapp.id); - values.put("name", upapp.name); - values.put("summary", upapp.summary); - values.put("icon", upapp.icon); - values.put("description", upapp.description); - values.put("license", upapp.license); - values.put("webURL", upapp.webURL); - values.put("trackerURL", upapp.trackerURL); - values.put("sourceURL", upapp.sourceURL); - values.put("installedVersion", upapp.installedVersion); - values.put("installedVerCode", upapp.installedVerCode); - values.put("marketVersion", upapp.marketVersion); - values.put("marketVercode", upapp.marketVercode); - values.put("antiFeatures", upapp.antiFeatures); - values.put("hasUpdates", upapp.hasUpdates ? 1 : 0); - if (oldapp != null) { - db.update(TABLE_APP, values, "id = ?", new String[] { oldapp.id }); - } else { - db.insert(TABLE_APP, null, values); - } - } - - // Update apk details in the database, if different to the - // previous ones. - // 'oldapk' - previous details - i.e. what's in the database. - // If null, this apk is not in the database at all and - // should be added. - // 'upapk' - updated details - private void updateApkIfDifferent(Apk oldapk, Apk upapk) { - ContentValues values = new ContentValues(); - values.put("id", upapk.id); - values.put("version", upapk.version); - values.put("vercode", upapk.vercode); - values.put("server", upapk.server); - values.put("hash", upapk.hash); - values.put("size", upapk.size); - values.put("apkName", upapk.apkName); - values.put("apkSource", upapk.apkSource); - if (oldapk != null) { - db.update(TABLE_APK, values, "id = ? and version =?", new String[] { - oldapk.id, oldapk.version }); - } else { - db.insert(TABLE_APK, null, values); - } - } - - public void setInstalledVersion(String id, String version, int vercode) { - ContentValues values = new ContentValues(); - values.put("installedVersion", version); - values.put("installedVerCode", vercode); - db.update(TABLE_APP, values, "id = ?", new String[] { id }); - } - - // Get a list of the configured repositories. - public Vector getRepos() { - Vector repos = new Vector(); - Cursor c = null; - try { - c = db.rawQuery("select address, inuse, priority from " - + TABLE_REPO + " order by priority", null); - c.moveToFirst(); - while (!c.isAfterLast()) { - Repo repo = new Repo(); - repo.address = c.getString(0); - repo.inuse = (c.getInt(1) == 1); - repo.priority = c.getInt(2); - repos.add(repo); - c.moveToNext(); - } - } catch (Exception e) { - } finally { - if (c != null) { - c.close(); - } - } - return repos; - } - - public void changeServerStatus(String address) { - db.rawQuery("update " + TABLE_REPO - + " set inuse=1-inuse where address= ?", - new String[] { address }); - } - - public void addServer(String address, int priority) { - ContentValues values = new ContentValues(); - values.put("address", address); - values.put("inuse", 1); - values.put("priority", priority); - db.insert(TABLE_REPO, null, values); - } - - public void removeServers(Vector addresses) { - for (String address : addresses) { - db.delete(TABLE_REPO, "address = ?", new String[] { address }); - } - } + private static final String DATABASE_NAME = "fdroid"; + + private SQLiteDatabase db; + + // The TABLE_APP table stores details of all the applications we know about. + // This information is retrieved from the repositories. + private static final String TABLE_APP = "fdroid_app"; + private static final String CREATE_TABLE_APP = "create table " + TABLE_APP + + " ( " + "id text not null, " + "name text not null, " + + "summary text not null, " + "icon text, " + + "description text not null, " + "license text not null, " + + "webURL text, " + "trackerURL text, " + "sourceURL text, " + + "installedVersion text," + "hasUpdates int not null," + + "primary key(id));"; + + public static class App { + + public App() { + name = "Unknown"; + summary = "Unknown application"; + icon = "noicon.png"; + id = "unknown"; + license = "Unknown"; + trackerURL = ""; + sourceURL = ""; + webURL = ""; + antiFeatures = null; + hasUpdates = false; + updated = false; + apks = new Vector(); + } + + public String id; + public String name; + public String summary; + public String icon; + public String description; + public String license; + public String webURL; + public String trackerURL; + public String sourceURL; + public String installedVersion; + public int installedVerCode; + public String marketVersion; + public int marketVercode; + + // Comma-separated list of anti-features (as defined in the metadata + // documentation) or null if there aren't any. + public String antiFeatures; + + // True if there are new versions (apks) that the user hasn't + // explicitly ignored. (We're currently not using the database + // field for this - we make the decision on the fly in getApps(). + public boolean hasUpdates; + + // Used internally for tracking during repo updates. + public boolean updated; + + public Vector apks; + + // Get the current version - this will be one of the Apks from 'apks'. + // Can return null if there are no available versions. + // This should be the 'current' version, as in the most recent stable + // one, that most users would want by default. It might not be the + // most recent, if for example there are betas etc. + public Apk getCurrentVersion() { + + // Try and return the version that's in Google's market first... + if (marketVersion != null && marketVercode > 0) { + for (Apk apk : apks) { + if (apk.vercode == marketVercode) + return apk; + } + } + + // If we don't know the market version, or we don't have it, we + // return the most recent version we have... + int latestcode = -1; + Apk latestapk = null; + for (Apk apk : apks) { + if (apk.vercode > latestcode) { + latestapk = apk; + latestcode = apk.vercode; + } + } + return latestapk; + } + + } + + // The TABLE_APK table stores details of all the application versions we + // know about. Each relates directly back to an entry in TABLE_APP. + // This information is retrieved from the repositories. + private static final String TABLE_APK = "fdroid_apk"; + private static final String CREATE_TABLE_APK = "create table " + TABLE_APK + + " ( " + "id text not null, " + "version text not null, " + + "server text not null, " + "hash text not null, " + + "vercode int not null," + "apkName text not null, " + + "size int not null," + "primary key(id,version));"; + + public static class Apk { + + public Apk() { + updated = false; + size = 0; + apkSource = null; + } + + public String id; + public String version; + public int vercode; + public int size; // Size in bytes - 0 means we don't know! + public String server; + public String hash; + public String apkName; + + // If null, the apk comes from the same server as the repo index. + // Otherwise + // this is the complete URL to download the apk from. + public String apkSource; + + // Used internally for tracking during repo updates. + public boolean updated; + + public String getURL() { + String path = apkName.replace(" ", "%20"); + return server + "/" + path; + } + } + + // The TABLE_REPO table stores the details of the repositories in use. + private static final String TABLE_REPO = "fdroid_repo"; + private static final String CREATE_TABLE_REPO = "create table " + + TABLE_REPO + " (" + "address text primary key, " + + "inuse integer not null, " + "priority integer not null);"; + + public static class Repo { + public String address; + public boolean inuse; + public int priority; + } + + // SQL to update the database to versions beyond the first. Here is + // how the database works: + // + // * The SQL to create the database tables always creates version + // 1. This SQL will never be altered. + // * In the array below there is SQL for each subsequent version + // from 2 onwards. + // * For a new install, the database is always initialised to version + // 1. + // * Then, whether it's a new install or not, all the upgrade SQL in + // the array below is executed in order to bring the database up to + // the latest version. + // * The current version is tracked by an entry in the TABLE_VERSION + // table. + // + private static final String[][] DB_UPGRADES = { + + // Version 2... + { "alter table " + TABLE_APP + " add marketVersion text", + "alter table " + TABLE_APP + " add marketVercode integer" }, + + // Version 3... + { "alter table " + TABLE_APK + " add apkSource text" }, + + // Version 4... + { "alter table " + TABLE_APP + " add installedVerCode integer" }, + + // Version 5... + { "alter table " + TABLE_APP + " add antiFeatures string" } + + }; + + private class DBHelper extends SQLiteOpenHelper { + + public DBHelper(Context context) { + super(context, DATABASE_NAME, null, DB_UPGRADES.length + 1); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(CREATE_TABLE_REPO); + db.execSQL(CREATE_TABLE_APP); + db.execSQL(CREATE_TABLE_APK); + ContentValues values = new ContentValues(); + values.put("address", "http://f-droid.org/repo"); + values.put("inuse", 1); + values.put("priority", 10); + db.insert(TABLE_REPO, null, values); + onUpgrade(db, 1, DB_UPGRADES.length + 1); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + for (int v = oldVersion + 1; v <= newVersion; v++) + for (int i = 0; i < DB_UPGRADES[v - 2].length; i++) + db.execSQL(DB_UPGRADES[v - 2][i]); + } + + } + + public static String getIconsPath() { + return "/sdcard/.fdroid/icons/"; + } + + private PackageManager mPm; + + public DB(Context ctx) { + + DBHelper h = new DBHelper(ctx); + db = h.getWritableDatabase(); + mPm = ctx.getPackageManager(); + } + + public void close() { + db.close(); + db = null; + } + + // Delete the database, which should cause it to be re-created next time + // it's used. + public static void delete(Context ctx) { + try { + ctx.deleteDatabase(DATABASE_NAME); + // Also try and delete the old one, from versions 0.13 and earlier. + ctx.deleteDatabase("fdroid_db"); + } catch (Exception ex) { + Log.d("FDroid", "Exception in DB.delete: " + ex.getMessage()); + } + } + + // Get the number of apps that have updates available. + public int getNumUpdates() { + Vector apps = getApps(null, null, false); + int count = 0; + for (App app : apps) { + if (app.hasUpdates) + count++; + } + return count; + } + + // Return a list of apps matching the given criteria. + // 'appid' - specific app id to retrieve, or null + // 'filter' - search text to filter on, or null + // 'update' - update installed version information from device, rather than + // simply using values cached in the database. Slower. + public Vector getApps(String appid, String filter, boolean update) { + Vector result = new Vector(); + Cursor c = null; + Cursor c2 = null; + try { + + String query = "select * from " + TABLE_APP; + if (appid != null) { + query += " where id = '" + appid + "'"; + } else if (filter != null) { + query += " where name like '%" + filter + "%'" + + " or description like '%" + filter + "%'"; + } + query += " order by name collate nocase"; + + c = db.rawQuery(query, null); + c.moveToFirst(); + while (!c.isAfterLast()) { + + App app = new App(); + app.id = c.getString(c.getColumnIndex("id")); + app.name = c.getString(c.getColumnIndex("name")); + app.summary = c.getString(c.getColumnIndex("summary")); + app.icon = c.getString(c.getColumnIndex("icon")); + app.description = c.getString(c.getColumnIndex("description")); + app.license = c.getString(c.getColumnIndex("license")); + app.webURL = c.getString(c.getColumnIndex("webURL")); + app.trackerURL = c.getString(c.getColumnIndex("trackerURL")); + app.sourceURL = c.getString(c.getColumnIndex("sourceURL")); + app.installedVersion = c.getString(c + .getColumnIndex("installedVersion")); + app.installedVerCode = c.getInt(c + .getColumnIndex("installedVerCode")); + app.marketVersion = c.getString(c + .getColumnIndex("marketVersion")); + app.marketVercode = c.getInt(c.getColumnIndex("marketVercode")); + app.antiFeatures = c + .getString(c.getColumnIndex("antiFeatures")); + app.hasUpdates = false; + + c2 = db.rawQuery("select * from " + TABLE_APK + + " where id = ? order by vercode desc", + new String[] { app.id }); + c2.moveToFirst(); + while (!c2.isAfterLast()) { + Apk apk = new Apk(); + apk.id = app.id; + apk.version = c2.getString(c2.getColumnIndex("version")); + apk.vercode = c2.getInt(c2.getColumnIndex("vercode")); + apk.server = c2.getString(c2.getColumnIndex("server")); + apk.hash = c2.getString(c2.getColumnIndex("hash")); + apk.size = c2.getInt(c2.getColumnIndex("size")); + apk.apkName = c2.getString(c2.getColumnIndex("apkName")); + apk.apkSource = c2 + .getString(c2.getColumnIndex("apkSource")); + app.apks.add(apk); + c2.moveToNext(); + } + c2.close(); + + result.add(app); + c.moveToNext(); + } + + } catch (Exception e) { + Log.d("FDroid", "Exception during database reading - " + + e.getMessage() + " ... " + e.toString()); + } finally { + if (c != null) { + c.close(); + } + if (c2 != null) { + c2.close(); + } + } + + if (update) { + getUpdates(result); + } + + // We'll say an application has updates if it's installed AND the + // installed version is not the 'current' one AND the installed + // version is older than the current one. + for (App app : result) { + Apk curver = app.getCurrentVersion(); + if (curver != null && app.installedVersion != null + && !app.installedVersion.equals(curver.version)) { + if (app.installedVerCode < curver.vercode) + app.hasUpdates = true; + } + } + + return result; + } + + // Verify installed status against the system's package list. + private void getUpdates(Vector apps) { + List installedPackages = mPm.getInstalledPackages(0); + Map systemApks = new HashMap(); + Log.d("FDroid", "Reading installed packages"); + for (PackageInfo appInfo : installedPackages) { + systemApks.put(appInfo.packageName, appInfo); + } + + for (DB.App app : apps) { + if (systemApks.containsKey(app.id)) { + PackageInfo sysapk = systemApks.get(app.id); + String version = sysapk.versionName; + int vercode = sysapk.versionCode; + if (app.installedVersion == null + || !app.installedVersion.equals(version)) { + setInstalledVersion(app.id, version, vercode); + app.installedVersion = version; + } + } else { + if (app.installedVersion != null) { + setInstalledVersion(app.id, null, 0); + app.installedVersion = null; + } + } + } + } + + private Vector updateApps = null; + + // Called before a repo update starts. + public void beginUpdate() { + // Get a list of all apps. All the apps and apks in this list will + // have 'updated' set to false at this point, and we will only set + // it to true when we see the app/apk in a repository. Thus, at the + // end, any that are still false can be removed. + // TODO: Need to ensure that UI and UpdateService can't both be doing + // an update at the same time. + updateApps = getApps(null, null, true); + Log.d("FDroid", "AppUpdate: " + updateApps.size() + + " apps before starting."); + } + + // Called when a repo update ends. Any applications that have not been + // updated (by a call to updateApplication) are assumed to be no longer + // in the repos. + public void endUpdate() { + if (updateApps == null) + return; + for (App app : updateApps) { + if (!app.updated) { + // The application hasn't been updated, so it's no longer + // in the repos. + Log.d("FDroid", "AppUpdate: " + app.name + + " is no longer in any repository - removing"); + db.delete(TABLE_APP, "id = ?", new String[] { app.id }); + db.delete(TABLE_APK, "id = ?", new String[] { app.id }); + } else { + for (Apk apk : app.apks) { + if (!apk.updated) { + // The package hasn't been updated, so this is a + // version that's no longer available. + Log.d("FDroid", "AppUpdate: Package " + apk.id + "/" + + apk.version + + " is no longer in any repository - removing"); + db.delete(TABLE_APK, "id = ? and version = ?", + new String[] { app.id, apk.version }); + } + } + } + } + Log.d("FDroid", "AppUpdate: " + updateApps.size() + + " apps on completion."); + updateApps = null; + return; + } + + // Called during update to supply new details for an application (or + // details of a completely new one). Calls to this must be wrapped by + // a call to beginUpdate and a call to endUpdate. + public void updateApplication(App upapp) { + + if (updateApps == null) { + return; + } + + boolean found = false; + for (App app : updateApps) { + if (app.id.equals(upapp.id)) { + Log.d("FDroid", "AppUpdate: " + app.id + + " is already in the database."); + updateAppIfDifferent(app, upapp); + app.updated = true; + found = true; + for (Apk upapk : upapp.apks) { + boolean afound = false; + for (Apk apk : app.apks) { + if (apk.version.equals(upapk.version)) { + Log.d("FDroid", "AppUpdate: " + apk.version + + " is a known version."); + updateApkIfDifferent(apk, upapk); + apk.updated = true; + afound = true; + break; + } + } + if (!afound) { + // A new version of this application. + Log.d("FDroid", "AppUpdate: " + upapk.version + + " is a new version."); + updateApkIfDifferent(null, upapk); + upapk.updated = true; + app.apks.add(upapk); + } + } + break; + } + } + if (!found) { + // It's a brand new application... + Log + .d("FDroid", "AppUpdate: " + upapp.id + + " is a new application."); + updateAppIfDifferent(null, upapp); + for (Apk upapk : upapp.apks) { + updateApkIfDifferent(null, upapk); + upapk.updated = true; + } + upapp.updated = true; + updateApps.add(upapp); + } + + } + + // Update application details in the database, if different to the + // previous ones. + // 'oldapp' - previous details - i.e. what's in the database. + // If null, this app is not in the database at all and + // should be added. + // 'upapp' - updated details + private void updateAppIfDifferent(App oldapp, App upapp) { + ContentValues values = new ContentValues(); + values.put("id", upapp.id); + values.put("name", upapp.name); + values.put("summary", upapp.summary); + values.put("icon", upapp.icon); + values.put("description", upapp.description); + values.put("license", upapp.license); + values.put("webURL", upapp.webURL); + values.put("trackerURL", upapp.trackerURL); + values.put("sourceURL", upapp.sourceURL); + values.put("installedVersion", upapp.installedVersion); + values.put("installedVerCode", upapp.installedVerCode); + values.put("marketVersion", upapp.marketVersion); + values.put("marketVercode", upapp.marketVercode); + values.put("antiFeatures", upapp.antiFeatures); + values.put("hasUpdates", upapp.hasUpdates ? 1 : 0); + if (oldapp != null) { + db.update(TABLE_APP, values, "id = ?", new String[] { oldapp.id }); + } else { + db.insert(TABLE_APP, null, values); + } + } + + // Update apk details in the database, if different to the + // previous ones. + // 'oldapk' - previous details - i.e. what's in the database. + // If null, this apk is not in the database at all and + // should be added. + // 'upapk' - updated details + private void updateApkIfDifferent(Apk oldapk, Apk upapk) { + ContentValues values = new ContentValues(); + values.put("id", upapk.id); + values.put("version", upapk.version); + values.put("vercode", upapk.vercode); + values.put("server", upapk.server); + values.put("hash", upapk.hash); + values.put("size", upapk.size); + values.put("apkName", upapk.apkName); + values.put("apkSource", upapk.apkSource); + if (oldapk != null) { + db.update(TABLE_APK, values, "id = ? and version =?", new String[] { + oldapk.id, oldapk.version }); + } else { + db.insert(TABLE_APK, null, values); + } + } + + public void setInstalledVersion(String id, String version, int vercode) { + ContentValues values = new ContentValues(); + values.put("installedVersion", version); + values.put("installedVerCode", vercode); + db.update(TABLE_APP, values, "id = ?", new String[] { id }); + } + + // Get a list of the configured repositories. + public Vector getRepos() { + Vector repos = new Vector(); + Cursor c = null; + try { + c = db.rawQuery("select address, inuse, priority from " + + TABLE_REPO + " order by priority", null); + c.moveToFirst(); + while (!c.isAfterLast()) { + Repo repo = new Repo(); + repo.address = c.getString(0); + repo.inuse = (c.getInt(1) == 1); + repo.priority = c.getInt(2); + repos.add(repo); + c.moveToNext(); + } + } catch (Exception e) { + } finally { + if (c != null) { + c.close(); + } + } + return repos; + } + + public void changeServerStatus(String address) { + db.rawQuery("update " + TABLE_REPO + + " set inuse=1-inuse where address= ?", + new String[] { address }); + } + + public void addServer(String address, int priority) { + ContentValues values = new ContentValues(); + values.put("address", address); + values.put("inuse", 1); + values.put("priority", priority); + db.insert(TABLE_REPO, null, values); + } + + public void removeServers(Vector addresses) { + for (String address : addresses) { + db.delete(TABLE_REPO, "address = ?", new String[] { address }); + } + } } diff --git a/src/org/fdroid/fdroid/FDroid.java b/src/org/fdroid/fdroid/FDroid.java index 7972ebc6e..36934d2ac 100644 --- a/src/org/fdroid/fdroid/FDroid.java +++ b/src/org/fdroid/fdroid/FDroid.java @@ -60,423 +60,423 @@ import android.widget.TabHost.TabSpec; public class FDroid extends TabActivity implements OnItemClickListener { - private class AppListAdapter extends BaseAdapter { - - private List items = new ArrayList(); - - public AppListAdapter(Context context) { - } - - public void addItem(DB.App app) { - items.add(app); - } + private class AppListAdapter extends BaseAdapter { + + private List items = new ArrayList(); + + public AppListAdapter(Context context) { + } + + public void addItem(DB.App app) { + items.add(app); + } - public void clear() { - items.clear(); - } + public void clear() { + items.clear(); + } - @Override - public int getCount() { - return items.size(); - } + @Override + public int getCount() { + return items.size(); + } - @Override - public Object getItem(int position) { - return items.get(position); - } + @Override + public Object getItem(int position) { + return items.get(position); + } - @Override - public long getItemId(int position) { - return position; - } + @Override + public long getItemId(int position) { + return position; + } - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View v = convertView; - if (v == null) { - LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - v = vi.inflate(R.layout.applistitem, null); - } - DB.App app = items.get(position); - - TextView name = (TextView) v.findViewById(R.id.name); - name.setText(app.name); - - String vs; - int numav = app.apks.size(); - if (numav == 1) - vs = getString(R.string.n_version_available); - else - vs = getString(R.string.n_versions_available); - TextView status = (TextView) v.findViewById(R.id.status); - status.setText(String.format(vs, numav)); - - TextView license = (TextView) v.findViewById(R.id.license); - license.setText(app.license); - - TextView summary = (TextView) v.findViewById(R.id.summary); - summary.setText(app.summary); - - ImageView icon = (ImageView) v.findViewById(R.id.icon); - String iconpath = new String(DB.getIconsPath() + app.icon); - File icn = new File(iconpath); - if (icn.exists() && icn.length() > 0) { - new Uri.Builder().build(); - icon.setImageURI(Uri.parse(iconpath)); - } else { - icon.setImageResource(android.R.drawable.sym_def_app_icon); - } - - return v; - } - } - - private String LOCAL_PATH = "/sdcard/.fdroid"; - - private static final int REQUEST_APPDETAILS = 0; - private static final int REQUEST_MANAGEREPOS = 1; - private static final int REQUEST_PREFS = 2; - - private static final int UPDATE_REPO = Menu.FIRST; - private static final int MANAGE_REPO = Menu.FIRST + 1; - private static final int PREFERENCES = Menu.FIRST + 2; - private static final int ABOUT = Menu.FIRST + 3; - - private DB db = null; - - // Apps that are available to be installed - private AppListAdapter apps_av = new AppListAdapter(this); - - // Apps that are installed - private AppListAdapter apps_in = new AppListAdapter(this); - - // Apps that can be upgraded - private AppListAdapter apps_up = new AppListAdapter(this); - - private ProgressDialog pd; - - private static final String TAB_IN = "INST"; - private static final String TAB_UN = "UNIN"; - private static final String TAB_UP = "UPDT"; - private TabHost tabHost; - private TabSpec ts; - private TabSpec ts1; - private TabSpec tsUp; - - private boolean triedEmptyUpdate; - - @Override - protected void onCreate(Bundle savedInstanceState) { - - super.onCreate(savedInstanceState); - - setContentView(R.layout.fdroid); - - File local_path = new File(LOCAL_PATH); - if (!local_path.exists()) - local_path.mkdir(); - - File icon_path = new File(DB.getIconsPath()); - if (!icon_path.exists()) - icon_path.mkdir(); - - tabHost = getTabHost(); - createTabs(); - - Intent i = getIntent(); - if (i.hasExtra("uri")) { - Intent call = new Intent(this, ManageRepo.class); - call.putExtra("uri", i.getStringExtra("uri")); - startActivityForResult(call, REQUEST_MANAGEREPOS); - } - - } - - @Override - protected void onStart() { - super.onStart(); - ((FDroidApp) getApplication()).inActivity++; - db = new DB(this); - triedEmptyUpdate = false; - populateLists(true); - } - - @Override - protected void onStop() { - db.close(); - ((FDroidApp) getApplication()).inActivity--; - super.onStop(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - - super.onCreateOptionsMenu(menu); - menu.add(Menu.NONE, UPDATE_REPO, 1, R.string.menu_update_repo).setIcon( - android.R.drawable.ic_menu_rotate); - menu.add(Menu.NONE, MANAGE_REPO, 2, R.string.menu_manage).setIcon( - android.R.drawable.ic_menu_agenda); - menu.add(Menu.NONE, PREFERENCES, 3, R.string.menu_preferences).setIcon( - android.R.drawable.ic_menu_preferences); - menu.add(Menu.NONE, ABOUT, 4, R.string.menu_about).setIcon( - android.R.drawable.ic_menu_help); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - - case UPDATE_REPO: - updateRepos(); - return true; - - case MANAGE_REPO: - Intent i = new Intent(this, ManageRepo.class); - startActivityForResult(i, REQUEST_MANAGEREPOS); - return true; - - case PREFERENCES: - Intent prefs = new Intent(getBaseContext(), Preferences.class); - startActivityForResult(prefs, REQUEST_PREFS); - return true; - - case ABOUT: - LayoutInflater li = LayoutInflater.from(this); - View view = li.inflate(R.layout.about, null); - - // Fill in the version... - TextView tv = (TextView) view.findViewById(R.id.version); - PackageManager pm = getPackageManager(); - PackageInfo pi; - try { - pi = pm.getPackageInfo( - getApplicationContext().getPackageName(), 0); - tv.setText(pi.versionName); - } catch (Exception e) { - } - - Builder p = new AlertDialog.Builder(this).setView(view); - final AlertDialog alrt = p.create(); - alrt.setIcon(R.drawable.icon); - alrt.setTitle(getString(R.string.about_title)); - alrt.setButton(AlertDialog.BUTTON_NEUTRAL, - getString(R.string.about_website), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int whichButton) { - Uri uri = Uri.parse("http://f-droid.org"); - startActivity(new Intent(Intent.ACTION_VIEW, uri)); - } - }); - alrt.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.ok), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int whichButton) { - } - }); - alrt.show(); - return true; - } - return super.onOptionsItemSelected(item); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - - switch (requestCode) { - case REQUEST_APPDETAILS: - break; - case REQUEST_MANAGEREPOS: - if (data.hasExtra("update")) { - AlertDialog.Builder ask_alrt = new AlertDialog.Builder(this); - ask_alrt.setTitle(getString(R.string.repo_update_title)); - ask_alrt.setIcon(android.R.drawable.ic_menu_rotate); - ask_alrt.setMessage(getString(R.string.repo_alrt)); - ask_alrt.setPositiveButton(getString(R.string.yes), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int whichButton) { - updateRepos(); - } - }); - ask_alrt.setNegativeButton(getString(R.string.no), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int whichButton) { - return; - } - }); - AlertDialog alert = ask_alrt.create(); - alert.show(); - } - break; - case REQUEST_PREFS: - // The automatic update settings may have changed, so reschedule (or - // unschedule) the - // service accordingly. It's cheap, so no need to check if the - // particular setting has - // actually been changed. - UpdateService.schedule(getBaseContext()); - break; - - } - } - - private void createTabs() { - tabHost.clearAllTabs(); - - // TabContentFactory that can generate the appropriate list for each - // tab... - TabHost.TabContentFactory tf = new TabHost.TabContentFactory() { - @Override - public View createTabContent(String tag) { - - AppListAdapter ad; - if (tag.equals(TAB_IN)) - ad = apps_in; - else if (tag.equals(TAB_UP)) - ad = apps_up; - else - ad = apps_av; - - ListView lst = new ListView(FDroid.this); - lst.setOnItemClickListener(FDroid.this); - lst.setAdapter(ad); - return lst; - } - }; - - // Create the tab of installed apps... - ts = tabHost.newTabSpec(TAB_IN); - ts.setIndicator(getString(R.string.tab_installed), getResources() - .getDrawable(drawable.star_off)); - ts.setContent(tf); - - // Create the tab of apps with updates... - tsUp = tabHost.newTabSpec(TAB_UP); - tsUp.setIndicator(getString(R.string.tab_updates), getResources() - .getDrawable(drawable.star_on)); - tsUp.setContent(tf); - - // Create the tab of available apps... - ts1 = tabHost.newTabSpec(TAB_UN); - ts1.setIndicator(getString(R.string.tab_noninstalled), getResources() - .getDrawable(drawable.ic_input_add)); - ts1.setContent(tf); - - tabHost.addTab(ts1); - tabHost.addTab(ts); - tabHost.addTab(tsUp); - - } - - // Populate the lists. - // 'update' - true to update the installed status of the applications - // by asking the system. - private void populateLists(boolean update) { - - apps_in.clear(); - apps_av.clear(); - apps_up.clear(); - - Vector apps = db.getApps(null, null, update); - if (apps.isEmpty()) { - // Don't attempt this more than once - we may have invalid - // repositories. - if (triedEmptyUpdate) - return; - // If there are no apps, update from the repos - it must be a - // new installation. - Log.d("FDroid", "Empty app list forces repo update"); - updateRepos(); - triedEmptyUpdate = true; - return; - } - Log.d("FDroid", "Updating lists - " + apps.size() + " apps in total"); - - for (DB.App app : apps) { - if (app.installedVersion == null) { - apps_av.addItem(app); - } else { - apps_in.addItem(app); - if (app.hasUpdates) - apps_up.addItem(app); - } - } - - // Update the count on the 'Updates' tab to show the number available. - // This is quite unpleasant, but seems to be the only way to do it. - TextView uptext = (TextView) tabHost.getTabWidget().getChildAt(2) - .findViewById(android.R.id.title); - uptext.setText(getString(R.string.tab_updates) + " (" - + Integer.toString(apps_up.getCount()) + ")"); - - // Tell the lists that the data behind the adapter has changed, so - // they can refresh... - apps_av.notifyDataSetChanged(); - apps_in.notifyDataSetChanged(); - apps_up.notifyDataSetChanged(); - - } - - public boolean updateRepos() { - pd = ProgressDialog.show(this, getString(R.string.process_wait_title), - getString(R.string.process_update_msg), true); - pd.setIcon(android.R.drawable.ic_dialog_info); - - // Check for connection first! - ConnectivityManager netstate = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - if (netstate.getNetworkInfo(1).getState() == NetworkInfo.State.CONNECTED - || netstate.getNetworkInfo(0).getState() == NetworkInfo.State.CONNECTED) { - new Thread() { - public void run() { - RepoXMLHandler.doUpdates(FDroid.this, db); - update_handler.sendEmptyMessage(0); - } - }.start(); - return true; - } else { - pd.dismiss(); - Toast.makeText(FDroid.this, getString(R.string.connection_error), - Toast.LENGTH_LONG).show(); - return false; - } - } - - /* - * Handlers for thread functions that need to access GUI - */ - private Handler update_handler = new Handler() { - @Override - public void handleMessage(Message msg) { - populateLists(true); - if (pd.isShowing()) - pd.dismiss(); - } - }; - - // Handler for a click on one of the items in an application list. Pops - // up a dialog that shows the details of the application and all its - // available versions, with buttons to allow installation etc. - public void onItemClick(AdapterView arg0, View arg1, final int arg2, - long arg3) { - - final DB.App app; - String curtab = tabHost.getCurrentTabTag(); - if (curtab.equalsIgnoreCase(TAB_IN)) { - app = (DB.App) apps_in.getItem(arg2); - } else if (curtab.equalsIgnoreCase(TAB_UP)) { - app = (DB.App) apps_up.getItem(arg2); - } else { - app = (DB.App) apps_av.getItem(arg2); - } - - Intent intent = new Intent(this, AppDetails.class); - intent.putExtra("appid", app.id); - startActivityForResult(intent, REQUEST_APPDETAILS); - - } + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View v = convertView; + if (v == null) { + LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + v = vi.inflate(R.layout.applistitem, null); + } + DB.App app = items.get(position); + + TextView name = (TextView) v.findViewById(R.id.name); + name.setText(app.name); + + String vs; + int numav = app.apks.size(); + if (numav == 1) + vs = getString(R.string.n_version_available); + else + vs = getString(R.string.n_versions_available); + TextView status = (TextView) v.findViewById(R.id.status); + status.setText(String.format(vs, numav)); + + TextView license = (TextView) v.findViewById(R.id.license); + license.setText(app.license); + + TextView summary = (TextView) v.findViewById(R.id.summary); + summary.setText(app.summary); + + ImageView icon = (ImageView) v.findViewById(R.id.icon); + String iconpath = new String(DB.getIconsPath() + app.icon); + File icn = new File(iconpath); + if (icn.exists() && icn.length() > 0) { + new Uri.Builder().build(); + icon.setImageURI(Uri.parse(iconpath)); + } else { + icon.setImageResource(android.R.drawable.sym_def_app_icon); + } + + return v; + } + } + + private String LOCAL_PATH = "/sdcard/.fdroid"; + + private static final int REQUEST_APPDETAILS = 0; + private static final int REQUEST_MANAGEREPOS = 1; + private static final int REQUEST_PREFS = 2; + + private static final int UPDATE_REPO = Menu.FIRST; + private static final int MANAGE_REPO = Menu.FIRST + 1; + private static final int PREFERENCES = Menu.FIRST + 2; + private static final int ABOUT = Menu.FIRST + 3; + + private DB db = null; + + // Apps that are available to be installed + private AppListAdapter apps_av = new AppListAdapter(this); + + // Apps that are installed + private AppListAdapter apps_in = new AppListAdapter(this); + + // Apps that can be upgraded + private AppListAdapter apps_up = new AppListAdapter(this); + + private ProgressDialog pd; + + private static final String TAB_IN = "INST"; + private static final String TAB_UN = "UNIN"; + private static final String TAB_UP = "UPDT"; + private TabHost tabHost; + private TabSpec ts; + private TabSpec ts1; + private TabSpec tsUp; + + private boolean triedEmptyUpdate; + + @Override + protected void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + + setContentView(R.layout.fdroid); + + File local_path = new File(LOCAL_PATH); + if (!local_path.exists()) + local_path.mkdir(); + + File icon_path = new File(DB.getIconsPath()); + if (!icon_path.exists()) + icon_path.mkdir(); + + tabHost = getTabHost(); + createTabs(); + + Intent i = getIntent(); + if (i.hasExtra("uri")) { + Intent call = new Intent(this, ManageRepo.class); + call.putExtra("uri", i.getStringExtra("uri")); + startActivityForResult(call, REQUEST_MANAGEREPOS); + } + + } + + @Override + protected void onStart() { + super.onStart(); + ((FDroidApp) getApplication()).inActivity++; + db = new DB(this); + triedEmptyUpdate = false; + populateLists(true); + } + + @Override + protected void onStop() { + db.close(); + ((FDroidApp) getApplication()).inActivity--; + super.onStop(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + + super.onCreateOptionsMenu(menu); + menu.add(Menu.NONE, UPDATE_REPO, 1, R.string.menu_update_repo).setIcon( + android.R.drawable.ic_menu_rotate); + menu.add(Menu.NONE, MANAGE_REPO, 2, R.string.menu_manage).setIcon( + android.R.drawable.ic_menu_agenda); + menu.add(Menu.NONE, PREFERENCES, 3, R.string.menu_preferences).setIcon( + android.R.drawable.ic_menu_preferences); + menu.add(Menu.NONE, ABOUT, 4, R.string.menu_about).setIcon( + android.R.drawable.ic_menu_help); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + + case UPDATE_REPO: + updateRepos(); + return true; + + case MANAGE_REPO: + Intent i = new Intent(this, ManageRepo.class); + startActivityForResult(i, REQUEST_MANAGEREPOS); + return true; + + case PREFERENCES: + Intent prefs = new Intent(getBaseContext(), Preferences.class); + startActivityForResult(prefs, REQUEST_PREFS); + return true; + + case ABOUT: + LayoutInflater li = LayoutInflater.from(this); + View view = li.inflate(R.layout.about, null); + + // Fill in the version... + TextView tv = (TextView) view.findViewById(R.id.version); + PackageManager pm = getPackageManager(); + PackageInfo pi; + try { + pi = pm.getPackageInfo( + getApplicationContext().getPackageName(), 0); + tv.setText(pi.versionName); + } catch (Exception e) { + } + + Builder p = new AlertDialog.Builder(this).setView(view); + final AlertDialog alrt = p.create(); + alrt.setIcon(R.drawable.icon); + alrt.setTitle(getString(R.string.about_title)); + alrt.setButton(AlertDialog.BUTTON_NEUTRAL, + getString(R.string.about_website), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + Uri uri = Uri.parse("http://f-droid.org"); + startActivity(new Intent(Intent.ACTION_VIEW, uri)); + } + }); + alrt.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.ok), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + } + }); + alrt.show(); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + + switch (requestCode) { + case REQUEST_APPDETAILS: + break; + case REQUEST_MANAGEREPOS: + if (data.hasExtra("update")) { + AlertDialog.Builder ask_alrt = new AlertDialog.Builder(this); + ask_alrt.setTitle(getString(R.string.repo_update_title)); + ask_alrt.setIcon(android.R.drawable.ic_menu_rotate); + ask_alrt.setMessage(getString(R.string.repo_alrt)); + ask_alrt.setPositiveButton(getString(R.string.yes), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + updateRepos(); + } + }); + ask_alrt.setNegativeButton(getString(R.string.no), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + return; + } + }); + AlertDialog alert = ask_alrt.create(); + alert.show(); + } + break; + case REQUEST_PREFS: + // The automatic update settings may have changed, so reschedule (or + // unschedule) the + // service accordingly. It's cheap, so no need to check if the + // particular setting has + // actually been changed. + UpdateService.schedule(getBaseContext()); + break; + + } + } + + private void createTabs() { + tabHost.clearAllTabs(); + + // TabContentFactory that can generate the appropriate list for each + // tab... + TabHost.TabContentFactory tf = new TabHost.TabContentFactory() { + @Override + public View createTabContent(String tag) { + + AppListAdapter ad; + if (tag.equals(TAB_IN)) + ad = apps_in; + else if (tag.equals(TAB_UP)) + ad = apps_up; + else + ad = apps_av; + + ListView lst = new ListView(FDroid.this); + lst.setOnItemClickListener(FDroid.this); + lst.setAdapter(ad); + return lst; + } + }; + + // Create the tab of installed apps... + ts = tabHost.newTabSpec(TAB_IN); + ts.setIndicator(getString(R.string.tab_installed), getResources() + .getDrawable(drawable.star_off)); + ts.setContent(tf); + + // Create the tab of apps with updates... + tsUp = tabHost.newTabSpec(TAB_UP); + tsUp.setIndicator(getString(R.string.tab_updates), getResources() + .getDrawable(drawable.star_on)); + tsUp.setContent(tf); + + // Create the tab of available apps... + ts1 = tabHost.newTabSpec(TAB_UN); + ts1.setIndicator(getString(R.string.tab_noninstalled), getResources() + .getDrawable(drawable.ic_input_add)); + ts1.setContent(tf); + + tabHost.addTab(ts1); + tabHost.addTab(ts); + tabHost.addTab(tsUp); + + } + + // Populate the lists. + // 'update' - true to update the installed status of the applications + // by asking the system. + private void populateLists(boolean update) { + + apps_in.clear(); + apps_av.clear(); + apps_up.clear(); + + Vector apps = db.getApps(null, null, update); + if (apps.isEmpty()) { + // Don't attempt this more than once - we may have invalid + // repositories. + if (triedEmptyUpdate) + return; + // If there are no apps, update from the repos - it must be a + // new installation. + Log.d("FDroid", "Empty app list forces repo update"); + updateRepos(); + triedEmptyUpdate = true; + return; + } + Log.d("FDroid", "Updating lists - " + apps.size() + " apps in total"); + + for (DB.App app : apps) { + if (app.installedVersion == null) { + apps_av.addItem(app); + } else { + apps_in.addItem(app); + if (app.hasUpdates) + apps_up.addItem(app); + } + } + + // Update the count on the 'Updates' tab to show the number available. + // This is quite unpleasant, but seems to be the only way to do it. + TextView uptext = (TextView) tabHost.getTabWidget().getChildAt(2) + .findViewById(android.R.id.title); + uptext.setText(getString(R.string.tab_updates) + " (" + + Integer.toString(apps_up.getCount()) + ")"); + + // Tell the lists that the data behind the adapter has changed, so + // they can refresh... + apps_av.notifyDataSetChanged(); + apps_in.notifyDataSetChanged(); + apps_up.notifyDataSetChanged(); + + } + + public boolean updateRepos() { + pd = ProgressDialog.show(this, getString(R.string.process_wait_title), + getString(R.string.process_update_msg), true); + pd.setIcon(android.R.drawable.ic_dialog_info); + + // Check for connection first! + ConnectivityManager netstate = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + if (netstate.getNetworkInfo(1).getState() == NetworkInfo.State.CONNECTED + || netstate.getNetworkInfo(0).getState() == NetworkInfo.State.CONNECTED) { + new Thread() { + public void run() { + RepoXMLHandler.doUpdates(FDroid.this, db); + update_handler.sendEmptyMessage(0); + } + }.start(); + return true; + } else { + pd.dismiss(); + Toast.makeText(FDroid.this, getString(R.string.connection_error), + Toast.LENGTH_LONG).show(); + return false; + } + } + + /* + * Handlers for thread functions that need to access GUI + */ + private Handler update_handler = new Handler() { + @Override + public void handleMessage(Message msg) { + populateLists(true); + if (pd.isShowing()) + pd.dismiss(); + } + }; + + // Handler for a click on one of the items in an application list. Pops + // up a dialog that shows the details of the application and all its + // available versions, with buttons to allow installation etc. + public void onItemClick(AdapterView arg0, View arg1, final int arg2, + long arg3) { + + final DB.App app; + String curtab = tabHost.getCurrentTabTag(); + if (curtab.equalsIgnoreCase(TAB_IN)) { + app = (DB.App) apps_in.getItem(arg2); + } else if (curtab.equalsIgnoreCase(TAB_UP)) { + app = (DB.App) apps_up.getItem(arg2); + } else { + app = (DB.App) apps_av.getItem(arg2); + } + + Intent intent = new Intent(this, AppDetails.class); + intent.putExtra("appid", app.id); + startActivityForResult(intent, REQUEST_APPDETAILS); + + } } diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java index ac4242bec..3670466dc 100644 --- a/src/org/fdroid/fdroid/FDroidApp.java +++ b/src/org/fdroid/fdroid/FDroidApp.java @@ -26,6 +26,6 @@ public class FDroidApp extends Application { // don't want a database update to run. Incremented when entering // one, and decremented when leaving, so if it's 0 it ought to be // ok! - public int inActivity=0; + public int inActivity = 0; } diff --git a/src/org/fdroid/fdroid/RepoXMLHandler.java b/src/org/fdroid/fdroid/RepoXMLHandler.java index 6013a43a5..7a34a5af3 100644 --- a/src/org/fdroid/fdroid/RepoXMLHandler.java +++ b/src/org/fdroid/fdroid/RepoXMLHandler.java @@ -42,203 +42,203 @@ import android.util.Log; public class RepoXMLHandler extends DefaultHandler { - String mserver; + String mserver; - private DB db; + private DB db; - private DB.App curapp = null; - private DB.Apk curapk = null; - private String curchars = null; + private DB.App curapp = null; + private DB.Apk curapk = null; + private String curchars = null; - public RepoXMLHandler(String srv, DB db) { - mserver = srv; - this.db = db; - } + public RepoXMLHandler(String srv, DB db) { + mserver = srv; + this.db = db; + } - @Override - public void characters(char[] ch, int start, int length) - throws SAXException { + @Override + public void characters(char[] ch, int start, int length) + throws SAXException { - super.characters(ch, start, length); + super.characters(ch, start, length); - String str = new String(ch).substring(start, start + length); - if (curchars == null) - curchars = str; - else - curchars += str; - } + String str = new String(ch).substring(start, start + length); + if (curchars == null) + curchars = str; + else + curchars += str; + } - @Override - public void endElement(String uri, String localName, String qName) - throws SAXException { + @Override + public void endElement(String uri, String localName, String qName) + throws SAXException { - super.endElement(uri, localName, qName); - String curel = localName; - String str = curchars; + super.endElement(uri, localName, qName); + String curel = localName; + String str = curchars; - if (curel == "application" && curapp != null) { - Log.d("FDroid", "Repo: Updating application " + curapp.id); - db.updateApplication(curapp); - getIcon(curapp); - curapp = null; - } else if (curel == "package" && curapk != null && curapp != null) { - Log.d("FDroid", "Repo: Package added (" + curapk.version + ")"); - curapp.apks.add(curapk); - curapk = null; - } else if (curapk != null && str != null) { - if (curel == "version") { - curapk.version = str; - } else if (curel == "versioncode") { - try { - curapk.vercode = Integer.parseInt(str); - } catch (NumberFormatException ex) { - curapk.vercode = 0; - } - } else if (curel == "size") { - try { - curapk.size = Integer.parseInt(str); - } catch (NumberFormatException ex) { - curapk.size = 0; - } - } else if (curel == "hash") { - curapk.hash = str; - } else if (curel == "apkname") { - curapk.apkName = str; - } else if (curel == "apksource") { - curapk.apkSource = str; - } - } else if (curapp != null && str != null) { - if (curel == "id") { - Log.d("FDroid", "App id is " + str); - curapp.id = str; - } else if (curel == "name") { - curapp.name = str; - } else if (curel == "icon") { - curapp.icon = str; - } else if (curel == "description") { - curapp.description = str; - } else if (curel == "summary") { - curapp.summary = str; - } else if (curel == "license") { - curapp.license = str; - } else if (curel == "source") { - curapp.sourceURL = str; - } else if (curel == "web") { - curapp.webURL = str; - } else if (curel == "tracker") { - curapp.trackerURL = str; - } else if (curel == "marketversion") { - curapp.marketVersion = str; - } else if (curel == "marketvercode") { - try { - curapp.marketVercode = Integer.parseInt(str); - } catch (NumberFormatException ex) { - curapp.marketVercode = 0; - } - } else if (curel == "antifeatures") { - curapp.antiFeatures = str; - } - } + if (curel == "application" && curapp != null) { + Log.d("FDroid", "Repo: Updating application " + curapp.id); + db.updateApplication(curapp); + getIcon(curapp); + curapp = null; + } else if (curel == "package" && curapk != null && curapp != null) { + Log.d("FDroid", "Repo: Package added (" + curapk.version + ")"); + curapp.apks.add(curapk); + curapk = null; + } else if (curapk != null && str != null) { + if (curel == "version") { + curapk.version = str; + } else if (curel == "versioncode") { + try { + curapk.vercode = Integer.parseInt(str); + } catch (NumberFormatException ex) { + curapk.vercode = 0; + } + } else if (curel == "size") { + try { + curapk.size = Integer.parseInt(str); + } catch (NumberFormatException ex) { + curapk.size = 0; + } + } else if (curel == "hash") { + curapk.hash = str; + } else if (curel == "apkname") { + curapk.apkName = str; + } else if (curel == "apksource") { + curapk.apkSource = str; + } + } else if (curapp != null && str != null) { + if (curel == "id") { + Log.d("FDroid", "App id is " + str); + curapp.id = str; + } else if (curel == "name") { + curapp.name = str; + } else if (curel == "icon") { + curapp.icon = str; + } else if (curel == "description") { + curapp.description = str; + } else if (curel == "summary") { + curapp.summary = str; + } else if (curel == "license") { + curapp.license = str; + } else if (curel == "source") { + curapp.sourceURL = str; + } else if (curel == "web") { + curapp.webURL = str; + } else if (curel == "tracker") { + curapp.trackerURL = str; + } else if (curel == "marketversion") { + curapp.marketVersion = str; + } else if (curel == "marketvercode") { + try { + curapp.marketVercode = Integer.parseInt(str); + } catch (NumberFormatException ex) { + curapp.marketVercode = 0; + } + } else if (curel == "antifeatures") { + curapp.antiFeatures = str; + } + } - } + } - @Override - public void startElement(String uri, String localName, String qName, - Attributes attributes) throws SAXException { + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { - super.startElement(uri, localName, qName, attributes); - if (localName == "application" && curapp == null) { - Log.d("FDroid", "Repo: Found application at " + mserver); - curapp = new DB.App(); - } else if (localName == "package" && curapp != null && curapk == null) { - Log.d("FDroid", "Repo: Found package for " + curapp.id); - curapk = new DB.Apk(); - curapk.id = curapp.id; - curapk.server = mserver; - } - curchars = null; - } + super.startElement(uri, localName, qName, attributes); + if (localName == "application" && curapp == null) { + Log.d("FDroid", "Repo: Found application at " + mserver); + curapp = new DB.App(); + } else if (localName == "package" && curapp != null && curapk == null) { + Log.d("FDroid", "Repo: Found package for " + curapp.id); + curapk = new DB.Apk(); + curapk.id = curapp.id; + curapk.server = mserver; + } + curchars = null; + } - private void getIcon(DB.App app) { - try { + private void getIcon(DB.App app) { + try { - String destpath = DB.getIconsPath() + app.icon; - File f = new File(destpath); - if (f.exists()) - return; + String destpath = DB.getIconsPath() + app.icon; + File f = new File(destpath); + if (f.exists()) + return; - BufferedInputStream getit = new BufferedInputStream(new URL(mserver - + "/icons/" + app.icon).openStream()); - FileOutputStream saveit = new FileOutputStream(destpath); - BufferedOutputStream bout = new BufferedOutputStream(saveit, 1024); - byte data[] = new byte[1024]; + BufferedInputStream getit = new BufferedInputStream(new URL(mserver + + "/icons/" + app.icon).openStream()); + FileOutputStream saveit = new FileOutputStream(destpath); + BufferedOutputStream bout = new BufferedOutputStream(saveit, 1024); + byte data[] = new byte[1024]; - int readed = getit.read(data, 0, 1024); - while (readed != -1) { - bout.write(data, 0, readed); - readed = getit.read(data, 0, 1024); - } - bout.close(); - getit.close(); - saveit.close(); - } catch (Exception e) { + int readed = getit.read(data, 0, 1024); + while (readed != -1) { + bout.write(data, 0, readed); + readed = getit.read(data, 0, 1024); + } + bout.close(); + getit.close(); + saveit.close(); + } catch (Exception e) { - } - } + } + } - public static void doUpdates(Context ctx, DB db) { - db.beginUpdate(); - Vector repos = db.getRepos(); - for (DB.Repo repo : repos) { - if (repo.inuse) { + public static void doUpdates(Context ctx, DB db) { + db.beginUpdate(); + Vector repos = db.getRepos(); + for (DB.Repo repo : repos) { + if (repo.inuse) { - try { + try { - FileOutputStream f = ctx.openFileOutput("tempindex.xml", - Context.MODE_PRIVATE); + FileOutputStream f = ctx.openFileOutput("tempindex.xml", + Context.MODE_PRIVATE); - // Download the index file from the repo... - BufferedInputStream getit = new BufferedInputStream( - new URL(repo.address + "/index.xml").openStream()); + // Download the index file from the repo... + BufferedInputStream getit = new BufferedInputStream( + new URL(repo.address + "/index.xml").openStream()); - BufferedOutputStream bout = new BufferedOutputStream(f, - 1024); - byte data[] = new byte[1024]; + BufferedOutputStream bout = new BufferedOutputStream(f, + 1024); + byte data[] = new byte[1024]; - int readed = getit.read(data, 0, 1024); - while (readed != -1) { - bout.write(data, 0, readed); - readed = getit.read(data, 0, 1024); - } - bout.close(); - getit.close(); - f.close(); + int readed = getit.read(data, 0, 1024); + while (readed != -1) { + bout.write(data, 0, readed); + readed = getit.read(data, 0, 1024); + } + bout.close(); + getit.close(); + f.close(); - // Process the index... - SAXParserFactory spf = SAXParserFactory.newInstance(); - SAXParser sp = spf.newSAXParser(); - XMLReader xr = sp.getXMLReader(); - RepoXMLHandler handler = new RepoXMLHandler(repo.address, - db); - xr.setContentHandler(handler); + // Process the index... + SAXParserFactory spf = SAXParserFactory.newInstance(); + SAXParser sp = spf.newSAXParser(); + XMLReader xr = sp.getXMLReader(); + RepoXMLHandler handler = new RepoXMLHandler(repo.address, + db); + xr.setContentHandler(handler); - InputStreamReader isr = new FileReader(new File(ctx - .getFilesDir() - + "/tempindex.xml")); - InputSource is = new InputSource(isr); - xr.parse(is); + InputStreamReader isr = new FileReader(new File(ctx + .getFilesDir() + + "/tempindex.xml")); + InputSource is = new InputSource(isr); + xr.parse(is); - } catch (Exception e) { - Log.d("FDroid", "Exception updating from " + repo.address - + " - " + e.getMessage()); - } finally { - ctx.deleteFile("tempindex.xml"); - } + } catch (Exception e) { + Log.d("FDroid", "Exception updating from " + repo.address + + " - " + e.getMessage()); + } finally { + ctx.deleteFile("tempindex.xml"); + } - } - } - db.endUpdate(); + } + } + db.endUpdate(); - } + } } diff --git a/src/org/fdroid/fdroid/UpdateService.java b/src/org/fdroid/fdroid/UpdateService.java index 29e785c2f..a10c925f6 100644 --- a/src/org/fdroid/fdroid/UpdateService.java +++ b/src/org/fdroid/fdroid/UpdateService.java @@ -35,129 +35,129 @@ import android.util.Log; public class UpdateService extends Service { - // Schedule (or cancel schedule for) this service, according to the - // current preferences. Should be called a) at boot, or b) if the preference - // is changed. - // TODO: What if we get upgraded? - public static void schedule(Context ctx) { + // Schedule (or cancel schedule for) this service, according to the + // current preferences. Should be called a) at boot, or b) if the preference + // is changed. + // TODO: What if we get upgraded? + public static void schedule(Context ctx) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(ctx); - String sint = prefs.getString("updateInterval", "0"); - int interval = Integer.parseInt(sint); + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(ctx); + String sint = prefs.getString("updateInterval", "0"); + int interval = Integer.parseInt(sint); - Intent intent = new Intent(ctx, UpdateService.class); - PendingIntent pending = PendingIntent.getService(ctx, 0, intent, 0); + Intent intent = new Intent(ctx, UpdateService.class); + PendingIntent pending = PendingIntent.getService(ctx, 0, intent, 0); - AlarmManager alarm = (AlarmManager) ctx - .getSystemService(Context.ALARM_SERVICE); - alarm.cancel(pending); - if (interval > 0) { - alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, - SystemClock.elapsedRealtime() + 5000, - AlarmManager.INTERVAL_HOUR, pending); - } + AlarmManager alarm = (AlarmManager) ctx + .getSystemService(Context.ALARM_SERVICE); + alarm.cancel(pending); + if (interval > 0) { + alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, + SystemClock.elapsedRealtime() + 5000, + AlarmManager.INTERVAL_HOUR, pending); + } - } + } - // For API levels <5 - @Override - public void onStart(Intent intent, int startId) { - handleCommand(); - } + // For API levels <5 + @Override + public void onStart(Intent intent, int startId) { + handleCommand(); + } - // For API levels >=5 - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - handleCommand(); - return START_REDELIVER_INTENT; - } + // For API levels >=5 + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + handleCommand(); + return START_REDELIVER_INTENT; + } - private void handleCommand() { + private void handleCommand() { - new Thread() { - public void run() { + new Thread() { + public void run() { - // If we're in one of our list activities, we don't want - // to run an update because the database will be out of - // sync with the display. - if (((FDroidApp) getApplication()).inActivity != 0) - return; + // If we're in one of our list activities, we don't want + // to run an update because the database will be out of + // sync with the display. + if (((FDroidApp) getApplication()).inActivity != 0) + return; - // See if it's time to actually do anything yet... - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getBaseContext()); - long lastUpdate = prefs.getLong("lastUpdateCheck", 0); - String sint = prefs.getString("updateInterval", "0"); - int interval = Integer.parseInt(sint); - if (interval == 0) - return; - if (lastUpdate + (interval * 60 * 60) > System - .currentTimeMillis()) - return; + // See if it's time to actually do anything yet... + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(getBaseContext()); + long lastUpdate = prefs.getLong("lastUpdateCheck", 0); + String sint = prefs.getString("updateInterval", "0"); + int interval = Integer.parseInt(sint); + if (interval == 0) + return; + if (lastUpdate + (interval * 60 * 60) > System + .currentTimeMillis()) + return; - // Make sure we have a connection... - ConnectivityManager netstate = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - if (netstate.getNetworkInfo(1).getState() != NetworkInfo.State.CONNECTED - && netstate.getNetworkInfo(0).getState() != NetworkInfo.State.CONNECTED) - return; + // Make sure we have a connection... + ConnectivityManager netstate = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + if (netstate.getNetworkInfo(1).getState() != NetworkInfo.State.CONNECTED + && netstate.getNetworkInfo(0).getState() != NetworkInfo.State.CONNECTED) + return; - // Do the update... - DB db = null; - try { - db = new DB(getBaseContext()); - boolean notify = prefs.getBoolean("updateNotify", false); + // Do the update... + DB db = null; + try { + db = new DB(getBaseContext()); + boolean notify = prefs.getBoolean("updateNotify", false); - // Get the number of updates available before we - // start, so we can notify if there are new ones. - // (But avoid doing it if the user doesn't want - // notifications, since it may be time consuming) - int prevUpdates = 0; - if (notify) - prevUpdates = db.getNumUpdates(); + // Get the number of updates available before we + // start, so we can notify if there are new ones. + // (But avoid doing it if the user doesn't want + // notifications, since it may be time consuming) + int prevUpdates = 0; + if (notify) + prevUpdates = db.getNumUpdates(); - RepoXMLHandler.doUpdates(getBaseContext(), db); + RepoXMLHandler.doUpdates(getBaseContext(), db); - if (notify) { - if (db.getNumUpdates() > prevUpdates) { - // And the user wants to know. - NotificationManager n = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - Notification notification = new Notification( - R.drawable.icon, - "FDroid Updates Available", System - .currentTimeMillis()); - Context context = getApplicationContext(); - CharSequence contentTitle = "FDroid"; - CharSequence contentText = "Updates are available."; - Intent notificationIntent = new Intent( - UpdateService.this, FDroid.class); - PendingIntent contentIntent = PendingIntent - .getActivity(UpdateService.this, 0, - notificationIntent, 0); - notification.setLatestEventInfo(context, - contentTitle, contentText, contentIntent); - notification.flags |= Notification.FLAG_AUTO_CANCEL; - n.notify(1, notification); - } - } + if (notify) { + if (db.getNumUpdates() > prevUpdates) { + // And the user wants to know. + NotificationManager n = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + Notification notification = new Notification( + R.drawable.icon, + "FDroid Updates Available", System + .currentTimeMillis()); + Context context = getApplicationContext(); + CharSequence contentTitle = "FDroid"; + CharSequence contentText = "Updates are available."; + Intent notificationIntent = new Intent( + UpdateService.this, FDroid.class); + PendingIntent contentIntent = PendingIntent + .getActivity(UpdateService.this, 0, + notificationIntent, 0); + notification.setLatestEventInfo(context, + contentTitle, contentText, contentIntent); + notification.flags |= Notification.FLAG_AUTO_CANCEL; + n.notify(1, notification); + } + } - } catch (Exception e) { - Log.d("FDroid", "Exception during handleCommand() - " - + e.getMessage()); - } finally { - if (db != null) - db.close(); - stopSelf(); - } + } catch (Exception e) { + Log.d("FDroid", "Exception during handleCommand() - " + + e.getMessage()); + } finally { + if (db != null) + db.close(); + stopSelf(); + } - } - }.start(); + } + }.start(); - } + } - @Override - public IBinder onBind(Intent intent) { - return null; - } + @Override + public IBinder onBind(Intent intent) { + return null; + } } From f05905c85559a9fee3fa05df5e2f7118e1e48713 Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Sun, 16 Jan 2011 16:07:42 +0000 Subject: [PATCH 5/7] Can now detect attempt to install an apk with a different signature and tell the user to uninstall first --- res/values/strings.xml | 1 + src/org/fdroid/fdroid/AppDetails.java | 43 +++++++++++++++-------- src/org/fdroid/fdroid/DB.java | 14 ++++++-- src/org/fdroid/fdroid/RepoXMLHandler.java | 2 ++ 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 4afc6faec..bced43756 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1,5 +1,6 @@ + The new version is signed with a different key to the old one. To install the new version, the old one must be uninstalled first. Please do this and try again. (Note that uninstalling will erase any internal data stored by the application) Version %d versions available %d version available diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index bd31e7681..7bd42ea28 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -171,6 +171,7 @@ public class AppDetails extends ListActivity { // The signature of the installed version. private Signature mInstalledSignature; + private String mInstalledSigID; @Override protected void onStart() { @@ -221,8 +222,21 @@ public class AppDetails extends ListActivity { PackageInfo pi = pm.getPackageInfo(appid, PackageManager.GET_SIGNATURES); mInstalledSignature = pi.signatures[0]; + MessageDigest md; + md = MessageDigest.getInstance("MD5"); + byte[] md5sum = new byte[32]; + md.update(mInstalledSignature.toCharsString().getBytes()); + md5sum = md.digest(); + BigInteger bigInt = new BigInteger(1, md5sum); + String md5hash = bigInt.toString(16); + while (md5hash.length() < 32) + md5hash = "0" + md5hash; + mInstalledSigID = md5hash; } catch (NameNotFoundException e) { Log.d("FDroid", "Failed to get installed signature"); + } catch (NoSuchAlgorithmException e) { + Log.d("FDroid", "Failed to calculate signature MD5 sum"); + mInstalledSignature = null; } } @@ -252,20 +266,8 @@ public class AppDetails extends ListActivity { tv = (TextView) findViewById(R.id.description); tv.setText(app.description); if (pref_expert && mInstalledSignature != null) { - try { - tv = (TextView) findViewById(R.id.signature); - MessageDigest md; - md = MessageDigest.getInstance("MD5"); - byte[] md5sum = new byte[32]; - md.update(mInstalledSignature.toCharsString().getBytes()); - md5sum = md.digest(); - BigInteger bigInt = new BigInteger(1, md5sum); - String md5hash = bigInt.toString(16); - while (md5hash.length() < 32) - md5hash = "0" + md5hash; - tv.setText("Signed: " + md5hash); - } catch (NoSuchAlgorithmException e) { - } + tv = (TextView) findViewById(R.id.signature); + tv.setText("Signed: " + mInstalledSigID); } // Set up the list... @@ -410,6 +412,19 @@ public class AppDetails extends ListActivity { // Install the version of this app denoted by 'curapk'. private void install() { + if (mInstalledSigID != null && !curapk.sig.equals(mInstalledSigID)) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(R.string.SignatureMismatch).setPositiveButton( + "Ok", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + return; + } + pd = new ProgressDialog(this); pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pd.setMessage(getString(R.string.download_server)); diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java index f9db98971..11eefa112 100644 --- a/src/org/fdroid/fdroid/DB.java +++ b/src/org/fdroid/fdroid/DB.java @@ -149,6 +149,11 @@ public class DB { public int size; // Size in bytes - 0 means we don't know! public String server; public String hash; + + // ID (md5 sum of public key) of signature. Might be null, in the + // transition to this field existing. + public String sig; + public String apkName; // If null, the apk comes from the same server as the repo index. @@ -194,7 +199,7 @@ public class DB { // private static final String[][] DB_UPGRADES = { - // Version 2... + // Version 2... { "alter table " + TABLE_APP + " add marketVersion text", "alter table " + TABLE_APP + " add marketVercode integer" }, @@ -205,7 +210,10 @@ public class DB { { "alter table " + TABLE_APP + " add installedVerCode integer" }, // Version 5... - { "alter table " + TABLE_APP + " add antiFeatures string" } + { "alter table " + TABLE_APP + " add antiFeatures string" }, + + // Version 6... + { "alter table " + TABLE_APK + " add sig string" } }; @@ -334,6 +342,7 @@ public class DB { apk.vercode = c2.getInt(c2.getColumnIndex("vercode")); apk.server = c2.getString(c2.getColumnIndex("server")); apk.hash = c2.getString(c2.getColumnIndex("hash")); + apk.sig = c2.getString(c2.getColumnIndex("sig")); apk.size = c2.getInt(c2.getColumnIndex("size")); apk.apkName = c2.getString(c2.getColumnIndex("apkName")); apk.apkSource = c2 @@ -555,6 +564,7 @@ public class DB { values.put("vercode", upapk.vercode); values.put("server", upapk.server); values.put("hash", upapk.hash); + values.put("sig", upapk.sig); values.put("size", upapk.size); values.put("apkName", upapk.apkName); values.put("apkSource", upapk.apkSource); diff --git a/src/org/fdroid/fdroid/RepoXMLHandler.java b/src/org/fdroid/fdroid/RepoXMLHandler.java index 7a34a5af3..01888a7a9 100644 --- a/src/org/fdroid/fdroid/RepoXMLHandler.java +++ b/src/org/fdroid/fdroid/RepoXMLHandler.java @@ -102,6 +102,8 @@ public class RepoXMLHandler extends DefaultHandler { } } else if (curel == "hash") { curapk.hash = str; + } else if (curel == "sig") { + curapk.sig = str; } else if (curel == "apkname") { curapk.apkName = str; } else if (curel == "apksource") { From 93a6abd44dd8a657d1412c709d6150d41fe08d73 Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Sun, 16 Jan 2011 16:15:24 +0000 Subject: [PATCH 6/7] Imported latest translations from pootle server --- locale/de.po | 58 +++---- locale/fr.po | 155 ++++++++++-------- locale/sv.po | 333 ++++++++++++++++++++++++++++++++++++++ res/values-de/strings.xml | 39 ++--- res/values-fr/strings.xml | 73 ++++++++- res/values-it/strings.xml | 24 +-- res/values-sv/strings.xml | 71 ++++++++ 7 files changed, 615 insertions(+), 138 deletions(-) create mode 100644 locale/sv.po create mode 100644 res/values-sv/strings.xml diff --git a/locale/de.po b/locale/de.po index a31deb4a2..4ff609967 100644 --- a/locale/de.po +++ b/locale/de.po @@ -7,74 +7,74 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2010-11-15 07:54+0000\n" -"PO-Revision-Date: 2010-10-31 21:13+0200\n" +"PO-Revision-Date: 2010-11-16 09:54+0200\n" "Last-Translator: Jan-Christoph \n" "Language-Team: de \n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Pootle 2.1.2\n" "Generated-By: Babel 1.0dev\n" -#, fuzzy msgctxt "version" msgid "Version" -msgstr "Server-Version:" +msgstr "Version" -#, fuzzy, python-format +#, python-format msgctxt "n_versions_available" msgid "%d versions available" -msgstr "Aktualisierungen verfügbar" +msgstr "%d verfügbare Versionen" -#, fuzzy, python-format +#, python-format msgctxt "n_version_available" msgid "%d version available" -msgstr "Aktualisierungen verfügbar" +msgstr "%d verfügbare Version" msgctxt "notify" msgid "Notify" -msgstr "" +msgstr "Benachrichtigen" msgctxt "storage" msgid "Storage" -msgstr "" +msgstr "Speicherplatz" msgctxt "cache_downloaded" msgid "Cache downloaded apps" -msgstr "" +msgstr "Heruntergeladene Anwendungen zwischenspeichern" msgctxt "keep_downloaded" msgid "Keep downloaded apk files on SD card" -msgstr "" +msgstr "Heruntergeladene Anwendungsdateien auf der SD-Karte behalten" -#, fuzzy msgctxt "updates" msgid "Updates" msgstr "Aktualisierungen" msgctxt "clear_all_cached_data" msgid "Clear all cached data" -msgstr "" +msgstr "Zwischenspeicher leeren" msgctxt "reset" msgid "Reset" -msgstr "" +msgstr "Zurücksetzen" msgctxt "maintenance" msgid "Maintenance" -msgstr "" +msgstr "Verwaltung" msgctxt "notify_updates_available" msgid "Notify when new updates are available" -msgstr "" +msgstr "Bei Aktualisierungen benachrichtigen" msgctxt "update_apps_list" msgid "Update apps list from repositories automatically" -msgstr "" +msgstr "Anwendungsliste automatisch aktualisieren" msgctxt "automatic_repo_scan" msgid "Automatic repo scan" -msgstr "" +msgstr "Automatische Archivkontrolle" msgctxt "app_name" msgid "FDroid" @@ -84,7 +84,6 @@ msgctxt "about_title" msgid "About FDroid" msgstr "Über FDroid" -#, fuzzy msgctxt "about_desc" msgid "" "Originally based on Aptoide.\n" @@ -97,15 +96,13 @@ msgctxt "about_site" msgid "Home:" msgstr "Internetseite:" -#, fuzzy msgctxt "about_mail" msgid "Email:" -msgstr "E-Mail:" +msgstr "Email:" -#, fuzzy msgctxt "about_version" msgid "Version:" -msgstr "Server-Version:" +msgstr "Version:" msgctxt "about_website" msgid "Web Site" @@ -159,7 +156,6 @@ msgctxt "cancel" msgid "Cancel" msgstr "Abbrechen" -#, fuzzy msgctxt "repo_delete_title" msgid "Choose repository to remove" msgstr "Zu entfernendes Archiv auswählen" @@ -272,7 +268,7 @@ msgstr "Archive verwalten" msgctxt "menu_preferences" msgid "Preferences" -msgstr "" +msgstr "Einstellungen" msgctxt "menu_about" msgid "About" @@ -314,22 +310,20 @@ msgctxt "menu_update" msgid "Update" msgstr "Aktualisieren" -#, fuzzy, python-format +#, python-format msgctxt "details_installed" msgid "Version %s installed" -msgstr "Nicht Installiert" +msgstr "Version %s installiert" #, python-format msgctxt "details_notinstalled" msgid "Not installed (%d available)" -msgstr "" +msgstr "Nicht installiert (%d verfügbar)" -#, fuzzy msgctxt "inst" msgid "Installed" msgstr "Installiert" msgctxt "corrupt_download" msgid "Downloaded file is corrupt" -msgstr "" - +msgstr "Heruntergeladene Datei ist fehlerhaft" diff --git a/locale/fr.po b/locale/fr.po index ac470e288..0f88a1df9 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -2,108 +2,113 @@ # Copyright (C) 2010 ORGANIZATION # This file is distributed under the same license as the PACKAGE project. # FIRST AUTHOR , 2010. -#, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2010-11-15 07:54+0000\n" -"PO-Revision-Date: 2010-11-15 07:54+0000\n" -"Last-Translator: FULL NAME \n" +"PO-Revision-Date: 2010-12-15 06:43+0200\n" +"Last-Translator: MagicFab \n" "Language-Team: fr \n" -"Plural-Forms: nplurals=2; plural=(n > 1)\n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Pootle 2.1.2\n" "Generated-By: Babel 1.0dev\n" msgctxt "version" msgid "Version" -msgstr "" +msgstr "Version" #, python-format msgctxt "n_versions_available" msgid "%d versions available" -msgstr "" +msgstr "%d versions disponibles" #, python-format msgctxt "n_version_available" msgid "%d version available" -msgstr "" +msgstr "%d version disponible" msgctxt "notify" msgid "Notify" -msgstr "" +msgstr "Notifier" +# According to granddictionnaire.com msgctxt "storage" msgid "Storage" -msgstr "" +msgstr "Mémoire" msgctxt "cache_downloaded" msgid "Cache downloaded apps" -msgstr "" +msgstr "Garder en mémoire cache les applications téléchargées" msgctxt "keep_downloaded" msgid "Keep downloaded apk files on SD card" -msgstr "" +msgstr "Garder les fichiers apk téléchargés sur la carte SD" msgctxt "updates" msgid "Updates" -msgstr "" +msgstr "Mises à jour" msgctxt "clear_all_cached_data" msgid "Clear all cached data" -msgstr "" +msgstr "Vider la mémoire cache" msgctxt "reset" msgid "Reset" -msgstr "" +msgstr "Réinitialisation" msgctxt "maintenance" msgid "Maintenance" -msgstr "" +msgstr "Maintenance" msgctxt "notify_updates_available" msgid "Notify when new updates are available" -msgstr "" +msgstr "Notifier quand de nouvelles mises à jour sont disponibles" msgctxt "update_apps_list" msgid "Update apps list from repositories automatically" msgstr "" +"Mettre à jour automatiquement la liste d'applications à partir des dépôts " msgctxt "automatic_repo_scan" msgid "Automatic repo scan" -msgstr "" +msgstr "Balayage automatique du dépôt" msgctxt "app_name" msgid "FDroid" -msgstr "" +msgstr "FDroid" msgctxt "about_title" msgid "About FDroid" -msgstr "" +msgstr "À propos de FDroid" msgctxt "about_desc" msgid "" "Originally based on Aptoide.\n" "Released under the GNU GPL v2 license." msgstr "" +"Originallement basée sur Aptoide.\n" +"Libéré sous licence GNU GPL v2." msgctxt "about_site" msgid "Home:" -msgstr "" +msgstr "Accueil:" msgctxt "about_mail" msgid "Email:" -msgstr "" +msgstr "Courriel:" msgctxt "about_version" msgid "Version:" -msgstr "" +msgstr "Version:" msgctxt "about_website" msgid "Web Site" -msgstr "" +msgstr "Site Web" msgctxt "no_repo" msgid "" @@ -114,202 +119,212 @@ msgid "" "\n" "A repository URL looks something like this: http://f-droid.org/repo" msgstr "" +"Aucun dépôt n'est configuré!\n" +"\n" +"Un dépôt est une source d'applications. Pour en ajouter un, appuyez sur MENU " +"maintenant et entrez l'adresse URL.\n" +"\n" +"L'URL d'un dépôt ressemble à ceci: http://f-droid.org/repo" msgctxt "not_inst" msgid "Not Installed" -msgstr "" +msgstr "Pas installée" msgctxt "installed_update" msgid "Update possible - Ver.:" -msgstr "" +msgstr "Mise à jour possible - Ver.:" msgctxt "ok" msgid "Ok" -msgstr "" +msgstr "Ok" msgctxt "yes" msgid "Yes" -msgstr "" +msgstr "Oui" msgctxt "no" msgid "No" -msgstr "" +msgstr "Non" msgctxt "repo_add_title" msgid "Add new repository" -msgstr "" +msgstr "Ajouter un nouveau dépôt" msgctxt "repo_add_add" msgid "Add" -msgstr "" +msgstr "Ajouter" msgctxt "cancel" msgid "Cancel" -msgstr "" +msgstr "Annuler" msgctxt "repo_delete_title" msgid "Choose repository to remove" -msgstr "" +msgstr "Choisissez le dépôt à supprimer" msgctxt "server_connection_error" msgid "Could not connect to server!" -msgstr "" +msgstr "Connection au serveur impossible!" msgctxt "repo_update_title" msgid "Update repositories" -msgstr "" +msgstr "Mettre à jour les dépôts" msgctxt "tab_installed" msgid "Installed" -msgstr "" +msgstr "Installée" msgctxt "tab_noninstalled" msgid "Available" -msgstr "" +msgstr "Disponible" msgctxt "tab_updates" msgid "Updates" -msgstr "" +msgstr "Mises à jour" msgctxt "update_available" msgid "Updates available" -msgstr "" +msgstr "Mises à jour disponibles" msgctxt "process_wait_title" msgid "Please Wait" -msgstr "" +msgstr "Patientez" msgctxt "process_update_msg" msgid "Updating application list..." -msgstr "" +msgstr "Mise à jour de la liste d'applications..." msgctxt "connection_error" msgid "Could not connect to the network." -msgstr "" +msgstr "Connection au réseau impossible." msgctxt "connection_timeout" msgid "Timeout" -msgstr "" +msgstr "Délai d'attente" msgctxt "connection_error_msg" msgid "Could not connect to server!" -msgstr "" +msgstr "Connection au serveur impossible!" msgctxt "download" msgid "Download" -msgstr "" +msgstr "Téléchargement" msgctxt "download_server" msgid "Getting application from" -msgstr "" +msgstr "Réception d'application de" msgctxt "repo_add_url" msgid "Repository URL" -msgstr "" +msgstr "URL du dépôt" msgctxt "isinst" msgid "Installed:" -msgstr "" +msgstr "Installé:" msgctxt "install" msgid "Install" -msgstr "" +msgstr "Installer" msgctxt "uninstall" msgid "Uninstall" -msgstr "" +msgstr "Supprimer" msgctxt "update" msgid "Update!" -msgstr "" +msgstr "Mise à jour!" msgctxt "update_alrt" msgid "" "There updates available for some installed applications.\n" "Do you wish to see them?" msgstr "" +"Des mises à jour sont disponibles pour certaines applications.\n" +"\n" +"Désirez-vous les voir?" msgctxt "repo_alrt" msgid "" "The list of repositories in use has been changed.\n" "Do you wish to update them?" msgstr "" +"La liste des dépôts a changé.\n" +"Voulez-vous les mettre à jour?" msgctxt "error_download_alrt" msgid "Could not connect to server or apk file is corrupt!" -msgstr "" +msgstr "Connections au serveur impossible ou fichier apk corrompu!" msgctxt "download_alrt" msgid "Getting application from:\n" -msgstr "" +msgstr "Réception d'application de:\n" msgctxt "menu_update_repo" msgid "Update" -msgstr "" +msgstr "Mise à jour" msgctxt "menu_manage" msgid "Manage Repos" -msgstr "" +msgstr "Gestion de dépôts" msgctxt "menu_preferences" msgid "Preferences" -msgstr "" +msgstr "Préférences" msgctxt "menu_about" msgid "About" -msgstr "" +msgstr "À propos de" msgctxt "menu_add_repo" msgid "New Repository" -msgstr "" +msgstr "Nouveau dépôt" msgctxt "menu_rem_repo" msgid "Remove Repository" -msgstr "" +msgstr "Supprimer un dépôt" msgctxt "menu_install" msgid "Install" -msgstr "" +msgstr "Installer" msgctxt "menu_uninstall" msgid "Uninstall" -msgstr "" +msgstr "Supprimer" msgctxt "menu_website" msgid "Web Site" -msgstr "" +msgstr "Site Web" msgctxt "menu_issues" msgid "Issues" -msgstr "" +msgstr "Problèmes" msgctxt "menu_source" msgid "Source Code" -msgstr "" +msgstr "Code source" msgctxt "menu_market" msgid "Market" -msgstr "" +msgstr "Marché" msgctxt "menu_update" msgid "Update" -msgstr "" +msgstr "Mise à jour" #, python-format msgctxt "details_installed" msgid "Version %s installed" -msgstr "" +msgstr "Version %s installée" #, python-format msgctxt "details_notinstalled" msgid "Not installed (%d available)" -msgstr "" +msgstr "Pas installée (%d disponible)" msgctxt "inst" msgid "Installed" -msgstr "" +msgstr "Installée" msgctxt "corrupt_download" msgid "Downloaded file is corrupt" -msgstr "" - +msgstr "Le fichier téléchargé est corrompu" diff --git a/locale/sv.po b/locale/sv.po new file mode 100644 index 000000000..790e6be50 --- /dev/null +++ b/locale/sv.po @@ -0,0 +1,333 @@ +# Translations template for PROJECT. +# Copyright (C) 2010 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2010. +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-11-15 07:54+0000\n" +"PO-Revision-Date: 2011-01-14 07:33+0200\n" +"Last-Translator: Henrik \n" +"Language-Team: LANGUAGE \n" +"Language: sv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Pootle 2.1.2\n" +"Generated-By: Babel 1.0dev\n" + +msgctxt "version" +msgid "Version" +msgstr "Version" + +#, python-format +msgctxt "n_versions_available" +msgid "%d versions available" +msgstr "%d versioner tillgängliga" + +#, python-format +msgctxt "n_version_available" +msgid "%d version available" +msgstr "%d version tillgänglig" + +msgctxt "notify" +msgid "Notify" +msgstr "Meddela" + +msgctxt "storage" +msgid "Storage" +msgstr "Lagring" + +# Låt oss vara konsekventa med denna term. +msgctxt "cache_downloaded" +msgid "Cache downloaded apps" +msgstr "Cacha nerladdade program" + +msgctxt "keep_downloaded" +msgid "Keep downloaded apk files on SD card" +msgstr "Behåll nerladdade apk-filer på SD-kortet" + +msgctxt "updates" +msgid "Updates" +msgstr "Uppdateringar" + +msgctxt "clear_all_cached_data" +msgid "Clear all cached data" +msgstr "Rensa all cachad data" + +msgctxt "reset" +msgid "Reset" +msgstr "Återställ" + +msgctxt "maintenance" +msgid "Maintenance" +msgstr "Underhåll" + +msgctxt "notify_updates_available" +msgid "Notify when new updates are available" +msgstr "Meddela mig när nya uppdateringar finns" + +msgctxt "update_apps_list" +msgid "Update apps list from repositories automatically" +msgstr "Uppdatera programlistan från förråd automatiskt" + +msgctxt "automatic_repo_scan" +msgid "Automatic repo scan" +msgstr "Automatisk förrådsskanning" + +msgctxt "app_name" +msgid "FDroid" +msgstr "FDroid" + +msgctxt "about_title" +msgid "About FDroid" +msgstr "Om FDroid" + +msgctxt "about_desc" +msgid "" +"Originally based on Aptoide.\n" +"Released under the GNU GPL v2 license." +msgstr "" +"Ursprungligen baserat på Aptoide.\n" +"Släppt under GNU GPLv2-licensen." + +msgctxt "about_site" +msgid "Home:" +msgstr "Hem:" + +msgctxt "about_mail" +msgid "Email:" +msgstr "E-post:" + +msgctxt "about_version" +msgid "Version:" +msgstr "Version:" + +msgctxt "about_website" +msgid "Web Site" +msgstr "Webbsida" + +msgctxt "no_repo" +msgid "" +"You have no repositories configured!\n" +"\n" +"A repository is a source of applications. To add one, press the MENU " +"button now and enter the URL.\n" +"\n" +"A repository URL looks something like this: http://f-droid.org/repo" +msgstr "" +"Du har inga förråd konfigurerade!\n" +"\n" +"Ett förråd är en källa för program. Tryck på Meny-knappen och ange URL:en " +"för att lägga till ett nu.\n" +"\n" +"En förråds-URL ser ut ungefär så här: http://f-droid.org/repo" + +msgctxt "not_inst" +msgid "Not Installed" +msgstr "Inte installerad" + +msgctxt "installed_update" +msgid "Update possible - Ver.:" +msgstr "Uppdatering möjlig - Ver.:" + +msgctxt "ok" +msgid "Ok" +msgstr "OK" + +msgctxt "yes" +msgid "Yes" +msgstr "Ja" + +msgctxt "no" +msgid "No" +msgstr "Nej" + +msgctxt "repo_add_title" +msgid "Add new repository" +msgstr "Lägg till nytt förråd" + +msgctxt "repo_add_add" +msgid "Add" +msgstr "Lägg till" + +msgctxt "cancel" +msgid "Cancel" +msgstr "Avbryt" + +msgctxt "repo_delete_title" +msgid "Choose repository to remove" +msgstr "Välj förråd att ta bort" + +msgctxt "server_connection_error" +msgid "Could not connect to server!" +msgstr "Kunde inte ansluta till servern!" + +msgctxt "repo_update_title" +msgid "Update repositories" +msgstr "Uppdatera förråd" + +# Rubrik för lista på installerade program. +msgctxt "tab_installed" +msgid "Installed" +msgstr "Installerade" + +msgctxt "tab_noninstalled" +msgid "Available" +msgstr "Tillgängliga" + +msgctxt "tab_updates" +msgid "Updates" +msgstr "Uppdateringar" + +msgctxt "update_available" +msgid "Updates available" +msgstr "Uppdateringar tillgängliga" + +msgctxt "process_wait_title" +msgid "Please Wait" +msgstr "Var vänlig vänta" + +msgctxt "process_update_msg" +msgid "Updating application list..." +msgstr "Uppdaterar programlistan..." + +msgctxt "connection_error" +msgid "Could not connect to the network." +msgstr "Kunde inte ansluta till nätverket." + +# Om vi inte ska följa tyskarna och kalla det "tidsöverskridning"...? +msgctxt "connection_timeout" +msgid "Timeout" +msgstr "Timeout" + +msgctxt "connection_error_msg" +msgid "Could not connect to server!" +msgstr "Kunde inte ansluta till servern!" + +msgctxt "download" +msgid "Download" +msgstr "Ladda ner" + +msgctxt "download_server" +msgid "Getting application from" +msgstr "Hämtar program från" + +msgctxt "repo_add_url" +msgid "Repository URL" +msgstr "Förråds-URL" + +msgctxt "isinst" +msgid "Installed:" +msgstr "Installerad:" + +msgctxt "install" +msgid "Install" +msgstr "Installera" + +msgctxt "uninstall" +msgid "Uninstall" +msgstr "Avinstallera" + +# Den här strängen tycks inte användas i programmet. Svårt att översätta utan sammanhang. +msgctxt "update" +msgid "Update!" +msgstr "" + +msgctxt "update_alrt" +msgid "" +"There updates available for some installed applications.\n" +"Do you wish to see them?" +msgstr "" +"Det finns uppdateringar för vissa av de installerade programmen.\n" +"Vill du se dem?" + +msgctxt "repo_alrt" +msgid "" +"The list of repositories in use has been changed.\n" +"Do you wish to update them?" +msgstr "" +"Listan över förråd har ändrats.\n" +"Vill du uppdatera dem?" + +msgctxt "error_download_alrt" +msgid "Could not connect to server or apk file is corrupt!" +msgstr "Kunde inte ansluta till servern eller så är apk-filen är skadad!" + +msgctxt "download_alrt" +msgid "Getting application from:\n" +msgstr "Hämtar program från:\n" + +msgctxt "menu_update_repo" +msgid "Update" +msgstr "Uppdatera" + +msgctxt "menu_manage" +msgid "Manage Repos" +msgstr "Hantera förråd" + +msgctxt "menu_preferences" +msgid "Preferences" +msgstr "Inställningar" + +msgctxt "menu_about" +msgid "About" +msgstr "Om" + +msgctxt "menu_add_repo" +msgid "New Repository" +msgstr "Nytt förråd" + +msgctxt "menu_rem_repo" +msgid "Remove Repository" +msgstr "Ta bort förråd" + +msgctxt "menu_install" +msgid "Install" +msgstr "Installera" + +msgctxt "menu_uninstall" +msgid "Uninstall" +msgstr "Avinstallera" + +msgctxt "menu_website" +msgid "Web Site" +msgstr "Webbsida" + +msgctxt "menu_issues" +msgid "Issues" +msgstr "" + +msgctxt "menu_source" +msgid "Source Code" +msgstr "Källkod" + +# Google kallar det "Market" även på svenska. +msgctxt "menu_market" +msgid "Market" +msgstr "Market" + +msgctxt "menu_update" +msgid "Update" +msgstr "Uppdatera" + +#, python-format +msgctxt "details_installed" +msgid "Version %s installed" +msgstr "Version %s är installerad" + +# Programmet är inte installerat, men %d versioner är tillgängliga. +#, python-format +msgctxt "details_notinstalled" +msgid "Not installed (%d available)" +msgstr "Inte installerat (%d tillgängliga)" + +msgctxt "inst" +msgid "Installed" +msgstr "Installerad" + +msgctxt "corrupt_download" +msgid "Downloaded file is corrupt" +msgstr "Den nerladdade filen är skadad" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 732d275b8..21b47fc6a 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -1,17 +1,29 @@ + Version + %d verfügbare Versionen + %d verfügbare Version + Benachrichtigen + Speicherplatz + Heruntergeladene Anwendungen zwischenspeichern + Heruntergeladene Anwendungsdateien auf der SD-Karte behalten + Aktualisierungen + Zwischenspeicher leeren + Zurücksetzen + Verwaltung + Bei Aktualisierungen benachrichtigen + Anwendungsliste automatisch aktualisieren + Automatische Archivkontrolle FDroid Über FDroid Basierend auf Aptoide.\nLizensiert unter GNU GPLv2. Internetseite: - E-Mail: + Email: + Version: Internetseite - Keine Anwendung gefunden! Sie haben keine Archive eingestellt!\n\nEin Archiv ist eine Sammlung von Anwendungen. Drücken Sie den Menü-Knopf und fügen Sie die Adresse eines Archivs hinzu.\n\nEine Archiv-Adresse sieht ungefähr so aus: http://f-droid.org/repo Nicht Installiert - Installiert – Version: Aktualisierung verfügbar – Version: - Fehler OK Ja Nein @@ -19,7 +31,6 @@ Hinzufügen Abbrechen Zu entfernendes Archiv auswählen - http://f-droid.org Die Serververbindung konnte nicht hergestellt werden! Archive aktualisieren Installiert @@ -33,21 +44,8 @@ Die Serververbindung konnte nicht hergestellt werden! Herunterladen Anwendung wird heruntergeladen von - Verfügbare Version - Anwendungsliste sortieren: - Alphabetisch - Installiert / nicht installiert - Neueste zuerst - Bewertung - Anwendungen anzeigen: - Nach Kategorie - Alle Anwendungen - Speichern Archiv-Adresse - Server: - Server-Version: Installiert: - Installierte Version: Installieren Entfernen Aktualisieren! @@ -57,6 +55,7 @@ Anwendung wird geladen von:\n Aktualisieren Archive verwalten + Einstellungen Über Archiv hinzufügen Archiv entfernen @@ -67,4 +66,8 @@ Quelltext Markt Aktualisieren + Version %s installiert + Nicht installiert (%d verfügbar) + Installiert + Heruntergeladene Datei ist fehlerhaft diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 94edc23d4..06b3625c3 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -1,2 +1,73 @@ - + + Version + %d versions disponibles + %d version disponible + Notifier + Mémoire + Garder en mémoire cache les applications téléchargées + Garder les fichiers apk téléchargés sur la carte SD + Mises à jour + Vider la mémoire cache + Réinitialisation + Maintenance + Notifier quand de nouvelles mises à jour sont disponibles + "Mettre à jour automatiquement la liste d\'applications à partir des dépôts " + Balayage automatique du dépôt + FDroid + À propos de FDroid + Originallement basée sur Aptoide.\nLibéré sous licence GNU GPL v2. + Accueil: + Courriel: + Version: + Site Web + Aucun dépôt n\'est configuré!\n\nUn dépôt est une source d\'applications. Pour en ajouter un, appuyez sur MENU maintenant et entrez l\'adresse URL.\n\nL\'URL d\'un dépôt ressemble à ceci: http://f-droid.org/repo + Pas installée + Mise à jour possible - Ver.: + Ok + Oui + Non + Ajouter un nouveau dépôt + Ajouter + Annuler + Choisissez le dépôt à supprimer + Connection au serveur impossible! + Mettre à jour les dépôts + Installée + Disponible + Mises à jour + Mises à jour disponibles + Patientez + Mise à jour de la liste d\'applications... + Connection au réseau impossible. + Délai d\'attente + Connection au serveur impossible! + Téléchargement + Réception d\'application de + URL du dépôt + Installé: + Installer + Supprimer + Mise à jour! + Des mises à jour sont disponibles pour certaines applications.\n\nDésirez-vous les voir? + La liste des dépôts a changé.\nVoulez-vous les mettre à jour? + Connections au serveur impossible ou fichier apk corrompu! + Réception d\'application de:\n + Mise à jour + Gestion de dépôts + Préférences + À propos de + Nouveau dépôt + Supprimer un dépôt + Installer + Supprimer + Site Web + Problèmes + Code source + Marché + Mise à jour + Version %s installée + Pas installée (%d disponible) + Installée + Le fichier téléchargé est corrompu + diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 695bb5655..1ee73f9a3 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -1,17 +1,19 @@ + Versione Server: + Aggiornamenti disponibili + Aggiornamenti disponibili + Aggiornamenti FDroid Informazioni Basato su Aptoide.\nRilasciato sotto licenza GNU GPL v2. Home: e-Mail: + Versione Server: Sito Web - Nessuna applicazione trovata! Non hai configurato nessun repository!\n\nUn repository contiene un insieme di applicazioni. Per aggiungerne uno, premi MENU e inserisci un indirizzo URL.\n\nUn indirizzo URL di esempio e\': http://f-droid.org/repo Non installato - Installato - Ver.: Aggiornamento: - Ver.: - Errore Ok No @@ -19,7 +21,6 @@ Aggiungi Annulla Rimuovi Repository - http://f-droid.org Impossibile connettersi al server! Aggiorna Repositories Installato @@ -33,21 +34,8 @@ Impossibile connettersi al server! Download Scaricamento applicazione da - disponibile v - Elenca applicazioni per: - ordine alfabetico - Installato / Non installato - Piu\' recente - Voto - Mostra applicazioni: - Categoria - Tutte - Salva Repository URL - Servers: - Versione Server: Installato: - Versione installata: Installa Disinstalla Aggiorna! @@ -67,4 +55,6 @@ Codice Sorgente Market Aggiornamento + Non installato + Installato diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml new file mode 100644 index 000000000..b093861b2 --- /dev/null +++ b/res/values-sv/strings.xml @@ -0,0 +1,71 @@ + + + Version + %d versioner tillgängliga + %d version tillgänglig + Meddela + Lagring + Cacha nerladdade program + Behåll nerladdade apk-filer på SD-kortet + Uppdateringar + Rensa all cachad data + Återställ + Underhåll + Meddela mig när nya uppdateringar finns + Uppdatera programlistan från förråd automatiskt + Automatisk förrådsskanning + FDroid + Om FDroid + Ursprungligen baserat på Aptoide.\nSläppt under GNU GPLv2-licensen. + Hem: + E-post: + Version: + Webbsida + Du har inga förråd konfigurerade!\n\nEtt förråd är en källa för program. Tryck på Meny-knappen och ange URL:en för att lägga till ett nu.\n\nEn förråds-URL ser ut ungefär så här: http://f-droid.org/repo + Inte installerad + Uppdatering möjlig - Ver.: + OK + Ja + Nej + Lägg till nytt förråd + Lägg till + Avbryt + Välj förråd att ta bort + Kunde inte ansluta till servern! + Uppdatera förråd + Installerade + Tillgängliga + Uppdateringar + Uppdateringar tillgängliga + Var vänlig vänta + Uppdaterar programlistan... + Kunde inte ansluta till nätverket. + Timeout + Kunde inte ansluta till servern! + Ladda ner + Hämtar program från + Förråds-URL + Installerad: + Installera + Avinstallera + Det finns uppdateringar för vissa av de installerade programmen.\nVill du se dem? + Listan över förråd har ändrats.\nVill du uppdatera dem? + Kunde inte ansluta till servern eller så är apk-filen är skadad! + Hämtar program från:\n + Uppdatera + Hantera förråd + Inställningar + Om + Nytt förråd + Ta bort förråd + Installera + Avinstallera + Webbsida + Källkod + Market + Uppdatera + Version %s är installerad + Inte installerat (%d tillgängliga) + Installerad + Den nerladdade filen är skadad + From 92b8911158c3e9a7a8edbdb64916db606a0e0095 Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Sun, 16 Jan 2011 16:16:31 +0000 Subject: [PATCH 7/7] Updated translation files with new/modified strings from the code --- locale/de.po | 98 +++++++++++++++++++++++++++++-------- locale/fr.po | 106 +++++++++++++++++++++++++++++++--------- locale/it.po | 91 ++++++++++++++++++++++++++++------ locale/nl.po | 80 ++++++++++++++++++++++++------ locale/sv.po | 116 ++++++++++++++++++++++++++++++++------------ locale/template.pot | 84 ++++++++++++++++++++++++++------ 6 files changed, 455 insertions(+), 120 deletions(-) diff --git a/locale/de.po b/locale/de.po index 4ff609967..de5abc736 100644 --- a/locale/de.po +++ b/locale/de.po @@ -6,18 +6,24 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2010-11-15 07:54+0000\n" +"POT-Creation-Date: 2011-01-16 16:16+0000\n" "PO-Revision-Date: 2010-11-16 09:54+0200\n" "Last-Translator: Jan-Christoph \n" "Language-Team: de \n" -"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Pootle 2.1.2\n" "Generated-By: Babel 1.0dev\n" +msgctxt "SignatureMismatch" +msgid "" +"The new version is signed with a different key to the old one. To install" +" the new version, the old one must be uninstalled first. Please do this " +"and try again. (Note that uninstalling will erase any internal data " +"stored by the application)" +msgstr "" + msgctxt "version" msgid "Version" msgstr "Version" @@ -68,8 +74,9 @@ msgctxt "notify_updates_available" msgid "Notify when new updates are available" msgstr "Bei Aktualisierungen benachrichtigen" +#, fuzzy msgctxt "update_apps_list" -msgid "Update apps list from repositories automatically" +msgid "Update app list from repositories automatically" msgstr "Anwendungsliste automatisch aktualisieren" msgctxt "automatic_repo_scan" @@ -84,17 +91,19 @@ msgctxt "about_title" msgid "About FDroid" msgstr "Über FDroid" +#, fuzzy msgctxt "about_desc" msgid "" "Originally based on Aptoide.\n" -"Released under the GNU GPL v2 license." +"Released under the GNU GPLv2 license." msgstr "" "Basierend auf Aptoide.\n" "Lizensiert unter GNU GPLv2." +#, fuzzy msgctxt "about_site" -msgid "Home:" -msgstr "Internetseite:" +msgid "Website:" +msgstr "Internetseite" msgctxt "about_mail" msgid "Email:" @@ -104,18 +113,20 @@ msgctxt "about_version" msgid "Version:" msgstr "Version:" +#, fuzzy msgctxt "about_website" -msgid "Web Site" +msgid "Website" msgstr "Internetseite" +#, fuzzy msgctxt "no_repo" msgid "" -"You have no repositories configured!\n" +"You don't have any repositories configured!\n" "\n" "A repository is a source of applications. To add one, press the MENU " "button now and enter the URL.\n" "\n" -"A repository URL looks something like this: http://f-droid.org/repo" +"A repository address looks something like this: http://f-droid.org/repo" msgstr "" "Sie haben keine Archive eingestellt!\n" "\n" @@ -133,8 +144,8 @@ msgid "Update possible - Ver.:" msgstr "Aktualisierung verfügbar – Version:" msgctxt "ok" -msgid "Ok" -msgstr "OK" +msgid "OK" +msgstr "" msgctxt "yes" msgid "Yes" @@ -212,8 +223,9 @@ msgctxt "download_server" msgid "Getting application from" msgstr "Anwendung wird heruntergeladen von" +#, fuzzy msgctxt "repo_add_url" -msgid "Repository URL" +msgid "Repository address" msgstr "Archiv-Adresse" msgctxt "isinst" @@ -232,18 +244,20 @@ msgctxt "update" msgid "Update!" msgstr "Aktualisieren!" +#, fuzzy msgctxt "update_alrt" msgid "" -"There updates available for some installed applications.\n" -"Do you wish to see them?" +"There are updates available for some installed applications.\n" +"Do you want to see them?" msgstr "" "Für einige installierte Anwendungen sind Aktualisierungen verfügbar.\n" "Sollen diese angezeigt werden?" +#, fuzzy msgctxt "repo_alrt" msgid "" -"The list of repositories in use has been changed.\n" -"Do you wish to update them?" +"The list of used repositories has changed.\n" +"Do you want to update them?" msgstr "" "Die Liste der genutzten Archive wurde geändert.\n" "Soll sie aktualisiert werden?" @@ -290,8 +304,9 @@ msgctxt "menu_uninstall" msgid "Uninstall" msgstr "Entfernen" +#, fuzzy msgctxt "menu_website" -msgid "Web Site" +msgid "Website" msgstr "Internetseite" msgctxt "menu_issues" @@ -327,3 +342,48 @@ msgstr "Installiert" msgctxt "corrupt_download" msgid "Downloaded file is corrupt" msgstr "Heruntergeladene Datei ist fehlerhaft" + +msgctxt "antifeatures" +msgid "Anti-Features" +msgstr "" + +msgctxt "antiads" +msgid "Advertising" +msgstr "" + +msgctxt "antiadslong" +msgid "Show apps that contain advertising" +msgstr "" + +msgctxt "antitrack" +msgid "Tracking" +msgstr "" + +msgctxt "antitracklong" +msgid "Show apps that track and report your activity" +msgstr "" + +msgctxt "antinonfreead" +msgid "Add-ons" +msgstr "" + +msgctxt "antinonfreeadlong" +msgid "Show apps that promote non-free add-ons" +msgstr "" + +msgctxt "antinonfreenet" +msgid "Network Services" +msgstr "" + +msgctxt "antinonfreenetlong" +msgid "Show apps that promote non-free network services" +msgstr "" + +msgctxt "expert" +msgid "Expert" +msgstr "" + +msgctxt "expert_mode" +msgid "Enabled expert mode" +msgstr "" + diff --git a/locale/fr.po b/locale/fr.po index 0f88a1df9..88f092a08 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -6,18 +6,24 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2010-11-15 07:54+0000\n" +"POT-Creation-Date: 2011-01-16 16:16+0000\n" "PO-Revision-Date: 2010-12-15 06:43+0200\n" "Last-Translator: MagicFab \n" "Language-Team: fr \n" -"Language: fr\n" +"Plural-Forms: nplurals=2; plural=(n > 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Pootle 2.1.2\n" "Generated-By: Babel 1.0dev\n" +msgctxt "SignatureMismatch" +msgid "" +"The new version is signed with a different key to the old one. To install" +" the new version, the old one must be uninstalled first. Please do this " +"and try again. (Note that uninstalling will erase any internal data " +"stored by the application)" +msgstr "" + msgctxt "version" msgid "Version" msgstr "Version" @@ -36,7 +42,6 @@ msgctxt "notify" msgid "Notify" msgstr "Notifier" -# According to granddictionnaire.com msgctxt "storage" msgid "Storage" msgstr "Mémoire" @@ -69,10 +74,10 @@ msgctxt "notify_updates_available" msgid "Notify when new updates are available" msgstr "Notifier quand de nouvelles mises à jour sont disponibles" +#, fuzzy msgctxt "update_apps_list" -msgid "Update apps list from repositories automatically" -msgstr "" -"Mettre à jour automatiquement la liste d'applications à partir des dépôts " +msgid "Update app list from repositories automatically" +msgstr "Mettre à jour automatiquement la liste d'applications à partir des dépôts " msgctxt "automatic_repo_scan" msgid "Automatic repo scan" @@ -86,17 +91,19 @@ msgctxt "about_title" msgid "About FDroid" msgstr "À propos de FDroid" +#, fuzzy msgctxt "about_desc" msgid "" "Originally based on Aptoide.\n" -"Released under the GNU GPL v2 license." +"Released under the GNU GPLv2 license." msgstr "" "Originallement basée sur Aptoide.\n" "Libéré sous licence GNU GPL v2." +#, fuzzy msgctxt "about_site" -msgid "Home:" -msgstr "Accueil:" +msgid "Website:" +msgstr "Site Web" msgctxt "about_mail" msgid "Email:" @@ -106,23 +113,25 @@ msgctxt "about_version" msgid "Version:" msgstr "Version:" +#, fuzzy msgctxt "about_website" -msgid "Web Site" +msgid "Website" msgstr "Site Web" +#, fuzzy msgctxt "no_repo" msgid "" -"You have no repositories configured!\n" +"You don't have any repositories configured!\n" "\n" "A repository is a source of applications. To add one, press the MENU " "button now and enter the URL.\n" "\n" -"A repository URL looks something like this: http://f-droid.org/repo" +"A repository address looks something like this: http://f-droid.org/repo" msgstr "" "Aucun dépôt n'est configuré!\n" "\n" -"Un dépôt est une source d'applications. Pour en ajouter un, appuyez sur MENU " -"maintenant et entrez l'adresse URL.\n" +"Un dépôt est une source d'applications. Pour en ajouter un, appuyez sur " +"MENU maintenant et entrez l'adresse URL.\n" "\n" "L'URL d'un dépôt ressemble à ceci: http://f-droid.org/repo" @@ -135,8 +144,8 @@ msgid "Update possible - Ver.:" msgstr "Mise à jour possible - Ver.:" msgctxt "ok" -msgid "Ok" -msgstr "Ok" +msgid "OK" +msgstr "" msgctxt "yes" msgid "Yes" @@ -214,8 +223,9 @@ msgctxt "download_server" msgid "Getting application from" msgstr "Réception d'application de" +#, fuzzy msgctxt "repo_add_url" -msgid "Repository URL" +msgid "Repository address" msgstr "URL du dépôt" msgctxt "isinst" @@ -234,19 +244,21 @@ msgctxt "update" msgid "Update!" msgstr "Mise à jour!" +#, fuzzy msgctxt "update_alrt" msgid "" -"There updates available for some installed applications.\n" -"Do you wish to see them?" +"There are updates available for some installed applications.\n" +"Do you want to see them?" msgstr "" "Des mises à jour sont disponibles pour certaines applications.\n" "\n" "Désirez-vous les voir?" +#, fuzzy msgctxt "repo_alrt" msgid "" -"The list of repositories in use has been changed.\n" -"Do you wish to update them?" +"The list of used repositories has changed.\n" +"Do you want to update them?" msgstr "" "La liste des dépôts a changé.\n" "Voulez-vous les mettre à jour?" @@ -291,8 +303,9 @@ msgctxt "menu_uninstall" msgid "Uninstall" msgstr "Supprimer" +#, fuzzy msgctxt "menu_website" -msgid "Web Site" +msgid "Website" msgstr "Site Web" msgctxt "menu_issues" @@ -328,3 +341,48 @@ msgstr "Installée" msgctxt "corrupt_download" msgid "Downloaded file is corrupt" msgstr "Le fichier téléchargé est corrompu" + +msgctxt "antifeatures" +msgid "Anti-Features" +msgstr "" + +msgctxt "antiads" +msgid "Advertising" +msgstr "" + +msgctxt "antiadslong" +msgid "Show apps that contain advertising" +msgstr "" + +msgctxt "antitrack" +msgid "Tracking" +msgstr "" + +msgctxt "antitracklong" +msgid "Show apps that track and report your activity" +msgstr "" + +msgctxt "antinonfreead" +msgid "Add-ons" +msgstr "" + +msgctxt "antinonfreeadlong" +msgid "Show apps that promote non-free add-ons" +msgstr "" + +msgctxt "antinonfreenet" +msgid "Network Services" +msgstr "" + +msgctxt "antinonfreenetlong" +msgid "Show apps that promote non-free network services" +msgstr "" + +msgctxt "expert" +msgid "Expert" +msgstr "" + +msgctxt "expert_mode" +msgid "Enabled expert mode" +msgstr "" + diff --git a/locale/it.po b/locale/it.po index 929ca3497..885e650a1 100644 --- a/locale/it.po +++ b/locale/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2010-11-15 07:54+0000\n" +"POT-Creation-Date: 2011-01-16 16:16+0000\n" "PO-Revision-Date: 2010-11-04 15:42+0200\n" "Last-Translator: graziano \n" "Language-Team: it \n" @@ -16,6 +16,14 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 1.0dev\n" +msgctxt "SignatureMismatch" +msgid "" +"The new version is signed with a different key to the old one. To install" +" the new version, the old one must be uninstalled first. Please do this " +"and try again. (Note that uninstalling will erase any internal data " +"stored by the application)" +msgstr "" + #, fuzzy msgctxt "version" msgid "Version" @@ -69,7 +77,7 @@ msgid "Notify when new updates are available" msgstr "" msgctxt "update_apps_list" -msgid "Update apps list from repositories automatically" +msgid "Update app list from repositories automatically" msgstr "" msgctxt "automatic_repo_scan" @@ -88,14 +96,15 @@ msgstr "Informazioni" msgctxt "about_desc" msgid "" "Originally based on Aptoide.\n" -"Released under the GNU GPL v2 license." +"Released under the GNU GPLv2 license." msgstr "" "Basato su Aptoide.\n" "Rilasciato sotto licenza GNU GPL v2." +#, fuzzy msgctxt "about_site" -msgid "Home:" -msgstr "Home:" +msgid "Website:" +msgstr "Sito Web" #, fuzzy msgctxt "about_mail" @@ -107,18 +116,20 @@ msgctxt "about_version" msgid "Version:" msgstr "Versione Server:" +#, fuzzy msgctxt "about_website" -msgid "Web Site" +msgid "Website" msgstr "Sito Web" +#, fuzzy msgctxt "no_repo" msgid "" -"You have no repositories configured!\n" +"You don't have any repositories configured!\n" "\n" "A repository is a source of applications. To add one, press the MENU " "button now and enter the URL.\n" "\n" -"A repository URL looks something like this: http://f-droid.org/repo" +"A repository address looks something like this: http://f-droid.org/repo" msgstr "" "Non hai configurato nessun repository!\n" "\n" @@ -136,8 +147,8 @@ msgid "Update possible - Ver.:" msgstr "Aggiornamento: - Ver.:" msgctxt "ok" -msgid "Ok" -msgstr "Ok" +msgid "OK" +msgstr "" msgctxt "yes" msgid "Yes" @@ -216,8 +227,9 @@ msgctxt "download_server" msgid "Getting application from" msgstr "Scaricamento applicazione da" +#, fuzzy msgctxt "repo_add_url" -msgid "Repository URL" +msgid "Repository address" msgstr "Repository URL" msgctxt "isinst" @@ -236,19 +248,21 @@ msgctxt "update" msgid "Update!" msgstr "Aggiorna!" +#, fuzzy msgctxt "update_alrt" msgid "" -"There updates available for some installed applications.\n" -"Do you wish to see them?" +"There are updates available for some installed applications.\n" +"Do you want to see them?" msgstr "" "Aggiornamenti disponibili.\n" "\n" "Vuoi visualizzarli?" +#, fuzzy msgctxt "repo_alrt" msgid "" -"The list of repositories in use has been changed.\n" -"Do you wish to update them?" +"The list of used repositories has changed.\n" +"Do you want to update them?" msgstr "" "L'elenco dei repository in uso e' cambiato.\n" "\n" @@ -296,8 +310,9 @@ msgctxt "menu_uninstall" msgid "Uninstall" msgstr "Disinstalla" +#, fuzzy msgctxt "menu_website" -msgid "Web Site" +msgid "Website" msgstr "Sito Web" msgctxt "menu_issues" @@ -335,3 +350,47 @@ msgctxt "corrupt_download" msgid "Downloaded file is corrupt" msgstr "" +msgctxt "antifeatures" +msgid "Anti-Features" +msgstr "" + +msgctxt "antiads" +msgid "Advertising" +msgstr "" + +msgctxt "antiadslong" +msgid "Show apps that contain advertising" +msgstr "" + +msgctxt "antitrack" +msgid "Tracking" +msgstr "" + +msgctxt "antitracklong" +msgid "Show apps that track and report your activity" +msgstr "" + +msgctxt "antinonfreead" +msgid "Add-ons" +msgstr "" + +msgctxt "antinonfreeadlong" +msgid "Show apps that promote non-free add-ons" +msgstr "" + +msgctxt "antinonfreenet" +msgid "Network Services" +msgstr "" + +msgctxt "antinonfreenetlong" +msgid "Show apps that promote non-free network services" +msgstr "" + +msgctxt "expert" +msgid "Expert" +msgstr "" + +msgctxt "expert_mode" +msgid "Enabled expert mode" +msgstr "" + diff --git a/locale/nl.po b/locale/nl.po index 05a68cb5c..3b64b3d99 100644 --- a/locale/nl.po +++ b/locale/nl.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2010-11-15 07:54+0000\n" +"POT-Creation-Date: 2011-01-16 16:16+0000\n" "PO-Revision-Date: 2010-11-15 07:54+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: nl \n" @@ -17,6 +17,14 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 1.0dev\n" +msgctxt "SignatureMismatch" +msgid "" +"The new version is signed with a different key to the old one. To install" +" the new version, the old one must be uninstalled first. Please do this " +"and try again. (Note that uninstalling will erase any internal data " +"stored by the application)" +msgstr "" + msgctxt "version" msgid "Version" msgstr "" @@ -68,7 +76,7 @@ msgid "Notify when new updates are available" msgstr "" msgctxt "update_apps_list" -msgid "Update apps list from repositories automatically" +msgid "Update app list from repositories automatically" msgstr "" msgctxt "automatic_repo_scan" @@ -86,11 +94,11 @@ msgstr "" msgctxt "about_desc" msgid "" "Originally based on Aptoide.\n" -"Released under the GNU GPL v2 license." +"Released under the GNU GPLv2 license." msgstr "" msgctxt "about_site" -msgid "Home:" +msgid "Website:" msgstr "" msgctxt "about_mail" @@ -102,17 +110,17 @@ msgid "Version:" msgstr "" msgctxt "about_website" -msgid "Web Site" +msgid "Website" msgstr "" msgctxt "no_repo" msgid "" -"You have no repositories configured!\n" +"You don't have any repositories configured!\n" "\n" "A repository is a source of applications. To add one, press the MENU " "button now and enter the URL.\n" "\n" -"A repository URL looks something like this: http://f-droid.org/repo" +"A repository address looks something like this: http://f-droid.org/repo" msgstr "" msgctxt "not_inst" @@ -124,7 +132,7 @@ msgid "Update possible - Ver.:" msgstr "" msgctxt "ok" -msgid "Ok" +msgid "OK" msgstr "" msgctxt "yes" @@ -204,7 +212,7 @@ msgid "Getting application from" msgstr "" msgctxt "repo_add_url" -msgid "Repository URL" +msgid "Repository address" msgstr "" msgctxt "isinst" @@ -225,14 +233,14 @@ msgstr "" msgctxt "update_alrt" msgid "" -"There updates available for some installed applications.\n" -"Do you wish to see them?" +"There are updates available for some installed applications.\n" +"Do you want to see them?" msgstr "" msgctxt "repo_alrt" msgid "" -"The list of repositories in use has been changed.\n" -"Do you wish to update them?" +"The list of used repositories has changed.\n" +"Do you want to update them?" msgstr "" msgctxt "error_download_alrt" @@ -276,7 +284,7 @@ msgid "Uninstall" msgstr "" msgctxt "menu_website" -msgid "Web Site" +msgid "Website" msgstr "" msgctxt "menu_issues" @@ -313,3 +321,47 @@ msgctxt "corrupt_download" msgid "Downloaded file is corrupt" msgstr "" +msgctxt "antifeatures" +msgid "Anti-Features" +msgstr "" + +msgctxt "antiads" +msgid "Advertising" +msgstr "" + +msgctxt "antiadslong" +msgid "Show apps that contain advertising" +msgstr "" + +msgctxt "antitrack" +msgid "Tracking" +msgstr "" + +msgctxt "antitracklong" +msgid "Show apps that track and report your activity" +msgstr "" + +msgctxt "antinonfreead" +msgid "Add-ons" +msgstr "" + +msgctxt "antinonfreeadlong" +msgid "Show apps that promote non-free add-ons" +msgstr "" + +msgctxt "antinonfreenet" +msgid "Network Services" +msgstr "" + +msgctxt "antinonfreenetlong" +msgid "Show apps that promote non-free network services" +msgstr "" + +msgctxt "expert" +msgid "Expert" +msgstr "" + +msgctxt "expert_mode" +msgid "Enabled expert mode" +msgstr "" + diff --git a/locale/sv.po b/locale/sv.po index 790e6be50..3da52d8b3 100644 --- a/locale/sv.po +++ b/locale/sv.po @@ -1,23 +1,29 @@ -# Translations template for PROJECT. +# Swedish translations for PACKAGE. # Copyright (C) 2010 ORGANIZATION -# This file is distributed under the same license as the PROJECT project. +# This file is distributed under the same license as the PACKAGE project. # FIRST AUTHOR , 2010. msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2010-11-15 07:54+0000\n" +"POT-Creation-Date: 2011-01-16 16:16+0000\n" "PO-Revision-Date: 2011-01-14 07:33+0200\n" "Last-Translator: Henrik \n" -"Language-Team: LANGUAGE \n" -"Language: sv\n" +"Language-Team: sv \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" +"Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Pootle 2.1.2\n" "Generated-By: Babel 1.0dev\n" +msgctxt "SignatureMismatch" +msgid "" +"The new version is signed with a different key to the old one. To install" +" the new version, the old one must be uninstalled first. Please do this " +"and try again. (Note that uninstalling will erase any internal data " +"stored by the application)" +msgstr "" + msgctxt "version" msgid "Version" msgstr "Version" @@ -40,7 +46,6 @@ msgctxt "storage" msgid "Storage" msgstr "Lagring" -# Låt oss vara konsekventa med denna term. msgctxt "cache_downloaded" msgid "Cache downloaded apps" msgstr "Cacha nerladdade program" @@ -69,8 +74,9 @@ msgctxt "notify_updates_available" msgid "Notify when new updates are available" msgstr "Meddela mig när nya uppdateringar finns" +#, fuzzy msgctxt "update_apps_list" -msgid "Update apps list from repositories automatically" +msgid "Update app list from repositories automatically" msgstr "Uppdatera programlistan från förråd automatiskt" msgctxt "automatic_repo_scan" @@ -85,17 +91,19 @@ msgctxt "about_title" msgid "About FDroid" msgstr "Om FDroid" +#, fuzzy msgctxt "about_desc" msgid "" "Originally based on Aptoide.\n" -"Released under the GNU GPL v2 license." +"Released under the GNU GPLv2 license." msgstr "" "Ursprungligen baserat på Aptoide.\n" "Släppt under GNU GPLv2-licensen." +#, fuzzy msgctxt "about_site" -msgid "Home:" -msgstr "Hem:" +msgid "Website:" +msgstr "Webbsida" msgctxt "about_mail" msgid "Email:" @@ -105,23 +113,25 @@ msgctxt "about_version" msgid "Version:" msgstr "Version:" +#, fuzzy msgctxt "about_website" -msgid "Web Site" +msgid "Website" msgstr "Webbsida" +#, fuzzy msgctxt "no_repo" msgid "" -"You have no repositories configured!\n" +"You don't have any repositories configured!\n" "\n" "A repository is a source of applications. To add one, press the MENU " "button now and enter the URL.\n" "\n" -"A repository URL looks something like this: http://f-droid.org/repo" +"A repository address looks something like this: http://f-droid.org/repo" msgstr "" "Du har inga förråd konfigurerade!\n" "\n" -"Ett förråd är en källa för program. Tryck på Meny-knappen och ange URL:en " -"för att lägga till ett nu.\n" +"Ett förråd är en källa för program. Tryck på Meny-knappen och ange URL:en" +" för att lägga till ett nu.\n" "\n" "En förråds-URL ser ut ungefär så här: http://f-droid.org/repo" @@ -134,8 +144,8 @@ msgid "Update possible - Ver.:" msgstr "Uppdatering möjlig - Ver.:" msgctxt "ok" -msgid "Ok" -msgstr "OK" +msgid "OK" +msgstr "" msgctxt "yes" msgid "Yes" @@ -169,7 +179,6 @@ msgctxt "repo_update_title" msgid "Update repositories" msgstr "Uppdatera förråd" -# Rubrik för lista på installerade program. msgctxt "tab_installed" msgid "Installed" msgstr "Installerade" @@ -198,7 +207,6 @@ msgctxt "connection_error" msgid "Could not connect to the network." msgstr "Kunde inte ansluta till nätverket." -# Om vi inte ska följa tyskarna och kalla det "tidsöverskridning"...? msgctxt "connection_timeout" msgid "Timeout" msgstr "Timeout" @@ -215,8 +223,9 @@ msgctxt "download_server" msgid "Getting application from" msgstr "Hämtar program från" +#, fuzzy msgctxt "repo_add_url" -msgid "Repository URL" +msgid "Repository address" msgstr "Förråds-URL" msgctxt "isinst" @@ -231,23 +240,24 @@ msgctxt "uninstall" msgid "Uninstall" msgstr "Avinstallera" -# Den här strängen tycks inte användas i programmet. Svårt att översätta utan sammanhang. msgctxt "update" msgid "Update!" msgstr "" +#, fuzzy msgctxt "update_alrt" msgid "" -"There updates available for some installed applications.\n" -"Do you wish to see them?" +"There are updates available for some installed applications.\n" +"Do you want to see them?" msgstr "" "Det finns uppdateringar för vissa av de installerade programmen.\n" "Vill du se dem?" +#, fuzzy msgctxt "repo_alrt" msgid "" -"The list of repositories in use has been changed.\n" -"Do you wish to update them?" +"The list of used repositories has changed.\n" +"Do you want to update them?" msgstr "" "Listan över förråd har ändrats.\n" "Vill du uppdatera dem?" @@ -292,8 +302,9 @@ msgctxt "menu_uninstall" msgid "Uninstall" msgstr "Avinstallera" +#, fuzzy msgctxt "menu_website" -msgid "Web Site" +msgid "Website" msgstr "Webbsida" msgctxt "menu_issues" @@ -304,7 +315,6 @@ msgctxt "menu_source" msgid "Source Code" msgstr "Källkod" -# Google kallar det "Market" även på svenska. msgctxt "menu_market" msgid "Market" msgstr "Market" @@ -318,7 +328,6 @@ msgctxt "details_installed" msgid "Version %s installed" msgstr "Version %s är installerad" -# Programmet är inte installerat, men %d versioner är tillgängliga. #, python-format msgctxt "details_notinstalled" msgid "Not installed (%d available)" @@ -331,3 +340,48 @@ msgstr "Installerad" msgctxt "corrupt_download" msgid "Downloaded file is corrupt" msgstr "Den nerladdade filen är skadad" + +msgctxt "antifeatures" +msgid "Anti-Features" +msgstr "" + +msgctxt "antiads" +msgid "Advertising" +msgstr "" + +msgctxt "antiadslong" +msgid "Show apps that contain advertising" +msgstr "" + +msgctxt "antitrack" +msgid "Tracking" +msgstr "" + +msgctxt "antitracklong" +msgid "Show apps that track and report your activity" +msgstr "" + +msgctxt "antinonfreead" +msgid "Add-ons" +msgstr "" + +msgctxt "antinonfreeadlong" +msgid "Show apps that promote non-free add-ons" +msgstr "" + +msgctxt "antinonfreenet" +msgid "Network Services" +msgstr "" + +msgctxt "antinonfreenetlong" +msgid "Show apps that promote non-free network services" +msgstr "" + +msgctxt "expert" +msgid "Expert" +msgstr "" + +msgctxt "expert_mode" +msgid "Enabled expert mode" +msgstr "" + diff --git a/locale/template.pot b/locale/template.pot index 592cbff77..644898cc8 100644 --- a/locale/template.pot +++ b/locale/template.pot @@ -1,14 +1,14 @@ # Translations template for PROJECT. -# Copyright (C) 2010 ORGANIZATION +# Copyright (C) 2011 ORGANIZATION # This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2010. +# FIRST AUTHOR , 2011. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2010-11-15 07:54+0000\n" +"POT-Creation-Date: 2011-01-16 16:16+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,6 +17,14 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 1.0dev\n" +msgctxt "SignatureMismatch" +msgid "" +"The new version is signed with a different key to the old one. To install" +" the new version, the old one must be uninstalled first. Please do this " +"and try again. (Note that uninstalling will erase any internal data " +"stored by the application)" +msgstr "" + msgctxt "version" msgid "Version" msgstr "" @@ -68,7 +76,7 @@ msgid "Notify when new updates are available" msgstr "" msgctxt "update_apps_list" -msgid "Update apps list from repositories automatically" +msgid "Update app list from repositories automatically" msgstr "" msgctxt "automatic_repo_scan" @@ -86,11 +94,11 @@ msgstr "" msgctxt "about_desc" msgid "" "Originally based on Aptoide.\n" -"Released under the GNU GPL v2 license." +"Released under the GNU GPLv2 license." msgstr "" msgctxt "about_site" -msgid "Home:" +msgid "Website:" msgstr "" msgctxt "about_mail" @@ -102,17 +110,17 @@ msgid "Version:" msgstr "" msgctxt "about_website" -msgid "Web Site" +msgid "Website" msgstr "" msgctxt "no_repo" msgid "" -"You have no repositories configured!\n" +"You don't have any repositories configured!\n" "\n" "A repository is a source of applications. To add one, press the MENU " "button now and enter the URL.\n" "\n" -"A repository URL looks something like this: http://f-droid.org/repo" +"A repository address looks something like this: http://f-droid.org/repo" msgstr "" msgctxt "not_inst" @@ -124,7 +132,7 @@ msgid "Update possible - Ver.:" msgstr "" msgctxt "ok" -msgid "Ok" +msgid "OK" msgstr "" msgctxt "yes" @@ -204,7 +212,7 @@ msgid "Getting application from" msgstr "" msgctxt "repo_add_url" -msgid "Repository URL" +msgid "Repository address" msgstr "" msgctxt "isinst" @@ -225,14 +233,14 @@ msgstr "" msgctxt "update_alrt" msgid "" -"There updates available for some installed applications.\n" -"Do you wish to see them?" +"There are updates available for some installed applications.\n" +"Do you want to see them?" msgstr "" msgctxt "repo_alrt" msgid "" -"The list of repositories in use has been changed.\n" -"Do you wish to update them?" +"The list of used repositories has changed.\n" +"Do you want to update them?" msgstr "" msgctxt "error_download_alrt" @@ -276,7 +284,7 @@ msgid "Uninstall" msgstr "" msgctxt "menu_website" -msgid "Web Site" +msgid "Website" msgstr "" msgctxt "menu_issues" @@ -313,3 +321,47 @@ msgctxt "corrupt_download" msgid "Downloaded file is corrupt" msgstr "" +msgctxt "antifeatures" +msgid "Anti-Features" +msgstr "" + +msgctxt "antiads" +msgid "Advertising" +msgstr "" + +msgctxt "antiadslong" +msgid "Show apps that contain advertising" +msgstr "" + +msgctxt "antitrack" +msgid "Tracking" +msgstr "" + +msgctxt "antitracklong" +msgid "Show apps that track and report your activity" +msgstr "" + +msgctxt "antinonfreead" +msgid "Add-ons" +msgstr "" + +msgctxt "antinonfreeadlong" +msgid "Show apps that promote non-free add-ons" +msgstr "" + +msgctxt "antinonfreenet" +msgid "Network Services" +msgstr "" + +msgctxt "antinonfreenetlong" +msgid "Show apps that promote non-free network services" +msgstr "" + +msgctxt "expert" +msgid "Expert" +msgstr "" + +msgctxt "expert_mode" +msgid "Enabled expert mode" +msgstr "" +