diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/SwapWorkflowActivity.java b/app/src/full/java/org/fdroid/fdroid/nearby/SwapWorkflowActivity.java index 1f744c1d8..0f8418f53 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/SwapWorkflowActivity.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/SwapWorkflowActivity.java @@ -2,6 +2,7 @@ package org.fdroid.fdroid.nearby; import static org.fdroid.fdroid.views.main.MainActivity.ACTION_REQUEST_SWAP; +import android.Manifest; import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -9,6 +10,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.graphics.LightingColorFilter; import android.net.Uri; import android.net.wifi.WifiManager; @@ -33,6 +35,8 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -133,6 +137,11 @@ public class SwapWorkflowActivity extends AppCompatActivity { private final CompositeDisposable compositeDisposable = new CompositeDisposable(); + private final ActivityResultLauncher requestPermissionLauncher = + registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { + if (isGranted) sendFDroidBluetooth(); + }); + public static void requestSwap(Context context, String repo) { requestSwap(context, Uri.parse(repo)); } @@ -671,10 +680,14 @@ public class SwapWorkflowActivity extends AppCompatActivity { public void sendFDroidBluetooth() { if (bluetoothAdapter.isEnabled()) { sendFDroidApk(); - } else { - Intent discoverBt = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); - discoverBt.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120); - startActivityForResult(discoverBt, REQUEST_BLUETOOTH_ENABLE_FOR_SEND); + } else if (Build.VERSION.SDK_INT >= 31) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED) { + Intent discoverBt = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); + discoverBt.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120); + startActivityForResult(discoverBt, REQUEST_BLUETOOTH_ENABLE_FOR_SEND); + } else { + requestPermissionLauncher.launch(Manifest.permission.BLUETOOTH_CONNECT); + } } } diff --git a/app/src/main/java/org/fdroid/fdroid/AppUpdateStatusManager.java b/app/src/main/java/org/fdroid/fdroid/AppUpdateStatusManager.java index b22fdfd92..dbb755aa0 100644 --- a/app/src/main/java/org/fdroid/fdroid/AppUpdateStatusManager.java +++ b/app/src/main/java/org/fdroid/fdroid/AppUpdateStatusManager.java @@ -396,11 +396,14 @@ public final class AppUpdateStatusManager { synchronized (appMapping) { isBatchUpdating = true; try { + int num = 0; for (UpdatableApp app : canUpdate) { Repository repo = FDroidApp.getRepoManager(context).getRepository(app.getUpdate().getRepoId()); + if (repo == null) continue; // if repo is gone, it was just deleted, so skip app addApk(new App(app), new Apk(app.getUpdate(), repo), Status.UpdateAvailable, null); + num++; } - setNumUpdatableApps(canUpdate.size()); + setNumUpdatableApps(num); } finally { isBatchUpdating = false; } diff --git a/app/src/main/java/org/fdroid/fdroid/UpdateService.java b/app/src/main/java/org/fdroid/fdroid/UpdateService.java index 9b396938d..5ee6a8d6e 100644 --- a/app/src/main/java/org/fdroid/fdroid/UpdateService.java +++ b/app/src/main/java/org/fdroid/fdroid/UpdateService.java @@ -535,6 +535,7 @@ public class UpdateService extends JobIntentService { return Single.fromCallable(() -> updateChecker.getUpdatableApps(releaseChannels)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) + .doOnError(throwable -> Log.e(TAG, "Error auto-downloading updates: ", throwable)) .subscribe(updatableApps -> downloadUpdates(context, updatableApps)); } diff --git a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java index 24b9d9bcc..8e0996e90 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java @@ -145,7 +145,12 @@ public abstract class Installer { } PackageManager pm = context.getPackageManager(); - String installerPackageName = pm.getInstallerPackageName(apk.packageName); + String installerPackageName = null; + try { + installerPackageName = pm.getInstallerPackageName(apk.packageName); + } catch (IllegalArgumentException e) { + Log.e(TAG, "App not installed: " + apk.packageName, e); + } if (Build.VERSION.SDK_INT >= 24 && ("com.android.packageinstaller".equals(installerPackageName) || "com.google.android.packageinstaller".equals(installerPackageName))) { diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/AppSecurityPermissions.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/AppSecurityPermissions.java index 8da2e3327..29ebd78f0 100644 --- a/app/src/main/java/org/fdroid/fdroid/privileged/views/AppSecurityPermissions.java +++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/AppSecurityPermissions.java @@ -116,7 +116,11 @@ public class AppSecurityPermissions { Drawable loadGroupIcon(Context context, PackageManager pm) { Drawable iconDrawable; if (icon != 0) { - iconDrawable = loadUnbadgedIcon(pm); + try { + iconDrawable = loadUnbadgedIcon(pm); + } catch (NullPointerException e) { // NOPMD + iconDrawable = ContextCompat.getDrawable(context, R.drawable.ic_perm_device_info); + } } else { iconDrawable = ContextCompat.getDrawable(context, R.drawable.ic_perm_device_info); } diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/UninstallDialogActivity.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/UninstallDialogActivity.java index 24505e973..0d756e00f 100644 --- a/app/src/main/java/org/fdroid/fdroid/privileged/views/UninstallDialogActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/UninstallDialogActivity.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Bundle; +import android.util.Log; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -59,7 +60,10 @@ public class UninstallDialogActivity extends FragmentActivity { appInfo = pm.getApplicationInfo(apk.packageName, PackageManager.GET_UNINSTALLED_PACKAGES); } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException("Failed to get ApplicationInfo for uninstalling"); + Log.e("UninstallDialogActivity", "Package to uninstall not found: " + apk.packageName, e); + // if it is not installed anymore, no work for us left to do. + finish(); + return; } final boolean isSystem = (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; diff --git a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsActivity.java b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsActivity.java index 50247d970..3c2cb2dc5 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsActivity.java @@ -359,6 +359,7 @@ public class AppDetailsActivity extends AppCompatActivity break; case REQUEST_PERMISSION_DIALOG: if (resultCode == AppCompatActivity.RESULT_OK) { + App app = data.getParcelableExtra(Installer.EXTRA_APP); Apk apk = data.getParcelableExtra(Installer.EXTRA_APK); InstallManagerService.queue(this, app, apk); } diff --git a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListActivity.java b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListActivity.java index b97359bf5..63597a157 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListActivity.java @@ -155,9 +155,9 @@ public class AppListActivity extends AppCompatActivity implements CategoryTextWa hiddenAppNotice = findViewById(R.id.hiddenAppNotice); hiddenAppNotice.setOnClickListener(v -> { - Intent intent = new Intent(getApplicationContext(), MainActivity.class); + Intent intent = new Intent(this, MainActivity.class); intent.putExtra(MainActivity.EXTRA_VIEW_SETTINGS, true); - getApplicationContext().startActivity(intent); + startActivity(intent); }); emptyState = findViewById(R.id.empty_state); 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 708bbafd9..84cabcec4 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 @@ -18,6 +18,7 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -496,7 +497,13 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder { if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.Installed) { Intent intent = activity.getPackageManager().getLaunchIntentForPackage(app.packageName); if (intent != null) { - activity.startActivity(intent); + try { + activity.startActivity(intent); + } catch (SecurityException e) { + Log.e(TAG, "Error starting app launch intent: ", e); + // apps that don't export their launch activity cause this + Toast.makeText(activity, R.string.app_error_open, Toast.LENGTH_SHORT).show(); + } // Once it is explicitly launched by the user, then we can pretty much forget about // any sort of notification that the app was successfully installed. It should be diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4a2b52d42..9a4e5f03a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -128,6 +128,7 @@ This often occurs with apps installed via Google Play or other sources, if they F-Droid needs the storage permission to install this to storage. Please allow it on the next screen to proceed with installation. Repository: %1$s Size: %1$s + Could not launch app. Some results were hidden based on your antifeature settings. Downloading %1$s