Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ android {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
jniLibs {
useLegacyPackaging = true
}
}
}

Expand Down
7 changes: 6 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="internalOnly">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
tools:ignore="ProtectedPermissions" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:extractNativeLibs="true"
android:theme="@style/AppTheme" >
<activity
android:name=".activity.ProcessActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.app.usage.UsageStatsManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
Expand All @@ -24,6 +25,7 @@
import com.livefront.processkiller.model.ProcessDetail;
import com.livefront.processkiller.task.ProcessDetailTask;

import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
Expand All @@ -32,6 +34,8 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
Expand Down Expand Up @@ -87,7 +91,9 @@ public int compare(ProcessDetail lhs, ProcessDetail rhs) {
private ProcessDetailTask mProcessDetailTask;
private PackageManager mPackageManager;
private List<String> mIgnoredPackages = new ArrayList<>();
private Executor mExecutor = Executors.newCachedThreadPool();
private UsageStatsManager mUsageStatsManager;
private Snackbar mSnackBar = null;
private Views mViews;

static class Views {
Expand Down Expand Up @@ -155,6 +161,60 @@ public void onDestroyView() {
}
}

private void killProcess(@NonNull String packageName) {
mSnackBar = Snackbar.make(
mViews.recyclerView,
R.string.snackbar_process_killing,
Snackbar.LENGTH_INDEFINITE);
mSnackBar.show();

mExecutor.execute(() -> {
sendAdbCommand("pm grant com.livefront.processkiller "
+ "android.permission.WRITE_SECURE_SETTINGS "
+ "&> /dev/null");

if (!sendAdbCommand("devices")) {
runOnUiThread(() -> {
mSnackBar.setText(R.string.snackbar_process_killing_failure_message)
.setDuration(Snackbar.LENGTH_LONG)
.show();
});
return;
}
if (!sendAdbCommand("shell am kill " + packageName)) {
runOnUiThread(() -> {
mSnackBar.setText(R.string.snackbar_process_killing_failure_message)
.setDuration(Snackbar.LENGTH_LONG)
.show();
});
return;
}

runOnUiThread(() -> {
// We can defer the remaining logic to the legacy code
killProcessLegacy(packageName);
});
});
}

private void killProcessLegacy(@NonNull String packageName) {
// This is no-op for Android 14+
mActivityManager.killBackgroundProcesses(packageName);

if (mSnackBar == null) {
mSnackBar = Snackbar.make(
mViews.recyclerView,
R.string.snackbar_process_killed_action,
Snackbar.LENGTH_LONG);
}
mSnackBar.setText(R.string.snackbar_process_killed_action)
.setDuration(Snackbar.LENGTH_LONG)
.setAction(
R.string.snackbar_process_killed_action,
v -> launchIntentForPackage(packageName))
.show();
}

/**
* Attempts to find (and launch) an intent to resume an application from its last known running
* task. In most cases this should simulate clicking on the application on the Recents screen.
Expand Down Expand Up @@ -245,6 +305,14 @@ public void onTaskComplete(@NonNull List<ProcessDetail> processDetails) {
mProcessDetailTask.execute();
}

private void runOnUiThread(@NonNull Runnable runnable) {
Activity activity = getActivity();
if (activity == null) {
return;
}
activity.runOnUiThread(runnable);
}

private void setupRecyclerView() {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(
getActivity(),
Expand All @@ -256,24 +324,31 @@ private void setupRecyclerView() {
mAdapter.setOnProcessDetailClickListener(new OnProcessDetailClickListener() {
@Override
public void onProcessDetailClick(@NonNull final ProcessDetail processDetail) {
mActivityManager.killBackgroundProcesses(processDetail.getPackageName());
Snackbar.make(
mViews.recyclerView,
R.string.snackbar_process_killed_message,
Snackbar.LENGTH_LONG)
.setAction(
R.string.snackbar_process_killed_action,
new View.OnClickListener() {
@Override
public void onClick(View v) {
launchIntentForPackage(processDetail.getPackageName());
}
})
.show();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
killProcessLegacy(processDetail.getPackageName());
} else {
killProcess(processDetail.getPackageName());
}
}
});
}

private boolean sendAdbCommand(String command) {
Activity activity = getActivity();
if (activity == null) {
return false;
}
// Note that this makes use of ADB libraries from
// https://github.com/tytydraco/LADB/tree/main/app/src/main/jniLibs
String adbPath = activity.getApplicationInfo().nativeLibraryDir + "/libadb.so";
String[] cmdLine = {"sh", "-c", adbPath + " " + command};
try {
return Runtime.getRuntime().exec(cmdLine).waitFor() == 0;
} catch (IOException | InterruptedException e) {
return false;
}
}

private void showProgress(boolean show) {
mViews.spinner.setVisibility(show ? View.VISIBLE : View.GONE);
}
Expand Down
Binary file added app/src/main/jniLibs/arm64-v8a/libadb.so
Binary file not shown.
Binary file added app/src/main/jniLibs/armeabi-v7a/libadb.so
Binary file not shown.
Binary file added app/src/main/jniLibs/x86/libadb.so
Binary file not shown.
Binary file added app/src/main/jniLibs/x86_64/libadb.so
Binary file not shown.
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
<!-- Killing process feedback -->
<string name="snackbar_process_killed_message">Process killed if running</string>
<string name="snackbar_process_killed_action">Go to app</string>
<string name="snackbar_process_killing">Killing</string>
<string name="snackbar_process_killing_failure_message">Failed to kill</string>
<string name="error_application_can_not_be_launched">That application can not be launched</string>

</resources>