release 0.2
This commit is contained in:
parent
7f2d62cea6
commit
37124bce80
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.belos.belmarket"
|
package="org.fdroid.fdroid"
|
||||||
android:installLocation="auto">
|
android:installLocation="auto">
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
|
@ -1,397 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
package org.fdroid.fdroid;
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.fdroid.fdroid.data.Apk;
|
|
||||||
import org.fdroid.fdroid.data.App;
|
|
||||||
import org.fdroid.fdroid.data.Repo;
|
|
||||||
import org.fdroid.fdroid.data.RepoPersister;
|
|
||||||
import org.fdroid.fdroid.data.RepoProvider;
|
|
||||||
import org.fdroid.fdroid.data.Schema.RepoTable;
|
|
||||||
import org.fdroid.fdroid.net.Downloader;
|
|
||||||
import org.fdroid.fdroid.net.DownloaderFactory;
|
|
||||||
import org.xml.sax.InputSource;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
import org.xml.sax.XMLReader;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.security.CodeSigner;
|
|
||||||
import java.security.cert.Certificate;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.jar.JarEntry;
|
|
||||||
import java.util.jar.JarFile;
|
|
||||||
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
import javax.xml.parsers.SAXParser;
|
|
||||||
import javax.xml.parsers.SAXParserFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responsible for updating an individual repository. This will:
|
|
||||||
* * Download the index.jar
|
|
||||||
* * Verify that it is signed correctly and by the correct certificate
|
|
||||||
* * Parse the index.xml from the .jar file
|
|
||||||
* * Save the resulting repo, apps, and apks to the database.
|
|
||||||
*
|
|
||||||
* <b>WARNING</b>: this class is the central piece of the entire security model of
|
|
||||||
* FDroid! Avoid modifying it when possible, if you absolutely must, be very,
|
|
||||||
* very careful with the changes that you are making!
|
|
||||||
*/
|
|
||||||
public class RepoUpdater {
|
|
||||||
|
|
||||||
private static final String TAG = "RepoUpdater";
|
|
||||||
|
|
||||||
private final String indexUrl;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private final Context context;
|
|
||||||
@NonNull
|
|
||||||
private final Repo repo;
|
|
||||||
private boolean hasChanged;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private ProgressListener downloadProgressListener;
|
|
||||||
private ProgressListener committingProgressListener;
|
|
||||||
private ProgressListener processXmlProgressListener;
|
|
||||||
private String cacheTag;
|
|
||||||
private X509Certificate signingCertFromJar;
|
|
||||||
|
|
||||||
@NonNull private final RepoPersister persister;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates an app repo as read out of the database into a {@link Repo} instance.
|
|
||||||
*
|
|
||||||
* @param repo A {@link Repo} read out of the local database
|
|
||||||
*/
|
|
||||||
public RepoUpdater(@NonNull Context context, @NonNull Repo repo) {
|
|
||||||
this.context = context;
|
|
||||||
this.repo = repo;
|
|
||||||
this.persister = new RepoPersister(context, repo);
|
|
||||||
|
|
||||||
String url = repo.address + "/index.jar";
|
|
||||||
String versionName = Utils.getVersionName(context);
|
|
||||||
if (versionName != null) {
|
|
||||||
url += "?client_version=" + versionName;
|
|
||||||
}
|
|
||||||
this.indexUrl = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDownloadProgressListener(ProgressListener progressListener) {
|
|
||||||
this.downloadProgressListener = progressListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setProcessXmlProgressListener(ProgressListener progressListener) {
|
|
||||||
this.processXmlProgressListener = progressListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCommittingProgressListener(ProgressListener progressListener) {
|
|
||||||
this.committingProgressListener = progressListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasChanged() {
|
|
||||||
return hasChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Downloader downloadIndex() throws UpdateException {
|
|
||||||
Downloader downloader = null;
|
|
||||||
try {
|
|
||||||
downloader = DownloaderFactory.create(context, indexUrl);
|
|
||||||
downloader.setCacheTag(repo.lastetag);
|
|
||||||
downloader.setListener(downloadProgressListener);
|
|
||||||
downloader.download();
|
|
||||||
|
|
||||||
if (downloader.isCached()) {
|
|
||||||
// The index is unchanged since we last read it. We just mark
|
|
||||||
// everything that came from this repo as being updated.
|
|
||||||
Utils.debugLog(TAG, "Repo index for " + indexUrl + " is up to date (by etag)");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (downloader != null && downloader.outputFile != null) {
|
|
||||||
if (!downloader.outputFile.delete()) {
|
|
||||||
Log.w(TAG, "Couldn't delete file: " + downloader.outputFile.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new UpdateException(repo, "Error getting index file", e);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// ignored if canceled, the local database just won't be updated
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return downloader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All repos are represented by a signed jar file, {@code index.jar}, which contains
|
|
||||||
* a single file, {@code index.xml}. This takes the {@code index.jar}, verifies the
|
|
||||||
* signature, then returns the unzipped {@code index.xml}.
|
|
||||||
*
|
|
||||||
* @throws UpdateException All error states will come from here.
|
|
||||||
*/
|
|
||||||
public void update() throws UpdateException {
|
|
||||||
|
|
||||||
final Downloader downloader = downloadIndex();
|
|
||||||
hasChanged = downloader.hasChanged();
|
|
||||||
|
|
||||||
if (hasChanged) {
|
|
||||||
// Don't worry about checking the status code for 200. If it was a
|
|
||||||
// successful download, then we will have a file ready to use:
|
|
||||||
cacheTag = downloader.getCacheTag();
|
|
||||||
processDownloadedFile(downloader.outputFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ContentValues repoDetailsToSave;
|
|
||||||
private String signingCertFromIndexXml;
|
|
||||||
|
|
||||||
private RepoXMLHandler.IndexReceiver createIndexReceiver() {
|
|
||||||
return new RepoXMLHandler.IndexReceiver() {
|
|
||||||
@Override
|
|
||||||
public void receiveRepo(String name, String description, String signingCert, int maxAge, int version, long timestamp) {
|
|
||||||
signingCertFromIndexXml = signingCert;
|
|
||||||
repoDetailsToSave = prepareRepoDetailsForSaving(name, description, maxAge, version, timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receiveApp(App app, List<Apk> packages) {
|
|
||||||
try {
|
|
||||||
persister.saveToDb(app, packages);
|
|
||||||
} catch (UpdateException e) {
|
|
||||||
throw new RuntimeException("Error while saving repo details to database.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processDownloadedFile(File downloadedFile) throws UpdateException {
|
|
||||||
InputStream indexInputStream = null;
|
|
||||||
try {
|
|
||||||
if (downloadedFile == null || !downloadedFile.exists()) {
|
|
||||||
throw new UpdateException(repo, downloadedFile + " does not exist!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Due to a bug in Android 5.0 Lollipop, the inclusion of spongycastle causes
|
|
||||||
// breakage when verifying the signature of the downloaded .jar. For more
|
|
||||||
// details, check out https://gitlab.com/fdroid/fdroidclient/issues/111.
|
|
||||||
FDroidApp.disableSpongyCastleOnLollipop();
|
|
||||||
|
|
||||||
JarFile jarFile = new JarFile(downloadedFile, true);
|
|
||||||
JarEntry indexEntry = (JarEntry) jarFile.getEntry("index.xml");
|
|
||||||
indexInputStream = new ProgressBufferedInputStream(jarFile.getInputStream(indexEntry),
|
|
||||||
processXmlProgressListener, new URL(repo.address), (int) indexEntry.getSize());
|
|
||||||
|
|
||||||
// Process the index...
|
|
||||||
SAXParserFactory factory = SAXParserFactory.newInstance();
|
|
||||||
factory.setNamespaceAware(true);
|
|
||||||
final SAXParser parser = factory.newSAXParser();
|
|
||||||
final XMLReader reader = parser.getXMLReader();
|
|
||||||
final RepoXMLHandler repoXMLHandler = new RepoXMLHandler(repo, createIndexReceiver());
|
|
||||||
reader.setContentHandler(repoXMLHandler);
|
|
||||||
reader.parse(new InputSource(indexInputStream));
|
|
||||||
|
|
||||||
long timestamp = repoDetailsToSave.getAsLong(RepoTable.Cols.TIMESTAMP);
|
|
||||||
if (timestamp < repo.timestamp) {
|
|
||||||
throw new UpdateException(repo, "index.jar is older that current index! "
|
|
||||||
+ timestamp + " < " + repo.timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
signingCertFromJar = getSigningCertFromJar(indexEntry);
|
|
||||||
|
|
||||||
// JarEntry can only read certificates after the file represented by that JarEntry
|
|
||||||
// has been read completely, so verification cannot run until now...
|
|
||||||
assertSigningCertFromXmlCorrect();
|
|
||||||
commitToDb();
|
|
||||||
} catch (SAXException | ParserConfigurationException | IOException e) {
|
|
||||||
throw new UpdateException(repo, "Error parsing index", e);
|
|
||||||
} finally {
|
|
||||||
FDroidApp.enableSpongyCastleOnLollipop();
|
|
||||||
Utils.closeQuietly(indexInputStream);
|
|
||||||
if (downloadedFile != null) {
|
|
||||||
if (!downloadedFile.delete()) {
|
|
||||||
Log.w(TAG, "Couldn't delete file: " + downloadedFile.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void commitToDb() throws UpdateException {
|
|
||||||
Log.i(TAG, "Repo signature verified, saving app metadata to database.");
|
|
||||||
if (committingProgressListener != null) {
|
|
||||||
try {
|
|
||||||
//TODO this should be an event, not a progress listener
|
|
||||||
committingProgressListener.onProgress(new URL(indexUrl), 0, -1);
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
persister.commit(repoDetailsToSave);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertSigningCertFromXmlCorrect() throws SigningException {
|
|
||||||
|
|
||||||
// no signing cert read from database, this is the first use
|
|
||||||
if (repo.signingCertificate == null) {
|
|
||||||
verifyAndStoreTOFUCerts(signingCertFromIndexXml, signingCertFromJar);
|
|
||||||
}
|
|
||||||
verifyCerts(signingCertFromIndexXml, signingCertFromJar);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update tracking data for the repo represented by this instance (index version, etag,
|
|
||||||
* description, human-readable name, etc.
|
|
||||||
*/
|
|
||||||
private ContentValues prepareRepoDetailsForSaving(String name, String description, int maxAge, int version, long timestamp) {
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
|
|
||||||
values.put(RepoTable.Cols.LAST_UPDATED, Utils.formatTime(new Date(), ""));
|
|
||||||
|
|
||||||
if (repo.lastetag == null || !repo.lastetag.equals(cacheTag)) {
|
|
||||||
values.put(RepoTable.Cols.LAST_ETAG, cacheTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version != -1 && version != repo.version) {
|
|
||||||
Utils.debugLog(TAG, "Repo specified a new version: from " + repo.version + " to " + version);
|
|
||||||
values.put(RepoTable.Cols.VERSION, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxAge != -1 && maxAge != repo.maxage) {
|
|
||||||
Utils.debugLog(TAG, "Repo specified a new maximum age - updated");
|
|
||||||
values.put(RepoTable.Cols.MAX_AGE, maxAge);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (description != null && !description.equals(repo.description)) {
|
|
||||||
values.put(RepoTable.Cols.DESCRIPTION, description);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name != null && !name.equals(repo.name)) {
|
|
||||||
values.put(RepoTable.Cols.NAME, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timestamp != repo.timestamp) {
|
|
||||||
values.put(RepoTable.Cols.TIMESTAMP, timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UpdateException extends Exception {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -4492452418826132803L;
|
|
||||||
public final Repo repo;
|
|
||||||
|
|
||||||
public UpdateException(Repo repo, String message) {
|
|
||||||
super(message);
|
|
||||||
this.repo = repo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UpdateException(Repo repo, String message, Exception cause) {
|
|
||||||
super(message, cause);
|
|
||||||
this.repo = repo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SigningException extends UpdateException {
|
|
||||||
public SigningException(Repo repo, String message) {
|
|
||||||
super(repo, "Repository was not signed correctly: " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FDroid's index.jar is signed using a particular format and does not allow lots of
|
|
||||||
* signing setups that would be valid for a regular jar. This validates those
|
|
||||||
* restrictions.
|
|
||||||
*/
|
|
||||||
private X509Certificate getSigningCertFromJar(JarEntry jarEntry) throws SigningException {
|
|
||||||
final CodeSigner[] codeSigners = jarEntry.getCodeSigners();
|
|
||||||
if (codeSigners == null || codeSigners.length == 0) {
|
|
||||||
throw new SigningException(repo, "No signature found in index");
|
|
||||||
}
|
|
||||||
/* we could in theory support more than 1, but as of now we do not */
|
|
||||||
if (codeSigners.length > 1) {
|
|
||||||
throw new SigningException(repo, "index.jar must be signed by a single code signer!");
|
|
||||||
}
|
|
||||||
List<? extends Certificate> certs = codeSigners[0].getSignerCertPath().getCertificates();
|
|
||||||
if (certs.size() != 1) {
|
|
||||||
throw new SigningException(repo, "index.jar code signers must only have a single certificate!");
|
|
||||||
}
|
|
||||||
return (X509Certificate) certs.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A new repo can be added with or without the fingerprint of the signing
|
|
||||||
* certificate. If no fingerprint is supplied, then do a pure TOFU and just
|
|
||||||
* store the certificate as valid. If there is a fingerprint, then first
|
|
||||||
* check that the signing certificate in the jar matches that fingerprint.
|
|
||||||
*/
|
|
||||||
private void verifyAndStoreTOFUCerts(String certFromIndexXml, X509Certificate rawCertFromJar)
|
|
||||||
throws SigningException {
|
|
||||||
if (repo.signingCertificate != null) {
|
|
||||||
return; // there is a repo.signingCertificate already, nothing to TOFU
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The first time a repo is added, it can be added with the signing certificate's
|
|
||||||
* fingerprint. In that case, check that fingerprint against what is
|
|
||||||
* actually in the index.jar itself. If no fingerprint, just store the
|
|
||||||
* signing certificate */
|
|
||||||
if (repo.fingerprint != null) {
|
|
||||||
String fingerprintFromIndexXml = Utils.calcFingerprint(certFromIndexXml);
|
|
||||||
String fingerprintFromJar = Utils.calcFingerprint(rawCertFromJar);
|
|
||||||
if (!repo.fingerprint.equalsIgnoreCase(fingerprintFromIndexXml)
|
|
||||||
|| !repo.fingerprint.equalsIgnoreCase(fingerprintFromJar)) {
|
|
||||||
throw new SigningException(repo, "Supplied certificate fingerprint does not match!");
|
|
||||||
}
|
|
||||||
} // else - no info to check things are valid, so just Trust On First Use
|
|
||||||
|
|
||||||
Utils.debugLog(TAG, "Saving new signing certificate in the database for " + repo.address);
|
|
||||||
ContentValues values = new ContentValues(2);
|
|
||||||
values.put(RepoTable.Cols.LAST_UPDATED, Utils.formatDate(new Date(), ""));
|
|
||||||
values.put(RepoTable.Cols.SIGNING_CERT, Hasher.hex(rawCertFromJar));
|
|
||||||
RepoProvider.Helper.update(context, repo, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FDroid works with three copies of the signing certificate:
|
|
||||||
* <li>in the downloaded jar</li>
|
|
||||||
* <li>in the index XML</li>
|
|
||||||
* <li>stored in the local database</li>
|
|
||||||
* It would work better removing the copy from the index XML, but it needs to stay
|
|
||||||
* there for backwards compatibility since the old TOFU process requires it. Therefore,
|
|
||||||
* since all three have to be present, all three are compared.
|
|
||||||
*
|
|
||||||
* @param certFromIndexXml the cert written into the header of the index XML
|
|
||||||
* @param rawCertFromJar the {@link X509Certificate} embedded in the downloaded jar
|
|
||||||
*/
|
|
||||||
private void verifyCerts(String certFromIndexXml, X509Certificate rawCertFromJar) throws SigningException {
|
|
||||||
// convert binary data to string version that is used in FDroid's database
|
|
||||||
String certFromJar = Hasher.hex(rawCertFromJar);
|
|
||||||
|
|
||||||
// repo and repo.signingCertificate must be pre-loaded from the database
|
|
||||||
if (TextUtils.isEmpty(repo.signingCertificate)
|
|
||||||
|| TextUtils.isEmpty(certFromJar)
|
|
||||||
|| TextUtils.isEmpty(certFromIndexXml)) {
|
|
||||||
throw new SigningException(repo, "A empty repo or signing certificate is invalid!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// though its called repo.signingCertificate, its actually a X509 certificate
|
|
||||||
if (repo.signingCertificate.equals(certFromJar)
|
|
||||||
&& repo.signingCertificate.equals(certFromIndexXml)
|
|
||||||
&& certFromIndexXml.equals(certFromJar)) {
|
|
||||||
return; // we have a match!
|
|
||||||
}
|
|
||||||
throw new SigningException(repo, "Signing certificate does not match!");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,307 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010-12 Ciaran Gultnieks, ciaran@ciarang.com
|
|
||||||
* Copyright (C) 2009 Roberto Jacinto, roberto.jacinto@caixamagica.pt
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 3
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.fdroid.fdroid;
|
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.fdroid.fdroid.data.Apk;
|
|
||||||
import org.fdroid.fdroid.data.App;
|
|
||||||
import org.fdroid.fdroid.data.Repo;
|
|
||||||
import org.xml.sax.Attributes;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
import org.xml.sax.helpers.DefaultHandler;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the index.xml into Java data structures.
|
|
||||||
*/
|
|
||||||
public class RepoXMLHandler extends DefaultHandler {
|
|
||||||
|
|
||||||
// The repo we're processing.
|
|
||||||
private final Repo repo;
|
|
||||||
|
|
||||||
private List<Apk> apksList = new ArrayList<>();
|
|
||||||
|
|
||||||
private App curapp;
|
|
||||||
private Apk curapk;
|
|
||||||
|
|
||||||
private String currentApkHashType;
|
|
||||||
|
|
||||||
// After processing the XML, these will be -1 if the index didn't specify
|
|
||||||
// them - otherwise it will be the value specified.
|
|
||||||
private int repoMaxAge = -1;
|
|
||||||
private int repoVersion;
|
|
||||||
private long repoTimestamp;
|
|
||||||
private String repoDescription;
|
|
||||||
private String repoName;
|
|
||||||
|
|
||||||
// the X.509 signing certificate stored in the header of index.xml
|
|
||||||
private String repoSigningCert;
|
|
||||||
|
|
||||||
private final StringBuilder curchars = new StringBuilder();
|
|
||||||
|
|
||||||
interface IndexReceiver {
|
|
||||||
void receiveRepo(String name, String description, String signingCert, int maxage, int version, long timestamp);
|
|
||||||
|
|
||||||
void receiveApp(App app, List<Apk> packages);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final IndexReceiver receiver;
|
|
||||||
|
|
||||||
public RepoXMLHandler(Repo repo, @NonNull IndexReceiver receiver) {
|
|
||||||
this.repo = repo;
|
|
||||||
this.receiver = receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void characters(char[] ch, int start, int length) {
|
|
||||||
curchars.append(ch, start, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endElement(String uri, String localName, String qName)
|
|
||||||
throws SAXException {
|
|
||||||
|
|
||||||
if ("application".equals(localName) && curapp != null) {
|
|
||||||
onApplicationParsed();
|
|
||||||
} else if ("package".equals(localName) && curapk != null && curapp != null) {
|
|
||||||
apksList.add(curapk);
|
|
||||||
curapk = null;
|
|
||||||
} else if ("repo".equals(localName)) {
|
|
||||||
onRepoParsed();
|
|
||||||
} else if (curchars.length() == 0) {
|
|
||||||
// All options below require non-empty content
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String str = curchars.toString().trim();
|
|
||||||
if (curapk != null) {
|
|
||||||
switch (localName) {
|
|
||||||
case "version":
|
|
||||||
curapk.versionName = str;
|
|
||||||
break;
|
|
||||||
case "versioncode":
|
|
||||||
curapk.versionCode = Utils.parseInt(str, -1);
|
|
||||||
break;
|
|
||||||
case "size":
|
|
||||||
curapk.size = Utils.parseInt(str, 0);
|
|
||||||
break;
|
|
||||||
case "hash":
|
|
||||||
if (currentApkHashType == null || "md5".equals(currentApkHashType)) {
|
|
||||||
if (curapk.hash == null) {
|
|
||||||
curapk.hash = str;
|
|
||||||
curapk.hashType = "SHA-256";
|
|
||||||
}
|
|
||||||
} else if ("sha256".equals(currentApkHashType)) {
|
|
||||||
curapk.hash = str;
|
|
||||||
curapk.hashType = "SHA-256";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "sig":
|
|
||||||
curapk.sig = str;
|
|
||||||
break;
|
|
||||||
case "srcname":
|
|
||||||
curapk.srcname = str;
|
|
||||||
break;
|
|
||||||
case "apkname":
|
|
||||||
curapk.apkName = str;
|
|
||||||
break;
|
|
||||||
case "sdkver":
|
|
||||||
curapk.minSdkVersion = Utils.parseInt(str, Apk.SDK_VERSION_MIN_VALUE);
|
|
||||||
break;
|
|
||||||
case "targetSdkVersion":
|
|
||||||
curapk.targetSdkVersion = Utils.parseInt(str, Apk.SDK_VERSION_MIN_VALUE);
|
|
||||||
break;
|
|
||||||
case "maxsdkver":
|
|
||||||
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);
|
|
||||||
break;
|
|
||||||
case "permissions":
|
|
||||||
curapk.permissions = Utils.parseCommaSeparatedString(str);
|
|
||||||
break;
|
|
||||||
case "features":
|
|
||||||
curapk.features = Utils.parseCommaSeparatedString(str);
|
|
||||||
break;
|
|
||||||
case "nativecode":
|
|
||||||
curapk.nativecode = Utils.parseCommaSeparatedString(str);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (curapp != null) {
|
|
||||||
switch (localName) {
|
|
||||||
case "name":
|
|
||||||
curapp.name = str;
|
|
||||||
break;
|
|
||||||
case "icon":
|
|
||||||
curapp.icon = str;
|
|
||||||
break;
|
|
||||||
case "description":
|
|
||||||
// This is the old-style description. We'll read it
|
|
||||||
// if present, to support old repos, but in newer
|
|
||||||
// repos it will get overwritten straight away!
|
|
||||||
curapp.description = "<p>" + str + "</p>";
|
|
||||||
break;
|
|
||||||
case "desc":
|
|
||||||
// New-style description.
|
|
||||||
curapp.description = str;
|
|
||||||
break;
|
|
||||||
case "summary":
|
|
||||||
curapp.summary = str;
|
|
||||||
break;
|
|
||||||
case "license":
|
|
||||||
curapp.license = str;
|
|
||||||
break;
|
|
||||||
case "author":
|
|
||||||
curapp.author = str;
|
|
||||||
break;
|
|
||||||
case "email":
|
|
||||||
curapp.email = str;
|
|
||||||
break;
|
|
||||||
case "source":
|
|
||||||
curapp.sourceURL = str;
|
|
||||||
break;
|
|
||||||
case "changelog":
|
|
||||||
curapp.changelogURL = str;
|
|
||||||
break;
|
|
||||||
case "donate":
|
|
||||||
curapp.donateURL = str;
|
|
||||||
break;
|
|
||||||
case "bitcoin":
|
|
||||||
curapp.bitcoinAddr = str;
|
|
||||||
break;
|
|
||||||
case "litecoin":
|
|
||||||
curapp.litecoinAddr = str;
|
|
||||||
break;
|
|
||||||
case "flattr":
|
|
||||||
curapp.flattrID = str;
|
|
||||||
break;
|
|
||||||
case "web":
|
|
||||||
curapp.webURL = str;
|
|
||||||
break;
|
|
||||||
case "tracker":
|
|
||||||
curapp.trackerURL = str;
|
|
||||||
break;
|
|
||||||
case "added":
|
|
||||||
curapp.added = Utils.parseDate(str, null);
|
|
||||||
break;
|
|
||||||
case "lastupdated":
|
|
||||||
curapp.lastUpdated = Utils.parseDate(str, null);
|
|
||||||
break;
|
|
||||||
case "marketversion":
|
|
||||||
curapp.upstreamVersionName = str;
|
|
||||||
break;
|
|
||||||
case "marketvercode":
|
|
||||||
curapp.upstreamVersionCode = Utils.parseInt(str, -1);
|
|
||||||
break;
|
|
||||||
case "categories":
|
|
||||||
curapp.categories = Utils.parseCommaSeparatedString(str);
|
|
||||||
break;
|
|
||||||
case "antifeatures":
|
|
||||||
curapp.antiFeatures = Utils.parseCommaSeparatedString(str);
|
|
||||||
break;
|
|
||||||
case "requirements":
|
|
||||||
curapp.requirements = Utils.parseCommaSeparatedString(str);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if ("description".equals(localName)) {
|
|
||||||
repoDescription = cleanWhiteSpace(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onApplicationParsed() {
|
|
||||||
receiver.receiveApp(curapp, apksList);
|
|
||||||
curapp = null;
|
|
||||||
apksList = new ArrayList<>();
|
|
||||||
// If the app packageName is already present in this apps list, then it
|
|
||||||
// means the same index file has a duplicate app, which should
|
|
||||||
// not be allowed.
|
|
||||||
// However, I'm thinking that it should be undefined behaviour,
|
|
||||||
// because it is probably a bug in the fdroid server that made it
|
|
||||||
// happen, and I don't *think* it will crash the client, because
|
|
||||||
// the first app will insert, the second one will update the newly
|
|
||||||
// inserted one.
|
|
||||||
|
|
||||||
//////#TODO
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
sample this as a xml handlers
|
|
||||||
should add screen shot id the project as it be
|
|
||||||
and also there is no way to contact developer
|
|
||||||
another is to create a place to collect userr data and vote for a package
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onRepoParsed() {
|
|
||||||
receiver.receiveRepo(repoName, repoDescription, repoSigningCert, repoMaxAge, repoVersion, repoTimestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startElement(String uri, String localName, String qName,
|
|
||||||
Attributes attributes) throws SAXException {
|
|
||||||
super.startElement(uri, localName, qName, attributes);
|
|
||||||
|
|
||||||
if ("repo".equals(localName)) {
|
|
||||||
repoSigningCert = attributes.getValue("", "pubkey");
|
|
||||||
repoMaxAge = Utils.parseInt(attributes.getValue("", "maxage"), -1);
|
|
||||||
repoVersion = Utils.parseInt(attributes.getValue("", "version"), -1);
|
|
||||||
repoName = cleanWhiteSpace(attributes.getValue("", "name"));
|
|
||||||
repoDescription = cleanWhiteSpace(attributes.getValue("", "description"));
|
|
||||||
repoTimestamp = parseLong(attributes.getValue("", "timestamp"), 0);
|
|
||||||
} else if ("application".equals(localName) && curapp == null) {
|
|
||||||
curapp = new App();
|
|
||||||
curapp.packageName = attributes.getValue("", "id");
|
|
||||||
} else if ("package".equals(localName) && curapp != null && curapk == null) {
|
|
||||||
curapk = new Apk();
|
|
||||||
curapk.packageName = curapp.packageName;
|
|
||||||
curapk.repo = repo.getId();
|
|
||||||
currentApkHashType = null;
|
|
||||||
|
|
||||||
} else if ("hash".equals(localName) && curapk != null) {
|
|
||||||
currentApkHashType = attributes.getValue("", "type");
|
|
||||||
}
|
|
||||||
curchars.setLength(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String cleanWhiteSpace(@Nullable String str) {
|
|
||||||
return str == null ? null : str.replaceAll("\\s", " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long parseLong(String str, long fallback) {
|
|
||||||
if (str == null || str.length() == 0) {
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
long result;
|
|
||||||
try {
|
|
||||||
result = Long.parseLong(str);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
result = fallback;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<string name="app_name">Bel Market</string>
|
<string name="app_name">F-Droid</string>
|
||||||
|
|
||||||
<string name="SignatureMismatch">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)</string>
|
<string name="SignatureMismatch">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)</string>
|
||||||
<string name="installIncompatible">It seems like this package is not compatible with your device. Do you want to try and install it anyway?</string>
|
<string name="installIncompatible">It seems like this package is not compatible with your device. Do you want to try and install it anyway?</string>
|
||||||
@ -27,7 +27,7 @@
|
|||||||
<string name="update_history">Update history</string>
|
<string name="update_history">Update history</string>
|
||||||
<string name="update_history_summ">Days to consider apps new or recent: %s</string>
|
<string name="update_history_summ">Days to consider apps new or recent: %s</string>
|
||||||
<string name="system_installer">Privileged Extension</string>
|
<string name="system_installer">Privileged Extension</string>
|
||||||
<string name="system_installer_on">Use Bel Market Privileged Extension to install, update, and remove packages</string>
|
<string name="system_installer_on">Use F-Droid Privileged Extension to install, update, and remove packages</string>
|
||||||
<string name="uninstall_system">Update/Uninstall Privileged Extension</string>
|
<string name="uninstall_system">Update/Uninstall Privileged Extension</string>
|
||||||
<string name="uninstall_system_summary">Open details screen of Privileged Extension to update/uninstall it</string>
|
<string name="uninstall_system_summary">Open details screen of Privileged Extension to update/uninstall it</string>
|
||||||
<string name="local_repo_name">Name of your Local Repo</string>
|
<string name="local_repo_name">Name of your Local Repo</string>
|
||||||
@ -44,7 +44,7 @@
|
|||||||
<string name="app_details">App Details</string>
|
<string name="app_details">App Details</string>
|
||||||
<string name="no_such_app">No such app found.</string>
|
<string name="no_such_app">No such app found.</string>
|
||||||
|
|
||||||
<string name="about_title">About Bel Market</string>
|
<string name="about_title">About F-Droid</string>
|
||||||
<string name="about_version">Version</string>
|
<string name="about_version">Version</string>
|
||||||
<string name="about_site">Website</string>
|
<string name="about_site">Website</string>
|
||||||
<string name="about_source">Source code</string>
|
<string name="about_source">Source code</string>
|
||||||
@ -82,7 +82,7 @@
|
|||||||
<string name="tab_updates_count">Updates (%d)</string>
|
<string name="tab_updates_count">Updates (%d)</string>
|
||||||
<string name="one_update_available">1 update is available.</string>
|
<string name="one_update_available">1 update is available.</string>
|
||||||
<string name="many_updates_available">%d updates are available.</string>
|
<string name="many_updates_available">%d updates are available.</string>
|
||||||
<string name="fdroid_updates_available">Bel Market Updates Available</string>
|
<string name="fdroid_updates_available">F-Droid Updates Available</string>
|
||||||
<string name="update_notification_more">+%1$d more…</string>
|
<string name="update_notification_more">+%1$d more…</string>
|
||||||
<string name="bluetooth_activity_not_found">No Bluetooth send method found, choose one!</string>
|
<string name="bluetooth_activity_not_found">No Bluetooth send method found, choose one!</string>
|
||||||
<string name="choose_bt_send">Choose Bluetooth send method</string>
|
<string name="choose_bt_send">Choose Bluetooth send method</string>
|
||||||
@ -101,7 +101,7 @@
|
|||||||
|
|
||||||
<string name="menu_update_repo">Update Repos</string>
|
<string name="menu_update_repo">Update Repos</string>
|
||||||
<string name="menu_manage">Repositories</string>
|
<string name="menu_manage">Repositories</string>
|
||||||
<string name="menu_send_apk_bt">Share Bel Market by Bluetooth</string>
|
<string name="menu_send_apk_bt">Share F-Droid by Bluetooth</string>
|
||||||
<string name="menu_settings">Settings</string>
|
<string name="menu_settings">Settings</string>
|
||||||
<string name="menu_about">About</string>
|
<string name="menu_about">About</string>
|
||||||
<string name="menu_search">Search</string>
|
<string name="menu_search">Search</string>
|
||||||
@ -152,7 +152,7 @@
|
|||||||
<string name="ignoreTouch_on">Always include apps that require touchscreen</string>
|
<string name="ignoreTouch_on">Always include apps that require touchscreen</string>
|
||||||
|
|
||||||
<string name="local_repo">Local Repo</string>
|
<string name="local_repo">Local Repo</string>
|
||||||
<string name="local_repo_running">Bel Market is ready to swap</string>
|
<string name="local_repo_running">F-Droid is ready to swap</string>
|
||||||
<string name="touch_to_configure_local_repo">Touch to view details and allow others to swap your apps.</string>
|
<string name="touch_to_configure_local_repo">Touch to view details and allow others to swap your apps.</string>
|
||||||
<string name="deleting_repo">Deleting current repo…</string>
|
<string name="deleting_repo">Deleting current repo…</string>
|
||||||
<string name="adding_apks_format">Adding %s to repo…</string>
|
<string name="adding_apks_format">Adding %s to repo…</string>
|
||||||
@ -216,12 +216,12 @@
|
|||||||
<string name="unknown">Unknown</string>
|
<string name="unknown">Unknown</string>
|
||||||
<string name="repo_confirm_delete_title">Delete Repository?</string>
|
<string name="repo_confirm_delete_title">Delete Repository?</string>
|
||||||
<string name="repo_confirm_delete_body">Deleting a repository means
|
<string name="repo_confirm_delete_body">Deleting a repository means
|
||||||
apps from it will no longer be available from Bel Market.\n\nNote: All
|
apps from it will no longer be available from F-Droid.\n\nNote: All
|
||||||
previously installed apps will remain on your device.</string>
|
previously installed apps will remain on your device.</string>
|
||||||
<string name="repo_disabled_notification">Disabled "%1$s".\n\nYou will
|
<string name="repo_disabled_notification">Disabled "%1$s".\n\nYou will
|
||||||
need to re-enable this repository to install apps from it.</string>
|
need to re-enable this repository to install apps from it.</string>
|
||||||
<string name="repo_added">Saved Bel Market repository %1$s.</string>
|
<string name="repo_added">Saved F-Droid repository %1$s.</string>
|
||||||
<string name="repo_searching_address">Looking for Bel Market repository at\n%1$s</string>
|
<string name="repo_searching_address">Looking for F-Droid repository at\n%1$s</string>
|
||||||
<string name="minsdk_or_later">%s or later</string>
|
<string name="minsdk_or_later">%s or later</string>
|
||||||
<string name="up_to_maxsdk">Up to %s</string>
|
<string name="up_to_maxsdk">Up to %s</string>
|
||||||
<string name="minsdk_up_to_maxsdk">%1$s up to %2$s</string>
|
<string name="minsdk_up_to_maxsdk">%1$s up to %2$s</string>
|
||||||
@ -255,7 +255,7 @@
|
|||||||
<string name="category_Time">Time</string>
|
<string name="category_Time">Time</string>
|
||||||
<string name="category_Writing">Writing</string>
|
<string name="category_Writing">Writing</string>
|
||||||
|
|
||||||
<string name="empty_installed_app_list">No apps installed.\n\nThere are apps on your device, but they are not available from Bel Market. This could be because you need to update your repositories, or the repositories genuinely don\'t have your apps available.</string>
|
<string name="empty_installed_app_list">No apps installed.\n\nThere are apps on your device, but they are not available from F-Droid. This could be because you need to update your repositories, or the repositories genuinely don\'t have your apps available.</string>
|
||||||
<string name="empty_available_app_list">No apps in this category.\n\nTry selecting a different category or updating your repositories to get a fresh list of apps.</string>
|
<string name="empty_available_app_list">No apps in this category.\n\nTry selecting a different category or updating your repositories to get a fresh list of apps.</string>
|
||||||
<string name="empty_can_update_app_list">All apps up to date.\n\nCongratulations! All of your apps are up to date (or your repositories are out of date).</string>
|
<string name="empty_can_update_app_list">All apps up to date.\n\nCongratulations! All of your apps are up to date (or your repositories are out of date).</string>
|
||||||
<string name="empty_search_installed_app_list">No matching installed applications.</string>
|
<string name="empty_search_installed_app_list">No matching installed applications.</string>
|
||||||
@ -264,31 +264,31 @@
|
|||||||
|
|
||||||
<string name="requesting_root_access_body">Requesting root access…</string>
|
<string name="requesting_root_access_body">Requesting root access…</string>
|
||||||
<string name="root_access_denied_title">Root access denied</string>
|
<string name="root_access_denied_title">Root access denied</string>
|
||||||
<string name="root_access_denied_body">Either your Android device is not rooted or you have denied root access for Bel Market.</string>
|
<string name="root_access_denied_body">Either your Android device is not rooted or you have denied root access for F-Droid.</string>
|
||||||
<string name="install_error_unknown">Failed to install due to an unknown error</string>
|
<string name="install_error_unknown">Failed to install due to an unknown error</string>
|
||||||
<string name="uninstall_error_unknown">Failed to uninstall due to an unknown error</string>
|
<string name="uninstall_error_unknown">Failed to uninstall due to an unknown error</string>
|
||||||
<string name="system_install_denied_title">Bel Market Privileged Extension is not available</string>
|
<string name="system_install_denied_title">F-Droid Privileged Extension is not available</string>
|
||||||
<string name="system_install_denied_body">This option is only available when Bel Market Privileged Extension is installed.</string>
|
<string name="system_install_denied_body">This option is only available when F-Droid Privileged Extension is installed.</string>
|
||||||
<string name="system_install_denied_signature">The signature of the extension is wrong! Please create a bug report!</string>
|
<string name="system_install_denied_signature">The signature of the extension is wrong! Please create a bug report!</string>
|
||||||
<string name="system_install_denied_permissions">The privileged permissions have not been granted to the extension! Please create a bug report!</string>
|
<string name="system_install_denied_permissions">The privileged permissions have not been granted to the extension! Please create a bug report!</string>
|
||||||
<string name="system_install_button_install">Install</string>
|
<string name="system_install_button_install">Install</string>
|
||||||
<string name="system_install_button_open">Open Extension</string>
|
<string name="system_install_button_open">Open Extension</string>
|
||||||
<string name="system_install_post_success">Successfully installed Bel Market Privileged Extension</string>
|
<string name="system_install_post_success">Successfully installed F-Droid Privileged Extension</string>
|
||||||
<string name="system_install_post_fail">Installation of Bel Market Privileged Extension failed</string>
|
<string name="system_install_post_fail">Installation of F-Droid Privileged Extension failed</string>
|
||||||
<string name="system_install_post_success_message">Bel Market Privileged Extension has been successfully installed. This allows Bel Market to install, upgrade and uninstall apps on its own.</string>
|
<string name="system_install_post_success_message">F-Droid Privileged Extension has been successfully installed. This allows F-Droid to install, upgrade and uninstall apps on its own.</string>
|
||||||
<string name="system_install_post_fail_message">The installation of Bel Market Privileged Extension has failed. The installation method is not supported by all Android distributions, please consult the Bel Market bug tracker for more information.</string>
|
<string name="system_install_post_fail_message">The installation of F-Droid Privileged Extension has failed. The installation method is not supported by all Android distributions, please consult the F-Droid bug tracker for more information.</string>
|
||||||
<string name="system_install_installing">Installing…</string>
|
<string name="system_install_installing">Installing…</string>
|
||||||
<string name="system_install_installing_rebooting">Installing and rebooting…</string>
|
<string name="system_install_installing_rebooting">Installing and rebooting…</string>
|
||||||
<string name="system_install_uninstalling">Uninstalling…</string>
|
<string name="system_install_uninstalling">Uninstalling…</string>
|
||||||
<string name="system_install_question">Do you want to install Bel Market Privileged Extension?</string>
|
<string name="system_install_question">Do you want to install F-Droid Privileged Extension?</string>
|
||||||
<string name="system_install_warning">This takes up to 10 seconds.</string>
|
<string name="system_install_warning">This takes up to 10 seconds.</string>
|
||||||
<string name="system_install_warning_lollipop">This takes up to 10 seconds and the device will be <b>rebooted</b> afterwards.</string>
|
<string name="system_install_warning_lollipop">This takes up to 10 seconds and the device will be <b>rebooted</b> afterwards.</string>
|
||||||
<string name="system_uninstall">Do you want to uninstall Bel Market Privileged Extension?</string>
|
<string name="system_uninstall">Do you want to uninstall F-Droid Privileged Extension?</string>
|
||||||
<string name="system_uninstall_button">Uninstall</string>
|
<string name="system_uninstall_button">Uninstall</string>
|
||||||
<string name="system_install_not_supported">Installation of Bel Market Privileged Extension is currently not supported on Android 5.1 or later.</string>
|
<string name="system_install_not_supported">Installation of F-Droid Privileged Extension is currently not supported on Android 5.1 or later.</string>
|
||||||
|
|
||||||
<string name="swap_nfc_title">Touch to swap</string>
|
<string name="swap_nfc_title">Touch to swap</string>
|
||||||
<string name="swap_nfc_description">If your friend has Bel Market and NFC turned on touch your devices together.</string>
|
<string name="swap_nfc_description">If your friend has F-Droid and NFC turned on touch your devices together.</string>
|
||||||
<string name="swap_join_same_wifi">Join the same Wi-Fi as your friend</string>
|
<string name="swap_join_same_wifi">Join the same Wi-Fi as your friend</string>
|
||||||
<string name="swap_join_same_wifi_desc">To swap using Wi-Fi, ensure you are on the same network. If you don\'t have access to the same network, one of you can create a Wi-Fi Hotspot.</string>
|
<string name="swap_join_same_wifi_desc">To swap using Wi-Fi, ensure you are on the same network. If you don\'t have access to the same network, one of you can create a Wi-Fi Hotspot.</string>
|
||||||
<string name="swap_join_this_hotspot">Help your friend join your hotspot</string>
|
<string name="swap_join_this_hotspot">Help your friend join your hotspot</string>
|
||||||
@ -299,7 +299,7 @@
|
|||||||
<string name="swap_view_available_networks">Tap to open available networks</string>
|
<string name="swap_view_available_networks">Tap to open available networks</string>
|
||||||
<string name="swap_switch_to_wifi">Tap to switch to a Wi-Fi network</string>
|
<string name="swap_switch_to_wifi">Tap to switch to a Wi-Fi network</string>
|
||||||
<string name="open_qr_code_scanner">Open QR Scanner</string>
|
<string name="open_qr_code_scanner">Open QR Scanner</string>
|
||||||
<string name="swap_welcome">Welcome to Bel Market!</string>
|
<string name="swap_welcome">Welcome to F-Droid!</string>
|
||||||
<string name="swap_confirm_connect">Do you want to get apps from %1$s now?</string>
|
<string name="swap_confirm_connect">Do you want to get apps from %1$s now?</string>
|
||||||
<string name="swap_dont_show_again">Don\'t show this again</string>
|
<string name="swap_dont_show_again">Don\'t show this again</string>
|
||||||
<string name="swap_scan_or_type_url">One person needs to scan the code, or type the URL of the other in a browser.</string>
|
<string name="swap_scan_or_type_url">One person needs to scan the code, or type the URL of the other in a browser.</string>
|
||||||
@ -318,13 +318,13 @@
|
|||||||
<string name="swap_not_visible_wifi">Not visible via Wi-Fi</string>
|
<string name="swap_not_visible_wifi">Not visible via Wi-Fi</string>
|
||||||
<string name="swap_wifi_device_name">Device Name</string>
|
<string name="swap_wifi_device_name">Device Name</string>
|
||||||
<string name="swap_cant_find_peers">Can\'t find who you\'re looking for?</string>
|
<string name="swap_cant_find_peers">Can\'t find who you\'re looking for?</string>
|
||||||
<string name="swap_send_fdroid">Send Bel Market</string>
|
<string name="swap_send_fdroid">Send F-Droid</string>
|
||||||
<string name="swap_no_peers_nearby">Could not find people nearby to swap with.</string>
|
<string name="swap_no_peers_nearby">Could not find people nearby to swap with.</string>
|
||||||
<string name="swap_connecting">Connecting</string>
|
<string name="swap_connecting">Connecting</string>
|
||||||
<string name="swap_confirm">Confirm swap</string>
|
<string name="swap_confirm">Confirm swap</string>
|
||||||
<string name="swap_qr_isnt_for_swap">The QR code you scanned doesn\'t look like a swap code.</string>
|
<string name="swap_qr_isnt_for_swap">The QR code you scanned doesn\'t look like a swap code.</string>
|
||||||
<string name="bluetooth_unavailable">Bluetooth unavailable</string>
|
<string name="bluetooth_unavailable">Bluetooth unavailable</string>
|
||||||
<string name="swap_cant_send_no_bluetooth">Cannot send Bel Market, because Bluetooth is unavailable on this device.</string>
|
<string name="swap_cant_send_no_bluetooth">Cannot send F-Droid, because Bluetooth is unavailable on this device.</string>
|
||||||
<string name="loading">Loading…</string>
|
<string name="loading">Loading…</string>
|
||||||
<string name="swap_connection_misc_error">An error occurred while connecting to device, we can\'t seem to swap with it.</string>
|
<string name="swap_connection_misc_error">An error occurred while connecting to device, we can\'t seem to swap with it.</string>
|
||||||
<string name="swap_not_enabled">Swapping not enabled</string>
|
<string name="swap_not_enabled">Swapping not enabled</string>
|
||||||
@ -383,10 +383,9 @@
|
|||||||
<string name="theme_dark">Dark</string>
|
<string name="theme_dark">Dark</string>
|
||||||
<string name="theme_night">Night</string>
|
<string name="theme_night">Night</string>
|
||||||
|
|
||||||
<string name="crash_dialog_title">Bel Market has crashed</string>
|
<string name="crash_dialog_title">F-Droid has crashed</string>
|
||||||
<string name="crash_dialog_text">An unexpected error occurred
|
<string name="crash_dialog_text">An unexpected error occurred
|
||||||
forcing the application to stop. Would you like to e-mail the
|
forcing the application to stop. Would you like to e-mail the
|
||||||
details to help fix the issue?</string>
|
details to help fix the issue?</string>
|
||||||
<string name="crash_dialog_comment_prompt">You can add extra information and comments here:</string>
|
<string name="crash_dialog_comment_prompt">You can add extra information and comments here:</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
358
values-fa/strings.xml
Normal file
358
values-fa/strings.xml
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<resources>
|
||||||
|
<string name="installIncompatible">بهنظر میرسد این بسته با دستگاه شما هماهنگ نیست. آیا میخواهید آن را به هر قیمتی آزمایش و نصب کنید؟</string>
|
||||||
|
<string name="installDowngrade">شما در حال قدیمیکردن و کاهش درجهٔ این برنامه هستید. انجام چنین کاری ممکن منجر به خرابی یا از دست رفتن دادههای شما شود. آیا میخواهید سعی کنید این برنامه را به هر قیمتی قدیمی کنید؟</string>
|
||||||
|
<string name="delete">حذف</string>
|
||||||
|
<string name="cache_downloaded">نگهداری کارههای انبار شده</string>
|
||||||
|
<string name="updates">بهروزرسانیها</string>
|
||||||
|
<string name="other">دیگر</string>
|
||||||
|
<string name="update_interval">بهروزرسانی دورهای خودکار</string>
|
||||||
|
<string name="update_interval_zero">بهروزرسانینکردن خودکار فهرست برنامهها</string>
|
||||||
|
<string name="automatic_scan_wifi">فقط روی وایفای</string>
|
||||||
|
<string name="automatic_scan_wifi_on">بهروز رسانی خودکار فهرست کارهها روی وایفای</string>
|
||||||
|
<string name="notify_on">نمایش یک آگهی هنگام موجود بودن بهروز رسانیها</string>
|
||||||
|
<string name="update_history">بهروزرسانی تاریخچه</string>
|
||||||
|
<string name="no_such_app">چنین کارهای پیدا نشد.</string>
|
||||||
|
<string name="about_title">دربارهٔ F-Droid</string>
|
||||||
|
<string name="about_mail">رایانامه</string>
|
||||||
|
<string name="app_installed">نصب شده</string>
|
||||||
|
<string name="app_not_installed">نصب نشده</string>
|
||||||
|
<string name="added_on">در %s افزوده شد</string>
|
||||||
|
<string name="ok">باشد</string>
|
||||||
|
<string name="yes">بله</string>
|
||||||
|
<string name="no">خیر</string>
|
||||||
|
<string name="repo_add_title">افزودن مخزنی جدید</string>
|
||||||
|
<string name="repo_add_add">افزودن</string>
|
||||||
|
<string name="cancel">لغو</string>
|
||||||
|
<string name="enable">فعّال کردن</string>
|
||||||
|
<string name="add_key">افزودن کلید</string>
|
||||||
|
<string name="overwrite">جانویسی</string>
|
||||||
|
<string name="tab_updates">بهروز رسانیها</string>
|
||||||
|
<string name="fdroid_updates_available">بهروزرسانیهای افدروید موجودند</string>
|
||||||
|
<string name="bluetooth_activity_not_found">هیچ روش ارسال با بلوتوثی پیدا نشد، یکی را برگزینید!</string>
|
||||||
|
<string name="choose_bt_send">گزینش روش ارسال بلوتوث</string>
|
||||||
|
<string name="repo_add_url">نشانی مخزن</string>
|
||||||
|
<string name="repo_add_fingerprint">اثر انگشت (اختیاری)</string>
|
||||||
|
<string name="menu_update_repo">بهروزرسانی مخزنها</string>
|
||||||
|
<string name="menu_manage">مخزنها</string>
|
||||||
|
<string name="menu_about">درباره</string>
|
||||||
|
<string name="menu_search">جستوجو</string>
|
||||||
|
<string name="menu_share">همرسانی</string>
|
||||||
|
<string name="menu_install">نصب</string>
|
||||||
|
<string name="menu_uninstall">حذف نصب</string>
|
||||||
|
<string name="menu_issues">اشکالها</string>
|
||||||
|
<string name="menu_source">کد مبدأ</string>
|
||||||
|
<string name="menu_donate">اعانه</string>
|
||||||
|
<string name="details_notinstalled">نصب نشده</string>
|
||||||
|
<string name="antiadslist">این کاره دارای تبلیغات است</string>
|
||||||
|
<string name="antitracklist">این کاره، فعّالیتهایتان را ردیابی و گزارش میکند</string>
|
||||||
|
<string name="antinonfreeadlist">این کاره، افزونههای غیر آزاد را تبلیغ میکند</string>
|
||||||
|
<string name="antinonfreenetlist">این کاره، خدمات شبکههای غیر آزاد را تبلیغ میکند</string>
|
||||||
|
<string name="antinonfreedeplist">این کاره، به دیگر کارههای غیر آزاد وابسته است</string>
|
||||||
|
<string name="antiupstreamnonfreelist">کد مبدأ بالادستی، کاملاً آزاد نیست</string>
|
||||||
|
<string name="display">نمایش</string>
|
||||||
|
<string name="expert_on">نمایش اطّلاعات و فعّال کردن تنظیمات اضافی</string>
|
||||||
|
<string name="search_hint">جستوجوی برنامهها</string>
|
||||||
|
<string name="appcompatibility">سازگاری برنامه</string>
|
||||||
|
<string name="rooted">نادیده گرفتن ریشه</string>
|
||||||
|
<string name="ignoreTouch">نادیده گرفتن صفحهلمسی</string>
|
||||||
|
<string name="category_All">همه</string>
|
||||||
|
<string name="status_download">در حال بارگیری\n%2$s / %3$s (%4$d%%) از\n%1$s</string>
|
||||||
|
<string name="status_connecting_to_repo">درحال اتّصال به\n%1$s</string>
|
||||||
|
<string name="no_permissions">اجازهای استفاده نشده است.</string>
|
||||||
|
<string name="theme">زمینه</string>
|
||||||
|
<string name="unsigned">امضا نشده</string>
|
||||||
|
<string name="repo_description">شرح</string>
|
||||||
|
<string name="repo_last_update">آخرین بهروز رسانی</string>
|
||||||
|
<string name="repo_name">نام</string>
|
||||||
|
<string name="unsigned_description">این یعنی فهرست برنامهها نتوانست تأیید شود. باید مراقب برنامههای بارگیری شده از شاخصهای امضا نشده باشید.</string>
|
||||||
|
<string name="unknown">ناشناخته</string>
|
||||||
|
<string name="minsdk_or_later">%s یا جدیدتر</string>
|
||||||
|
<string name="up_to_maxsdk">تا %s</string>
|
||||||
|
<string name="minsdk_up_to_maxsdk">%1$s تا %2$s</string>
|
||||||
|
<string name="requires_features">نیاز دارد: %1$s</string>
|
||||||
|
<string name="app_name">اِف-دروید</string>
|
||||||
|
|
||||||
|
<string name="SignatureMismatch">نگارش جدید با کلیدی متفاوت با قبلی امضا شده است. برای نصب نگارش جدید، باید نخست نگارش قدیمی را حذف کرد. لطفاً این کار را انجام داده و دوباره تلاش کنید. (به خاطر داشته باشید که حذف نصب، تمام دادههای درونی ذخیره شدهٔ کاره را پاک میکند)</string>
|
||||||
|
<string name="version">نگارش</string>
|
||||||
|
<string name="enable_nfc_send">فعّال کردن ارسال NFC…</string>
|
||||||
|
<string name="unstable_updates">بهروز رسانیهای ناپایدار</string>
|
||||||
|
<string name="unstable_updates_summary">پیشنهاد بهروز رسانی به نگارشهای ناپایدار</string>
|
||||||
|
<string name="notify">آگهیهای بهروز رسانی</string>
|
||||||
|
<string name="update_history_summ">روزهایی که کاره جدید یا اخیر محسوب شود: %s</string>
|
||||||
|
<string name="system_installer">افزونهٔ ممتاز</string>
|
||||||
|
<string name="system_installer_on">استفاده از افزونهٔ ممتاز افدروید برای نصب، بهروز رسانی و حذف بستهها</string>
|
||||||
|
<string name="uninstall_system">بهروز رسانی/حذف افزونهٔ ممتاز</string>
|
||||||
|
<string name="uninstall_system_summary">گشودن صفحهٔ جزییات افزونهٔ ممتاز برای بهروز رسانی/حذف آن</string>
|
||||||
|
<string name="local_repo_name">نام مخزن محلّی شما</string>
|
||||||
|
<string name="local_repo_name_summary">عنوان تبلغی مخزن محلّی شما: %s</string>
|
||||||
|
<string name="local_repo_https">استفاده از اتّصال خصوصی</string>
|
||||||
|
<string name="local_repo_https_on">استفاده از اتّصال رمز شدهٔ http برای مخزن محلّی</string>
|
||||||
|
|
||||||
|
<string name="login_title">نیاز به تأیید هویت</string>
|
||||||
|
<string name="login_name">نام کاربری</string>
|
||||||
|
<string name="login_password">گذرواژه</string>
|
||||||
|
<string name="repo_edit_credentials">تغییر گذرواژه</string>
|
||||||
|
<string name="repo_error_empty_username">نام کاربری خالی، مشخّصات تغییری نکرد</string>
|
||||||
|
|
||||||
|
<string name="app_details">جزییات کاره</string>
|
||||||
|
<string name="about_version">نگارش</string>
|
||||||
|
<string name="about_site">پایگاه وب</string>
|
||||||
|
<string name="about_source">کد مبدأ</string>
|
||||||
|
<string name="about_license">پروانه</string>
|
||||||
|
|
||||||
|
<string name="app_incompatible">ناسازگار</string>
|
||||||
|
<string name="app_inst_known_source">نصب شده (از %s)</string>
|
||||||
|
<string name="app_inst_unknown_source">نصب شده (از مبدأ ناشناس)</string>
|
||||||
|
|
||||||
|
<string name="links">پیوندها</string>
|
||||||
|
<string name="more">بیشتر</string>
|
||||||
|
<string name="less">کمتر</string>
|
||||||
|
|
||||||
|
<string name="back">بازگشت</string>
|
||||||
|
<string name="tab_available_apps">موجود</string>
|
||||||
|
<string name="tab_installed_apps">نصب شده</string>
|
||||||
|
<string name="tab_installed_apps_count">نصب شده (%d)</string>
|
||||||
|
<string name="tab_updates_count">بهروز رسانیها (%d)</string>
|
||||||
|
<string name="one_update_available">۱ بهروز رسانی موجود است.</string>
|
||||||
|
<string name="many_updates_available">%d به روز رسانی موجود است.</string>
|
||||||
|
<string name="update_notification_more">+%1$d بیشتر…</string>
|
||||||
|
<string name="send_via_bluetooth">ارسال با بلوتوث</string>
|
||||||
|
|
||||||
|
<string name="repo_exists_add_fingerprint">این مخزن از پیش برپا شده است. این کار، اظّلاعات کلید جدیدی را میافزاید.</string>
|
||||||
|
<string name="repo_exists_enable">این مخزن از پیش برپا شده است. تأیید کنید که میخواهید دوباره فعّالش کنید.</string>
|
||||||
|
<string name="bad_fingerprint">اثر انگشت بد</string>
|
||||||
|
<string name="invalid_url">این یک نشانی اینترنتی معتیبر نیست.</string>
|
||||||
|
<string name="menu_send_apk_bt">همرسانی افدروید با بلوتوث</string>
|
||||||
|
<string name="menu_settings">تنظیمات</string>
|
||||||
|
<string name="menu_add_repo">مخزن جدید</string>
|
||||||
|
|
||||||
|
<string name="menu_launch">اجرا</string>
|
||||||
|
<string name="menu_ignore_all">نادیده گرفتن تمام بهروز رسانیها</string>
|
||||||
|
<string name="menu_ignore_this">نادیده گرفتن این بهروز رسانی</string>
|
||||||
|
<string name="menu_website">پایگاه وب</string>
|
||||||
|
<string name="menu_changelog">گزارش تغییرات</string>
|
||||||
|
<string name="menu_upgrade">ارتقا</string>
|
||||||
|
<string name="menu_bitcoin">بیتکوین</string>
|
||||||
|
<string name="menu_litecoin">لایتکوین</string>
|
||||||
|
<string name="menu_flattr">فلتر</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="details_installed">نگارش %s نصب شده</string>
|
||||||
|
<string name="expert">حالت حرفهای</string>
|
||||||
|
<string name="show_incompat_versions">نگارشهای ناسازگار</string>
|
||||||
|
<string name="show_incompat_versions_on">نمایش نگارشهای ناساگار کاره با این افزاره</string>
|
||||||
|
<string name="rooted_on">خاکستری نکردن کارههای نیازمند دسترسی ریشه</string>
|
||||||
|
<string name="ignoreTouch_on">همواره شامل کارههایی که نیازمند صفحهلمسی هستند</string>
|
||||||
|
|
||||||
|
<string name="local_repo">مخزن محلّی</string>
|
||||||
|
<string name="local_repo_running">افدروید آمادهٔ تبادل است</string>
|
||||||
|
<string name="touch_to_configure_local_repo">برای دیدن جزییات و اجازه به دیگرات برای تبادل کارههایتان، لمس کنید.</string>
|
||||||
|
<string name="deleting_repo">در حال حذف مخزن جاری…</string>
|
||||||
|
<string name="adding_apks_format">در حال افزودن %s به مخزن…</string>
|
||||||
|
<string name="writing_index_jar">در حال نوشتن پروندهٔ شاخص امضا شده (index.jar)…</string>
|
||||||
|
<string name="icon">نقشک</string>
|
||||||
|
<string name="linking_apks">در حال پیوند دادن apkها در مخزن…</string>
|
||||||
|
<string name="copying_icons">در حال رونوشت نقشک کارهها در مخزن…</string>
|
||||||
|
<string name="next">بعدی</string>
|
||||||
|
<string name="skip">پرش</string>
|
||||||
|
|
||||||
|
<string name="proxy">پیشکار</string>
|
||||||
|
<string name="enable_proxy_title">فعّال کردن پیشکار http</string>
|
||||||
|
<string name="enable_proxy_summary">پیکربندی پیشکار http برای تمام درخواستهای شبکه</string>
|
||||||
|
<string name="proxy_host">میزبان پیشکار</string>
|
||||||
|
<string name="proxy_host_summary">پیکربندی نام میزبان پیشکارتان (مثلاً 127.0.0.1)</string>
|
||||||
|
<string name="proxy_port">درگاه پیشکار</string>
|
||||||
|
<string name="proxy_port_summary">پیکربندی شمارهٔ درگاه پیشکارتان (مثلاً 8118)</string>
|
||||||
|
|
||||||
|
<string name="status_download_unknown_size">در حال بارگیری\n%2$s از\n%1$s</string>
|
||||||
|
<string name="update_notification_title">در حال بهروز رسانی مخزنها</string>
|
||||||
|
<string name="status_processing_xml_percent">در حال پردازش %2$s / %3$s (%4$d%%) از %1$s</string>
|
||||||
|
<string name="status_inserting_apps">در حال ذخیرهٔ جزییات برنامه</string>
|
||||||
|
<string name="repos_unchanged">تمام مخزنها بهروز هستند</string>
|
||||||
|
<string name="global_error_updating_repos">خطا هنگام بهروز رسانی: %s</string>
|
||||||
|
<string name="permissions">اجازهها</string>
|
||||||
|
<string name="no_handler_app">هیچ برنامهای موجود ندارید که بتواند %s را مدیریت کند.</string>
|
||||||
|
<string name="unverified">تأیید نشده</string>
|
||||||
|
<string name="repo_details">مخزن</string>
|
||||||
|
<string name="repo_url">نشانی</string>
|
||||||
|
<string name="repo_num_apps">تعداد کارهها</string>
|
||||||
|
<string name="repo_fingerprint">اثر انگشت کلید امضا (SHA-256)</string>
|
||||||
|
<string name="repo_confirm_delete_title">حذف مخزن؟</string>
|
||||||
|
<string name="not_on_same_wifi">افزارهتان روی همان وایفای مخزن محلّیای که افزودید نیست! لطفاً به این شبکه وصل شوید: %s</string>
|
||||||
|
<string name="pref_language">زبان</string>
|
||||||
|
<string name="pref_language_default">پیشگزیدهٔ سامانه</string>
|
||||||
|
|
||||||
|
<string name="app_icon">نقشک کاره</string>
|
||||||
|
<string name="wifi">وایفای</string>
|
||||||
|
<string name="wifi_ap">نقطهٔ داغ</string>
|
||||||
|
|
||||||
|
<string name="category_Whats_New">تازهها</string>
|
||||||
|
<string name="category_Recently_Updated">بهروز رسانیهای اخیر</string>
|
||||||
|
<string name="category_Connectivity">اتّصال</string>
|
||||||
|
<string name="category_Development">توسعه</string>
|
||||||
|
<string name="category_Games">بازی</string>
|
||||||
|
<string name="category_Graphics">گرافیک</string>
|
||||||
|
<string name="category_Internet">اینترنت</string>
|
||||||
|
<string name="category_Money">مالی</string>
|
||||||
|
<string name="category_Multimedia">چندرسانهای</string>
|
||||||
|
<string name="category_Navigation">ناوبری</string>
|
||||||
|
<string name="category_Phone_SMS">تلفن و پیامک</string>
|
||||||
|
<string name="category_Reading">خواندن</string>
|
||||||
|
<string name="category_Science_Education">علم و آموزش</string>
|
||||||
|
<string name="category_Security">امنیت</string>
|
||||||
|
<string name="category_Sports_Health">ورزش و سلامت</string>
|
||||||
|
<string name="category_System">سامانه</string>
|
||||||
|
<string name="category_Theming">شخصیسازی</string>
|
||||||
|
<string name="category_Time">زمان</string>
|
||||||
|
<string name="category_Writing">نوشتن</string>
|
||||||
|
|
||||||
|
<string name="requesting_root_access_body">در حال درخواست دسترسی ریشه…</string>
|
||||||
|
<string name="empty_can_update_app_list">تمام کارهها بهروز هستند.\n\nتبریک! تمام کارههایتان بهروز هستند (یا مخزنهایتان منقضی شدهاند).</string>
|
||||||
|
|
||||||
|
<string name="root_access_denied_title">دسترسی ریشه داده نشد</string>
|
||||||
|
<string name="root_access_denied_body">یا افزارهٔ اندرویدیتان روت شده نیست، یا درخواست ریشه برای افدروید را رد کردید.</string>
|
||||||
|
<string name="system_uninstall_button">حذف نصب</string>
|
||||||
|
|
||||||
|
<string name="swap_nfc_title">برای تبادل، لمس کنید</string>
|
||||||
|
<string name="swap_join_same_wifi">به همان وایفای دوستتان بپیوندید</string>
|
||||||
|
<string name="swap_join_this_hotspot">دوستتان را در پیوستن به نقطهٔ داغتان راهنمایی کنید</string>
|
||||||
|
<string name="swap">تبادل کارهها</string>
|
||||||
|
<string name="swap_success">تبادل موفّقیتآمیز!</string>
|
||||||
|
<string name="swap_no_wifi_network">هنوز هیچ شبکهای وجود ندارد</string>
|
||||||
|
<string name="swap_active_hotspot">%1$s (نقطهٔ داغتان)</string>
|
||||||
|
<string name="swap_view_available_networks">برای گشودن شبکههای موجود، ضربه بزنید</string>
|
||||||
|
<string name="swap_switch_to_wifi">برای تعویض به یک شبکهٔ وایفای، ضربه بزنید</string>
|
||||||
|
<string name="open_qr_code_scanner">گشودن پویشگر QR</string>
|
||||||
|
<string name="swap_welcome">به افدروید خوش آمدید!</string>
|
||||||
|
<string name="swap_confirm_connect">میخواهید اکنون کارهها را از %1$s بگیرید؟</string>
|
||||||
|
<string name="swap_dont_show_again">دیگر این را نشان نده</string>
|
||||||
|
<string name="swap_scan_or_type_url">لازم است یک نفر رمز دیگری را پوییده، یا نشانی اینترنتیش را در مرورگری بنویسد.</string>
|
||||||
|
<string name="swap_choose_apps">گزینش کارهها</string>
|
||||||
|
<string name="swap_scan_qr">پویش رمز QR</string>
|
||||||
|
<string name="swap_people_nearby">افراد نزدیک</string>
|
||||||
|
<string name="swap_scanning_for_peers">در حال جستوجو برای افراد نزدیک…</string>
|
||||||
|
<string name="swap_nearby">تبادل نزدیک</string>
|
||||||
|
<string name="swap_intro">اتّصال و مبادلهٔ کارهها با افراد نزدیکتان.</string>
|
||||||
|
<string name="swap_visible_bluetooth">قابل مشاهده از طریق بلوتوث</string>
|
||||||
|
<string name="swap_setting_up_bluetooth">در حال برپا سازی بلوتوث…</string>
|
||||||
|
<string name="swap_not_visible_bluetooth">غیرقابل مشاهده از طریق بلوتوث</string>
|
||||||
|
<string name="swap_visible_wifi">قابل مشاهده از طریق وایفای</string>
|
||||||
|
<string name="swap_setting_up_wifi">در حال برپا سازی وایفای…</string>
|
||||||
|
<string name="swap_not_visible_wifi">غیرقابل مشاهده از طریق وایفای</string>
|
||||||
|
<string name="swap_wifi_device_name">نام افزاره</string>
|
||||||
|
<string name="swap_cant_find_peers">نمیتوانید کسی که دنبالشید را پیدا کنید؟</string>
|
||||||
|
<string name="swap_send_fdroid">ارسال افدروید</string>
|
||||||
|
<string name="swap_no_peers_nearby">نمیتوان افراد نزدیک را برای تبادل با آنها پیدا کرد.</string>
|
||||||
|
<string name="swap_connecting">در حال اتّصال</string>
|
||||||
|
<string name="swap_confirm">تأیید تبادل</string>
|
||||||
|
<string name="newPerms">جدید</string>
|
||||||
|
<string name="allPerms">همه</string>
|
||||||
|
<string name="perm_costs_money">ممکن است این کار برایتان خرج داشته باشد</string>
|
||||||
|
<string name="tap_to_install">بارگیری کامل شد، برای نصب ضربه بزنید</string>
|
||||||
|
<string name="download_error">بارگیری موفّق نبود</string>
|
||||||
|
|
||||||
|
<string name="perms_new_perm_prefix">"جدید: "</string>
|
||||||
|
<string name="perms_description_app">فراهم شده توسّط: %1$s.</string>
|
||||||
|
<string name="downloading">در حال بارگیری…</string>
|
||||||
|
|
||||||
|
<string name="interval_never">هرگز</string>
|
||||||
|
<string name="interval_1h">هر ساعت</string>
|
||||||
|
<string name="interval_4h">هر ۴ ساعت</string>
|
||||||
|
<string name="interval_12h">هر ۱۲ ساعت</string>
|
||||||
|
<string name="interval_1d">هر روز</string>
|
||||||
|
<string name="interval_1w">هر هفته</string>
|
||||||
|
<string name="interval_2w">هر ۲ هفته</string>
|
||||||
|
|
||||||
|
<string name="theme_light">روشن</string>
|
||||||
|
<string name="theme_dark">تیره</string>
|
||||||
|
<string name="theme_night">شب</string>
|
||||||
|
<string name="repo_exists_and_enabled">مخزنن ورودی از پیش برپا و فعّال شده است.</string>
|
||||||
|
<string name="repo_delete_to_overwrite">باید پیش از افزودن مخزن جدیدی با کلید متفاوت، این را حذف کنید.</string>
|
||||||
|
<string name="malformed_repo_uri">نادیده گرفتن نشانی اینترنتی مخزن ناهنجار: %s</string>
|
||||||
|
|
||||||
|
<string name="all_other_repos_fine">تمام مخزنهای دیگر خطایی ایاد نکردند.</string>
|
||||||
|
<string name="repo_not_yet_updated">این مخزن هنوز استفاده نشده است. برای دیدن کارههایی که فراهم میکند، باید فعّالش کنید.</string>
|
||||||
|
<string name="repo_confirm_delete_body">حذف یک مخزن به این معناست که کارههایش دیگر در افدروید موجود نخواهند بود.\n\nتوجّه: تمام کارههای نصب شدهٔ پیشین، روی افزارهتان باقی خواهند ماند.</string>
|
||||||
|
<string name="repo_disabled_notification">%1$s غیرفعّال شد.\n\nبرای نصب کارهها از آن باید دوباره فعّالش کنید.</string>
|
||||||
|
<string name="repo_added">مخزن افدروید %1$s ذخیره شد.</string>
|
||||||
|
<string name="repo_searching_address">در حال گشتن به دنبال مخزن افدروید در\n%1$s</string>
|
||||||
|
<string name="empty_installed_app_list">هیچ کارهای نصب نشده است.\n\nکارههایی روی افزارهتان وجود دارند، ولی در افدروید موجود نیستند. دلیل این امر میتواند این دلیل باشد که مخازنتان کارههایتان را موجود ندارند یا باید بهروز شوند.</string>
|
||||||
|
<string name="empty_available_app_list">هیچ کارهای در این دسته نیست.\n\nتلاش کنید دستهای دیگر برگزیده یا مخازنتان را برای گرفتن فهرست تازهای از کارهها بهروز کنید.</string>
|
||||||
|
<string name="install_error_unknown">شکست در نصب به دلیل خطایی ناشناخته</string>
|
||||||
|
<string name="uninstall_error_unknown">شکست در نصب به دلیل خطایی ناشناخته</string>
|
||||||
|
<string name="system_install_denied_title">افزونهٔ ممتاز افدروید موجود نیست</string>
|
||||||
|
<string name="system_install_denied_body">این گزینه فقط هنگام نصب بودن افزونهٔ ممتاز افدروید، موجود است.</string>
|
||||||
|
<string name="system_install_denied_signature">امضای افزونه اشتباه است! لطفاً یک گزارش خطا ایجاد کنید!</string>
|
||||||
|
<string name="system_install_denied_permissions">اجازههای ممتاز به افزونه داده نشده است! لطفاً یک گزارش خطا ایجاد کنید!</string>
|
||||||
|
<string name="system_install_button_install">نصب</string>
|
||||||
|
<string name="system_install_button_open">گشودن افزونه</string>
|
||||||
|
<string name="system_install_post_success">افزونهٔ ممتاز افدروید با موفّقیت نصب شد</string>
|
||||||
|
<string name="system_install_post_fail">نصب افزونهٔ ممتاز افدروید شکست خورد</string>
|
||||||
|
<string name="system_install_post_success_message">افزونهٔ ممتاز افدروید با موفّقیت نصب شد. این مار به افدروید اجازه میدهد کارهها را توسّط خودش نصب کرده، ارتفا دهد و حدف کند.</string>
|
||||||
|
<string name="system_install_post_fail_message">نصب افزونهٔ ممتاز افدروید شکست خورد. روش نصب توسّط همهٔ توزیعهای اندروید پشتیبانی نمیشود. لطفاً برای اطّلاعات بیشتر با ردیاب مشکل افدروید مشاوره کنید.</string>
|
||||||
|
<string name="system_install_installing">در حال نصب…</string>
|
||||||
|
<string name="system_install_installing_rebooting">در حال نصب و راهانداری دوباره…</string>
|
||||||
|
<string name="system_install_uninstalling">در حال حذف نصب…</string>
|
||||||
|
<string name="system_install_question">میخواهید افزونهٔ ممتاز افدروید را نصب کنید؟</string>
|
||||||
|
<string name="system_install_warning">این کار بیش از ۱۰ ثانیه زمان میبرد.</string>
|
||||||
|
<string name="system_install_warning_lollipop">این کار بیش از ۱۰ ثانیه زمان برده و پس از آن، افزاره <b>راهاندازی مجدّد</b> خواهد شد.</string>
|
||||||
|
<string name="system_uninstall">میخواهید افزونهٔ ممتاز افدروید را حذف کنید؟</string>
|
||||||
|
<string name="swap_nfc_description">اگر دوستتان افدروید داشته و NFC اش روشن است، افزارههایتان را با هم تماس بدهید.</string>
|
||||||
|
<string name="swap_join_same_wifi_desc">برای تبادل با استفاده از وایفای، مطمئن شوید که روی شبکهٔ یکسانی هستید. اگر به شبکهٔ یکسان دسترسی ندارید، یکی از شما میتواند یک نقطهٔ داغ وایفای ایجاد کند.</string>
|
||||||
|
<string name="swap_qr_isnt_for_swap">رمز QR ای که پوییدید، شبیه یک رمز تبادل نیست.</string>
|
||||||
|
<string name="bluetooth_unavailable">بلوتوث موجود نیست</string>
|
||||||
|
<string name="swap_cant_send_no_bluetooth">نمیتوان افدروید را فرستاد، زیرا بلوتوث روی این افزاره موحود نیست.</string>
|
||||||
|
<string name="loading">در حال بار کردن…</string>
|
||||||
|
<string name="swap_connection_misc_error">هنگام اتّصال به افزاره خطایی رخ داد. به نظر میرسد با این وضع نمیتوانیم تبادل کنیم.</string>
|
||||||
|
<string name="swap_not_enabled">تبادل فعّال نیست</string>
|
||||||
|
<string name="swap_not_enabled_description">پیش از تبادل، افزارهتان باید قابل مشاهده باشد.</string>
|
||||||
|
|
||||||
|
<string name="install_confirm">نیاز به دسترسی به</string>
|
||||||
|
<string name="install_confirm_update">میخواهید بهروزرسانیای برای این برنامه نصب کنید؟ دادههای موجودتان از بین نخواهند رفت. برنامهٔ بهروز شده دسترسیهای زیر را خواهد داشت:</string>
|
||||||
|
<string name="install_confirm_update_system">میخواهید بهروزرسانیای برای این برنامهٔ داخلی نصب کنید؟ دادههای موجودتان از بین نخواهند رفت. برنامهٔ بهروز شده دسترسیهای زیر را خواهد داشت:</string>
|
||||||
|
<string name="install_confirm_update_no_perms">میخواهید بهروزرسانیای برای این برنامه نصب کنید؟ دادههای موجودتان از بین نخواهند رفت. نیاز به هیچ دسترسی خاصّی ندارد.</string>
|
||||||
|
<string name="install_confirm_update_system_no_perms">میخواهید بهروزرسانیای برای این برنامهٔ داخلی نصب کنید؟ دادههای موجودتان از بین نخواهند رفت. نیاز به هیچ دسترسی خاصّی ندارد.</string>
|
||||||
|
<string name="uninstall_update_confirm">مایلید این نرمافزار را جایگزین نسخه کارخانه کنید؟</string>
|
||||||
|
<string name="uninstall_confirm">میخواهید این نرمافزار را حذف کنید؟</string>
|
||||||
|
<string name="by_author">توسّط</string>
|
||||||
|
<string name="menu_email">رایانامه به نویسنده</string>
|
||||||
|
<string name="antinonfreeassetslist">این کاره شامل ابزارهای غیرآزاد است</string>
|
||||||
|
|
||||||
|
<string name="empty_search_installed_app_list">هیچ برنامهٔ نصب شدهٔ مطابقی وجود ندارد.</string>
|
||||||
|
<string name="empty_search_available_app_list">هیچ برنامهٔ مطابقی وجود ندارد.</string>
|
||||||
|
<string name="empty_search_can_update_app_list">هیچ برنامهٔ مطابقی برای بهروز رسانی وجود ندارد.</string>
|
||||||
|
|
||||||
|
<string name="crash_dialog_title">افدروید از کار افتاد</string>
|
||||||
|
<string name="crash_dialog_text">خطایی پیشبینی نشده رخ داد که برنامه را وادار به توقّف کرد. میخواهید جزئیات را برای کمک به حل کردن مشکل رایانامه کنید؟</string>
|
||||||
|
<string name="crash_dialog_comment_prompt">در اینجا میتوانید اطّلاعات اضافی و نظرها را بیفزایید:</string>
|
||||||
|
<string name="useTor">استفاده از تور</string>
|
||||||
|
<string name="useTorSummary">اجبار ترافیک بارگیری به استفاده از تور برای محرمانگی بیشتر</string>
|
||||||
|
<string name="swap_stopping_wifi">در حال توقّف وایفای…</string>
|
||||||
|
<string name="repo_provider">مخزن: %s</string>
|
||||||
|
|
||||||
|
<string name="update_auto_download">بارگیری خودکار بهروز رسانیها</string>
|
||||||
|
<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="system_install_not_supported">در حال حاضر نصب افزونهٔ ممتاز افدروید روی اندروید ۵.۱ یا بالاتر پشتیبانی نمیشود.</string>
|
||||||
|
|
||||||
|
<string name="keep_hour">۱ ساعت</string>
|
||||||
|
<string name="keep_day">۱ روز</string>
|
||||||
|
<string name="keep_week">۱ هفته</string>
|
||||||
|
<string name="keep_month">۱ ماه</string>
|
||||||
|
<string name="keep_year">۱ سال</string>
|
||||||
|
<string name="keep_forever">برای همیشه</string>
|
||||||
|
|
||||||
|
<string name="uninstalling">درحال حذف نصب…</string>
|
||||||
|
|
||||||
|
<string name="installing">در حال نصب…</string>
|
||||||
|
<string name="install_error_notify_title">خطای نصب %s</string>
|
||||||
|
<string name="uninstall_error_notify_title">خطا در حذف نصب %s</string>
|
||||||
|
|
||||||
|
|
||||||
|
</resources>
|
Loading…
x
Reference in New Issue
Block a user