-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
33 changed files
with
1,196 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
plugins { | ||
id("com.android.application") | ||
} | ||
|
||
android { | ||
namespace = "com.asameer.killappnow" | ||
compileSdk = 34 | ||
|
||
defaultConfig { | ||
applicationId = "com.asameer.killappnow" | ||
minSdk = 34 | ||
targetSdk = 34 | ||
versionCode = 1 | ||
versionName = "0.1" | ||
} | ||
|
||
buildTypes { | ||
release { | ||
isMinifyEnabled = true | ||
isShrinkResources = true | ||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") | ||
} | ||
} | ||
packaging { | ||
resources { | ||
// Not needed as long as we don't use reflection with Kotlin | ||
excludes.add("**/*.kotlin_builtins") | ||
excludes.add("**/*.kotlin_module") | ||
} | ||
} | ||
compileOptions { | ||
sourceCompatibility = JavaVersion.VERSION_1_8 | ||
targetCompatibility = JavaVersion.VERSION_1_8 | ||
} | ||
|
||
// Set the APK file name here | ||
applicationVariants.all { | ||
val variant = this | ||
variant.outputs | ||
.map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl } | ||
.forEach { output -> | ||
val outputFileName = "KillAppNow_${variant.versionName}.apk" | ||
println("OutputFileName: $outputFileName") | ||
output.outputFileName = outputFileName | ||
} | ||
}} | ||
|
||
dependencies { | ||
|
||
implementation("androidx.annotation:annotation:1.7.1") | ||
compileOnly ("de.robv.android.xposed:api:82") | ||
|
||
testImplementation("junit:junit:4.13.2") | ||
androidTestImplementation("androidx.test.ext:junit:1.1.5") | ||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
## This file must *NOT* be checked into Version Control Systems, | ||
# as it contains information specific to your local configuration. | ||
# | ||
# Location of the SDK. This is only used by Gradle. | ||
# For customization when using a Version Control System, please read the | ||
# header note. | ||
#Mon Dec 25 21:01:57 PKT 2023 | ||
sdk.dir=C\:\\Users\\Sameer\\AppData\\Local\\Android\\Sdk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Add project specific ProGuard rules here. | ||
# You can control the set of applied configuration files using the | ||
# proguardFiles setting in build.gradle. | ||
# | ||
# For more details, see | ||
# http://developer.android.com/guide/developing/tools/proguard.html | ||
|
||
# If your project uses WebView with JS, uncomment the following | ||
# and specify the fully qualified class name to the JavaScript interface | ||
# class: | ||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||
# public *; | ||
#} | ||
|
||
# Uncomment this to preserve the line number information for | ||
# debugging stack traces. | ||
-keepattributes SourceFile,LineNumberTable | ||
|
||
# If you keep the line number information, uncomment this to | ||
# hide the original source file name. | ||
#-renamesourcefileattribute SourceFile | ||
|
||
-dontobfuscate | ||
|
||
-keep class com.asameer.killappnow.KillAppNow |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:tools="http://schemas.android.com/tools"> | ||
|
||
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" /> | ||
<application | ||
android:allowBackup="true" | ||
android:dataExtractionRules="@xml/data_extraction_rules" | ||
android:fullBackupContent="@xml/backup_rules" | ||
android:icon="@mipmap/ic_launcher" | ||
android:label="@string/app_name" | ||
android:roundIcon="@mipmap/ic_launcher_round" | ||
android:supportsRtl="true"> | ||
<meta-data | ||
android:name="xposedmodule" | ||
android:value="true" /> | ||
<meta-data | ||
android:name="xposeddescription" | ||
android:value="@string/xposed_desc" /> | ||
<meta-data | ||
android:name="xposedminversion" | ||
android:value="82" /> | ||
<meta-data | ||
android:name="xposedscope" | ||
android:resource="@array/module_scope" /> | ||
</application> | ||
|
||
</manifest> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
com.asameer.killappnow.KillAppNow |
119 changes: 119 additions & 0 deletions
119
app/src/main/java/com/asameer/killappnow/KillAppNow.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package com.asameer.killappnow; | ||
|
||
import android.app.ActivityManager; | ||
import android.content.Context; | ||
import android.content.pm.PackageManager; | ||
import android.os.Handler; | ||
import android.os.Looper; | ||
import android.util.Log; | ||
import android.view.KeyEvent; | ||
import android.widget.Toast; | ||
import android.content.pm.ApplicationInfo; | ||
import android.content.Intent; | ||
import android.content.pm.ResolveInfo; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
import de.robv.android.xposed.IXposedHookLoadPackage; | ||
import de.robv.android.xposed.XC_MethodHook; | ||
import de.robv.android.xposed.XposedHelpers; | ||
import de.robv.android.xposed.callbacks.XC_LoadPackage; | ||
|
||
|
||
public class KillAppNow implements IXposedHookLoadPackage { | ||
|
||
private final List<String> mKillIgnoreList = new ArrayList<>(Arrays.asList( | ||
"com.android.systemui", | ||
"com.google.android.systemui" | ||
)); | ||
|
||
private Context mContext; | ||
|
||
@Override | ||
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) { | ||
if (!lpparam.packageName.equals("android")) | ||
return; | ||
|
||
mContext = (Context) XposedHelpers.callMethod( | ||
XposedHelpers.callStaticMethod(XposedHelpers.findClass("android.app.ActivityThread", null), | ||
"currentActivityThread"), "getSystemContext"); | ||
|
||
XposedHelpers.findAndHookMethod("com.android.server.policy.PhoneWindowManager", | ||
lpparam.classLoader, "interceptKeyBeforeQueueing", KeyEvent.class, | ||
int.class, new XC_MethodHook() { | ||
@Override | ||
protected void beforeHookedMethod(MethodHookParam param) { | ||
KeyEvent event = (KeyEvent) param.args[0]; | ||
int keyCode = event.getKeyCode(); | ||
|
||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { | ||
long downTime = event.getDownTime(); | ||
long eventTime = event.getEventTime(); | ||
|
||
if (eventTime - downTime > 300) { | ||
killForegroundApp(); | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
private void killForegroundApp() { | ||
Handler mainHandler = new Handler(Looper.getMainLooper()); | ||
|
||
mainHandler.post(() -> { | ||
try { | ||
String mStrAppKilled = "Killed: "; | ||
String mStrNothingToKill = "Nothing to kill."; | ||
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); | ||
List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses(); | ||
|
||
String targetKilled = null; | ||
for (ActivityManager.RunningAppProcessInfo processInfo : processes) { | ||
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND | ||
&& !mKillIgnoreList.contains(processInfo.processName)) { | ||
targetKilled = processInfo.processName; | ||
break; | ||
} | ||
} | ||
|
||
if (targetKilled != null) { | ||
// Check if the target app is the launcher app | ||
String launcherPackage = getDefaultLauncherPackageName(); | ||
if (!targetKilled.equals(launcherPackage)) { | ||
XposedHelpers.callMethod(am, "forceStopPackage", targetKilled); | ||
final String finalAppLabel = getApplicationLabel(targetKilled, mContext.getPackageManager()); | ||
mainHandler.post(() -> Toast.makeText(mContext, mStrAppKilled + finalAppLabel, Toast.LENGTH_SHORT).show()); | ||
} else { | ||
mainHandler.post(() -> Toast.makeText(mContext, mStrNothingToKill, Toast.LENGTH_SHORT).show()); | ||
} | ||
} else { | ||
mainHandler.post(() -> Toast.makeText(mContext, mStrNothingToKill, Toast.LENGTH_SHORT).show()); | ||
} | ||
} catch (Throwable t) { | ||
Log.e("KillAppNow", "Error in killForegroundApp", t); | ||
} | ||
}); | ||
} | ||
|
||
// Helper method to get the default launcher package name | ||
private String getDefaultLauncherPackageName() { | ||
Intent intent = new Intent(Intent.ACTION_MAIN); | ||
intent.addCategory(Intent.CATEGORY_HOME); | ||
ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); | ||
if (resolveInfo != null) { | ||
return resolveInfo.activityInfo.packageName; | ||
} | ||
return null; | ||
} | ||
|
||
private String getApplicationLabel(String packageName, PackageManager pm) { | ||
try { | ||
ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0); | ||
return (String) pm.getApplicationLabel(appInfo); | ||
} catch (PackageManager.NameNotFoundException e) { | ||
return packageName; // Fallback to package name if the app label is not found | ||
} | ||
} | ||
} |
Oops, something went wrong.