Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
876ab253b5 | ||
![]() |
3680c6b914 | ||
![]() |
198ad843c1 | ||
![]() |
01de14f84e | ||
![]() |
ae15e3a22d | ||
![]() |
ba2f706166 | ||
![]() |
7b54c07c31 | ||
![]() |
55f4a5938e | ||
![]() |
550a107c9c | ||
![]() |
ad498faf92 | ||
![]() |
f5fcde9867 | ||
![]() |
57fee437ba | ||
![]() |
41d1278579 | ||
![]() |
c941440a34 | ||
![]() |
8322fd046c | ||
![]() |
4f514deca5 | ||
![]() |
cd9ad9cdbf | ||
![]() |
8eda7d0273 | ||
![]() |
5f3dde4060 | ||
![]() |
b3f79da341 | ||
![]() |
f1b09a5c43 | ||
![]() |
3dfbadc24d | ||
![]() |
3fedbdaff3 | ||
![]() |
5084982ce2 | ||
![]() |
ba42d3a507 | ||
![]() |
db9bdc315d | ||
![]() |
5f1aee8f0d | ||
![]() |
388dbbb2de | ||
![]() |
c3ae8008ba | ||
![]() |
59a533234e |
@ -1,4 +1,4 @@
|
||||
### 0.100 (2016-05-??)
|
||||
### 0.100 (2016-06-07)
|
||||
|
||||
* Ability to download apps in the background
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.fdroid.fdroid"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="100007"
|
||||
android:versionName="0.100-alpha7"
|
||||
android:versionCode="100050"
|
||||
android:versionName="0.100"
|
||||
>
|
||||
|
||||
<uses-sdk
|
||||
|
@ -7,7 +7,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
@ -20,7 +19,6 @@ import java.io.File;
|
||||
* {@link FDroidApp#onCreate()}
|
||||
*/
|
||||
public class CleanCacheService extends IntentService {
|
||||
private static final String TAG = "CleanCacheService";
|
||||
|
||||
/**
|
||||
* Schedule or cancel this service to update the app index, according to the
|
||||
@ -33,7 +31,6 @@ public class CleanCacheService extends IntentService {
|
||||
if (keepTime < interval) {
|
||||
interval = keepTime * 1000;
|
||||
}
|
||||
Log.i(TAG, "schedule " + keepTime + " " + interval);
|
||||
|
||||
Intent intent = new Intent(context, CleanCacheService.class);
|
||||
PendingIntent pending = PendingIntent.getService(context, 0, intent, 0);
|
||||
@ -53,6 +50,29 @@ public class CleanCacheService extends IntentService {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
|
||||
Utils.clearOldFiles(Utils.getApkCacheDir(this), Preferences.get().getKeepCacheTime());
|
||||
deleteStrayIndexFiles();
|
||||
deleteOldInstallerFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.fdroid.fdroid.installer.Installer} instances copy the APK into
|
||||
* a safe place before installing. It doesn't clean up them reliably yet.
|
||||
*/
|
||||
private void deleteOldInstallerFiles() {
|
||||
File filesDir = getFilesDir();
|
||||
if (filesDir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final File[] files = filesDir.listFiles();
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (File f : files) {
|
||||
if (f.getName().startsWith("install-")) {
|
||||
FileUtils.deleteQuietly(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,7 +139,9 @@ public class FDroidApp extends Application {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the settings needed to run a local swap repo.
|
||||
* Initialize the settings needed to run a local swap repo. This should
|
||||
* only ever be called in {@link org.fdroid.fdroid.net.WifiStateChangeService.WifiInfoThread},
|
||||
* after the single init call in {@link FDroidApp#onCreate()}.
|
||||
*/
|
||||
public static void initWifiSettings() {
|
||||
port = 8888;
|
||||
|
@ -57,7 +57,9 @@ public class RepoUpdater {
|
||||
@NonNull
|
||||
private final Repo repo;
|
||||
private boolean hasChanged;
|
||||
|
||||
@Nullable
|
||||
private ProgressListener downloadProgressListener;
|
||||
private ProgressListener committingProgressListener;
|
||||
private ProgressListener processXmlProgressListener;
|
||||
private String cacheTag;
|
||||
@ -83,6 +85,10 @@ public class RepoUpdater {
|
||||
this.indexUrl = url;
|
||||
}
|
||||
|
||||
public void setDownloadProgressListener(ProgressListener progressListener) {
|
||||
this.downloadProgressListener = progressListener;
|
||||
}
|
||||
|
||||
public void setProcessXmlProgressListener(ProgressListener progressListener) {
|
||||
this.processXmlProgressListener = progressListener;
|
||||
}
|
||||
@ -100,6 +106,7 @@ public class RepoUpdater {
|
||||
try {
|
||||
downloader = DownloaderFactory.create(context, indexUrl);
|
||||
downloader.setCacheTag(repo.lastetag);
|
||||
downloader.setListener(downloadProgressListener);
|
||||
downloader.download();
|
||||
|
||||
if (downloader.isCached()) {
|
||||
|
@ -125,10 +125,14 @@ public class RepoXMLHandler extends DefaultHandler {
|
||||
curapk.apkName = str;
|
||||
break;
|
||||
case "sdkver":
|
||||
curapk.minSdkVersion = Utils.parseInt(str, 0);
|
||||
curapk.minSdkVersion = Utils.parseInt(str, Apk.SDK_VERSION_MIN_VALUE);
|
||||
break;
|
||||
case "maxsdkver":
|
||||
curapk.maxSdkVersion = Utils.parseInt(str, 0);
|
||||
curapk.maxSdkVersion = Utils.parseInt(str, Apk.SDK_VERSION_MAX_VALUE);
|
||||
if (curapk.maxSdkVersion == 0) {
|
||||
// before fc0df0dcf4dd0d5f13de82d7cd9254b2b48cb62d, this could be 0
|
||||
curapk.maxSdkVersion = Apk.SDK_VERSION_MAX_VALUE;
|
||||
}
|
||||
break;
|
||||
case "added":
|
||||
curapk.added = Utils.parseDate(str, null);
|
||||
|
@ -49,8 +49,6 @@ import org.fdroid.fdroid.data.AppProvider;
|
||||
import org.fdroid.fdroid.data.Repo;
|
||||
import org.fdroid.fdroid.data.RepoProvider;
|
||||
import org.fdroid.fdroid.installer.InstallManagerService;
|
||||
import org.fdroid.fdroid.net.Downloader;
|
||||
import org.fdroid.fdroid.net.DownloaderService;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
@ -166,7 +164,6 @@ public class UpdateService extends IntentService {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
notificationManager.cancel(NOTIFY_ID_UPDATING);
|
||||
localBroadcastManager.unregisterReceiver(downloadProgressReceiver);
|
||||
localBroadcastManager.unregisterReceiver(updateStatusReceiver);
|
||||
}
|
||||
|
||||
@ -195,26 +192,6 @@ public class UpdateService extends IntentService {
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
private final BroadcastReceiver downloadProgressReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String repoAddress = intent.getDataString();
|
||||
int downloadedSize = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, -1);
|
||||
String downloadedSizeFriendly = Utils.getFriendlySize(downloadedSize);
|
||||
int totalSize = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, -1);
|
||||
int percent = (int) ((double) downloadedSize / totalSize * 100);
|
||||
String message;
|
||||
if (totalSize == -1) {
|
||||
message = getString(R.string.status_download_unknown_size, repoAddress, downloadedSizeFriendly);
|
||||
percent = -1;
|
||||
} else {
|
||||
String totalSizeFriendly = Utils.getFriendlySize(totalSize);
|
||||
message = getString(R.string.status_download, repoAddress, downloadedSizeFriendly, totalSizeFriendly, percent);
|
||||
}
|
||||
sendStatus(context, STATUS_INFO, message, percent);
|
||||
}
|
||||
};
|
||||
|
||||
// For receiving results from the UpdateService when we've told it to
|
||||
// update in response to a user request.
|
||||
private final BroadcastReceiver updateStatusReceiver = new BroadcastReceiver() {
|
||||
@ -337,8 +314,12 @@ public class UpdateService extends IntentService {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
|
||||
|
||||
final long startTime = System.currentTimeMillis();
|
||||
String address = intent.getStringExtra(EXTRA_ADDRESS);
|
||||
boolean manualUpdate = intent.getBooleanExtra(EXTRA_MANUAL_UPDATE, false);
|
||||
boolean manualUpdate = false;
|
||||
String address = null;
|
||||
if (intent != null) {
|
||||
address = intent.getStringExtra(EXTRA_ADDRESS);
|
||||
manualUpdate = intent.getBooleanExtra(EXTRA_MANUAL_UPDATE, false);
|
||||
}
|
||||
|
||||
try {
|
||||
// See if it's time to actually do anything yet...
|
||||
@ -375,10 +356,7 @@ public class UpdateService extends IntentService {
|
||||
|
||||
sendStatus(this, STATUS_INFO, getString(R.string.status_connecting_to_repo, repo.address));
|
||||
RepoUpdater updater = new RepoUpdater(getBaseContext(), repo);
|
||||
localBroadcastManager.registerReceiver(downloadProgressReceiver,
|
||||
DownloaderService.getIntentFilter(updater.indexUrl, Downloader.ACTION_PROGRESS));
|
||||
updater.setProcessXmlProgressListener(processXmlProgressListener);
|
||||
updater.setCommittingProgressListener(committingProgressListener);
|
||||
setProgressListeners(updater);
|
||||
try {
|
||||
updater.update();
|
||||
if (updater.hasChanged()) {
|
||||
@ -392,7 +370,6 @@ public class UpdateService extends IntentService {
|
||||
repoErrors.add(e.getMessage());
|
||||
Log.e(TAG, "Error updating repository " + repo.address, e);
|
||||
}
|
||||
localBroadcastManager.unregisterReceiver(downloadProgressReceiver);
|
||||
|
||||
// now that downloading the index is done, start downloading updates
|
||||
if (changes && fdroidPrefs.isAutoDownloadEnabled()) {
|
||||
@ -528,25 +505,53 @@ public class UpdateService extends IntentService {
|
||||
notificationManager.notify(NOTIFY_ID_UPDATES_AVAILABLE, builder.build());
|
||||
}
|
||||
|
||||
private final ProgressListener processXmlProgressListener = new ProgressListener() {
|
||||
@Override
|
||||
public void onProgress(URL sourceUrl, int bytesRead, int totalBytes) {
|
||||
String downloadedSize = Utils.getFriendlySize(bytesRead);
|
||||
String totalSize = Utils.getFriendlySize(totalBytes);
|
||||
int percent = -1;
|
||||
if (totalBytes > 0) {
|
||||
percent = (int) ((double) bytesRead / totalBytes * 100);
|
||||
/**
|
||||
* Set up the various {@link ProgressListener}s needed to get feedback to the UI.
|
||||
* Note: {@code ProgressListener}s do not need to be unregistered, they can just
|
||||
* be set again for each download.
|
||||
*/
|
||||
private void setProgressListeners(RepoUpdater updater) {
|
||||
updater.setDownloadProgressListener(new ProgressListener() {
|
||||
@Override
|
||||
public void onProgress(URL sourceUrl, int bytesRead, int totalBytes) {
|
||||
Log.i(TAG, "downloadProgressReceiver " + sourceUrl);
|
||||
String downloadedSizeFriendly = Utils.getFriendlySize(bytesRead);
|
||||
int percent = -1;
|
||||
if (totalBytes > 0) {
|
||||
percent = (int) ((double) bytesRead / totalBytes * 100);
|
||||
}
|
||||
String message;
|
||||
if (totalBytes == -1) {
|
||||
message = getString(R.string.status_download_unknown_size, sourceUrl, downloadedSizeFriendly);
|
||||
percent = -1;
|
||||
} else {
|
||||
String totalSizeFriendly = Utils.getFriendlySize(totalBytes);
|
||||
message = getString(R.string.status_download, sourceUrl, downloadedSizeFriendly, totalSizeFriendly, percent);
|
||||
}
|
||||
sendStatus(getApplicationContext(), STATUS_INFO, message, percent);
|
||||
}
|
||||
String message = getString(R.string.status_processing_xml_percent, sourceUrl, downloadedSize, totalSize, percent);
|
||||
sendStatus(getApplicationContext(), STATUS_INFO, message, percent);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
private final ProgressListener committingProgressListener = new ProgressListener() {
|
||||
@Override
|
||||
public void onProgress(URL sourceUrl, int bytesRead, int totalBytes) {
|
||||
String message = getString(R.string.status_inserting_apps);
|
||||
sendStatus(getApplicationContext(), STATUS_INFO, message);
|
||||
}
|
||||
};
|
||||
updater.setProcessXmlProgressListener(new ProgressListener() {
|
||||
@Override
|
||||
public void onProgress(URL sourceUrl, int bytesRead, int totalBytes) {
|
||||
String downloadedSize = Utils.getFriendlySize(bytesRead);
|
||||
String totalSize = Utils.getFriendlySize(totalBytes);
|
||||
int percent = -1;
|
||||
if (totalBytes > 0) {
|
||||
percent = (int) ((double) bytesRead / totalBytes * 100);
|
||||
}
|
||||
String message = getString(R.string.status_processing_xml_percent, sourceUrl, downloadedSize, totalSize, percent);
|
||||
sendStatus(getApplicationContext(), STATUS_INFO, message, percent);
|
||||
}
|
||||
});
|
||||
|
||||
updater.setCommittingProgressListener(new ProgressListener() {
|
||||
@Override
|
||||
public void onProgress(URL sourceUrl, int bytesRead, int totalBytes) {
|
||||
String message = getString(R.string.status_inserting_apps);
|
||||
sendStatus(getApplicationContext(), STATUS_INFO, message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import com.nostra13.universalimageloader.utils.StorageUtils;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.fdroid.fdroid.compat.FileCompat;
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
import org.fdroid.fdroid.data.Repo;
|
||||
import org.fdroid.fdroid.data.SanitizedFile;
|
||||
import org.xml.sax.XMLReader;
|
||||
@ -226,8 +227,8 @@ public final class Utils {
|
||||
|
||||
/* PackageManager doesn't give us the min and max sdk versions, so we have
|
||||
* to parse it */
|
||||
private static int getMinMaxSdkVersion(Context context, String packageName,
|
||||
String attrName) {
|
||||
private static int getSdkVersion(Context context, String packageName,
|
||||
String attrName, final int defaultValue) {
|
||||
try {
|
||||
AssetManager am = context.createPackageContext(packageName, 0).getAssets();
|
||||
XmlResourceParser xml = am.openXmlResourceParser("AndroidManifest.xml");
|
||||
@ -245,15 +246,15 @@ public final class Utils {
|
||||
} catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) {
|
||||
Log.e(TAG, "Could not get min/max sdk version", e);
|
||||
}
|
||||
return 0;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static int getMinSdkVersion(Context context, String packageName) {
|
||||
return getMinMaxSdkVersion(context, packageName, "minSdkVersion");
|
||||
return getSdkVersion(context, packageName, "minSdkVersion", Apk.SDK_VERSION_MIN_VALUE);
|
||||
}
|
||||
|
||||
public static int getMaxSdkVersion(Context context, String packageName) {
|
||||
return getMinMaxSdkVersion(context, packageName, "maxSdkVersion");
|
||||
return getSdkVersion(context, packageName, "maxSdkVersion", Apk.SDK_VERSION_MAX_VALUE);
|
||||
}
|
||||
|
||||
// return a fingerprint formatted for display
|
||||
|
@ -4,6 +4,7 @@ import android.annotation.TargetApi;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.fdroid.fdroid.Utils;
|
||||
|
||||
@ -13,6 +14,7 @@ public class Apk extends ValueObject implements Comparable<Apk> {
|
||||
|
||||
// Using only byte-range keeps it only 8-bits in the SQLite database
|
||||
public static final int SDK_VERSION_MAX_VALUE = Byte.MAX_VALUE;
|
||||
public static final int SDK_VERSION_MIN_VALUE = 0;
|
||||
|
||||
public String packageName;
|
||||
public String versionName;
|
||||
@ -21,7 +23,7 @@ public class Apk extends ValueObject implements Comparable<Apk> {
|
||||
public long repo; // ID of the repo it comes from
|
||||
public String hash;
|
||||
public String hashType;
|
||||
public int minSdkVersion; // 0 if unknown
|
||||
public int minSdkVersion = SDK_VERSION_MIN_VALUE; // 0 if unknown
|
||||
public int maxSdkVersion = SDK_VERSION_MAX_VALUE; // "infinity" if not set
|
||||
public Date added;
|
||||
public Utils.CommaSeparatedList permissions; // null if empty or
|
||||
@ -49,7 +51,12 @@ public class Apk extends ValueObject implements Comparable<Apk> {
|
||||
public String repoAddress;
|
||||
public Utils.CommaSeparatedList incompatibleReasons;
|
||||
|
||||
public Apk() { }
|
||||
public Apk() {
|
||||
}
|
||||
|
||||
public Apk(Parcelable parcelable) {
|
||||
this(new ContentValuesCursor((ContentValues) parcelable));
|
||||
}
|
||||
|
||||
public Apk(Cursor cursor) {
|
||||
|
||||
|
@ -8,6 +8,7 @@ import android.content.pm.FeatureInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
@ -21,8 +22,12 @@ import java.io.InputStream;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class App extends ValueObject implements Comparable<App> {
|
||||
|
||||
@ -108,12 +113,21 @@ public class App extends ValueObject implements Comparable<App> {
|
||||
|
||||
public boolean uninstallable;
|
||||
|
||||
public static String getIconName(String packageName, int versionCode) {
|
||||
return packageName + "_" + versionCode + ".png";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(App app) {
|
||||
return name.compareToIgnoreCase(app.name);
|
||||
}
|
||||
|
||||
public App() { }
|
||||
public App() {
|
||||
}
|
||||
|
||||
public App(Parcelable parcelable) {
|
||||
this(new ContentValuesCursor((ContentValues) parcelable));
|
||||
}
|
||||
|
||||
public App(Cursor cursor) {
|
||||
|
||||
@ -273,21 +287,35 @@ public class App extends ValueObject implements Comparable<App> {
|
||||
+ ", last updated on " + this.lastUpdated + ")</p>";
|
||||
|
||||
this.name = (String) appInfo.loadLabel(pm);
|
||||
this.icon = getIconName(packageName, packageInfo.versionCode);
|
||||
|
||||
final SanitizedFile apkFile = SanitizedFile.knownSanitized(appInfo.publicSourceDir);
|
||||
final Apk apk = new Apk();
|
||||
apk.versionName = packageInfo.versionName;
|
||||
apk.versionCode = packageInfo.versionCode;
|
||||
apk.hashType = "sha256";
|
||||
apk.hash = Utils.getBinaryHash(apkFile, apk.hashType);
|
||||
apk.added = this.added;
|
||||
apk.minSdkVersion = Utils.getMinSdkVersion(context, packageName);
|
||||
apk.maxSdkVersion = Utils.getMaxSdkVersion(context, packageName);
|
||||
apk.packageName = this.packageName;
|
||||
apk.installedFile = apkFile;
|
||||
apk.permissions = Utils.CommaSeparatedList.make(packageInfo.requestedPermissions);
|
||||
apk.apkName = apk.packageName + "_" + apk.versionCode + ".apk";
|
||||
|
||||
final SanitizedFile apkFile = SanitizedFile.knownSanitized(appInfo.publicSourceDir);
|
||||
apk.hashType = "sha256";
|
||||
apk.hash = Utils.getBinaryHash(apkFile, apk.hashType);
|
||||
apk.installedFile = apkFile;
|
||||
|
||||
JarFile jarFile = new JarFile(apkFile);
|
||||
HashSet<String> abis = new HashSet<>(3);
|
||||
Pattern pattern = Pattern.compile("^lib/([a-z0-9-]+)/.*");
|
||||
for (Enumeration<JarEntry> jarEntries = jarFile.entries(); jarEntries.hasMoreElements();) {
|
||||
JarEntry jarEntry = jarEntries.nextElement();
|
||||
Matcher matcher = pattern.matcher(jarEntry.getName());
|
||||
if (matcher.matches()) {
|
||||
abis.add(matcher.group(1));
|
||||
}
|
||||
}
|
||||
apk.nativecode = Utils.CommaSeparatedList.make(abis.toArray(new String[abis.size()]));
|
||||
|
||||
final FeatureInfo[] features = packageInfo.reqFeatures;
|
||||
if (features != null && features.length > 0) {
|
||||
final String[] featureNames = new String[features.length];
|
||||
|
@ -0,0 +1,90 @@
|
||||
package org.fdroid.fdroid.data;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.AbstractCursor;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* In order to keep {@link App#App(Cursor)} and {@link Apk#Apk(Cursor)} as
|
||||
* efficient as possible, this wrapper class is used to instantiate {@code App}
|
||||
* and {@code Apk} from {@link App#toContentValues()} and
|
||||
* {@link Apk#toContentValues()} included as extras {@link Bundle}s in the
|
||||
* {@link android.content.Intent} that starts
|
||||
* {@link org.fdroid.fdroid.installer.InstallManagerService}
|
||||
* <p>
|
||||
* This implemented to throw an {@link IllegalArgumentException} if the types
|
||||
* do not match what they are expected to be so that things fail fast. So that
|
||||
* means only types used in {@link App#toContentValues()} and
|
||||
* {@link Apk#toContentValues()} are implemented.
|
||||
*/
|
||||
public class ContentValuesCursor extends AbstractCursor {
|
||||
|
||||
private final String[] keys;
|
||||
private final Object[] values;
|
||||
|
||||
public ContentValuesCursor(ContentValues contentValues) {
|
||||
super();
|
||||
keys = new String[contentValues.size()];
|
||||
values = new Object[contentValues.size()];
|
||||
int i = 0;
|
||||
for (Map.Entry<String, Object> entry : contentValues.valueSet()) {
|
||||
keys[i] = entry.getKey();
|
||||
values[i] = entry.getValue();
|
||||
i++;
|
||||
}
|
||||
moveToFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int i) {
|
||||
return (String) values[i];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int i) {
|
||||
if (values[i] instanceof Long) {
|
||||
return ((Long) values[i]).intValue();
|
||||
} else if (values[i] instanceof Integer) {
|
||||
return (int) values[i];
|
||||
}
|
||||
throw new IllegalArgumentException("unimplemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int i) {
|
||||
throw new IllegalArgumentException("unimplemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(int i) {
|
||||
throw new IllegalArgumentException("unimplemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(int i) {
|
||||
throw new IllegalArgumentException("unimplemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int i) {
|
||||
throw new IllegalArgumentException("unimplemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull(int i) {
|
||||
return values[i] == null;
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@ import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.fdroid.fdroid.AppDetails;
|
||||
import org.fdroid.fdroid.R;
|
||||
@ -36,10 +35,12 @@ import java.util.Set;
|
||||
* requests an APK to be installed. It handles checking whether the APK is cached,
|
||||
* downloading it, putting up and maintaining a {@link Notification}, and more.
|
||||
* <p>
|
||||
* Data is sent via {@link Intent}s so that Android handles the message queuing
|
||||
* and {@link Service} lifecycle for us, although it adds one layer of redirection
|
||||
* between the static method to send the {@code Intent} and the method to
|
||||
* actually process it.
|
||||
* The {@link App} and {@link Apk} instances are sent via
|
||||
* {@link Intent#putExtra(String, android.os.Bundle)}
|
||||
* so that Android handles the message queuing and {@link Service} lifecycle for us.
|
||||
* For example, if this {@code InstallManagerService} gets killed, Android will cache
|
||||
* and then redeliver the {@link Intent} for us, which includes all of the data needed
|
||||
* for {@code InstallManagerService} to do its job for the whole lifecycle of an install.
|
||||
* <p>
|
||||
* The full URL for the APK file to download is also used as the unique ID to
|
||||
* represent the download itself throughout F-Droid. This follows the model
|
||||
@ -59,7 +60,10 @@ import java.util.Set;
|
||||
public class InstallManagerService extends Service {
|
||||
public static final String TAG = "InstallManagerService";
|
||||
|
||||
private static final String ACTION_INSTALL = "org.fdroid.fdroid.InstallManagerService.action.INSTALL";
|
||||
private static final String ACTION_INSTALL = "org.fdroid.fdroid.installer.action.INSTALL";
|
||||
|
||||
private static final String EXTRA_APP = "org.fdroid.fdroid.installer.extra.APP";
|
||||
private static final String EXTRA_APK = "org.fdroid.fdroid.installer.extra.APK";
|
||||
|
||||
/**
|
||||
* The collection of {@link Apk}s that are actively going through this whole process,
|
||||
@ -135,12 +139,31 @@ public class InstallManagerService extends Service {
|
||||
Utils.debugLog(TAG, "onStartCommand " + intent);
|
||||
|
||||
if (!ACTION_INSTALL.equals(intent.getAction())) {
|
||||
Log.i(TAG, "Ignoring " + intent + " as it is not an " + ACTION_INSTALL + " intent");
|
||||
Utils.debugLog(TAG, "Ignoring " + intent + " as it is not an " + ACTION_INSTALL + " intent");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
String urlString = intent.getDataString();
|
||||
Apk apk = ACTIVE_APKS.get(urlString);
|
||||
if (TextUtils.isEmpty(urlString)) {
|
||||
Utils.debugLog(TAG, "empty urlString, nothing to do");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
if (!intent.hasExtra(EXTRA_APP) || !intent.hasExtra(EXTRA_APK)) {
|
||||
Utils.debugLog(TAG, urlString + " did not include both an App and Apk instance, ignoring");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
if ((flags & START_FLAG_REDELIVERY) == START_FLAG_REDELIVERY
|
||||
&& !DownloaderService.isQueuedOrActive(urlString)) {
|
||||
Utils.debugLog(TAG, urlString + " finished downloading while InstallManagerService was killed.");
|
||||
cancelNotification(urlString);
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
App app = new App(intent.getParcelableExtra(EXTRA_APP));
|
||||
Apk apk = new Apk(intent.getParcelableExtra(EXTRA_APK));
|
||||
addToActive(urlString, app, apk);
|
||||
|
||||
Notification notification = createNotification(intent.getDataString(), apk).build();
|
||||
notificationManager.notify(urlString.hashCode(), notification);
|
||||
@ -331,9 +354,18 @@ public class InstallManagerService extends Service {
|
||||
TEMP_HACK_APP_NAMES.put(urlString, app.name); // TODO delete me once InstallerService exists
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the {@link App} and {@Apk} instances that are associated with
|
||||
* {@code urlString} from the {@link Map} of active apps. This can be
|
||||
* called after this service has been destroyed and recreated based on the
|
||||
* {@link BroadcastReceiver}s, in which case {@code urlString} would not
|
||||
* find anything in the active maps.
|
||||
*/
|
||||
private static Apk removeFromActive(String urlString) {
|
||||
Apk apk = ACTIVE_APKS.remove(urlString);
|
||||
ACTIVE_APPS.remove(apk.packageName);
|
||||
if (apk != null) {
|
||||
ACTIVE_APPS.remove(apk.packageName);
|
||||
}
|
||||
return apk;
|
||||
}
|
||||
|
||||
@ -346,10 +378,11 @@ public class InstallManagerService extends Service {
|
||||
public static void queue(Context context, App app, Apk apk) {
|
||||
String urlString = apk.getUrl();
|
||||
Utils.debugLog(TAG, "queue " + app.packageName + " " + apk.versionCode + " from " + urlString);
|
||||
addToActive(urlString, app, apk);
|
||||
Intent intent = new Intent(context, InstallManagerService.class);
|
||||
intent.setAction(ACTION_INSTALL);
|
||||
intent.setData(Uri.parse(urlString));
|
||||
intent.putExtra(EXTRA_APP, app.toContentValues());
|
||||
intent.putExtra(EXTRA_APK, apk.toContentValues());
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
|
@ -170,7 +170,7 @@ public abstract class Installer {
|
||||
*/
|
||||
public void installPackage(File apkFile, String packageName, String urlString)
|
||||
throws InstallFailedException {
|
||||
SanitizedFile apkToInstall;
|
||||
SanitizedFile apkToInstall = null;
|
||||
try {
|
||||
Map<String, Object> attributes = AndroidXMLDecompress.getManifestHeaderAttributes(apkFile.getAbsolutePath());
|
||||
|
||||
@ -232,6 +232,22 @@ public abstract class Installer {
|
||||
throw new InstallFailedException(e);
|
||||
} catch (ClassCastException e) {
|
||||
throw new InstallFailedException("F-Droid Privileged can only be updated using an activity!");
|
||||
} finally {
|
||||
// 20 minutes the start of the install process, delete the file
|
||||
final File apkToDelete = apkToInstall;
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);
|
||||
try {
|
||||
Thread.sleep(1200000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
FileUtils.deleteQuietly(apkToDelete);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ package org.fdroid.fdroid.localrepo;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.Bitmap;
|
||||
@ -24,6 +23,7 @@ import org.fdroid.fdroid.Hasher;
|
||||
import org.fdroid.fdroid.Preferences;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
import org.fdroid.fdroid.data.App;
|
||||
import org.fdroid.fdroid.data.SanitizedFile;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
@ -237,6 +237,13 @@ public final class LocalRepoManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@code index.jar} file that represents the local swap repo.
|
||||
*/
|
||||
public File getIndexJar() {
|
||||
return xmlIndexJar;
|
||||
}
|
||||
|
||||
public void deleteRepo() {
|
||||
deleteContents(repoDir);
|
||||
}
|
||||
@ -267,8 +274,6 @@ public final class LocalRepoManager {
|
||||
if (!app.isValid()) {
|
||||
return;
|
||||
}
|
||||
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_META_DATA);
|
||||
app.icon = getIconFile(packageName, packageInfo.versionCode).getName();
|
||||
} catch (PackageManager.NameNotFoundException | CertificateEncodingException | IOException e) {
|
||||
Log.e(TAG, "Error adding app to local repo", e);
|
||||
return;
|
||||
@ -277,10 +282,6 @@ public final class LocalRepoManager {
|
||||
apps.put(packageName, app);
|
||||
}
|
||||
|
||||
public List<String> getApps() {
|
||||
return new ArrayList<>(apps.keySet());
|
||||
}
|
||||
|
||||
public void copyIconsToRepo() {
|
||||
ApplicationInfo appInfo;
|
||||
for (final App app : apps.values()) {
|
||||
@ -321,7 +322,7 @@ public final class LocalRepoManager {
|
||||
}
|
||||
|
||||
private File getIconFile(String packageName, int versionCode) {
|
||||
return new File(iconsDir, packageName + "_" + versionCode + ".png");
|
||||
return new File(iconsDir, App.getIconName(packageName, versionCode));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -451,11 +452,16 @@ public final class LocalRepoManager {
|
||||
tagHash(app);
|
||||
tag("sig", app.installedApk.sig.toLowerCase(Locale.US));
|
||||
tag("size", app.installedApk.installedFile.length());
|
||||
tag("sdkver", app.installedApk.minSdkVersion);
|
||||
tag("maxsdkver", app.installedApk.maxSdkVersion);
|
||||
tag("added", app.installedApk.added);
|
||||
if (app.installedApk.minSdkVersion > Apk.SDK_VERSION_MIN_VALUE) {
|
||||
tag("sdkver", app.installedApk.minSdkVersion);
|
||||
}
|
||||
if (app.installedApk.maxSdkVersion < Apk.SDK_VERSION_MAX_VALUE) {
|
||||
tag("maxsdkver", app.installedApk.maxSdkVersion);
|
||||
}
|
||||
tagFeatures(app);
|
||||
tagPermissions(app);
|
||||
tagNativecode(app);
|
||||
|
||||
serializer.endTag("", "package");
|
||||
}
|
||||
@ -485,6 +491,14 @@ public final class LocalRepoManager {
|
||||
serializer.endTag("", "features");
|
||||
}
|
||||
|
||||
private void tagNativecode(App app) throws IOException {
|
||||
if (app.installedApk.nativecode != null) {
|
||||
serializer.startTag("", "nativecode");
|
||||
serializer.text(Utils.CommaSeparatedList.str(app.installedApk.nativecode));
|
||||
serializer.endTag("", "nativecode");
|
||||
}
|
||||
}
|
||||
|
||||
private void tagHash(App app) throws IOException {
|
||||
serializer.startTag("", "hash");
|
||||
serializer.attribute("", "type", app.installedApk.hashType);
|
||||
|
@ -145,7 +145,7 @@ public abstract class Downloader {
|
||||
totalBytes = totalDownloadSize();
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
|
||||
timer.scheduleAtFixedRate(progressTask, 0, 100);
|
||||
timer.scheduleAtFixedRate(progressTask, 0, 500);
|
||||
|
||||
// Getting the total download size could potentially take time, depending on how
|
||||
// it is implemented, so we may as well check this before we proceed.
|
||||
|
@ -30,7 +30,6 @@ import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.PatternMatcher;
|
||||
import android.os.Process;
|
||||
import android.support.v4.content.IntentCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
@ -147,8 +146,7 @@ public class DownloaderService extends Service {
|
||||
public static PendingIntent getCancelPendingIntent(Context context, String urlString) {
|
||||
Intent cancelIntent = new Intent(context.getApplicationContext(), DownloaderService.class)
|
||||
.setData(Uri.parse(urlString))
|
||||
.setAction(ACTION_CANCEL)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
.setAction(ACTION_CANCEL);
|
||||
return PendingIntent.getService(context.getApplicationContext(),
|
||||
urlString.hashCode(),
|
||||
cancelIntent,
|
||||
|
@ -36,6 +36,12 @@ import java.util.Locale;
|
||||
* which is how it can be triggered by code, or it came in from the system
|
||||
* via {@link org.fdroid.fdroid.receiver.WifiStateChangeReceiver}, in
|
||||
* which case an instance of {@link NetworkInfo} is included.
|
||||
*
|
||||
* The work is done in a {@link Thread} so that new incoming {@code Intents}
|
||||
* are not blocked by processing. A new {@code Intent} immediately nullifies
|
||||
* the current state because it means that something about the wifi has
|
||||
* changed. Having the {@code Thread} also makes it easy to kill work
|
||||
* that is in progress.
|
||||
*/
|
||||
public class WifiStateChangeService extends IntentService {
|
||||
private static final String TAG = "WifiStateChangeService";
|
||||
@ -52,8 +58,11 @@ public class WifiStateChangeService extends IntentService {
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);
|
||||
if (intent == null) {
|
||||
Utils.debugLog(TAG, "received null Intent, ignoring");
|
||||
return;
|
||||
}
|
||||
Utils.debugLog(TAG, "WiFi change service started, clearing info about wifi state until we have figured it out again.");
|
||||
FDroidApp.initWifiSettings();
|
||||
NetworkInfo ni = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
|
||||
wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
|
||||
int wifiState = wifiManager.getWifiState();
|
||||
@ -79,6 +88,7 @@ public class WifiStateChangeService extends IntentService {
|
||||
public void run() {
|
||||
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);
|
||||
try {
|
||||
FDroidApp.initWifiSettings();
|
||||
Utils.debugLog(TAG, "Checking wifi state (in background thread).");
|
||||
WifiInfo wifiInfo = null;
|
||||
|
||||
@ -185,7 +195,11 @@ public class WifiStateChangeService extends IntentService {
|
||||
@TargetApi(9)
|
||||
private void setIpInfoFromNetworkInterface() {
|
||||
try {
|
||||
for (Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements();) {
|
||||
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
|
||||
if (networkInterfaces == null) {
|
||||
return;
|
||||
}
|
||||
while (networkInterfaces.hasMoreElements()) {
|
||||
NetworkInterface netIf = networkInterfaces.nextElement();
|
||||
|
||||
for (Enumeration<InetAddress> inetAddresses = netIf.getInetAddresses(); inetAddresses.hasMoreElements();) {
|
||||
@ -202,9 +216,15 @@ public class WifiStateChangeService extends IntentService {
|
||||
}
|
||||
// the following methods were not added until android-9/Gingerbread
|
||||
for (InterfaceAddress address : netIf.getInterfaceAddresses()) {
|
||||
short networkPrefixLength = address.getNetworkPrefixLength();
|
||||
if (networkPrefixLength > 32) {
|
||||
// something is giving a "/64" netmask, IPv6?
|
||||
// java.lang.IllegalArgumentException: Value [64] not in range [0,32]
|
||||
continue;
|
||||
}
|
||||
if (inetAddress.equals(address.getAddress()) && !TextUtils.isEmpty(FDroidApp.ipAddressString)) {
|
||||
String cidr = String.format(Locale.ENGLISH, "%s/%d",
|
||||
FDroidApp.ipAddressString, address.getNetworkPrefixLength());
|
||||
FDroidApp.ipAddressString, networkPrefixLength);
|
||||
FDroidApp.subnetInfo = new SubnetUtils(cidr).getInfo();
|
||||
break;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public class BluetoothConnection {
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
public void open() throws IOException {
|
||||
if (!socket.isConnected()) {
|
||||
if (Build.VERSION.SDK_INT >= 14 && !socket.isConnected()) {
|
||||
// Server sockets will already be connected when they are passed to us,
|
||||
// client sockets require us to call connect().
|
||||
socket.connect();
|
||||
|
@ -22,7 +22,6 @@ public abstract class AppListAdapter extends CursorAdapter {
|
||||
private DisplayImageOptions displayImageOptions;
|
||||
private String upgradeFromTo;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public AppListAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
init(context);
|
||||
|
@ -2,18 +2,10 @@ package org.fdroid.fdroid.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
|
||||
public class AvailableAppListAdapter extends AppListAdapter {
|
||||
|
||||
public static AvailableAppListAdapter create(Context context, Cursor cursor, int flags) {
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
return new AvailableAppListAdapter(context, cursor, flags);
|
||||
}
|
||||
return new AvailableAppListAdapter(context, cursor);
|
||||
}
|
||||
|
||||
private AvailableAppListAdapter(Context context, Cursor c) {
|
||||
public AvailableAppListAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
}
|
||||
|
||||
@ -21,7 +13,7 @@ public class AvailableAppListAdapter extends AppListAdapter {
|
||||
super(context, c, autoRequery);
|
||||
}
|
||||
|
||||
private AvailableAppListAdapter(Context context, Cursor c, int flags) {
|
||||
public AvailableAppListAdapter(Context context, Cursor c, int flags) {
|
||||
super(context, c, flags);
|
||||
}
|
||||
|
||||
|
@ -2,18 +2,10 @@ package org.fdroid.fdroid.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
|
||||
public class CanUpdateAppListAdapter extends AppListAdapter {
|
||||
|
||||
public static CanUpdateAppListAdapter create(Context context, Cursor cursor, int flags) {
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
return new CanUpdateAppListAdapter(context, cursor, flags);
|
||||
}
|
||||
return new CanUpdateAppListAdapter(context, cursor);
|
||||
}
|
||||
|
||||
private CanUpdateAppListAdapter(Context context, Cursor c) {
|
||||
public CanUpdateAppListAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
}
|
||||
|
||||
@ -21,7 +13,7 @@ public class CanUpdateAppListAdapter extends AppListAdapter {
|
||||
super(context, c, autoRequery);
|
||||
}
|
||||
|
||||
private CanUpdateAppListAdapter(Context context, Cursor c, int flags) {
|
||||
public CanUpdateAppListAdapter(Context context, Cursor c, int flags) {
|
||||
super(context, c, flags);
|
||||
}
|
||||
|
||||
|
@ -2,18 +2,10 @@ package org.fdroid.fdroid.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
|
||||
public class InstalledAppListAdapter extends AppListAdapter {
|
||||
|
||||
public static InstalledAppListAdapter create(Context context, Cursor cursor, int flags) {
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
return new InstalledAppListAdapter(context, cursor, flags);
|
||||
}
|
||||
return new InstalledAppListAdapter(context, cursor);
|
||||
}
|
||||
|
||||
private InstalledAppListAdapter(Context context, Cursor c) {
|
||||
public InstalledAppListAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
}
|
||||
|
||||
@ -21,7 +13,7 @@ public class InstalledAppListAdapter extends AppListAdapter {
|
||||
super(context, c, autoRequery);
|
||||
}
|
||||
|
||||
private InstalledAppListAdapter(Context context, Cursor c, int flags) {
|
||||
public InstalledAppListAdapter(Context context, Cursor c, int flags) {
|
||||
super(context, c, flags);
|
||||
}
|
||||
|
||||
|
@ -767,7 +767,7 @@ public class ManageReposActivity extends ActionBarActivity {
|
||||
setRetainInstance(true);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
repoAdapter = RepoAdapter.create(getActivity(), null, 0);
|
||||
repoAdapter = new RepoAdapter(getActivity(), null);
|
||||
repoAdapter.setEnabledListener(this);
|
||||
setListAdapter(repoAdapter);
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package org.fdroid.fdroid.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@ -23,14 +22,7 @@ public class RepoAdapter extends CursorAdapter {
|
||||
|
||||
private EnabledListener enabledListener;
|
||||
|
||||
public static RepoAdapter create(Context context, Cursor cursor, int flags) {
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
return new RepoAdapter(context, cursor, flags);
|
||||
}
|
||||
return new RepoAdapter(context, cursor);
|
||||
}
|
||||
|
||||
private RepoAdapter(Context context, Cursor c, int flags) {
|
||||
public RepoAdapter(Context context, Cursor c, int flags) {
|
||||
super(context, c, flags);
|
||||
inflater = LayoutInflater.from(context);
|
||||
}
|
||||
@ -40,8 +32,7 @@ public class RepoAdapter extends CursorAdapter {
|
||||
inflater = LayoutInflater.from(context);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private RepoAdapter(Context context, Cursor c) {
|
||||
public RepoAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
inflater = LayoutInflater.from(context);
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ public abstract class AppListFragment extends ListFragment implements
|
||||
super.onResume();
|
||||
|
||||
//Starts a new or restarts an existing Loader in this manager
|
||||
getLoaderManager().restartLoader(0, null, this);
|
||||
getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -59,7 +59,7 @@ public class AvailableAppsFragment extends AppListFragment implements
|
||||
@Override
|
||||
protected AppListAdapter getAppListAdapter() {
|
||||
if (adapter == null) {
|
||||
final AppListAdapter a = AvailableAppListAdapter.create(getActivity(), null, 0);
|
||||
final AppListAdapter a = new AvailableAppListAdapter(getActivity(), null);
|
||||
Preferences.get().registerUpdateHistoryListener(new Preferences.ChangeListener() {
|
||||
@Override
|
||||
public void onPreferenceChange() {
|
||||
@ -205,7 +205,6 @@ public class AvailableAppsFragment extends AppListFragment implements
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
/* restore the saved Category Spinner position */
|
||||
Activity activity = getActivity();
|
||||
SharedPreferences p = activity.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
|
||||
@ -221,6 +220,7 @@ public class AvailableAppsFragment extends AppListFragment implements
|
||||
}
|
||||
|
||||
setCurrentCategory(currentCategory);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,7 +15,7 @@ public class CanUpdateAppsFragment extends AppListFragment {
|
||||
|
||||
@Override
|
||||
protected AppListAdapter getAppListAdapter() {
|
||||
return CanUpdateAppListAdapter.create(getActivity(), null, 0);
|
||||
return new CanUpdateAppListAdapter(getActivity(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,7 +15,7 @@ public class InstalledAppsFragment extends AppListFragment {
|
||||
|
||||
@Override
|
||||
protected AppListAdapter getAppListAdapter() {
|
||||
return InstalledAppListAdapter.create(getActivity(), null, 0);
|
||||
return new InstalledAppListAdapter(getActivity(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -26,7 +26,7 @@ import android.widget.CompoundButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.fdroid.fdroid.FDroidApp;
|
||||
@ -42,7 +42,7 @@ import cc.mvdan.accesspoint.WifiApControl;
|
||||
import rx.Subscriber;
|
||||
import rx.Subscription;
|
||||
|
||||
public class StartSwapView extends ScrollView implements SwapWorkflowActivity.InnerView {
|
||||
public class StartSwapView extends RelativeLayout implements SwapWorkflowActivity.InnerView {
|
||||
|
||||
private static final String TAG = "StartSwapView";
|
||||
|
||||
|
@ -245,6 +245,9 @@ public class SwapAppsView extends ListView implements
|
||||
private final BroadcastReceiver downloadProgressReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (progressView.getVisibility() != View.VISIBLE) {
|
||||
showProgress();
|
||||
}
|
||||
int read = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0);
|
||||
int total = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
||||
if (total > 0) {
|
||||
|
@ -376,7 +376,10 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
||||
getService().swapWith(null);
|
||||
|
||||
if (!getService().isEnabled()) {
|
||||
prepareInitialRepo();
|
||||
if (!LocalRepoManager.get(this).getIndexJar().exists()) {
|
||||
Utils.debugLog(TAG, "Preparing initial repo with only F-Droid, until we have allowed the user to configure their own repo.");
|
||||
new PrepareInitialSwapRepo().execute();
|
||||
}
|
||||
}
|
||||
|
||||
inflateInnerView(R.layout.swap_blank);
|
||||
@ -452,16 +455,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareInitialRepo() {
|
||||
// TODO: Make it so that this and updateSwappableAppsTask (the _real_ swap repo task)
|
||||
// don't stomp on eachothers toes. The other one should wait for this to finish, or cancel
|
||||
// this, but this should never take precedence over the other.
|
||||
// TODO: Also don't allow this to run multiple times (e.g. if a user keeps navigating back
|
||||
// to the main screen.
|
||||
Utils.debugLog(TAG, "Preparing initial repo with only F-Droid, until we have allowed the user to configure their own repo.");
|
||||
new PrepareInitialSwapRepo().execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Once the UpdateAsyncTask has finished preparing our repository index, we can
|
||||
* show the next screen to the user. This will be one of two things:
|
||||
|
7
app/src/main/res/layout-ldpi/start_swap_header.xml
Normal file
7
app/src/main/res/layout-ldpi/start_swap_header.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true">
|
||||
</RelativeLayout>
|
7
app/src/main/res/layout-small/start_swap_header.xml
Normal file
7
app/src/main/res/layout-small/start_swap_header.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true">
|
||||
</RelativeLayout>
|
32
app/src/main/res/layout/start_swap_header.xml
Normal file
32
app/src/main/res/layout/start_swap_header.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="130dp"
|
||||
android:layout_alignParentTop="true"
|
||||
tools:showIn="@layout/swap_blank">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/swap_start_header" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:padding="20dp"
|
||||
android:paddingLeft="30dp"
|
||||
android:paddingRight="30dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center"
|
||||
android:text="@string/swap_intro"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="18sp"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
</RelativeLayout>
|
@ -9,232 +9,202 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".views.swap.SwapWorkflowActivity">
|
||||
|
||||
<!-- Misc header -->
|
||||
<include layout="@layout/start_swap_header" />
|
||||
|
||||
<!-- Bluetooth swap status + toggle -->
|
||||
<LinearLayout
|
||||
android:id="@+id/bluetooth_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_below="@id/header"
|
||||
android:padding="10dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="130dp">
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:tint="@color/swap_grey_icon"
|
||||
android:src="@drawable/ic_bluetooth_white" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/swap_start_header"/>
|
||||
|
||||
<!--
|
||||
Removed for now, because there is no UI to hook this up to.
|
||||
However, the general principle of having a help screen made accessible from the
|
||||
start swap screen is still desirable, and so should be revisited in the future.
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_info_white"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
/>-->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingStart="15dp"
|
||||
android:layout_weight="1.00"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bluetooth_visible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:padding="20dp"
|
||||
android:paddingLeft="30dp"
|
||||
android:paddingRight="30dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center"
|
||||
android:text="@string/swap_intro"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="18sp"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bluetooth_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:tint="@color/swap_grey_icon"
|
||||
android:src="@drawable/ic_bluetooth_white" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingStart="15dp"
|
||||
android:layout_weight="1.00"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bluetooth_visible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/swap_visible_bluetooth"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/device_id_bluetooth"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="SP-120"
|
||||
android:textColor="@color/swap_light_text" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:checked="true"
|
||||
android:id="@+id/switch_bluetooth" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:tint="@color/swap_grey_icon"
|
||||
android:src="@drawable/ic_network_wifi_white" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingStart="15dp"
|
||||
android:layout_weight="1.00"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/wifi_visible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/swap_not_visible_wifi"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/device_id_wifi"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/swap_wifi_device_name"
|
||||
android:textColor="@color/swap_light_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/wifi_network"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="wifi network name"
|
||||
android:textColor="@color/swap_bright_blue"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:checked="false"
|
||||
android:id="@+id/switch_wifi" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingTop="20dp">
|
||||
tools:text="@string/swap_visible_bluetooth"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_people_nearby"
|
||||
android:layout_width="0dp"
|
||||
android:id="@+id/device_id_bluetooth"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/swap_people_nearby"
|
||||
android:textColor="@color/swap_light_text"
|
||||
android:layout_weight="1.00"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/searching_people_nearby"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:indeterminate="true" />
|
||||
tools:text="SP-120"
|
||||
android:textColor="@color/swap_light_text" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/list_people_nearby"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
</ListView>
|
||||
|
||||
<TextView
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/swap_cant_find_peers"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingTop="20dp"
|
||||
android:textColor="@color/swap_light_text" />
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_send_fdroid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableLeft="@drawable/ic_fdroid_grey"
|
||||
android:drawableStart="@drawable/ic_fdroid_grey"
|
||||
android:text="@string/swap_send_fdroid"
|
||||
android:drawablePadding="10dp"
|
||||
android:paddingLeft="25dp"
|
||||
android:paddingRight="25dp"
|
||||
android:paddingEnd="25dp"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_qr_scanner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableLeft="@drawable/ic_qr_grey"
|
||||
android:drawableStart="@drawable/ic_qr_grey"
|
||||
android:text="@string/swap_scan_qr"
|
||||
android:drawablePadding="10dp"
|
||||
android:paddingLeft="25dp"
|
||||
android:paddingRight="25dp"
|
||||
android:paddingEnd="25dp"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
</LinearLayout>
|
||||
tools:checked="true"
|
||||
android:id="@+id/switch_bluetooth" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- WiFi swap status + toggle -->
|
||||
<LinearLayout
|
||||
android:id="@+id/wifi_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/bluetooth_info"
|
||||
android:padding="10dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:tint="@color/swap_grey_icon"
|
||||
android:src="@drawable/ic_network_wifi_white" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingStart="15dp"
|
||||
android:layout_weight="1.00"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/wifi_visible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/swap_not_visible_wifi"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/device_id_wifi"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/swap_wifi_device_name"
|
||||
android:textColor="@color/swap_light_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/wifi_network"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="wifi network name"
|
||||
android:textColor="@color/swap_bright_blue"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:checked="false"
|
||||
android:id="@+id/switch_wifi" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Feedback for "searching for nearby people..." -->
|
||||
<LinearLayout
|
||||
android:id="@+id/feedback_searching"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/wifi_info"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_people_nearby"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/swap_people_nearby"
|
||||
android:textColor="@color/swap_light_text"
|
||||
android:layout_weight="1.00"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/searching_people_nearby"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<!-- Buttons to help the user when they can't find any peers. Shown at bottom of relative layout -->
|
||||
<LinearLayout
|
||||
android:id="@+id/cant_find_peers"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_send_fdroid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableLeft="@drawable/ic_fdroid_grey"
|
||||
android:drawableStart="@drawable/ic_fdroid_grey"
|
||||
android:text="@string/swap_send_fdroid"
|
||||
android:drawablePadding="10dp"
|
||||
android:paddingLeft="25dp"
|
||||
android:paddingRight="25dp"
|
||||
android:paddingEnd="25dp"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_qr_scanner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableLeft="@drawable/ic_qr_grey"
|
||||
android:drawableStart="@drawable/ic_qr_grey"
|
||||
android:text="@string/swap_scan_qr"
|
||||
android:drawablePadding="10dp"
|
||||
android:paddingLeft="25dp"
|
||||
android:paddingRight="25dp"
|
||||
android:paddingEnd="25dp"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Heading for "can't find peers" -->
|
||||
<TextView
|
||||
android:id="@+id/header_cant_find_peers"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/cant_find_peers"
|
||||
android:text="@string/swap_cant_find_peers"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingTop="20dp"
|
||||
android:textColor="@color/swap_light_text" />
|
||||
|
||||
<!-- List of all currently known peers (i.e. bluetooth and wifi devices that have been identified -->
|
||||
<ListView
|
||||
android:id="@+id/list_people_nearby"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/feedback_searching"
|
||||
android:layout_above="@id/header_cant_find_peers">
|
||||
|
||||
</ListView>
|
||||
|
||||
</org.fdroid.fdroid.views.swap.StartSwapView>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="SignatureMismatch">La versión nueva ta roblada con una clave diferente. Pa instalar la versión nueva, tien de desinstalase primero la vieya. Por favor, failo ya inténtalo de nueves. (Decátate que la desinstalación desaniciará cualesquier datu internu atroxáu pola aplicación)</string>
|
||||
<string name="installIncompatible">Paez qu\'esti paquete nun ye compatible col to preséu. ¿Quies intentar instalalu de toes toes?</string>
|
||||
<string name="installDowngrade">Tas intentando baxar de versión esta aplicación. Eso quiciabes faiga que l\'aplicación furrule mal o incluso, pierdas los tos datos. ¿Quies intentalo y baxala de versión de toes toes?</string>
|
||||
<string name="SignatureMismatch">La versión nueva ta roblada con una clave diferente. Pa instalar la versión nueva, tien de desinstalase primero la vieya. Por favor, failo y volvi tentalo. (Decátate que la desinstalación desaniciará cualesquier datu internu atroxáu pola aplicación)</string>
|
||||
<string name="installIncompatible">Paez qu\'esti paquete nun ye compatible col to preséu. ¿Quies tentar d\'instalalu de toes toes?</string>
|
||||
<string name="installDowngrade">Tas tentando de baxar de versión esta aplicación. Eso quiciabes faiga que l\'aplicación furrule mal o incluso, pierdas los tos datos. ¿Quies tentalo y baxala de versión de toes toes?</string>
|
||||
<string name="version">Versión</string>
|
||||
<string name="delete">Desaniciar</string>
|
||||
<string name="enable_nfc_send">Habilitar unviu per NFC…</string>
|
||||
@ -135,18 +135,18 @@
|
||||
<string name="repo_name">Nome</string>
|
||||
<string name="unsigned_description">Esto quier dicir que nun pudo verificase\'l llistáu d\'aplicaciones.
|
||||
Deberíes tener procuru con aplicaciones baxaes dende índices ensin roblar.</string>
|
||||
<string name="repo_not_yet_updated">Entá nun s\'usó esti repositoriu. Necesites anovalu pa ver les aplicaciones que forne.</string>
|
||||
<string name="repo_not_yet_updated">Entá nun s\'usó esti repositoriu. Precises anovalu pa ver les aplicaciones que forne.</string>
|
||||
<string name="unknown">Desconocíu</string>
|
||||
<string name="repo_confirm_delete_title">¿Desaniciar repositoriu?</string>
|
||||
<string name="repo_confirm_delete_body">Desaniciar un repositoriu quier dicir que les aplicaciones d\'elli nun tarán disponibles dende F-Droid.
|
||||
\n\nNota: Toles aplicaciones instalaes d\'enantes quedaránse nel preséu.</string>
|
||||
<string name="repo_disabled_notification">Deshabilitóse %1$s.\n\nNecesitarás rehabilitar esti repositoriu pa instalar aplicaciones dende elli.</string>
|
||||
<string name="repo_disabled_notification">Deshabilitóse %1$s.\n\nPrecisarás rehabilitar esti repositoriu pa instalar aplicaciones dende elli.</string>
|
||||
<string name="repo_added">Guardóse\'l repositoriu F-Droid %1$s.</string>
|
||||
<string name="repo_searching_address">Guetando repositorios F-Droid en\n%1$s</string>
|
||||
<string name="minsdk_or_later">%s ó posterior</string>
|
||||
<string name="up_to_maxsdk">Fasta %s</string>
|
||||
<string name="minsdk_up_to_maxsdk">De %1$s fasta %2$s</string>
|
||||
<string name="not_on_same_wifi">¡El to preséu nun ta na mesma rede Wi-Fi que\'l repositoriu llocal que tas acabante d\'amestar! Intenta xunite a esta rede: %s</string>
|
||||
<string name="not_on_same_wifi">¡El to preséu nun ta na mesma rede Wi-Fi que\'l repositoriu llocal que tas acabante d\'amestar! Tenta de xunite a esta rede: %s</string>
|
||||
<string name="requires_features">Rique: %1$s</string>
|
||||
<string name="app_icon">Iconu d\'aplicación</string>
|
||||
<string name="category_Development">Desendolcu</string>
|
||||
@ -229,9 +229,9 @@ Deberíes tener procuru con aplicaciones baxaes dende índices ensin roblar.</st
|
||||
<string name="category_Time">Tiempu</string>
|
||||
<string name="category_Writing">Escritura</string>
|
||||
|
||||
<string name="empty_installed_app_list">Nun hai aplicaciones instalaes.\n\nHai aplicaciones nel to preséu pero nun tán disponibles dende F-Droid. Esto quiciabes sía porque necesites anovar los tos repositorios o porque los repositorios nun tengan daveres les tos aplicaciones disponibles.</string>
|
||||
<string name="empty_available_app_list">Nun hai aplicaciones nesta estaya.\n\nIntenta esbillar una estaya distinta o anovar los tos repositorios pa consiguir un llistáu frescu d\'aplicaciones.</string>
|
||||
<string name="empty_can_update_app_list">Tán anovaes toles aplicaciones.\n\n¡Norabona! Toles tos aplicaciones tán anovaes (o los tos repositorios tán ensin anovar).</string>
|
||||
<string name="empty_installed_app_list">Nun hai aplicaciones instalaes.\n\nHai aplicaciones nel to preséu pero nun tán disponibles dende F-Droid. Esto quiciabes sía porque precises anovar los tos repositorios o porque los repositorios nun tengan daveres les tos aplicaciones disponibles.</string>
|
||||
<string name="empty_available_app_list">Nun hai aplicaciones nesta estaya.\n\nTenta d\'esbillar una estaya distinta o anovar los tos repositorios pa consiguir un llistáu frescu d\'aplicaciones.</string>
|
||||
<string name="empty_can_update_app_list">Anováronse toles aplicaciones.\n\n¡Norabona! Toles tos aplicaciones tán anovaes (o los tos repositorios tán ensin anovar).</string>
|
||||
|
||||
<string name="install_error_title">Fallu d\'instalación</string>
|
||||
<string name="install_error_unknown">Fallu al instalar pola mor d\'un fallu desconocíu</string>
|
||||
@ -255,7 +255,7 @@ Deberíes tener procuru con aplicaciones baxaes dende índices ensin roblar.</st
|
||||
<string name="swap_nfc_title">Toca pa intercambiar</string>
|
||||
<string name="swap_join_same_wifi_desc">Pa intercambiar usando W-FI asegúrate que tais na mesma rede. Si nun tienéis accesu a la mesma rede, ún de vós pues crear una fastera Wi-Fi.</string>
|
||||
<string name="swap_join_this_hotspot">Ayuda al to collaciu a coneutalu cola to fastera Wi-Fi</string>
|
||||
<string name="swap_scan_or_type_url">Una persona necesita escaniar el códigu o escribir la URL nel restolador d\'otru.</string>
|
||||
<string name="swap_scan_or_type_url">Una persona precisa escaniar el códigu o escribir la URL nel restolador d\'otru.</string>
|
||||
<string name="swap_choose_apps">Escoyer aplicaciones</string>
|
||||
<string name="swap_scan_qr">Escaniar códigu QR</string>
|
||||
<string name="swap_people_nearby">Xente averao</string>
|
||||
@ -266,7 +266,7 @@ Deberíes tener procuru con aplicaciones baxaes dende índices ensin roblar.</st
|
||||
<string name="swap_setting_up_wifi">Configurando Wi-Fi…</string>
|
||||
<string name="swap_not_visible_wifi">Nun ye visible pente Wi-Fi</string>
|
||||
<string name="swap_no_peers_nearby">Nun pudo alcontrase xente averao cola qu\'intercambiar.</string>
|
||||
<string name="swap_attempt_install">Intentar instalación</string>
|
||||
<string name="swap_attempt_install">Tentar d\'instalar</string>
|
||||
<string name="swap_not_enabled_description">Enantes d\'intercambiar, el to preséu ha tar visible.</string>
|
||||
|
||||
<string name="install_confirm">¿Quies instalar esta aplicación? Tendrá accesu a:</string>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<string name="version">Versjon</string>
|
||||
<string name="delete">Slett</string>
|
||||
<string name="enable_nfc_send">Skru på NFC-sending…</string>
|
||||
<string name="cache_downloaded">Lagre nedlastede programmer i mellomlager</string>
|
||||
<string name="cache_downloaded">Behold mellomlagrede programmer</string>
|
||||
<string name="updates">Oppdateringer</string>
|
||||
<string name="other">Andre</string>
|
||||
<string name="update_interval">Intervall for automatisk oppdatering</string>
|
||||
@ -344,4 +344,11 @@
|
||||
|
||||
<string name="downloading_apk">Laster ned %1$s</string>
|
||||
|
||||
<string name="keep_hour">én time</string>
|
||||
<string name="keep_day">én dag</string>
|
||||
<string name="keep_week">ei uke</string>
|
||||
<string name="keep_month">én måned</string>
|
||||
<string name="keep_year">ett år</string>
|
||||
<string name="keep_forever">For alltid</string>
|
||||
|
||||
</resources>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<string name="version">Versie</string>
|
||||
<string name="delete">Verwijderen</string>
|
||||
<string name="enable_nfc_send">Versturen via NFC aanzetten…</string>
|
||||
<string name="cache_downloaded">Bewaar gedownloade apps</string>
|
||||
<string name="cache_downloaded">Bewaar gecachete apps</string>
|
||||
<string name="updates">Updates</string>
|
||||
<string name="other">Overig</string>
|
||||
<string name="update_interval">Automatische vernieuwingsinterval</string>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<string name="version">Versione</string>
|
||||
<string name="delete">Burra</string>
|
||||
<string name="enable_nfc_send">Abìlita imbiu NFC…</string>
|
||||
<string name="cache_downloaded">Pachetos cache</string>
|
||||
<string name="cache_downloaded">Mantènne sas aplicatziones in sa cache</string>
|
||||
<string name="updates">Agiornamentos</string>
|
||||
<string name="other">Àteru</string>
|
||||
<string name="update_interval">Intervallu agiornamentu automàticu</string>
|
||||
@ -346,4 +346,13 @@
|
||||
|
||||
<string name="downloading_apk">Iscarrighende %1$s</string>
|
||||
|
||||
<string name="system_install_not_supported">S\'installatzione de s\'estensione F-Droid cun permissos de sistema no est, pro como, suportada pro Android 5.1 o prus nou.</string>
|
||||
|
||||
<string name="keep_hour">1 Ora</string>
|
||||
<string name="keep_day">1 Die</string>
|
||||
<string name="keep_week">1 Chida</string>
|
||||
<string name="keep_month">1 Mese</string>
|
||||
<string name="keep_year">1 Annu</string>
|
||||
<string name="keep_forever">Pro semper</string>
|
||||
|
||||
</resources>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<resources>
|
||||
<string name="SignatureMismatch">Нова версія підписана не тим ключем, що стара. Перш ніж встановити нову версію, самостійно зітріть стару. (Зауважте, що стирання програми призведе до знищення всіх даних цієї програми)</string>
|
||||
<string name="version">Версія</string>
|
||||
<string name="cache_downloaded">Зберігати пакунки</string>
|
||||
<string name="cache_downloaded">Зберігати кешовані пакунки</string>
|
||||
<string name="updates">Оновлення</string>
|
||||
<string name="notify">Сповіщення про оновлення</string>
|
||||
<string name="about_title">Про F-Droid</string>
|
||||
@ -352,4 +352,15 @@
|
||||
<string name="update_auto_download_summary">Завантажувати файли оновлення у фоновому режимі</string>
|
||||
<string name="tap_to_install_format">Торкніться для встановлення %s</string>
|
||||
<string name="tap_to_update_format">Торкніться для оновлення %s</string>
|
||||
<string name="download_pending">Очікування початку завантаження…</string>
|
||||
|
||||
<string name="downloading_apk">Завантаження %1$s</string>
|
||||
|
||||
<string name="keep_hour">1 година</string>
|
||||
<string name="keep_day">1 день</string>
|
||||
<string name="keep_week">1 тиждень</string>
|
||||
<string name="keep_month">1 місяць</string>
|
||||
<string name="keep_year">1 рік</string>
|
||||
<string name="keep_forever">Завжди</string>
|
||||
|
||||
</resources>
|
||||
|
@ -322,4 +322,6 @@
|
||||
<string name="swap_view_available_networks">點擊打開可用的網路</string>
|
||||
<string name="swap_switch_to_wifi">點擊以切換到一個 Wi-Fi 網路</string>
|
||||
<string name="swap_confirm_connect">您現在想要從 %1$s 取得應用程式嗎?</string>
|
||||
<string name="swap_nearby">近距交換</string>
|
||||
<string name="swap_intro">與您附近的人連結並且交換應用程式。</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user