diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4d2de069b..f6a6a23d5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -508,6 +508,7 @@
+
diff --git a/app/src/main/java/org/fdroid/fdroid/AppDetails2.java b/app/src/main/java/org/fdroid/fdroid/AppDetails2.java
index 8e23089ca..d62c43e36 100644
--- a/app/src/main/java/org/fdroid/fdroid/AppDetails2.java
+++ b/app/src/main/java/org/fdroid/fdroid/AppDetails2.java
@@ -310,7 +310,7 @@ public class AppDetails2 extends AppCompatActivity implements ShareChooserDialog
shareIntent.putExtra(Intent.EXTRA_SUBJECT, app.name);
shareIntent.putExtra(Intent.EXTRA_TEXT, app.name + " (" + app.summary + ") - https://f-droid.org/app/" + app.packageName);
- boolean showNearbyItem = app.isInstalled() && fdroidApp.bluetoothAdapter != null;
+ boolean showNearbyItem = app.isInstalled(getApplicationContext()) && fdroidApp.bluetoothAdapter != null;
ShareChooserDialog.createChooser((CoordinatorLayout) findViewById(R.id.rootCoordinator), this, this, shareIntent, showNearbyItem);
return true;
} else if (item.getItemId() == R.id.action_ignore_all) {
@@ -778,8 +778,12 @@ public class AppDetails2 extends AppCompatActivity implements ShareChooserDialog
Apk apk = app.installedApk;
if (apk == null) {
// TODO ideally, app would be refreshed immediately after install, then this
- // workaround would be unnecessary
- apk = getInstalledApk();
+ // workaround would be unnecessary - unless it is a media file
+ apk = app.getMediaApkifInstalled(getApplicationContext());
+ if (apk == null) {
+ // When the app isn't a media file - the above workaround refers to this.
+ apk = getInstalledApk();
+ }
app.installedApk = apk;
}
Installer installer = InstallerFactory.create(this, apk);
diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java
index 94cca9eef..8047e0d0a 100644
--- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java
+++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java
@@ -39,12 +39,12 @@ import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
+
import com.nostra13.universalimageloader.cache.disc.impl.LimitedAgeDiskCache;
import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
-import info.guardianproject.netcipher.NetCipher;
-import info.guardianproject.netcipher.proxy.OrbotHelper;
+
import org.acra.ACRA;
import org.acra.ReportingInteractionMode;
import org.acra.annotation.ReportsCrashes;
@@ -55,12 +55,11 @@ import org.fdroid.fdroid.compat.PRNGFixes;
import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.data.InstalledAppProviderService;
import org.fdroid.fdroid.data.Repo;
-import org.fdroid.fdroid.installer.ApkFileProvider;
import org.fdroid.fdroid.data.SanitizedFile;
+import org.fdroid.fdroid.installer.ApkFileProvider;
import org.fdroid.fdroid.installer.InstallHistoryService;
import org.fdroid.fdroid.net.ImageLoaderForUIL;
import org.fdroid.fdroid.net.WifiStateChangeService;
-import sun.net.www.protocol.bluetooth.Handler;
import java.io.IOException;
import java.net.URL;
@@ -69,6 +68,10 @@ import java.net.URLStreamHandlerFactory;
import java.security.Security;
import java.util.List;
+import info.guardianproject.netcipher.NetCipher;
+import info.guardianproject.netcipher.proxy.OrbotHelper;
+import sun.net.www.protocol.bluetooth.Handler;
+
@ReportsCrashes(mailTo = "reports@f-droid.org",
mode = ReportingInteractionMode.DIALOG,
reportDialogClass = org.fdroid.fdroid.acra.CrashReportActivity.class,
@@ -80,6 +83,8 @@ public class FDroidApp extends Application {
public static final String SYSTEM_DIR_NAME = Environment.getRootDirectory().getAbsolutePath();
+ private static FDroidApp instance;
+
// for the local repo on this device, all static since there is only one
public static volatile int port;
public static volatile String ipAddressString;
@@ -204,6 +209,7 @@ public class FDroidApp extends Application {
@Override
public void onCreate() {
super.onCreate();
+ instance = this;
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
@@ -449,4 +455,8 @@ public class FDroidApp extends Application {
public static boolean isUsingTor() {
return useTor;
}
+
+ public static Context getInstance() {
+ return instance;
+ }
}
diff --git a/app/src/main/java/org/fdroid/fdroid/NotificationHelper.java b/app/src/main/java/org/fdroid/fdroid/NotificationHelper.java
index 84bdc76ad..00ce60cbe 100644
--- a/app/src/main/java/org/fdroid/fdroid/NotificationHelper.java
+++ b/app/src/main/java/org/fdroid/fdroid/NotificationHelper.java
@@ -248,7 +248,7 @@ class NotificationHelper {
case Downloading:
return app.name;
case ReadyToInstall:
- return context.getString(app.isInstalled() ? R.string.notification_title_single_ready_to_install_update : R.string.notification_title_single_ready_to_install);
+ return context.getString(app.isInstalled(context) ? R.string.notification_title_single_ready_to_install_update : R.string.notification_title_single_ready_to_install);
case Installing:
return app.name;
case Installed:
@@ -264,7 +264,7 @@ class NotificationHelper {
case UpdateAvailable:
return app.name;
case Downloading:
- return context.getString(app.isInstalled() ? R.string.notification_content_single_downloading_update : R.string.notification_content_single_downloading, app.name);
+ return context.getString(app.isInstalled(context) ? R.string.notification_content_single_downloading_update : R.string.notification_content_single_downloading, app.name);
case ReadyToInstall:
return app.name;
case Installing:
@@ -282,9 +282,9 @@ class NotificationHelper {
case UpdateAvailable:
return context.getString(R.string.notification_title_summary_update_available);
case Downloading:
- return context.getString(app.isInstalled() ? R.string.notification_title_summary_downloading_update : R.string.notification_title_summary_downloading);
+ return context.getString(app.isInstalled(context) ? R.string.notification_title_summary_downloading_update : R.string.notification_title_summary_downloading);
case ReadyToInstall:
- return context.getString(app.isInstalled() ? R.string.notification_title_summary_ready_to_install_update : R.string.notification_title_summary_ready_to_install);
+ return context.getString(app.isInstalled(context) ? R.string.notification_title_summary_ready_to_install_update : R.string.notification_title_summary_ready_to_install);
case Installing:
return context.getString(R.string.notification_title_summary_installing);
case Installed:
diff --git a/app/src/main/java/org/fdroid/fdroid/data/Apk.java b/app/src/main/java/org/fdroid/fdroid/data/Apk.java
index cbbbee3ad..28c12bf29 100644
--- a/app/src/main/java/org/fdroid/fdroid/data/Apk.java
+++ b/app/src/main/java/org/fdroid/fdroid/data/Apk.java
@@ -2,15 +2,21 @@ package org.fdroid.fdroid.data;
import android.annotation.TargetApi;
import android.content.ContentValues;
+import android.content.Context;
import android.content.pm.PackageInfo;
import android.database.Cursor;
import android.os.Build;
+import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
+import android.text.TextUtils;
+import android.webkit.MimeTypeMap;
+
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
+
import org.fdroid.fdroid.RepoXMLHandler;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Schema.ApkTable.Cols;
@@ -470,4 +476,55 @@ public class Apk extends ValueObject implements Comparable, Parcelable {
}
requestedPermissions = set.toArray(new String[set.size()]);
}
+
+ /**
+ * Get the install path for a "non-apk" media file
+ * Defaults to {@link android.os.Environment#DIRECTORY_DOWNLOADS}
+ *
+ * @return the install path for this {@link Apk}
+ */
+
+ public File getMediaInstallPath(Context context) {
+ File path = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS); // Default for all other non-apk/media files
+ String fileExtension = MimeTypeMap.getFileExtensionFromUrl(this.getUrl());
+ if (TextUtils.isEmpty(fileExtension)) return path;
+ MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
+ String[] mimeType = mimeTypeMap.getMimeTypeFromExtension(fileExtension).split("/");
+ String topLevelType;
+ if (mimeType.length == 0) {
+ topLevelType = "";
+ } else {
+ topLevelType = mimeType[0];
+ }
+ if ("audio".equals(topLevelType)) {
+ path = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_MUSIC);
+ } else if ("image".equals(topLevelType)) {
+ path = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES);
+ } else if ("video".equals(topLevelType)) {
+ path = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_MOVIES);
+ // TODO support OsmAnd map files, other map apps?
+ //} else if (mimeTypeMap.hasExtension("map")) { // OsmAnd map files
+ //} else if (this.apkName.matches(".*.ota_[0-9]*.zip")) { // Over-The-Air update ZIP files
+ } else if (this.apkName.endsWith(".zip")) { // Over-The-Air update ZIP files
+ path = new File(context.getApplicationInfo().dataDir + "/ota");
+ }
+ return path;
+ }
+
+ public boolean isMediaInstalled(Context context) {
+ return new File(this.getMediaInstallPath(context), this.apkName).isFile();
+ }
+
+ /**
+ * Default to assuming apk if apkName is null since that has always been
+ * what we had.
+ * @return true if this is an apk instead of a non-apk/media file
+ */
+ public boolean isApk() {
+ return this.apkName == null || this.apkName.endsWith(".apk");
+ }
}
diff --git a/app/src/main/java/org/fdroid/fdroid/data/App.java b/app/src/main/java/org/fdroid/fdroid/data/App.java
index e8e7621bc..58f209a36 100644
--- a/app/src/main/java/org/fdroid/fdroid/data/App.java
+++ b/app/src/main/java/org/fdroid/fdroid/data/App.java
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.fdroid.fdroid.AppFilter;
import org.fdroid.fdroid.FDroidApp;
+import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols;
import org.xmlpull.v1.XmlPullParser;
@@ -42,6 +43,7 @@ import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -853,8 +855,38 @@ public class App extends ValueObject implements Comparable, Parcelable {
return values;
}
- public boolean isInstalled() {
- return installedVersionCode > 0;
+ public boolean isInstalled(Context context) {
+ return isMediaInstalled(context) || installedVersionCode > 0;
+ }
+
+ public boolean isMediaInstalled(Context context) {
+ return getMediaApkifInstalled(context) != null;
+ }
+
+ /**
+ * Gets the installed media apk from all the apks of this {@link App}, if any.
+ *
+ * @return The installed media {@link Apk} if it exists, null otherwise.
+ */
+ public Apk getMediaApkifInstalled(Context context) {
+ // This is always null for media files. We could skip the code below completely if it wasn't
+ if (this.installedApk != null && !this.installedApk.isApk() && this.installedApk.isMediaInstalled(context)) {
+ return this.installedApk;
+ }
+ // This code comes from AppDetailsRecyclerViewAdapter
+ final List apks = ApkProvider.Helper.findByPackageName(context, this.packageName);
+ for (final Apk apk : apks) {
+ boolean allowByCompatability = apk.compatible || Preferences.get().showIncompatibleVersions();
+ boolean allowBySig = this.installedSig == null || TextUtils.equals(this.installedSig, apk.sig);
+ if (allowByCompatability && allowBySig) {
+ if (!apk.isApk()) {
+ if (apk.isMediaInstalled(context)) {
+ return apk;
+ }
+ }
+ }
+ }
+ return null;
}
/**
@@ -966,11 +998,10 @@ public class App extends ValueObject implements Comparable, Parcelable {
return 0;
}
- /**
- * System apps aren't uninstallable, only their updates are.
- */
public boolean isUninstallable(Context context) {
- if (this.isInstalled()) {
+ if (this.isMediaInstalled(context)) {
+ return true;
+ } else if (this.isInstalled(context)) {
PackageManager pm = context.getPackageManager();
ApplicationInfo appInfo;
try {
@@ -980,8 +1011,9 @@ public class App extends ValueObject implements Comparable, Parcelable {
return false;
}
+ // System apps aren't uninstallable.
final boolean isSystem = (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- return !isSystem && this.isInstalled();
+ return !isSystem && this.isInstalled(context);
} else {
return false;
}
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/DummyInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/FileInstaller.java
similarity index 50%
rename from app/src/main/java/org/fdroid/fdroid/installer/DummyInstaller.java
rename to app/src/main/java/org/fdroid/fdroid/installer/FileInstaller.java
index ee5658391..edb82acd4 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/DummyInstaller.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/FileInstaller.java
@@ -19,15 +19,16 @@
package org.fdroid.fdroid.installer;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import org.fdroid.fdroid.data.Apk;
-public class DummyInstaller extends Installer {
+public class FileInstaller extends Installer {
- public DummyInstaller(Context context, Apk apk) {
+ public FileInstaller(Context context, Apk apk) {
super(context, apk);
}
@@ -43,17 +44,41 @@ public class DummyInstaller extends Installer {
@Override
public void installPackage(Uri localApkUri, Uri downloadUri) {
- // Do nothing
+ installPackageInternal(localApkUri, downloadUri);
}
@Override
protected void installPackageInternal(Uri localApkUri, Uri downloadUri) {
- // Do nothing
+ Intent installIntent = new Intent(context, FileInstallerActivity.class);
+ installIntent.setAction(FileInstallerActivity.ACTION_INSTALL_FILE);
+ installIntent.putExtra(Installer.EXTRA_DOWNLOAD_URI, downloadUri);
+ installIntent.putExtra(Installer.EXTRA_APK, apk);
+ installIntent.setData(localApkUri);
+
+ PendingIntent installPendingIntent = PendingIntent.getActivity(
+ context.getApplicationContext(),
+ localApkUri.hashCode(),
+ installIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_USER_INTERACTION,
+ installPendingIntent);
}
@Override
protected void uninstallPackage() {
- // Do nothing
+ sendBroadcastUninstall(Installer.ACTION_UNINSTALL_STARTED);
+
+ Intent uninstallIntent = new Intent(context, FileInstallerActivity.class);
+ uninstallIntent.setAction(FileInstallerActivity.ACTION_UNINSTALL_FILE);
+ uninstallIntent.putExtra(Installer.EXTRA_APK, apk);
+ PendingIntent uninstallPendingIntent = PendingIntent.getActivity(
+ context.getApplicationContext(),
+ apk.packageName.hashCode(),
+ uninstallIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ sendBroadcastUninstall(Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
}
@Override
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/FileInstallerActivity.java b/app/src/main/java/org/fdroid/fdroid/installer/FileInstallerActivity.java
new file mode 100644
index 000000000..2e51f267f
--- /dev/null
+++ b/app/src/main/java/org/fdroid/fdroid/installer/FileInstallerActivity.java
@@ -0,0 +1,179 @@
+package org.fdroid.fdroid.installer;
+
+import android.Manifest;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
+import android.view.ContextThemeWrapper;
+import android.widget.Toast;
+
+import org.apache.commons.io.FileUtils;
+import org.fdroid.fdroid.FDroidApp;
+import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.data.Apk;
+
+import java.io.File;
+import java.io.IOException;
+
+public class FileInstallerActivity extends FragmentActivity {
+
+ private static final String TAG = "FileInstallerActivity";
+ private static final int MY_PERMISSIONS_REQUEST_STORAGE = 1;
+
+ static final String ACTION_INSTALL_FILE
+ = "org.fdroid.fdroid.installer.FileInstaller.action.INSTALL_PACKAGE";
+ static final String ACTION_UNINSTALL_FILE
+ = "org.fdroid.fdroid.installer.FileInstaller.action.UNINSTALL_PACKAGE";
+
+ private FileInstallerActivity activity;
+
+ // for the broadcasts
+ private FileInstaller installer;
+
+ private Apk apk;
+ private Uri localApkUri;
+ private Uri downloadUri;
+
+ private int act = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ activity = this;
+ Intent intent = getIntent();
+ String action = intent.getAction();
+ localApkUri = intent.getData();
+ downloadUri = intent.getParcelableExtra(Installer.EXTRA_DOWNLOAD_URI);
+ apk = intent.getParcelableExtra(Installer.EXTRA_APK);
+ installer = new FileInstaller(this, apk);
+ if (ACTION_INSTALL_FILE.equals(action)) {
+ if (hasStoragePermission()) {
+ installPackage(localApkUri, downloadUri, apk);
+ } else {
+ requestPermission();
+ act = 1;
+ }
+ } else if (ACTION_UNINSTALL_FILE.equals(action)) {
+ if (hasStoragePermission()) {
+ uninstallPackage(apk);
+ } else {
+ requestPermission();
+ act = 2;
+ }
+ } else {
+ throw new IllegalStateException("Intent action not specified!");
+ }
+
+ }
+
+ private boolean hasStoragePermission() {
+ return ContextCompat.checkSelfPermission(this,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private void requestPermission() {
+ if (!hasStoragePermission()) {
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
+ showDialog();
+ } else {
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ MY_PERMISSIONS_REQUEST_STORAGE);
+ }
+ }
+ }
+
+ private void showDialog() {
+
+ // hack to get theme applied (which is not automatically applied due to activity's Theme.NoDisplay
+ ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(theme);
+ builder.setMessage(R.string.app_permission_storage)
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ ActivityCompat.requestPermissions(activity,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ MY_PERMISSIONS_REQUEST_STORAGE);
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ if (act == 1) {
+ installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED);
+ } else if (act == 2) {
+ installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
+ }
+ finish();
+ }
+ })
+ .create().show();
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String[] permissions, int[] grantResults) {
+ switch (requestCode) {
+ case MY_PERMISSIONS_REQUEST_STORAGE:
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (act == 1) {
+ installPackage(localApkUri, downloadUri, apk);
+ } else if (act == 2) {
+ uninstallPackage(apk);
+ }
+ } else {
+ if (act == 1) {
+ installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED);
+ } else if (act == 2) {
+ installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
+ }
+ }
+ finish();
+ }
+ }
+
+ private void installPackage(Uri localApkUri, Uri downloadUri, Apk apk) {
+ Utils.debugLog(TAG, "Installing: " + localApkUri.getPath());
+ installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_STARTED);
+ File path = apk.getMediaInstallPath(activity.getApplicationContext());
+ path.mkdirs();
+ try {
+ FileUtils.copyFileToDirectory(new File(localApkUri.getPath()), path);
+ } catch (IOException e) {
+ Utils.debugLog(TAG, "Failed to copy: " + e.getMessage());
+ installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED);
+ }
+ if (apk.isMediaInstalled(activity.getApplicationContext())) { // Copying worked
+ Utils.debugLog(TAG, "Copying worked: " + localApkUri.getPath());
+ Toast.makeText(this, String.format(this.getString(R.string.app_installed_media), path.toString()),
+ Toast.LENGTH_LONG).show();
+ installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_COMPLETE);
+ } else {
+ installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED);
+ }
+ finish();
+ }
+
+ private void uninstallPackage(Apk apk) {
+ if (apk.isMediaInstalled(activity.getApplicationContext())) {
+ File file = new File(apk.getMediaInstallPath(activity.getApplicationContext()), apk.apkName);
+ if (!file.delete()) {
+ installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
+ return;
+ }
+ }
+ installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_COMPLETE);
+ finish();
+ }
+}
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java
index b228a2fea..c8589d058 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java
@@ -343,7 +343,7 @@ public class InstallManagerService extends Service {
appUpdateStatusManager.updateApk(downloadUrl, AppUpdateStatusManager.Status.Installed, null);
Apk apkComplete = appUpdateStatusManager.getApk(downloadUrl);
- if (apkComplete != null) {
+ if (apkComplete != null && apkComplete.isApk()) {
try {
PackageManagerCompat.setInstaller(context, getPackageManager(), apkComplete.packageName);
} catch (SecurityException e) {
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java
index 066cec1d3..072b1de74 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java
@@ -22,8 +22,7 @@ package org.fdroid.fdroid.installer;
import android.content.Context;
import android.text.TextUtils;
-import android.widget.Toast;
-import org.fdroid.fdroid.R;
+
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Apk;
@@ -47,11 +46,9 @@ public class InstallerFactory {
Installer installer;
- if (apk.apkName != null && !apk.apkName.endsWith(".apk")) {
- String msg = context.getString(R.string.install_error_not_yet_supported, apk.apkName);
- Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
- Utils.debugLog(TAG, msg);
- installer = new DummyInstaller(context, apk);
+ if (!apk.isApk()) {
+ Utils.debugLog(TAG, "Using FileInstaller for non-apk file");
+ installer = new FileInstaller(context, apk);
} else if (PrivilegedInstaller.isDefault(context)) {
Utils.debugLog(TAG, "privileged extension correctly installed -> PrivilegedInstaller");
installer = new PrivilegedInstaller(context, apk);
diff --git a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java
index 46bb5cb39..1ddb1fd80 100644
--- a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java
+++ b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java
@@ -478,14 +478,14 @@ public class AppDetailsRecyclerViewAdapter
if (callbacks.isAppDownloading()) {
buttonPrimaryView.setText(R.string.downloading);
buttonPrimaryView.setEnabled(false);
- } else if (!app.isInstalled() && suggestedApk != null) {
+ } else if (!app.isInstalled(context) && suggestedApk != null) {
// Check count > 0 due to incompatible apps resulting in an empty list.
callbacks.disableAndroidBeam();
// Set Install button and hide second button
buttonPrimaryView.setText(R.string.menu_install);
buttonPrimaryView.setOnClickListener(onInstallClickListener);
buttonPrimaryView.setEnabled(true);
- } else if (app.isInstalled()) {
+ } else if (app.isInstalled(context)) {
callbacks.enableAndroidBeam();
if (app.canAndWantToUpdate(context) && suggestedApk != null) {
buttonPrimaryView.setText(R.string.menu_upgrade);
diff --git a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java
index 0fd8f9f7c..93c2f7489 100644
--- a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java
+++ b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java
@@ -322,7 +322,7 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
}
protected AppListItemState getViewStateReadyToInstall(@NonNull App app) {
- int actionButtonLabel = app.isInstalled()
+ int actionButtonLabel = app.isInstalled(activity.getApplicationContext())
? R.string.app__install_downloaded_update
: R.string.menu_install;
diff --git a/app/src/main/java/org/fdroid/fdroid/views/apps/StandardAppListItemController.java b/app/src/main/java/org/fdroid/fdroid/views/apps/StandardAppListItemController.java
index 35b102795..dcfa73e9d 100644
--- a/app/src/main/java/org/fdroid/fdroid/views/apps/StandardAppListItemController.java
+++ b/app/src/main/java/org/fdroid/fdroid/views/apps/StandardAppListItemController.java
@@ -35,7 +35,7 @@ public class StandardAppListItemController extends AppListItemController {
private CharSequence getStatusText(@NonNull App app) {
if (!app.compatible) {
return activity.getString(R.string.app_incompatible);
- } else if (app.isInstalled()) {
+ } else if (app.isInstalled(activity.getApplicationContext())) {
if (app.canAndWantToUpdate(activity)) {
return activity.getString(R.string.app_version_x_available, app.getSuggestedVersionName());
} else {
@@ -47,7 +47,7 @@ public class StandardAppListItemController extends AppListItemController {
}
private boolean shouldShowInstall(@NonNull App app) {
- boolean installable = app.canAndWantToUpdate(activity) || !app.isInstalled();
+ boolean installable = app.canAndWantToUpdate(activity) || !app.isInstalled(activity.getApplicationContext());
boolean shouldAllow = app.compatible && !app.isFiltered();
return installable && shouldAllow;
diff --git a/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java b/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java
index 3ac28cf41..47704dfd9 100644
--- a/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java
+++ b/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java
@@ -16,9 +16,11 @@ import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.ViewGroup;
import android.widget.Toast;
+
import com.ashokvarma.bottomnavigation.BadgeItem;
import com.ashokvarma.bottomnavigation.BottomNavigationBar;
import com.ashokvarma.bottomnavigation.BottomNavigationItem;
+
import org.fdroid.fdroid.AppDetails2;
import org.fdroid.fdroid.AppUpdateStatusManager;
import org.fdroid.fdroid.FDroidApp;
@@ -388,5 +390,4 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
}
}
};
-
}
\ No newline at end of file
diff --git a/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java b/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java
index c38264c10..664ed1d08 100644
--- a/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java
+++ b/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java
@@ -346,7 +346,7 @@ public class SwapAppsView extends ListView implements
btnInstall.setVisibility(View.VISIBLE);
statusIncompatible.setVisibility(View.GONE);
statusInstalled.setVisibility(View.GONE);
- } else if (app.isInstalled()) {
+ } else if (app.isInstalled(getContext())) {
btnInstall.setVisibility(View.GONE);
statusIncompatible.setVisibility(View.GONE);
statusInstalled.setVisibility(View.VISIBLE);
diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml
index 28cfd1ebd..2dd5c2763 100644
--- a/app/src/main/res/values-af/strings.xml
+++ b/app/src/main/res/values-af/strings.xml
@@ -489,7 +489,6 @@
- Kyk na alle %d
- Lêertipe kan nog nie geïnstalleer word nie: %s
Vandag opgedateer
- %1$s dag gelede opgedateer
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 0161c72fd..aa67ad5c6 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -460,7 +460,6 @@
أضف مصادر أخرى للتطبيقات
الرخصة: %s
الملف المطلوب غير موجود.
- نوع الملف لا يمكن بعد تثبيته: %s
جاري التحميل، اكتمل %1$d%%
التصنيف %1$s
diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml
index 406e60dd3..ee77d857e 100644
--- a/app/src/main/res/values-ast/strings.xml
+++ b/app/src/main/res/values-ast/strings.xml
@@ -460,7 +460,6 @@
- Ver toles %d
- Entá nun puede instalase\'l tipu de ficheru: %s
¿Nun tienes Internet? ¡Descarga apps de xente cercana!
Alcontrar persones cercanes
Les dos partes necesiten %1$s pa usar la cercanía.
diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml
index ba5e514f3..81340becb 100644
--- a/app/src/main/res/values-be/strings.xml
+++ b/app/src/main/res/values-be/strings.xml
@@ -489,7 +489,6 @@
Адмяніць спампоўку
Спампоўка, %1$d%% скончана
Ліцэнзія: %s
- Тып файла не можа быць усталяваны: %s
- %1$d абнаўленне
- %1$d абнаўленні
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index f408d26fa..d249a3d53 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -479,7 +479,6 @@
- Veure\'ls tots %d
- El tipus de fitxer no es pot instal·lar: %s
Descarregant, %1$d%% completat
- %1$d actualització
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index 138214d62..eb2cd79d3 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -431,7 +431,6 @@
- Vis alle %d
- Kan endnu ikke installere filtypen: %s
Ingen internet? Hent apps fra folk i nærheden af dig!
Find folk i nærheden af mig
Begge parter skal have %1$s for at benytte i nærheden.
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index c51ca7c35..ad2b5443e 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -481,7 +481,6 @@
Herunterladen abbrechen
wird heruntergeladen, %1$d%% vervollständigt
Lizenz: %s
- Dateityp kann nicht installiert werden: %s
- +%1$d weitere …
- +%1$d weitere …
diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml
index 9855fb756..eed014584 100644
--- a/app/src/main/res/values-eo/strings.xml
+++ b/app/src/main/res/values-eo/strings.xml
@@ -456,7 +456,6 @@
Nuligi elŝutadon
Elŝutado, %1$d%% kompleta
Permesilo: %s
- Dosier-tipo ne povas ankoraŭ esti instalita: %s
- +%1$d pli…
- +1$d pli…
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 8260d85fc..568ff8bda 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -473,7 +473,6 @@
Cancelar descarga
Descargando, %1$d%% completado
Licencia: %s
- No se puede instalar el tipo de fichero: %s
- +%1$d más…
- +%1$d más…
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index 67f62912f..8768a0074 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -488,7 +488,6 @@
Ez dago kategoriarik erakusteko
- Fitxategi mota ezin da oraindik instalatu: %s
Internetik ez? Eskuratu aplikazioak inguruko jendearengandik!
Aurkitu inguruko jendea
Biek %1$s erabili behar dute inguruan elkar aurkitzeko.
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index a142cf845..d3e782d86 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -436,7 +436,6 @@
- %1$sd روز پیش بهروز شده
پروانه: %s
- این نوع فایل نمی تواند نصب شود: %s
- +%1$d بیشتر…
- +%1$d بیشتر…
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 249f115d0..d98bb1643 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -496,7 +496,6 @@
Mis à jour aujourd\'hui
Annuler le téléchargement
Licence: %s
- Ce type de fichier ne peut être installé pour l\'instant: %s
Téléchargement, %1$d%% complété
- +%1$d autre…
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index 02cbea67a..e605ff245 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -409,7 +409,6 @@
- הצגת כל ה־%d
- עדיין לא ניתן להתקין את סוג הקובץ: %s
אין חיבור לאינטרנט? ניתן להוריד יישומונים מאנשים בקרבתך!
חיפוש אנשים בקרבתי
שני הצדדים צריכים %1$s כדי להשתמש בקרבה.
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
index 420da896d..ba328a144 100644
--- a/app/src/main/res/values-id/strings.xml
+++ b/app/src/main/res/values-id/strings.xml
@@ -466,7 +466,6 @@
Batalkan unduhan
Mengunduh, %1$d%% selesai
Lisensi: %s
- Tipe berkas belum dapat dipasang: %s
- +%1$d lainnya…
diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml
index 1f6dffe99..b1b349700 100644
--- a/app/src/main/res/values-is/strings.xml
+++ b/app/src/main/res/values-is/strings.xml
@@ -502,7 +502,6 @@
Þetta forrit er með eiginleika sem ekki er víst að þér líki við.
Neikvæðir eiginleikar
Umbeðin skrá fannst ekki.
- Skráartegundina er ekki enn hægt að setja upp: %s
Ekkert Internet? Fáðu forrit frá fólki í nágrenninu!
Finna fólk nálægt mér
Báðir aðilar þurfa %1$s til að nota í nálægð.
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index cdb0e45b7..5a56bcb37 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -437,7 +437,6 @@
ダウンロードをキャンセル
ダウンロード中、%1$d%% 完了
ライセンス: %s
- ファイルの種類はまだインストールできません: %s
- +%1$d さらに…
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index 73ebdb994..46202236a 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -293,7 +293,6 @@
അഭിനന്ദനങ്ങൾ! നിങ്ങളുടെ പ്രയോഗങ്ങളെല്ലാം കാലികമാണ് (അല്ലെങ്കിൽ നിങ്ങളുടെ സംഭരണികള് കാലഹരണപ്പെട്ടതാണ്).
ഒരു അജ്ഞാത പിശക് കാരണം സ്ഥാപിക്കല് പരാജയപ്പെട്ടു
കാരണം ഒരു അജ്ഞാത പിശക് ഒഴിവാക്കല് പരാജയപ്പെട്ടു
- ഫയൽ തരം സ്ഥാപിക്കാന് കഴിയില്ല: %s
വിപുലീകരണത്തിന്റെ ഒപ്പ് തെറ്റാണ്! ഒരു ബഗ് റിപ്പോർട്ട് സൃഷ്ടിക്കുക!
പ്രത്യേക അനുമതികൾ വിപുലീകരണത്തിന് നൽകിയിട്ടില്ല! ഒരു ബഗ് റിപ്പോർട്ട് സൃഷ്ടിക്കുക!
പ്രത്യേക അനുമതി ആവശ്യമുള്ള എഫ്-ഡ്രോയ്ഡ് വിപുലീകരണം സ്ഥാപിച്ചു
diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml
index 4d87ba6e3..0ca8934f0 100644
--- a/app/src/main/res/values-nb/strings.xml
+++ b/app/src/main/res/values-nb/strings.xml
@@ -479,7 +479,6 @@
Laster ned, %1$d%% fullført
Lisens: %s
- Filtypen kan ikke installeres enda: %s
- +%1$d til…
- +%1$d til…
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index c5a6cd838..15b5683eb 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -450,7 +450,6 @@
Annuleer download
Downloaden, %1$d%% voltooid
Licentie: %s
- Bestandstype kan nog niet geïnstalleerd worden: %s
- +%1$d meer…
- +%1$d meer…
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index a23360d15..8c05a31f6 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -467,7 +467,6 @@
Anuluj pobieranie
Pobieranie, %1$d%% ukończono
Licencja: %s
- Typ pliku nie może być jeszcze zainstalowany: %s
- %1$d Aktualizacja
- %1$d Aktualizacje
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 2d09b04f9..9138f404b 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -482,7 +482,6 @@
Cancelar download
Baixando, %1$d%% completo
Licença: %s
- Tipo de arquivo ainda não pode ser instalado: %s
- +%1$d mais…
- +%1$d mais…
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index 1565628b9..17bc8d7a7 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -489,7 +489,6 @@
Vídeo
Licença: %s
- Este tipo de ficheiro ainda não pode ser instalado: %s
A descarregar, %1$d%% completo
Cancelar descarga
de %s
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index 717d19446..4081523e8 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -438,7 +438,6 @@
Anulează descărcarea
Video
Licență: %s
- Acest tip de fișier nu se poate încă instala: %s
Nu ai acces la Internet? Descarcă aplicații de la persoanele de lângă tine!
Găsește persoane lângă mine
Ambele persoane au nevoie de %1$s pentru a putea folosi această opțiune.
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 476496c10..21e6343af 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -486,7 +486,6 @@
Отменить скачивание
Скачано %1$d%%
Лицензия: %s
- Тип файла пока не может быть установлен: %s
- %1$d обновление
- %1$d обновления
diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml
index 748f16326..688e54794 100644
--- a/app/src/main/res/values-sc/strings.xml
+++ b/app/src/main/res/values-sc/strings.xml
@@ -493,7 +493,6 @@
Firma s\'iscarrigamentu
Iscarrighende, %1$d%% cumpridu
Litzèntzia: %s
- Sa casta de documentu non podet èssere installada: %s
- +%1$d àtera…
- àteras +%1$d…
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 2bc358eec..7f714cb58 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -462,7 +462,6 @@
- Dostupných %d aplikácií
- Typ súboru zatiaľ nie je možné nainštalovať: %s
Žiadny Internet? Stiahnite si aplikácie od ľudí v blízkosti vás!
Nájdi ľudí blízko mňa
Obidve strany potrebujú %1$s na použitie v okolí.
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 10b7bf932..32ccc76d4 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -486,7 +486,6 @@
Откажи преузимање
Лиценца: %s
Преузимам, %1$d%% завршено
- Тип фајла још не може бити инсталиран: %s
од %s
- +још %1$d…
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 229c66120..55a75502c 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -466,7 +466,6 @@
Uppdaterad idag
Avbryt hämtning
Licens: %s
- Filtypen kan inte ännu installeras: %s
Hämtar, %1$d%% färdigt
- +%1$d till…
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 3d7ccde7f..cca8966f8 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -463,7 +463,6 @@
İndirmeyi iptal et
İndiriliyor, %%%1$d tamamlandı
Lisans: %s
- Dosya türü henüz kurulamaz: %s
- +%1$d daha…
- +%1$d daha…
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 9cf386102..58a469fba 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -466,7 +466,6 @@
- Усіх %d застосунків
- Цей вид файлу не може бути встановленим: %s
Обидві частини потребують %1$s для користання поблизу.
Стягування, %1$d%% записано
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 2f2f734c9..2c6485fd0 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -388,7 +388,6 @@
- 查看全部 %d
- 文件类型无法安装:%s
没有连上互联网?请从附近的 F-Droid 用户那里获取应用!
查找附近的人
双方都需要 %1$s 才可使用附近的人功能。
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 5f020614e..6afe4a00e 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -448,7 +448,6 @@
取消下載
正在下载的 %1$d%% 完成
授權:%s
- 還未安裝檔案類型:%s
- +%1$d 更多…
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4a8e56659..a907ea613 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -73,6 +73,8 @@
Added on %s
Cancel download
Update
+ File installed to %s
+ F-Droid needs the storage permission to install this to storage. Please allow it on the next screen to proceed with installation.
Downloading %1$s
%1$s installed
@@ -330,7 +332,6 @@
Failed to install due to an unknown error
Failed to uninstall due to an unknown error
- File type cannot yet be installed: %s
The signature of the extension is wrong! Please create a bug
report!