mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-04-19 22:37:09 -04:00
Merge branch 'foreground-background-notification' into 'master'
fix background service crashes and related fixes Closes #2645 and acra-crash-reports#584 See merge request fdroid/fdroidclient!1256
This commit is contained in:
@@ -168,9 +168,11 @@
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".nearby.TreeUriScannerIntentService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".nearby.SDCardScannerService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="false" />
|
||||
|
||||
</application>
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package org.fdroid.fdroid.nearby;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.IntentService;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -29,6 +28,8 @@ import android.os.Environment;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.JobIntentService;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.fdroid.fdroid.Utils;
|
||||
@@ -46,7 +47,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An {@link IntentService} subclass for scanning removable "external storage"
|
||||
* An {@link JobIntentService} subclass for scanning removable "external storage"
|
||||
* for F-Droid package repos, e.g. SD Cards. This is intended to support
|
||||
* sharable package repos, so it ignores non-removable storage, like the fake
|
||||
* emulated sdcard from devices with only built-in storage. This method will
|
||||
@@ -63,26 +64,24 @@ import java.util.List;
|
||||
* @see <a href="https://stackoverflow.com/a/40201333">Universal way to write to external SD card on Android</a>
|
||||
* @see <a href="https://commonsware.com/blog/2017/11/14/storage-situation-external-storage.html"> The Storage Situation: External Storage </a>
|
||||
*/
|
||||
public class SDCardScannerService extends IntentService {
|
||||
public class SDCardScannerService extends JobIntentService {
|
||||
public static final String TAG = "SDCardScannerService";
|
||||
private static final int JOB_ID = TAG.hashCode();
|
||||
|
||||
private static final String ACTION_SCAN = "org.fdroid.fdroid.nearby.SCAN";
|
||||
|
||||
private static final List<String> SKIP_DIRS = Arrays.asList(".android_secure", "LOST.DIR");
|
||||
|
||||
public SDCardScannerService() {
|
||||
super("SDCardScannerService");
|
||||
}
|
||||
|
||||
public static void scan(Context context) {
|
||||
Intent intent = new Intent(context, SDCardScannerService.class);
|
||||
intent.setAction(ACTION_SCAN);
|
||||
context.startService(intent);
|
||||
JobIntentService.enqueueWork(context, SDCardScannerService.class, JOB_ID, intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
if (intent == null || !ACTION_SCAN.equals(intent.getAction())) {
|
||||
protected void onHandleWork(@NonNull Intent intent) {
|
||||
if (!ACTION_SCAN.equals(intent.getAction())) {
|
||||
return;
|
||||
}
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
|
||||
@@ -94,7 +93,14 @@ public class SDCardScannerService extends IntentService {
|
||||
continue;
|
||||
}
|
||||
Log.i(TAG, "getExternalFilesDirs " + f);
|
||||
if (Environment.isExternalStorageRemovable(f)) {
|
||||
boolean isExternalStorageRemovable;
|
||||
try {
|
||||
isExternalStorageRemovable = Environment.isExternalStorageRemovable(f);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Utils.debugLog(TAG, e.toString());
|
||||
continue;
|
||||
}
|
||||
if (isExternalStorageRemovable) {
|
||||
String state = Environment.getExternalStorageState(f);
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
@@ -28,6 +28,8 @@ import android.os.Process;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.JobIntentService;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
@@ -54,7 +56,7 @@ import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
/**
|
||||
* An {@link IntentService} subclass for handling asynchronous scanning of a
|
||||
* An {@link JobIntentService} subclass for handling asynchronous scanning of a
|
||||
* removable storage device like an SD Card or USB OTG thumb drive using the
|
||||
* Storage Access Framework. Permission must first be granted by the user
|
||||
* {@link android.content.Intent#ACTION_OPEN_DOCUMENT_TREE} or
|
||||
@@ -73,25 +75,23 @@ import java.util.jar.JarInputStream;
|
||||
* @see <a href="https://developer.android.com/training/articles/scoped-directory-access.html">Using Scoped Directory Access</a>
|
||||
* @see <a href="https://developer.android.com/guide/topics/providers/document-provider.html">Open Files using Storage Access Framework</a>
|
||||
*/
|
||||
public class TreeUriScannerIntentService extends IntentService {
|
||||
public class TreeUriScannerIntentService extends JobIntentService {
|
||||
public static final String TAG = "TreeUriScannerIntentSer";
|
||||
private static final int JOB_ID = TAG.hashCode();
|
||||
|
||||
private static final String ACTION_SCAN_TREE_URI = "org.fdroid.fdroid.nearby.action.SCAN_TREE_URI";
|
||||
|
||||
/**
|
||||
* @see <a href="https://android.googlesource.com/platform/frameworks/base/+/android-10.0.0_r38/core/java/android/provider/DocumentsContract.java#238">DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY</a>
|
||||
* @see <a href="https://android.googlesource.com/platform/frameworks/base/+/android-10.0.0_r38/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java#70">ExternalStorageProvider.AUTHORITY</a>
|
||||
*/
|
||||
public static final String EXTERNAL_STORAGE_PROVIDER_AUTHORITY = "com.android.externalstorage.documents";
|
||||
|
||||
public TreeUriScannerIntentService() {
|
||||
super("TreeUriScannerIntentService");
|
||||
}
|
||||
|
||||
public static void scan(Context context, Uri data) {
|
||||
Intent intent = new Intent(context, TreeUriScannerIntentService.class);
|
||||
intent.setAction(ACTION_SCAN_TREE_URI);
|
||||
intent.setData(data);
|
||||
context.startService(intent);
|
||||
JobIntentService.enqueueWork(context, TreeUriScannerIntentService.class, JOB_ID, intent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,8 +115,8 @@ public class TreeUriScannerIntentService extends IntentService {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
if (intent == null || !ACTION_SCAN_TREE_URI.equals(intent.getAction())) {
|
||||
protected void onHandleWork(@NonNull Intent intent) {
|
||||
if (!ACTION_SCAN_TREE_URI.equals(intent.getAction())) {
|
||||
return;
|
||||
}
|
||||
Uri treeUri = intent.getData();
|
||||
|
||||
@@ -49,11 +49,13 @@ import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Handles shared preferences for FDroid, looking after the names of
|
||||
* preferences, default values and caching. Needs to be setup in the FDroidApp
|
||||
* (using {@link Preferences#setup(android.content.Context)} before it gets
|
||||
* accessed via the {@link org.fdroid.fdroid.Preferences#get()}
|
||||
* singleton method.
|
||||
* Handles the preferences that are shown the Settings UI, looking after the
|
||||
* names of preferences, default values and caching. Needs to be setup in the
|
||||
* {@link org.fdroid.fdroid.FDroidApp} (using
|
||||
* {@link Preferences#setup(android.content.Context)} before it gets accessed
|
||||
* via the {@link org.fdroid.fdroid.Preferences#get()} singleton method. This
|
||||
* structure also lets it be used in places in the code where there is no
|
||||
* {@link Context}.
|
||||
* <p>
|
||||
* All defaults should be set in {@code res/xml/preferences.xml}. The one
|
||||
* exception is {@link Preferences#PREF_LOCAL_REPO_NAME} since it needs to be
|
||||
|
||||
@@ -300,9 +300,13 @@ public final class Utils {
|
||||
"7.1", // 25
|
||||
"8.0", // 26
|
||||
"8.1", // 27
|
||||
"9.0", // 28
|
||||
"10.0", // 29
|
||||
"11.0", // 30
|
||||
"9", // 28
|
||||
"10", // 29
|
||||
"11", // 30
|
||||
"12", // 31
|
||||
"12.1", // 32
|
||||
"13", // 33
|
||||
"14", // 34
|
||||
};
|
||||
|
||||
public static String getAndroidVersionName(int sdkLevel) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.Manifest;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@@ -39,6 +40,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
@@ -90,6 +92,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private static final String ADD_REPO_INTENT_HANDLED = "addRepoIntentHandled";
|
||||
|
||||
private static final String BOTTOM_NAVIGATION_MENU_ID = "bottomNavigationMenuId";
|
||||
|
||||
private static final String ACTION_ADD_REPO = "org.fdroid.fdroid.MainActivity.ACTION_ADD_REPO";
|
||||
public static final String ACTION_REQUEST_SWAP = "requestSwap";
|
||||
|
||||
@@ -122,10 +126,14 @@ public class MainActivity extends AppCompatActivity {
|
||||
pager.setAdapter(adapter);
|
||||
|
||||
bottomNavigation = findViewById(R.id.bottom_navigation);
|
||||
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
setSelectedMenuInNav(sharedPreferences.getInt(BOTTOM_NAVIGATION_MENU_ID, R.id.latest));
|
||||
bottomNavigation.setOnNavigationItemSelectedListener(item -> {
|
||||
pager.scrollToPosition(item.getOrder());
|
||||
|
||||
if (item.getItemId() == R.id.nearby) {
|
||||
final int itemId = item.getItemId();
|
||||
sharedPreferences.edit().putInt(BOTTOM_NAVIGATION_MENU_ID, itemId).apply();
|
||||
if (itemId == R.id.nearby) {
|
||||
NearbyViewBinder.updateUsbOtg(MainActivity.this);
|
||||
}
|
||||
|
||||
@@ -140,13 +148,21 @@ public class MainActivity extends AppCompatActivity {
|
||||
AppUpdateStatusManager.getInstance(this).getNumUpdatableApps().observe(this, this::refreshUpdatesBadge);
|
||||
|
||||
Intent intent = getIntent();
|
||||
if (handleMainViewSelectIntent(intent)) {
|
||||
return;
|
||||
}
|
||||
handleSearchOrAppViewIntent(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link android.material.navigation.NavigationBarView} says "Menu items
|
||||
* can also be used for programmatically selecting which destination is
|
||||
* currently active. It can be done using {@code MenuItem.setChecked(true)}".
|
||||
*/
|
||||
private void setSelectedMenuInNav(int menuId) {
|
||||
int position = adapter.adapterPositionFromItemId(menuId);
|
||||
pager.scrollToPosition(position);
|
||||
bottomNavigation.setSelectedItemId(position);
|
||||
bottomNavigation.getMenu().getItem(position).setChecked(true);
|
||||
}
|
||||
|
||||
private void initialRepoUpdateIfRequired() {
|
||||
@@ -162,17 +178,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
FDroidApp.checkStartTor(this, Preferences.get());
|
||||
|
||||
if (getIntent().hasExtra(EXTRA_VIEW_UPDATES)) {
|
||||
getIntent().removeExtra(EXTRA_VIEW_UPDATES);
|
||||
setSelectedMenuInNav(R.id.updates);
|
||||
} else if (getIntent().hasExtra(EXTRA_VIEW_NEARBY)) {
|
||||
getIntent().removeExtra(EXTRA_VIEW_NEARBY);
|
||||
setSelectedMenuInNav(R.id.nearby);
|
||||
} else if (getIntent().hasExtra(EXTRA_VIEW_SETTINGS)) {
|
||||
getIntent().removeExtra(EXTRA_VIEW_SETTINGS);
|
||||
setSelectedMenuInNav(R.id.settings);
|
||||
}
|
||||
|
||||
// AppDetailsActivity and RepoDetailsActivity set different NFC actions, so reset here
|
||||
NfcHelper.setAndroidBeam(this, getApplication().getPackageName());
|
||||
checkForAddRepoIntent(getIntent());
|
||||
@@ -194,6 +199,11 @@ public class MainActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
||||
if (handleMainViewSelectIntent(intent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleSearchOrAppViewIntent(intent);
|
||||
|
||||
// This is called here as well as onResume(), because onNewIntent() is not called the first
|
||||
@@ -231,6 +241,23 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an {@link Intent} that shows a specific tab in the main view.
|
||||
*/
|
||||
private boolean handleMainViewSelectIntent(Intent intent) {
|
||||
if (intent.hasExtra(EXTRA_VIEW_NEARBY)) {
|
||||
setSelectedMenuInNav(R.id.nearby);
|
||||
return true;
|
||||
} else if (intent.hasExtra(EXTRA_VIEW_UPDATES)) {
|
||||
setSelectedMenuInNav(R.id.updates);
|
||||
return true;
|
||||
} else if (intent.hasExtra(EXTRA_VIEW_SETTINGS)) {
|
||||
setSelectedMenuInNav(R.id.settings);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since any app could send this {@link Intent}, and the search terms are
|
||||
* fed into a SQL query, the data must be strictly sanitized to avoid
|
||||
|
||||
Reference in New Issue
Block a user