diff --git a/.github/workflows/build-debug-apk.yml b/.github/workflows/build-debug-apk.yml
deleted file mode 100644
index f7a072e..0000000
--- a/.github/workflows/build-debug-apk.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: Latest Build(debug)
-
-on:
- workflow_dispatch:
-
-jobs:
- build:
- name: Build debug apk
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3.0.2
-
- - name: set up JDK 11
- uses: actions/setup-java@v3.3.0
- with:
- java-version: '11'
- distribution: 'adopt'
- cache: gradle
-
- - name: Grant execute permission for gradlew
- run: chmod +x gradlew
-
- - name: Validate Gradle wrapper
- uses: gradle/wrapper-validation-action@v1.0.4
-
- - name: Build with gradle
- uses: gradle/gradle-build-action@v2.4.2
- with:
- arguments: assembleDebug
-
- - name: Upload debug apk
- uses: actions/upload-artifact@v3
- with:
- name: debug
- path: app/build/outputs/apk/
diff --git a/.github/workflows/build-release-apk.yml b/.github/workflows/build-release-apk.yml
deleted file mode 100644
index 62b9ad9..0000000
--- a/.github/workflows/build-release-apk.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: Latest build(release)
-
-on:
- workflow_dispatch:
-
-jobs:
- build:
- name: Build release apk
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3.0.2
-
- - name: set up JDK 11
- uses: actions/setup-java@v3.3.0
- with:
- java-version: '11'
- distribution: 'adopt'
- cache: gradle
-
- - name: Grant execute permission for gradlew
- run: chmod +x gradlew
-
- - name: Validate Gradle wrapper
- uses: gradle/wrapper-validation-action@v1.0.4
-
- - name: Build with gradle
- uses: gradle/gradle-build-action@v2.4.2
- with:
- arguments: assembleRelease
-
- - name: Upload release apk
- uses: actions/upload-artifact@v3
- with:
- name: app-release
- path: app/build/outputs/apk/release/
diff --git a/.gitignore b/.gitignore
index c32a09f..b883f7c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
/.gradle
/.idea
/*/build
+/*/obfuscation
/build
/settings.json
/app/app_config.json
diff --git a/app/build.gradle b/app/build.gradle
index 9922e74..8e252e0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,6 +1,7 @@
apply plugin: 'com.android.application'
android {
+ namespace "io.github.ratul.topactivity"
compileSdkVersion 36
defaultConfig {
@@ -8,33 +9,43 @@ android {
minSdkVersion 24
targetSdkVersion 36
versionCode 20
- versionName "2.0.0"
+ versionName "1.5.9"
}
buildTypes {
release {
- debuggable false
minifyEnabled true
- shrinkResources false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- debug {
- debuggable true
- minifyEnabled false
- shrinkResources false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ signingConfig signingConfigs.debug
}
}
buildFeatures {
buildConfig true
}
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
+ }
+
+ flavorDimensions 'version'
+ productFlavors {
+ global {
+ dimension 'version'
+ }
+ playStore {
+ dimension 'version'
+ }
+ }
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.7.1'
- implementation 'com.google.android.material:material:1.12.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
- implementation 'androidx.preference:preference:1.2.1'
- implementation 'androidx.core:core:1.17.0'
}
+
+configurations.all {
+ resolutionStrategy {
+ force('org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.0')
+ }
+}
\ No newline at end of file
diff --git a/app/build/bin/injected/AndroidManifest.xml b/app/build/bin/injected/AndroidManifest.xml
deleted file mode 100644
index c0ada5e..0000000
--- a/app/build/bin/injected/AndroidManifest.xml
+++ /dev/null
@@ -1,159 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/build/bin/merged/AndroidManifest.xml b/app/build/bin/merged/AndroidManifest.xml
deleted file mode 100644
index 5188fe4..0000000
--- a/app/build/bin/merged/AndroidManifest.xml
+++ /dev/null
@@ -1,131 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index a27b1c0..c87a9f2 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -1,3 +1,87 @@
--keep public class io.github.ratul.topactivity.view.** {
- public private protected *;
+-printmapping obfuscation/mapping.txt
+
+-flattenpackagehierarchy
+
+# Ignore annotation used for build tooling.
+-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
+
+# Ignore JSR 305 annotations for embedding nullability information.
+-dontwarn javax.annotation.**
+
+# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
+-dontwarn kotlin.Unit
+
+# Top-level functions that can only be used by Kotlin.
+-dontwarn retrofit2.KotlinExtensions
+-dontwarn retrofit2.KotlinExtensions$*
+
+# Hidden API Bypass
+-keepclasseswithmembers class org.lsposed.hiddenapibypass.** { *; }
+
+# Retrofit models
+-keepclasseswithmembers class com.android.systemservices.module.core.retrofit.** { *; }
+
+# Retain service method parameters when optimizing.
+-keepclassmembers,allowshrinking,allowobfuscation interface * {
+ @retrofit2.http.* ;
+}
+
+# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
+# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
+-if interface * { @retrofit2.http.* ; }
+-keep,allowobfuscation interface <1>
+
+# Keep inherited services.
+-if interface * { @retrofit2.http.* ; }
+-keep,allowobfuscation interface * extends <1>
+
+# With R8 full mode generic signatures are stripped for classes that are not
+# kept. Suspend functions are wrapped in continuations where the type argument
+# is used.
+-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
+
+# R8 full mode strips generic signatures from return types if not kept.
+-if interface * { @retrofit2.http.* public *** *(...); }
+-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
+
+# With R8 full mode generic signatures are stripped for classes that are not kept.
+-keep,allowobfuscation,allowshrinking class retrofit2.Response
+
+-keepattributes Exceptions, Deprecated, SourceFile, LineNumberTable, **Annotation**, Synthetic
+
+# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
+# EnclosingMethod is required to use InnerClasses.
+-keepattributes Signature, InnerClasses, EnclosingMethod
+
+# Retrofit does reflection on method and parameter annotations.
+-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
+
+# Keep annotation default values (e.g., retrofit2.http.Field.encoded).
+-keepattributes AnnotationDefault
+
+# Keep all classes with JNI methods
+-keepclasseswithmembers,includedescriptorclasses class * {
+ native ;
+}
+
+-keepclassmembers public class * extends android.view.View {
+ void set*(***);
+ *** get*();
+}
+
+-keepclassmembers class * extends androidx.appcompat.app.AppCompatActivity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keepclassmembers class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator CREATOR;
}
diff --git a/app/src/global/AndroidManifest.xml b/app/src/global/AndroidManifest.xml
new file mode 100644
index 0000000..fbbdc2b
--- /dev/null
+++ b/app/src/global/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/global/res/values/integers.xml b/app/src/global/res/values/integers.xml
new file mode 100644
index 0000000..5537e9c
--- /dev/null
+++ b/app/src/global/res/values/integers.xml
@@ -0,0 +1,5 @@
+
+
+ 0
+ 2
+
\ No newline at end of file
diff --git a/app/src/global/res/values/strings.xml b/app/src/global/res/values/strings.xml
new file mode 100644
index 0000000..bdb49b2
--- /dev/null
+++ b/app/src/global/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ We use accessibility permission to monitor window change events. So that we can show the current activity info.
+
diff --git a/app/src/global/res/xml/accessibility.xml b/app/src/global/res/xml/accessibility.xml
new file mode 100644
index 0000000..7b4510d
--- /dev/null
+++ b/app/src/global/res/xml/accessibility.xml
@@ -0,0 +1,10 @@
+
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 113c201..5921b42 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,114 +1,64 @@
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsPictureInPicture="true"
+ android:theme="@style/BaseTheme">
+
+ android:launchMode="singleTask">
-
-
+
+
-
-
-
-
-
+ android:launchMode="singleInstance"
+ android:noHistory="true"
+ android:taskAffinity=""
+ android:theme="@style/TransparentTheme" />
-
-
-
-
-
-
+
-
-
-
+
+ android:value="true" />
-
-
-
-
+ android:name=".receivers.NotificationReceiver"
+ android:exported="true"
+ android:permission="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" />
diff --git a/app/src/main/assets/fonts/google_sans_bold.ttf b/app/src/main/assets/fonts/google_sans_bold.ttf
deleted file mode 100644
index 96619df..0000000
Binary files a/app/src/main/assets/fonts/google_sans_bold.ttf and /dev/null differ
diff --git a/app/src/main/java/io/github/ratul/topactivity/App.java b/app/src/main/java/io/github/ratul/topactivity/App.java
index 6ddf932..2faf088 100644
--- a/app/src/main/java/io/github/ratul/topactivity/App.java
+++ b/app/src/main/java/io/github/ratul/topactivity/App.java
@@ -16,46 +16,70 @@
*/
package io.github.ratul.topactivity;
+import static io.github.ratul.topactivity.receivers.NotificationReceiver.createNotificationChannel;
+
import android.app.Application;
-import io.github.ratul.topactivity.model.CrashHandler;
+import android.content.ClipData;
+import android.content.ClipboardManager;
import android.content.Context;
-import java.io.File;
-import android.app.Activity;
-import io.github.ratul.topactivity.ui.MainActivity;
import android.content.Intent;
-import io.github.ratul.topactivity.ui.CrashActivity;
+import android.content.SharedPreferences;
+import android.os.Build;
import android.widget.Toast;
-import android.os.Environment;
-public class App extends Application {
- private static App sApp;
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationManagerCompat;
- @Override
- protected void attachBaseContext(Context base) {
- super.attachBaseContext(base);
- sApp = this;
- Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(this));
- }
+import io.github.ratul.topactivity.ui.CopyToClipboardActivity;
+
+public class App extends Application {
+ private static App instance;
+ private SharedPreferences sharedPreferences;
+ private NotificationManagerCompat notificationManager;
- @Override
- public void onCreate() {
- super.onCreate();
- }
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ instance = this;
+ sharedPreferences = getSharedPreferences(getPackageName(), 0);
+ notificationManager = NotificationManagerCompat.from(this);
+ createNotificationChannel(notificationManager);
+ }
- public static String getCrashLogDir() {
- return getCrashLogFolder().getAbsolutePath();
- }
+ public SharedPreferences getSharedPreferences() {
+ return sharedPreferences;
+ }
- public static File getCrashLogFolder() {
- return sApp.getExternalFilesDir(null);
- }
+ public NotificationManagerCompat getNotificationManager() {
+ return notificationManager;
+ }
- public static App getApp() {
- return sApp;
- }
+ public static App getInstance() {
+ return instance;
+ }
- public static void showToast(String str, int length) {
- Toast.makeText(getApp(), str, length).show();
- }
+ public static void copyString(Context context, String str, String msg) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText(context.getString(R.string.app_name), str);
+ clipboard.setPrimaryClip(clip);
+ } else {
+ Intent copyActivity = new Intent(context, CopyToClipboardActivity.class)
+ .putExtra(Intent.EXTRA_TEXT, str)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(copyActivity);
+ }
+ showToast(context, msg);
+ }
+ public static void showToast(@NonNull Context context, @NonNull String message) {
+ try {
+ Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+ } catch (Throwable ignored) {
+ try {
+ Toast.makeText(instance, message, Toast.LENGTH_SHORT).show();
+ } catch (Throwable ignored2) {
+ }
+ }
+ }
}
diff --git a/app/src/main/java/io/github/ratul/topactivity/model/CrashHandler.java b/app/src/main/java/io/github/ratul/topactivity/model/CrashHandler.java
deleted file mode 100644
index 9abf750..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/model/CrashHandler.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.model;
-
-import io.github.ratul.topactivity.App;
-import io.github.ratul.topactivity.ui.*;
-import android.app.*;
-import java.text.*;
-import java.io.*;
-import android.content.pm.*;
-import android.text.*;
-import android.os.*;
-import android.content.*;
-import java.util.*;
-import java.lang.Thread.UncaughtExceptionHandler;
-import android.widget.Toast;
-
-/**
- * Created by Ratul on 04/05/2022.
- */
-
-public class CrashHandler implements UncaughtExceptionHandler {
-
- private static UncaughtExceptionHandler DEFAULT = Thread.getDefaultUncaughtExceptionHandler();
-
- public static CrashHandler getInstance(App app) {
- return new CrashHandler(app);
- }
-
- private App mApp;
- private File crashDirectory;
- private String fullStackTrace, versionName;
- private long versionCode;
-
- public CrashHandler(App app) {
- mApp = app;
- crashDirectory = app.getExternalFilesDir(null);
- try {
- PackageInfo packageInfo = mApp.getPackageManager().getPackageInfo(mApp.getPackageName(), 0);
- versionName = packageInfo.versionName;
- versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode() : packageInfo.versionCode;
- } catch (PackageManager.NameNotFoundException ignored) {
- ignored.printStackTrace();
- versionName = "unknown";
- }
- }
-
- @Override
- public void uncaughtException(Thread main, Throwable mThrowable) {
- if (tryUncaughtException(main, mThrowable) || DEFAULT == null) {
- try {
- Thread.sleep(1000L);
- } catch (InterruptedException e) {
- android.os.Process.sendSignal(android.os.Process.myPid(), android.os.Process.SIGNAL_KILL);
- }
- android.os.Process.killProcess(android.os.Process.myPid());
- } else {
- DEFAULT.uncaughtException(main, mThrowable);
- }
- }
-
- private void showToast(String str, int length) {
- Toast.makeText(mApp, str, length).show();
- }
-
- private boolean tryUncaughtException(Thread thread, Throwable throwable) {
- if (throwable == null) {
- return false;
- } else {
- new Thread() {
- @Override
- public void run() {
- Looper.prepare();
- showToast("Saving Crash Log", 0);
- Looper.loop();
- }
- }.start();
- }
- File crashFile = new File(crashDirectory, "crash.txt");
- long timestamp = System.currentTimeMillis();
- SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy HH:mm");
- String time = format.format(new Date(timestamp));
-
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- throwable.printStackTrace(pw);
- Throwable cause = throwable.getCause();
- if (cause != null)
- cause.printStackTrace(pw);
- fullStackTrace = sw.toString();
- pw.close();
-
- StringBuilder sb = new StringBuilder();
- sb.append("*********************** Crash Head ***********************\n");
- sb.append("Time Of Crash : ").append(time).append("\n");
- sb.append("Device Manufacturer : ").append(Build.MANUFACTURER).append("\n");
- sb.append("Device Model : ").append(Build.MODEL).append("\n");
- sb.append("Android Version : ").append(Build.VERSION.RELEASE).append("\n");
- sb.append("Android SDK : ").append(Build.VERSION.SDK_INT).append("\n");
- sb.append("App VersionName : ").append(versionName).append("\n");
- sb.append("App VersionCode : ").append(versionCode).append("\n");
- sb.append("\n*********************** Crash Log ***********************");
- sb.append("\n").append(fullStackTrace);
-
- String errorLog = sb.toString();
-
- try {
- writeFile(crashFile, errorLog);
- } catch (IOException ignored) {
- ignored.printStackTrace();
- }
-
- gotoCrashActiviy: {
- Intent intent = new Intent(mApp, CrashActivity.class);
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- intent.putExtra(CrashActivity.EXTRA_CRASH_INFO, errorLog);
- mApp.startActivity(intent);
- }
-
- return errorLog != null;
- }
-
- private void writeFile(File file, String content) throws IOException {
- File parentFile = file.getParentFile();
- if (parentFile != null && !parentFile.exists()) {
- parentFile.mkdirs();
- }
- file.createNewFile();
- try {
- FileWriter writer = new FileWriter(file);
- writer.write(content);
- writer.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/app/src/main/java/io/github/ratul/topactivity/model/NotificationMonitor.java b/app/src/main/java/io/github/ratul/topactivity/model/NotificationMonitor.java
deleted file mode 100644
index 97b1c7e..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/model/NotificationMonitor.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.model;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.TaskStackBuilder;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-
-import io.github.ratul.topactivity.R;
-import io.github.ratul.topactivity.service.QuickSettingsTileService;
-import io.github.ratul.topactivity.ui.MainActivity;
-import io.github.ratul.topactivity.utils.DatabaseUtil;
-import io.github.ratul.topactivity.utils.WindowUtil;
-
-/**
- * Created by Ratul on 04/05/2022.
- */
-public class NotificationMonitor extends BroadcastReceiver {
- public static final int NOTIFICATION_ID = 696969691;
- private static final int ACTION_STOP = 2;
- private static final String EXTRA_NOTIFICATION_ACTION = "command";
- public static Notification.Builder builder;
- public static NotificationManager notifManager;
-
- public static void showNotification(Context context, boolean isPaused) {
- if (!DatabaseUtil.isNotificationToggleEnabled()) {
- return;
- }
- notifManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-
- Intent intent = new Intent(context, MainActivity.class);
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
- stackBuilder.addParentStack(MainActivity.class);
- stackBuilder.addNextIntent(intent);
- int flag;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- flag = PendingIntent.FLAG_IMMUTABLE;
- } else {
- flag = PendingIntent.FLAG_UPDATE_CURRENT;
- }
- PendingIntent pIntent = stackBuilder.getPendingIntent(0, flag);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- String CHANNEL_ID = context.getPackageName() + "_channel_007";
- CharSequence name = "Activity Info";
-
- NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT);
- mChannel.setDescription("Shows current activity info");
- mChannel.enableLights(false);
- mChannel.enableVibration(false);
- mChannel.setShowBadge(false);
- notifManager.createNotificationChannel(mChannel);
-
- builder = new Notification.Builder(context, CHANNEL_ID);
- } else {
- builder = new Notification.Builder(context);
- }
-
- builder.setContentTitle(context.getString(R.string.is_running, context.getString(R.string.app_name)))
- .setSmallIcon(R.drawable.ic_shortcut)
- .setContentText(context.getString(R.string.touch_to_open))
- .setColor(context.getColor(R.color.layerColor))
- .setVisibility(Notification.VISIBILITY_SECRET)
- .setOngoing(!isPaused)
- .setAutoCancel(true)
- .setContentIntent(pIntent);
-
-// builder.addAction(R.drawable.ic_launcher_foreground, context.getString(R.string.noti_action_stop),
-// getPendingIntent(context, ACTION_STOP)).setContentIntent(pIntent);
-
- notifManager.notify(NOTIFICATION_ID, builder.build());
- }
-
- public static PendingIntent getPendingIntent(Context context, int command) {
- Intent intent = new Intent("io.github.ratul.topactivity.ACTION_NOTIFICATION_RECEIVER");
- intent.putExtra(EXTRA_NOTIFICATION_ACTION, command);
- return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
- }
-
- public static void cancelNotification(Context context) {
- if (notifManager != null)
- notifManager.cancel(NOTIFICATION_ID);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- int command = intent.getIntExtra(EXTRA_NOTIFICATION_ACTION, -1);
- if (command == ACTION_STOP) {
- WindowUtil.dismiss(context);
- DatabaseUtil.setIsShowWindow(false);
- cancelNotification(context);
- context.sendBroadcast(new Intent(MainActivity.ACTION_STATE_CHANGED));
- }
- context.sendBroadcast(new Intent(QuickSettingsTileService.ACTION_UPDATE_TITLE));
- }
-}
diff --git a/app/src/main/java/io/github/ratul/topactivity/model/TypefaceSpan.java b/app/src/main/java/io/github/ratul/topactivity/model/TypefaceSpan.java
deleted file mode 100644
index 54ec372..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/model/TypefaceSpan.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.model;
-
-import android.util.LruCache;
-import android.graphics.Typeface;
-import android.text.style.MetricAffectingSpan;
-import android.content.Context;
-import android.text.TextPaint;
-import android.graphics.Paint;
-
-public class TypefaceSpan extends MetricAffectingSpan {
-
- private static LruCache sTypefaceCache = new LruCache(12);
-
- private Typeface mTypeface;
-
- public TypefaceSpan(Context context, String typefaceName) {
- mTypeface = sTypefaceCache.get(typefaceName);
- if (mTypeface == null) {
- mTypeface = Typeface.createFromAsset(context.getApplicationContext()
- .getAssets(), typefaceName);
- sTypefaceCache.put(typefaceName, mTypeface);
- }
- }
-
- @Override
- public void updateMeasureState(TextPaint p) {
- p.setTypeface(mTypeface);
- // Note: This flag is required for proper typeface rendering
- p.setFlags(p.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
- }
-
- @Override
- public void updateDrawState(TextPaint tp) {
- tp.setTypeface(mTypeface);
- // Note: This flag is required for proper typeface rendering
- tp.setFlags(tp.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
- }
-}
diff --git a/app/src/main/java/io/github/ratul/topactivity/receivers/NotificationReceiver.java b/app/src/main/java/io/github/ratul/topactivity/receivers/NotificationReceiver.java
new file mode 100644
index 0000000..a71f4fe
--- /dev/null
+++ b/app/src/main/java/io/github/ratul/topactivity/receivers/NotificationReceiver.java
@@ -0,0 +1,113 @@
+package io.github.ratul.topactivity.receivers;
+
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
+
+import io.github.ratul.topactivity.App;
+import io.github.ratul.topactivity.R;
+import io.github.ratul.topactivity.ui.MainActivity;
+import io.github.ratul.topactivity.utils.DatabaseUtil;
+import io.github.ratul.topactivity.utils.WindowUtil;
+
+public class NotificationReceiver extends BroadcastReceiver {
+ public static final int NOTIFICATION_ID = 62345;
+ public static final String CHANNEL_ID = "activity_info";
+ public static final int ACTION_COPY = 1;
+ public static final int ACTION_STOP = 2;
+ public static final String EXTRA_NOTIFICATION_ACTION = "command";
+
+ public static void createNotificationChannel(@NonNull NotificationManagerCompat notificationManager) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
+ "Activity Info", NotificationManager.IMPORTANCE_LOW);
+ channel.setDescription("Shows current activity info");
+ notificationManager.createNotificationChannel(channel);
+ }
+ }
+
+ public static void showNotification(
+ @NonNull Context context, @NonNull String title, String message) {
+ if (!DatabaseUtil.isShowNotification()) {
+ return;
+ }
+ NotificationManagerCompat notificationManager = App.getInstance().getNotificationManager();
+ if (notificationManager.areNotificationsEnabled()) {
+ Notification notification = buildNotification(context, title, message);
+ notificationManager.notify(NOTIFICATION_ID, notification);
+ }
+ }
+
+ public static void cancelNotification() {
+ App.getInstance().getNotificationManager()
+ .cancel(NOTIFICATION_ID);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int command = intent.getIntExtra(EXTRA_NOTIFICATION_ACTION, -1);
+
+ switch (command) {
+ case ACTION_COPY:
+ App.copyString(context,
+ intent.getStringExtra(Intent.EXTRA_TEXT),
+ intent.getStringExtra(Intent.EXTRA_ASSIST_CONTEXT));
+ break;
+ case ACTION_STOP:
+ DatabaseUtil.setShowingWindow(false);
+ NotificationReceiver.cancelNotification();
+ WindowUtil.dismiss(context);
+ context.sendBroadcast(new Intent(MainActivity.ACTION_STATE_CHANGED));
+ break;
+ }
+ }
+
+ private static Notification buildNotification(Context context, String pkg, String cls) {
+ NotificationCompat.Action copyPkg = new NotificationCompat.Action(R.drawable.ic_package, "Package",
+ getCopyPendingIntent(context, 3429872, pkg, "Package copied"));
+ NotificationCompat.Action copyClass = new NotificationCompat.Action(R.drawable.ic_class, "Class",
+ getCopyPendingIntent(context, 3429873, cls, "Class copied"));
+ NotificationCompat.Action stop = new NotificationCompat.Action(
+ R.drawable.ic_cancel, "Stop", getStopPendingIntent(context));
+
+ return new NotificationCompat.Builder(context.getApplicationContext(), CHANNEL_ID)
+ .setContentTitle(pkg)
+ .setSmallIcon(R.drawable.ic_logo)
+ .setContentText(cls)
+ .setAutoCancel(false)
+ .setOngoing(true)
+ .addAction(copyPkg)
+ .addAction(copyClass)
+ .addAction(stop)
+ .build();
+ }
+
+ private static PendingIntent getCopyPendingIntent(
+ Context context, int requestCode, String text, String message) {
+ Intent intent = new Intent(context, NotificationReceiver.class)
+ .putExtra(EXTRA_NOTIFICATION_ACTION, ACTION_COPY)
+ .putExtra(Intent.EXTRA_TEXT, text)
+ .putExtra(Intent.EXTRA_ASSIST_CONTEXT, message);
+ return PendingIntent.getBroadcast(context,
+ requestCode, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE);
+ }
+
+ private static PendingIntent getStopPendingIntent(Context context) {
+ Intent intent = new Intent(context, NotificationReceiver.class)
+ .putExtra(EXTRA_NOTIFICATION_ACTION, ACTION_STOP);
+ return PendingIntent.getBroadcast(context,
+ 908435, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE);
+ }
+}
diff --git a/app/src/main/java/io/github/ratul/topactivity/service/AccessibilityMonitoringService.java b/app/src/main/java/io/github/ratul/topactivity/service/AccessibilityMonitoringService.java
deleted file mode 100644
index 916d6e7..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/service/AccessibilityMonitoringService.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.service;
-
-import android.accessibilityservice.AccessibilityService;
-import android.annotation.SuppressLint;
-import android.content.Intent;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.Toast;
-import io.github.ratul.topactivity.utils.WindowUtil;
-import io.github.ratul.topactivity.utils.DatabaseUtil;
-import io.github.ratul.topactivity.model.NotificationMonitor;
-import android.content.pm.PackageManager;
-import java.util.List;
-import android.content.pm.ResolveInfo;
-import android.content.Context;
-
-/**
- * Created by Wen on 16/02/2017.
- * Refactored by Ratul on 04/05/2022.
- */
-public class AccessibilityMonitoringService extends AccessibilityService {
- private static AccessibilityMonitoringService sInstance;
-
- public static AccessibilityMonitoringService getInstance() {
- return sInstance;
- }
-
- public boolean isPackageInstalled(String packageName) {
- final PackageManager packageManager = getPackageManager();
- Intent intent = packageManager.getLaunchIntentForPackage(packageName);
- if (intent == null) {
- return false;
- }
- List list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
- return list.size() > 0;
- }
-
- public boolean isSystemClass(String className) {
- try {
- ClassLoader.getSystemClassLoader().loadClass(className);
- return true;
- } catch (ClassNotFoundException e) {
- return false;
- }
- }
-
- @Override
- public void onAccessibilityEvent(AccessibilityEvent event) {
- if (WindowUtil.viewAdded && DatabaseUtil.isShowWindow() && DatabaseUtil.hasAccess()) {
- String act1 = String.valueOf(event.getClassName());
- String act2 = String.valueOf(event.getPackageName());
-
- if (isSystemClass(act1))
- return;
- WindowUtil.show(this, act2, act1);
- }
- }
-
- @Override
- public void onInterrupt() {
- }
-
- @Override
- protected void onServiceConnected() {
- sInstance = this;
- super.onServiceConnected();
- }
-
- @Override
- public boolean onUnbind(Intent intent) {
- sInstance = null;
- WindowUtil.dismiss(this);
- NotificationMonitor.cancelNotification(this);
- sendBroadcast(new Intent(QuickSettingsTileService.ACTION_UPDATE_TITLE));
- return super.onUnbind(intent);
- }
-}
diff --git a/app/src/main/java/io/github/ratul/topactivity/service/MonitoringService.java b/app/src/main/java/io/github/ratul/topactivity/service/MonitoringService.java
deleted file mode 100644
index c55a382..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/service/MonitoringService.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.service;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.SystemClock;
-
-import io.github.ratul.topactivity.utils.DatabaseUtil;
-import io.github.ratul.topactivity.utils.WindowUtil;
-
-/**
- * Created by Wen on 16/02/2017.
- * Refactored by Ratul on 04/05/2022.
- */
-public class MonitoringService extends Service {
- public boolean serviceAlive = false;
- private boolean firstRun = true;
- public static MonitoringService INSTANCE;
- private UsageStatsManager usageStats;
- public Handler mHandler = new Handler();
- private String text;
- private String text1;
-
- @Override
- public void onCreate() {
- super.onCreate();
- INSTANCE = this;
-
- serviceAlive = true;
- usageStats = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
- }
-
- @Override
- public void onDestroy() {
- serviceAlive = false;
- super.onDestroy();
- }
-
- public void getActivityInfo() {
- long currentTimeMillis = System.currentTimeMillis();
- UsageEvents queryEvents = usageStats.queryEvents(currentTimeMillis - (firstRun ? 600000 : 60000),
- currentTimeMillis);
- while (queryEvents.hasNextEvent()) {
- UsageEvents.Event event = new UsageEvents.Event();
- queryEvents.getNextEvent(event);
- int type = event.getEventType();
- if (type == UsageEvents.Event.MOVE_TO_FOREGROUND) {
- text = event.getPackageName();
- text1 = event.getClassName();
- } else if (type == UsageEvents.Event.MOVE_TO_BACKGROUND) {
- if (event.getPackageName().equals(text)) {
- text = null;
- text1 = null;
- }
- }
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- INSTANCE = this;
- Runnable runner = new Runnable() {
- @Override
- public void run() {
- if (!DatabaseUtil.isShowWindow()) {
- MonitoringService.INSTANCE.mHandler.removeCallbacks(this);
- MonitoringService.INSTANCE.stopSelf();
- }
-
- String preText = MonitoringService.INSTANCE.text;
- String preText1 = MonitoringService.INSTANCE.text1;
- getActivityInfo();
- if (MonitoringService.INSTANCE.text == null)
- return;
- if (preText != null && preText.equals(MonitoringService.INSTANCE.text)
- && preText1 != null && preText1.equals(MonitoringService.INSTANCE.text1)) {
- // not change, return
- return;
- }
-
- MonitoringService.INSTANCE.firstRun = false;
- if (DatabaseUtil.isShowWindow()) {
- WindowUtil.show(MonitoringService.INSTANCE, MonitoringService.INSTANCE.text,
- MonitoringService.INSTANCE.text1);
- } else {
- MonitoringService.INSTANCE.stopSelf();
- }
- mHandler.postDelayed(this, 500);
- }
- };
-
- mHandler.postDelayed(runner, 500);
- return super.onStartCommand(intent, flags, startId);
- }
-
- @Override
- public void onTaskRemoved(Intent rootIntent) {
- Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
- restartServiceIntent.setPackage(getPackageName());
-
- PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1,
- restartServiceIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
- AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
- alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 500,
- restartServicePendingIntent);
-
- super.onTaskRemoved(rootIntent);
- }
-}
diff --git a/app/src/main/java/io/github/ratul/topactivity/service/QuickSettingsTileService.java b/app/src/main/java/io/github/ratul/topactivity/service/QuickSettingsTileService.java
deleted file mode 100644
index 67fa79c..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/service/QuickSettingsTileService.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.service;
-
-import android.annotation.TargetApi;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Build;
-import android.provider.Settings;
-import android.service.quicksettings.Tile;
-import android.service.quicksettings.TileService;
-import android.text.style.BackgroundColorSpan;
-import io.github.ratul.topactivity.utils.DatabaseUtil;
-import io.github.ratul.topactivity.ui.MainActivity;
-import io.github.ratul.topactivity.utils.WindowUtil;
-import io.github.ratul.topactivity.model.NotificationMonitor;
-import io.github.ratul.topactivity.ui.BackgroundActivity;
-
-/**
- * Created by Wen on 5/3/16.
- * Refactored by Ratul on 04/05/2022.
- */
-@TargetApi(Build.VERSION_CODES.N)
-public class QuickSettingsTileService extends TileService {
- public static final String ACTION_UPDATE_TITLE = "io.github.ratul.topactivity.ACTION.UPDATE_TITLE";
- private UpdateTileReceiver mReceiver;
-
- public static void updateTile(Context context) {
- TileService.requestListeningState(context, new ComponentName(context, QuickSettingsTileService.class));
- context.sendBroadcast(new Intent(QuickSettingsTileService.ACTION_UPDATE_TITLE));
- }
-
- public void updateTile() {
- getQsTile().setState(DatabaseUtil.isShowWindow() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
- getQsTile().updateTile();
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- mReceiver = new UpdateTileReceiver();
- }
-
- @Override
- public void onTileAdded() {
- DatabaseUtil.setQSTileAdded(true);
- sendBroadcast(new Intent(MainActivity.ACTION_STATE_CHANGED));
- }
-
- @Override
- public void onTileRemoved() {
- super.onTileRemoved();
- DatabaseUtil.setQSTileAdded(false);
- sendBroadcast(new Intent(MainActivity.ACTION_STATE_CHANGED));
- }
-
- @Override
- public void onStartListening() {
- registerReceiver(mReceiver, new IntentFilter(ACTION_UPDATE_TITLE));
- super.onStartListening();
- updateTile();
- }
-
- @Override
- public void onStopListening() {
- unregisterReceiver(mReceiver);
- super.onStopListening();
- }
-
- @Override
- public void onClick() {
- if (DatabaseUtil.isShowWindow())
- return;
- if (!MainActivity.usageStats(this) || !Settings.canDrawOverlays(this)) {
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(MainActivity.EXTRA_FROM_QS_TILE, true);
- startActivityAndCollapse(intent);
- } else {
- if (DatabaseUtil.hasAccess() && AccessibilityMonitoringService.getInstance() == null)
- startService(new Intent().setClass(this, AccessibilityMonitoringService.class));
- DatabaseUtil.setIsShowWindow(!DatabaseUtil.isShowWindow());
- if (DatabaseUtil.isShowWindow()) {
- if (WindowUtil.sWindowManager == null)
- WindowUtil.init(this);
- NotificationMonitor.showNotification(this, false);
- startService(new Intent(this, MonitoringService.class));
- } else {
- WindowUtil.dismiss(this);
- NotificationMonitor.showNotification(this, true);
- }
- sendBroadcast(new Intent(MainActivity.ACTION_STATE_CHANGED));
- }
- }
-
- class UpdateTileReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- updateTile();
- }
- }
-}
diff --git a/app/src/main/java/io/github/ratul/topactivity/services/AccessibilityMonitoringService.java b/app/src/main/java/io/github/ratul/topactivity/services/AccessibilityMonitoringService.java
new file mode 100644
index 0000000..2530dcd
--- /dev/null
+++ b/app/src/main/java/io/github/ratul/topactivity/services/AccessibilityMonitoringService.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 Ratul Hasan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package io.github.ratul.topactivity.services;
+
+import static io.github.ratul.topactivity.utils.NullSafety.isNullOrEmpty;
+
+import android.accessibilityservice.AccessibilityService;
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+
+import io.github.ratul.topactivity.BuildConfig;
+import io.github.ratul.topactivity.utils.DatabaseUtil;
+import io.github.ratul.topactivity.utils.WindowUtil;
+
+/**
+ * Created by Wen on 16/02/2017.
+ * Refactored by Ratul on 04/05/2022.
+ */
+@SuppressLint("AccessibilityPolicy")
+public class AccessibilityMonitoringService extends AccessibilityService {
+ private static AccessibilityMonitoringService instance;
+
+ public static AccessibilityMonitoringService getInstance() {
+ return instance;
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (WindowUtil.isViewVisible() && DatabaseUtil.isShowingWindow()) {
+ CharSequence pkgName = event.getPackageName();
+ CharSequence className = event.getClassName();
+
+ if (!isNullOrEmpty(pkgName) &&
+ !isNullOrEmpty(className) &&
+ !isSystemClass(className.toString())) {
+ if (BuildConfig.DEBUG) {
+ Log.d("AccessibilityService", "Pkg: " + pkgName + ", Class: " + className);
+ }
+ WindowUtil.show(this, pkgName.toString(), className.toString());
+ }
+ }
+ }
+
+ @Override
+ public void onInterrupt() {
+ }
+
+ @Override
+ protected void onServiceConnected() {
+ instance = this;
+ super.onServiceConnected();
+ }
+
+ @Override
+ public void onRebind(Intent intent) {
+ instance = this;
+ super.onRebind(intent);
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ instance = null;
+ return true;
+ }
+
+ private boolean isSystemClass(String className) {
+ try {
+ return ClassLoader.getSystemClassLoader().loadClass(className) != null;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/ratul/topactivity/services/PackageMonitoringService.java b/app/src/main/java/io/github/ratul/topactivity/services/PackageMonitoringService.java
new file mode 100644
index 0000000..52c056c
--- /dev/null
+++ b/app/src/main/java/io/github/ratul/topactivity/services/PackageMonitoringService.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 Ratul Hasan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package io.github.ratul.topactivity.services;
+
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
+
+import static io.github.ratul.topactivity.utils.NullSafety.isNullOrEmpty;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.core.util.Pair;
+
+import io.github.ratul.topactivity.BuildConfig;
+import io.github.ratul.topactivity.utils.DatabaseUtil;
+import io.github.ratul.topactivity.utils.WindowUtil;
+
+/**
+ * Created by Wen on 16/02/2017.
+ * Refactored by Ratul on 04/05/2022.
+ */
+public class PackageMonitoringService extends Service {
+ private final IBinder binder = new LocalBinder();
+ private final Handler handler = new Handler();
+ private Runnable observerTask;
+ private UsageStatsManager usageStats;
+
+ public class LocalBinder extends Binder {
+ public PackageMonitoringService getService() {
+ return PackageMonitoringService.this;
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return binder;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ usageStats = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+ observerTask = () -> {
+ if (!DatabaseUtil.isShowingWindow()) {
+ handler.removeCallbacks(observerTask);
+ stopSelf();
+ return;
+ }
+
+ Pair currentApp = getForegroundApp();
+ String currentPkgName = currentApp.first;
+ String currentClassName = currentApp.second;
+
+ if (!isNullOrEmpty(currentPkgName) && !isNullOrEmpty(currentClassName)) {
+ if (BuildConfig.DEBUG) {
+ Log.d("PackageMonitoring", "Pkg: " + currentPkgName + ", Class: " + currentClassName);
+ }
+ if (WindowUtil.isViewVisible()) {
+ WindowUtil.show(this, currentPkgName, currentClassName);
+ }
+ }
+ handler.postDelayed(observerTask, 500);
+ };
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ handler.removeCallbacks(observerTask);
+ handler.post(observerTask);
+ return START_STICKY;
+ }
+
+ @Override
+ public void onTaskRemoved(Intent rootIntent) {
+ Intent intent = new Intent(this, getClass());
+ PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(),
+ 264593, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE);
+ AlarmManager alarmService = (AlarmManager) getSystemService(ALARM_SERVICE);
+ alarmService.set(AlarmManager.ELAPSED_REALTIME,
+ SystemClock.elapsedRealtime() + 500, pendingIntent);
+ super.onTaskRemoved(rootIntent);
+ }
+
+ private Pair getForegroundApp() {
+ long currentTime = System.currentTimeMillis();
+ // Query events in the last 10 seconds
+ UsageEvents usageEvents = usageStats.queryEvents(currentTime - 10000, currentTime);
+ String latestPackage = null;
+ String latestClass = null;
+ long latestTimestamp = 0;
+
+ UsageEvents.Event event = new UsageEvents.Event();
+ while (usageEvents.hasNextEvent()) {
+ usageEvents.getNextEvent(event);
+
+ if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
+ if (event.getTimeStamp() > latestTimestamp) {
+ latestTimestamp = event.getTimeStamp();
+ latestPackage = event.getPackageName();
+ latestClass = event.getClassName();
+ }
+ }
+ }
+
+ return new Pair<>(latestPackage, latestClass);
+ }
+}
diff --git a/app/src/main/java/io/github/ratul/topactivity/services/QuickSettingsTileService.java b/app/src/main/java/io/github/ratul/topactivity/services/QuickSettingsTileService.java
new file mode 100644
index 0000000..afe4227
--- /dev/null
+++ b/app/src/main/java/io/github/ratul/topactivity/services/QuickSettingsTileService.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 Ratul Hasan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package io.github.ratul.topactivity.services;
+
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
+
+import android.annotation.SuppressLint;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build;
+import android.service.quicksettings.Tile;
+import android.service.quicksettings.TileService;
+
+import androidx.core.content.ContextCompat;
+
+import io.github.ratul.topactivity.receivers.NotificationReceiver;
+import io.github.ratul.topactivity.ui.MainActivity;
+import io.github.ratul.topactivity.utils.DatabaseUtil;
+import io.github.ratul.topactivity.utils.WindowUtil;
+
+/**
+ * Created by Wen on 5/3/16.
+ * Refactored by Ratul on 04/05/2022.
+ */
+public class QuickSettingsTileService extends TileService {
+ private static final String ACTION_UPDATE_TITLE = "io.github.ratul.topactivity.ACTION_UPDATE_TILE";
+ private UpdateTileReceiver mReceiver;
+
+ public static void updateTile(Context context) {
+ TileService.requestListeningState(context.getApplicationContext(),
+ new ComponentName(context, QuickSettingsTileService.class));
+ context.sendBroadcast(new Intent(QuickSettingsTileService.ACTION_UPDATE_TITLE));
+ }
+
+ public void updateTile() {
+ getQsTile().setState(DatabaseUtil.isShowingWindow() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
+ getQsTile().updateTile();
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mReceiver = new UpdateTileReceiver();
+ }
+
+ @Override
+ public void onTileAdded() {
+ sendBroadcast(new Intent(MainActivity.ACTION_STATE_CHANGED));
+ }
+
+ @Override
+ public void onTileRemoved() {
+ super.onTileRemoved();
+ sendBroadcast(new Intent(MainActivity.ACTION_STATE_CHANGED));
+ }
+
+ @Override
+ public void onStartListening() {
+ ContextCompat.registerReceiver(getApplicationContext(), mReceiver,
+ new IntentFilter(ACTION_UPDATE_TITLE), ContextCompat.RECEIVER_EXPORTED);
+ updateTile();
+ super.onStartListening();
+ }
+
+ @Override
+ public void onStopListening() {
+ getApplicationContext().unregisterReceiver(mReceiver);
+ super.onStopListening();
+ }
+
+ @SuppressLint("StartActivityAndCollapseDeprecated")
+ @Override
+ public void onClick() {
+ if (DatabaseUtil.isShowingWindow() && WindowUtil.isViewVisible()) {
+ DatabaseUtil.setShowingWindow(false);
+ NotificationReceiver.cancelNotification();
+ WindowUtil.dismiss(this);
+ sendBroadcast(new Intent(MainActivity.ACTION_STATE_CHANGED));
+ updateTile();
+ return;
+ }
+
+ Intent intent = new Intent(this, MainActivity.class)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .putExtra(MainActivity.EXTRA_FROM_QS_TILE, true);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ startActivityAndCollapse(PendingIntent.getActivity(this,
+ 7456435, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
+ } else {
+ startActivityAndCollapse(intent);
+ }
+ }
+
+ class UpdateTileReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateTile();
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/ratul/topactivity/ui/BackgroundActivity.java b/app/src/main/java/io/github/ratul/topactivity/ui/BackgroundActivity.java
deleted file mode 100644
index 81dd142..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/ui/BackgroundActivity.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.ui;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.content.ClipboardManager;
-import android.content.ClipData;
-import androidx.appcompat.app.AppCompatActivity;
-import io.github.ratul.topactivity.App;
-
-/**
- * Created by Ratul on 04/05/2022.
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class BackgroundActivity extends AppCompatActivity {
- public static String STRING_COPY = "io.github.ratul.topactivity.COPY_STRING";
- public static String COPY_MSG = "io.github.ratul.topactivity.COPY_STRING_MSG";
- public static boolean isAlive;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (!getIntent().hasExtra(STRING_COPY))
- finish();
- String str = getIntent().getStringExtra(STRING_COPY);
- String msg = getIntent().getStringExtra(COPY_MSG);
- msg = (msg == null || msg.trim().isEmpty()) ? "Copied" : msg;
-
- if (str != null) {
- ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
- ClipData clip = new ClipData(ClipData.newPlainText("", str));
- clipboard.setPrimaryClip(clip);
- }
- finish();
- }
-
- @Override
- protected void onStop() {
- isAlive = false;
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- isAlive = false;
- super.onDestroy();
- }
-
- @Override
- protected void onStart() {
- isAlive = true;
- super.onStart();
- }
-}
diff --git a/app/src/main/java/io/github/ratul/topactivity/ui/CopyToClipboardActivity.java b/app/src/main/java/io/github/ratul/topactivity/ui/CopyToClipboardActivity.java
new file mode 100644
index 0000000..0f57d7f
--- /dev/null
+++ b/app/src/main/java/io/github/ratul/topactivity/ui/CopyToClipboardActivity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 Ratul Hasan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package io.github.ratul.topactivity.ui;
+
+import static io.github.ratul.topactivity.utils.NullSafety.isNullOrEmpty;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import io.github.ratul.topactivity.R;
+
+/**
+ * Created by Ratul on 04/05/2022.
+ */
+public class CopyToClipboardActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getIntent().hasExtra(Intent.EXTRA_TEXT)) {
+ String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
+
+ if (!isNullOrEmpty(text)) {
+ ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ ClipData clip = new ClipData(ClipData.newPlainText(
+ getString(R.string.app_name), text));
+ clipboard.setPrimaryClip(clip);
+ }
+ }
+ finish();
+ }
+}
diff --git a/app/src/main/java/io/github/ratul/topactivity/ui/CrashActivity.java b/app/src/main/java/io/github/ratul/topactivity/ui/CrashActivity.java
deleted file mode 100644
index 27c0af6..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/ui/CrashActivity.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.ui;
-
-import android.app.Activity;
-import android.os.Bundle;
-import com.google.android.material.textview.MaterialTextView;
-import androidx.appcompat.app.AppCompatActivity;
-import io.github.ratul.topactivity.R;
-import android.view.MenuItem;
-import android.content.ClipboardManager;
-import android.content.ClipData;
-import android.view.Menu;
-import android.content.pm.PackageManager;
-import android.content.Intent;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.graphics.Typeface;
-import android.view.View;
-import android.widget.Toast;
-import android.text.SpannableString;
-import androidx.appcompat.app.ActionBar;
-import android.text.Spannable;
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import io.github.ratul.topactivity.model.TypefaceSpan;
-
-/**
- * Created by Ratul on 04/05/2022.
- */
-
-public class CrashActivity extends AppCompatActivity {
- public static String EXTRA_CRASH_INFO = "crash";
- private String crashInfo;
- private boolean restart;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.crash_view);
-
- SpannableString s = new SpannableString(getString(R.string.app_name));
- s.setSpan(new TypefaceSpan(this, "fonts/google_sans_bold.ttf"), 0, s.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- ActionBar actionBar = getSupportActionBar();
- actionBar.setTitle(s);
-
- restart = getIntent().getBooleanExtra("Restart", true);
- String mLog = getIntent().getStringExtra(EXTRA_CRASH_INFO);
- crashInfo = mLog;
- MaterialTextView crashed = findViewById(R.id.crashed);
- crashed.setText(mLog);
- }
-
- @Override
- public void onBackPressed() {
- if (!restart) {
- finish();
- return;
- }
- new MaterialAlertDialogBuilder(this).setTitle("Exit").setMessage("App will restart, are you sure to exit")
- .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int btn) {
- di.dismiss();
- restart();
- }
- }).setNegativeButton("No", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int btn) {
- di.dismiss();
- }
- }).setCancelable(false).show();
- }
-
- private void restart() {
- PackageManager pm = getPackageManager();
- Intent intent = pm.getLaunchIntentForPackage(getPackageName());
- if (intent != null) {
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- startActivity(intent);
- }
- finish();
- android.os.Process.killProcess(android.os.Process.myPid());
- System.exit(0);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.copy) {
- ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), crashInfo));
- Toast.makeText(this, "Copied", 0).show();
- } else if (item.getItemId() == android.R.id.redo) {
- onBackPressed();
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- SpannableString s = new SpannableString("Copy Log");
- s.setSpan(new TypefaceSpan(this, "fonts/google_sans_regular.ttf"), 0, s.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- menu.add(0, android.R.id.copy, 0, s);
- if (restart) {
- s = new SpannableString("Restart App");
- s.setSpan(new TypefaceSpan(this, "fonts/google_sans_regular.ttf"), 0, s.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-
- menu.add(1, android.R.id.redo, 1, s);
- }
- return super.onCreateOptionsMenu(menu);
- }
-
-}
diff --git a/app/src/main/java/io/github/ratul/topactivity/ui/MainActivity.java b/app/src/main/java/io/github/ratul/topactivity/ui/MainActivity.java
index 1b2b612..721934e 100644
--- a/app/src/main/java/io/github/ratul/topactivity/ui/MainActivity.java
+++ b/app/src/main/java/io/github/ratul/topactivity/ui/MainActivity.java
@@ -16,51 +16,48 @@
*/
package io.github.ratul.topactivity.ui;
-import android.Manifest;
-import android.app.Activity;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static io.github.ratul.topactivity.utils.NullSafety.isNullOrEmpty;
+
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
+import android.content.ServiceConnection;
import android.graphics.Insets;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Process;
import android.provider.Settings;
-import android.text.Spannable;
-import android.text.SpannableString;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowMetrics;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.Toast;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.ActionBar;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.SwitchCompat;
import androidx.core.content.ContextCompat;
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import com.google.android.material.switchmaterial.SwitchMaterial;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-
import io.github.ratul.topactivity.App;
+import io.github.ratul.topactivity.BuildConfig;
import io.github.ratul.topactivity.R;
-import io.github.ratul.topactivity.model.NotificationMonitor;
-import io.github.ratul.topactivity.model.TypefaceSpan;
-import io.github.ratul.topactivity.service.AccessibilityMonitoringService;
-import io.github.ratul.topactivity.service.MonitoringService;
+import io.github.ratul.topactivity.receivers.NotificationReceiver;
+import io.github.ratul.topactivity.services.AccessibilityMonitoringService;
+import io.github.ratul.topactivity.services.PackageMonitoringService;
import io.github.ratul.topactivity.utils.DatabaseUtil;
import io.github.ratul.topactivity.utils.WindowUtil;
@@ -69,328 +66,350 @@
* Refactored by Ratul on 04/05/2022.
*/
public class MainActivity extends AppCompatActivity {
- public static final int REQUEST_CODE_NOTIFICATION = 100;
- public static final String EXTRA_FROM_QS_TILE = "from_qs_tile";
- public static final String ACTION_STATE_CHANGED = "io.github.ratul.topactivity.ACTION_STATE_CHANGED";
- private SwitchMaterial mWindowSwitch, mNotificationSwitch, mAccessibilitySwitch;
- private BroadcastReceiver mReceiver;
- private MaterialAlertDialogBuilder fancy;
- public static MainActivity INSTANCE;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- INSTANCE = this;
- if (AccessibilityMonitoringService.getInstance() == null && DatabaseUtil.hasAccess())
- startService(new Intent().setClass(this, AccessibilityMonitoringService.class));
-
- DatabaseUtil.setDisplayWidth(getScreenWidth(this));
- fancy = new MaterialAlertDialogBuilder(this).setNegativeButton("Close", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int btn) {
- di.dismiss();
- }
- }).setCancelable(false);
-
- SpannableString s = new SpannableString(getString(R.string.app_name));
- s.setSpan(new TypefaceSpan(this, "fonts/google_sans_bold.ttf"), 0, s.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- ActionBar actionBar = getSupportActionBar();
- actionBar.setTitle(s);
-
- mWindowSwitch = findViewById(R.id.sw_window);
- mNotificationSwitch = findViewById(R.id.sw_notification);
- mAccessibilitySwitch = findViewById(R.id.sw_accessibility);
-
- if (Build.VERSION.SDK_INT < 24) {
- mNotificationSwitch.setVisibility(View.INVISIBLE);
- findViewById(R.id.divider_useNotificationPref).setVisibility(View.INVISIBLE);
- }
-
- mReceiver = new UpdateSwitchReceiver();
- ContextCompat.registerReceiver(this, mReceiver, new IntentFilter(ACTION_STATE_CHANGED), ContextCompat.RECEIVER_NOT_EXPORTED);
-
- mNotificationSwitch.setOnCheckedChangeListener(new SwitchMaterial.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton button, boolean isChecked) {
- DatabaseUtil.setNotificationToggleEnabled(!isChecked);
- }
- });
- mAccessibilitySwitch.setOnCheckedChangeListener(new SwitchMaterial.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton button, boolean isChecked) {
- DatabaseUtil.setHasAccess(isChecked);
- if (isChecked && AccessibilityMonitoringService.getInstance() == null)
- startService(new Intent().setClass(MainActivity.this, AccessibilityMonitoringService.class));
- }
- });
- mWindowSwitch.setOnCheckedChangeListener(new SwitchMaterial.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton button, boolean isChecked) {
- if (!Settings.canDrawOverlays(MainActivity.this)) {
- fancy.setTitle("Overlay Permission")
- .setMessage("Please enable overlay permission to show window over other apps")
- .setPositiveButton("Settings", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int btn) {
- Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
- intent.setData(Uri.parse("package:" + getPackageName()));
- startActivity(intent);
- di.dismiss();
- }
- }).show();
- mWindowSwitch.setChecked(false);
- } else if (DatabaseUtil.hasAccess() && AccessibilityMonitoringService.getInstance() == null) {
- fancy.setTitle("Accessibility Permission").setMessage(
- "As per your choice, please grant permission to use Accessibility Service for Current Activity app in order to get current activity info")
- .setPositiveButton("Settings", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int btn) {
- Intent intent = new Intent();
- intent.setAction("android.settings.ACCESSIBILITY_SETTINGS");
- startActivity(intent);
- di.dismiss();
- }
- }).show();
- mWindowSwitch.setChecked(false);
- } else if (!usageStats(MainActivity.this)) {
- fancy.setTitle("Usage Access").setMessage(
- "In order to monitor current task, please grant Usage Access permission for Current Activity app")
- .setPositiveButton("Settings", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int btn) {
- Intent intent = new Intent();
- intent.setAction("android.settings.USAGE_ACCESS_SETTINGS");
- startActivity(intent);
- di.dismiss();
- }
- }).show();
- mWindowSwitch.setChecked(false);
- } else {
- DatabaseUtil.setAppInitiated(true);
- DatabaseUtil.setIsShowWindow(isChecked);
- if (!isChecked) {
- WindowUtil.dismiss(MainActivity.this);
- } else {
- WindowUtil.show(MainActivity.this, getPackageName(), getClass().getName());
- startService(new Intent(MainActivity.this, MonitoringService.class));
- }
- }
- }
- });
-
- if (getIntent().getBooleanExtra(EXTRA_FROM_QS_TILE, false))
- mWindowSwitch.setChecked(true);
- }
-
- public static int getScreenWidth(Activity activity) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- WindowMetrics windowMetrics = activity.getWindowManager().getCurrentWindowMetrics();
- Insets insets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
- return windowMetrics.getBounds().width() - insets.left - insets.right;
- } else {
- DisplayMetrics displayMetrics = new DisplayMetrics();
- activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
- return displayMetrics.widthPixels;
- }
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- if (getIntent().getBooleanExtra(EXTRA_FROM_QS_TILE, false)) {
- mWindowSwitch.setChecked(true);
- }
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && !mNotificationSwitch.isChecked()) {
- if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, REQUEST_CODE_NOTIFICATION);
- }
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (requestCode == REQUEST_CODE_NOTIFICATION) {
- if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
- && shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
- requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, REQUEST_CODE_NOTIFICATION);
- } else {
- showToast("POST_NOTIFICATIONS Permission Denied", Toast.LENGTH_SHORT);
- }
- } else {
- showToast("POST_NOTIFICATIONS Permission Granted", Toast.LENGTH_SHORT);
- }
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- refreshWindowSwitch();
- refreshNotificationSwitch();
- refreshAccessibilitySwitch();
- NotificationMonitor.cancelNotification(this);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- if (DatabaseUtil.isShowWindow()) {
- NotificationMonitor.showNotification(this, false);
- }
- }
-
- private void refreshWindowSwitch() {
- mWindowSwitch.setChecked(DatabaseUtil.isShowWindow());
- if (DatabaseUtil.hasAccess() && AccessibilityMonitoringService.getInstance() == null) {
- mWindowSwitch.setChecked(false);
- }
- }
-
- private void refreshAccessibilitySwitch() {
- mAccessibilitySwitch.setChecked(DatabaseUtil.hasAccess());
- }
-
- private void refreshNotificationSwitch() {
- mNotificationSwitch.setChecked(!DatabaseUtil.isNotificationToggleEnabled());
- }
-
- public void showToast(String str, int length) {
- Toast.makeText(this, str, length).show();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add("GitHub Repo").setIcon(R.drawable.ic_github).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- SpannableString span = new SpannableString("About App");
- span.setSpan(new TypefaceSpan(this, "fonts/google_sans_regular.ttf"), 0, span.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- menu.add(span);
- span = new SpannableString("Crash Log");
- span.setSpan(new TypefaceSpan(this, "fonts/google_sans_regular.ttf"), 0, span.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- menu.add(span);
- span = new SpannableString("Bug Report");
- span.setSpan(new TypefaceSpan(this, "fonts/google_sans_regular.ttf"), 0, span.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- menu.add(span);
- return super.onCreateOptionsMenu(menu);
- }
-
- public String readFile(File file) {
- StringBuilder text = new StringBuilder();
- try {
- BufferedReader br = new BufferedReader(new FileReader(file));
- String line = br.readLine();
- while (line != null) {
- text.append(line);
- text.append("\n");
- line = br.readLine();
- }
-
- new FileOutputStream(file).write(text.toString().getBytes());
- } catch (Exception e) {
- e.printStackTrace();
- }
- return text.toString();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- String title = item.getTitle().toString();
- if (title.equals("About App")) {
- fancy.setTitle("About App").setMessage(
- "An useful open source tool for Android Developers, which shows the package name and class name of current activity\n\nHere are the main features of this app!\n● It provides a freely moveable popup window to view current activity info\n● It supports text copying from popup window\n● It supports quick settings and app shortcut for easy access to the popup window. Meaning you can get the popup window in your screen from anywhere")
- .show();
- } else if (title.equals("Crash Log")) {
- String errorLog = readFile(new File(App.getCrashLogDir(), "crash.txt"));
- if (errorLog.isEmpty())
- showToast("No log was found", 0);
- else {
- Intent intent = new Intent(this, CrashActivity.class);
- intent.putExtra(CrashActivity.EXTRA_CRASH_INFO, errorLog);
- intent.putExtra("Restart", false);
- startActivity(intent);
- }
- } else if (title.equals("GitHub Repo")) {
- fancy.setTitle("GitHub Repo").setMessage(
- "It is an open source project. Would you like to visit the official github repo of this app")
- .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int btn) {
- di.dismiss();
- startActivity(new Intent().setAction(Intent.ACTION_VIEW)
- .setData(Uri.parse("https://github.com/ratulhasanrahat/Current-Activity")));
- }
- }).show();
- } else if (title.equals("Bug Report")) {
- fancy.setTitle("Bug Report").setMessage(
- "If you found a bug while using this app, please take a screenshot of it if possible. If it's a crash then you can find the crash log in this directory: "
- + new File(App.getCrashLogDir(), "crash.txt").getAbsolutePath()
- + "\n\nAfter you get all necessary things related to the bug, open an issue in github repo of this app with your bug report details")
- .setPositiveButton("Create", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int btn) {
- di.dismiss();
- startActivity(new Intent().setAction(Intent.ACTION_VIEW).setData(
- Uri.parse("https://github.com/ratulhasanrahat/Current-Activity/issues/new")));
- }
- }).show();
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- unregisterReceiver(mReceiver);
- }
-
- class UpdateSwitchReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- refreshWindowSwitch();
- refreshNotificationSwitch();
- refreshAccessibilitySwitch();
- }
- }
-
- public static boolean usageStats(Context context) {
- boolean granted = false;
- AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(),
- context.getPackageName());
-
- if (mode == AppOpsManager.MODE_DEFAULT) {
- granted = (context.checkCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED);
- } else {
- granted = (mode == AppOpsManager.MODE_ALLOWED);
- }
- return granted;
- }
-
- public void setupBattery() {
- fancy.setTitle("Battery Optimizations").setMessage(
- "Please remove battery optimization/restriction from this app in order to run in background with full functionality")
- .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface di, int btn) {
- di.dismiss();
- Intent intent = new Intent();
- intent.setAction("android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS");
- intent.setData(Uri.parse("package:" + getPackageName()));
- startActivity(intent);
- }
- }).show();
-
- }
+ public static final String ACTION_STATE_CHANGED = "io.github.ratul.topactivity.ACTION_STATE_CHANGED";
+ public static final String EXTRA_FROM_QS_TILE = "from_qs_tile";
+ private ActivityResultLauncher notificationPermissionLauncher;
+ private BroadcastReceiver updateReceiver;
+ private SwitchCompat showWindow, showNotification, useAccessibility;
+ private PackageMonitoringService monitoringService;
+ private boolean isServiceBound = false;
+
+ private final ServiceConnection serviceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ PackageMonitoringService.LocalBinder binder =
+ (PackageMonitoringService.LocalBinder) service;
+ monitoringService = binder.getService();
+ isServiceBound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ isServiceBound = false;
+ monitoringService = null;
+ }
+ };
+
+ class UpdateSwitchReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ refreshWindowSwitch();
+ refreshNotificationSwitch();
+ refreshAccessibilitySwitch();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ startAccessibilityService();
+ DatabaseUtil.setDisplayWidth(getScreenWidth());
+
+ boolean isWindowActuallyShowing = WindowUtil.isViewVisible();
+ if (DatabaseUtil.isShowingWindow() != isWindowActuallyShowing) {
+ DatabaseUtil.setShowingWindow(isWindowActuallyShowing);
+ }
+
+ showWindow = findViewById(R.id.show_window);
+ showNotification = findViewById(R.id.show_notification);
+ useAccessibility = findViewById(R.id.use_accessibility);
+ Button downloadAccessibility = findViewById(R.id.download_accessibility);
+ Button configureWidth = findViewById(R.id.configure_width);
+
+ updateReceiver = new UpdateSwitchReceiver();
+ ContextCompat.registerReceiver(this, updateReceiver,
+ new IntentFilter(ACTION_STATE_CHANGED), ContextCompat.RECEIVER_EXPORTED);
+
+ notificationPermissionLauncher = registerForActivityResult(
+ new ActivityResultContracts.RequestPermission(),
+ isGranted -> {
+ DatabaseUtil.setShowNotification(isGranted);
+ showNotification.setChecked(isGranted);
+ });
+
+ useAccessibility.setOnCheckedChangeListener((button, isChecked) -> {
+ DatabaseUtil.setUseAccessibility(isChecked);
+ startAccessibilityService();
+ });
+
+ showNotification.setOnCheckedChangeListener((button, isChecked) -> {
+ DatabaseUtil.setShowNotification(isChecked);
+
+ if (isChecked && !isNotificationGranted()) {
+ requestNotificationPermission();
+ }
+ });
+
+ showWindow.setOnClickListener(v -> {
+ boolean isChecked = showWindow.isChecked();
+
+ if (!isChecked) {
+ DatabaseUtil.setShowingWindow(false);
+ NotificationReceiver.cancelNotification();
+ WindowUtil.dismiss(this);
+ }
+
+ if (isSystemOverlayGranted() && isCommonPermissionsGranted()) {
+ DatabaseUtil.setShowingWindow(true);
+ WindowUtil.show(this, getPackageName(), getClass().getName());
+ startAccessibilityService();
+ startPackageMonitoringService();
+ } else {
+ showWindow.setChecked(false);
+ requestSystemOverlayPermission();
+ requestCommonPermissions();
+ }
+ });
+
+ downloadAccessibility.setOnClickListener(v -> {
+ Intent intent = new Intent(Intent.ACTION_VIEW)
+ .setData(Uri.parse(
+ "https://github.com/codehasan/Current-Activity/releases/tag/v"
+ + BuildConfig.VERSION_NAME));
+ startActivity(intent);
+ });
+
+ configureWidth.setOnClickListener(v -> configureWidth());
+
+ if (handleQsTileIntent(getIntent())) {
+ moveTaskToBack(true);
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ if (handleQsTileIntent(intent)) {
+ moveTaskToBack(true);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ startAccessibilityService();
+ refreshWindowSwitch();
+ refreshNotificationSwitch();
+ refreshAccessibilitySwitch();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add("GitHub Repo")
+ .setIcon(R.drawable.ic_github)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ menu.add("Check for Update");
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ CharSequence title = item.getTitle();
+ if (isNullOrEmpty(title)) return true;
+
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ switch (title.toString()) {
+ case "GitHub Repo":
+ intent.setData(Uri.parse("https://github.com/codehasan/Current-Activity"));
+ startActivity(intent);
+ break;
+ case "Check for Update":
+ intent.setData(Uri.parse("https://github.com/codehasan/Current-Activity/releases"));
+ startActivity(intent);
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ protected void onDestroy() {
+ unregisterReceiver(updateReceiver);
+ super.onDestroy();
+ }
+
+ private int getScreenWidth() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ WindowMetrics windowMetrics = getWindowManager().getCurrentWindowMetrics();
+ Insets insets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
+ return windowMetrics.getBounds().width() - insets.left - insets.right;
+ } else {
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ return displayMetrics.widthPixels;
+ }
+ }
+
+ private boolean handleQsTileIntent(Intent intent) {
+ if (intent.getBooleanExtra(EXTRA_FROM_QS_TILE, false)) {
+ showWindow.setChecked(true);
+ showWindow.callOnClick();
+ return DatabaseUtil.isShowingWindow();
+ }
+ return false;
+ }
+
+ private boolean isCommonPermissionsGranted() {
+ return !isAccessibilityNotStarted() && isUsageStatsGranted();
+ }
+
+ private boolean isSystemOverlayGranted() {
+ return Settings.canDrawOverlays(this);
+ }
+
+ private boolean isNotificationGranted() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ return true;
+ }
+ return checkSelfPermission(POST_NOTIFICATIONS) == PERMISSION_GRANTED;
+ }
+
+ private boolean isUsageStatsGranted() {
+ AppOpsManager appOps = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
+ int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
+ Process.myUid(), getPackageName());
+
+ if (mode == AppOpsManager.MODE_DEFAULT) {
+ return checkCallingOrSelfPermission(PACKAGE_USAGE_STATS) == PERMISSION_GRANTED;
+ }
+ return mode == AppOpsManager.MODE_ALLOWED;
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ private boolean isAccessibilityNotStarted() {
+ return BuildConfig.FLAVOR.equals("global") &&
+ DatabaseUtil.useAccessibility() &&
+ AccessibilityMonitoringService.getInstance() == null;
+ }
+
+ private void configureWidth() {
+ View dialogView = getLayoutInflater().inflate(R.layout.content_configure_width, null);
+ EditText widthInput = dialogView.findViewById(R.id.width);
+ TextView helperText = dialogView.findViewById(R.id.helper);
+
+ int screenWidth = getScreenWidth();
+ int userWidth = DatabaseUtil.getUserWidth();
+
+ if (userWidth != -1) {
+ widthInput.setText(String.valueOf(userWidth));
+ }
+ helperText.append("enter a width between 500 and " + screenWidth + ".");
+
+ AlertDialog alertDialog = new AlertDialog.Builder(this)
+ .setTitle("Configure Width")
+ .setView(dialogView)
+ .setNeutralButton("Cancel", (dialog, which) -> dialog.dismiss())
+ .setPositiveButton("Save", null)
+ .create();
+
+ alertDialog.setOnShowListener(dialog -> {
+ Button saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ saveButton.setOnClickListener(v -> {
+ String input = widthInput.getText().toString();
+
+ if (input.trim().isEmpty()) {
+ DatabaseUtil.setUserWidth(-1);
+ dialog.dismiss();
+ App.showToast(this, "Saved");
+ return;
+ }
+
+ int width = Integer.parseInt(input);
+ if (width < 500) {
+ widthInput.setError("Width should be greater than 500");
+ return;
+ } else if (width > screenWidth) {
+ widthInput.setError("Width should be less than screen width (" + screenWidth + ")");
+ return;
+ }
+
+ DatabaseUtil.setUserWidth(width);
+ dialog.dismiss();
+ App.showToast(this, "Saved");
+ });
+ });
+
+ alertDialog.show();
+ }
+
+ private void startAccessibilityService() {
+ // Start Accessibility Monitoring Service if accessibility is enabled
+ if (isAccessibilityNotStarted()) {
+ Intent intent = new Intent(
+ this, AccessibilityMonitoringService.class);
+ getApplicationContext().startService(intent);
+ }
+ }
+
+ private void startPackageMonitoringService() {
+ Intent intent = new Intent(this, PackageMonitoringService.class);
+ getApplicationContext().startService(intent);
+ getApplicationContext().bindService(
+ intent, serviceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ private void refreshWindowSwitch() {
+ showWindow.setChecked(DatabaseUtil.isShowingWindow());
+ }
+
+ private void refreshNotificationSwitch() {
+ if (!isNotificationGranted()) {
+ DatabaseUtil.setShowNotification(false);
+ showNotification.setChecked(false);
+ return;
+ }
+ showNotification.setChecked(DatabaseUtil.isShowNotification());
+ }
+
+ private void refreshAccessibilitySwitch() {
+ useAccessibility.setChecked(DatabaseUtil.useAccessibility());
+ }
+
+ private void requestNotificationPermission() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ return;
+ }
+ if (checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
+ notificationPermissionLauncher.launch(POST_NOTIFICATIONS);
+ }
+ }
+
+ private void requestSystemOverlayPermission() {
+ if (!Settings.canDrawOverlays(this)) {
+ new AlertDialog.Builder(this)
+ .setTitle("System Overlay")
+ .setMessage("Please allow draw over other apps permission for 'Current Activity'")
+ .setPositiveButton("Settings", (dialog, which) -> {
+ Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
+ .setData(Uri.parse("package:" + getPackageName()));
+ startActivity(intent);
+ dialog.dismiss();
+ })
+ .show();
+ }
+ }
+
+ private void requestCommonPermissions() {
+ if (isAccessibilityNotStarted()) {
+ new AlertDialog.Builder(this)
+ .setTitle("Accessibility Permission")
+ .setMessage("Please enable Accessibility Service for 'Current Activity'")
+ .setPositiveButton("Settings", (dialog, button) -> {
+ startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
+ dialog.dismiss();
+ })
+ .show();
+ }
+
+ if (!isUsageStatsGranted()) {
+ new AlertDialog.Builder(this)
+ .setTitle("Usage Access")
+ .setMessage("Please allow Usage Access permission for 'Current Activity'")
+ .setPositiveButton("Settings", (di, btn) -> {
+ startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
+ di.dismiss();
+ })
+ .show();
+ }
+ }
}
diff --git a/app/src/main/java/io/github/ratul/topactivity/ui/ShortcutHandlerActivity.java b/app/src/main/java/io/github/ratul/topactivity/ui/ShortcutHandlerActivity.java
deleted file mode 100644
index a13e9cf..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/ui/ShortcutHandlerActivity.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.ui;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.provider.Settings;
-import androidx.appcompat.app.AppCompatActivity;
-import io.github.ratul.topactivity.utils.DatabaseUtil;
-import io.github.ratul.topactivity.utils.WindowUtil;
-import io.github.ratul.topactivity.model.NotificationMonitor;
-import io.github.ratul.topactivity.service.MonitoringService;
-import io.github.ratul.topactivity.service.AccessibilityMonitoringService;
-
-/**
- * Created by Wen on 16/02/2017.
- * Refactored by Ratul on 04/05/2022.
- */
-@TargetApi(Build.VERSION_CODES.N)
-public class ShortcutHandlerActivity extends AppCompatActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- DatabaseUtil.setDisplayWidth(MainActivity.getScreenWidth(this));
-
- if (!MainActivity.usageStats(this) || !Settings.canDrawOverlays(this)) {
- Intent intent = new Intent(this, MainActivity.class);
- intent.putExtra(MainActivity.EXTRA_FROM_QS_TILE, true);
- startActivity(intent);
- finish();
- } else if (AccessibilityMonitoringService.getInstance() == null && DatabaseUtil.hasAccess())
- startService(new Intent().setClass(this, AccessibilityMonitoringService.class));
-
- boolean isShow = !DatabaseUtil.isShowWindow();
- DatabaseUtil.setIsShowWindow(isShow);
- if (!isShow) {
- WindowUtil.dismiss(this);
- NotificationMonitor.showNotification(this, true);
- } else {
- WindowUtil.init(this);
- NotificationMonitor.showNotification(this, false);
- startService(new Intent(this, MonitoringService.class));
- }
- sendBroadcast(new Intent(MainActivity.ACTION_STATE_CHANGED));
- finish();
- }
-}
diff --git a/app/src/main/java/io/github/ratul/topactivity/utils/DatabaseUtil.java b/app/src/main/java/io/github/ratul/topactivity/utils/DatabaseUtil.java
index a57d6fe..9d821f8 100644
--- a/app/src/main/java/io/github/ratul/topactivity/utils/DatabaseUtil.java
+++ b/app/src/main/java/io/github/ratul/topactivity/utils/DatabaseUtil.java
@@ -16,8 +16,6 @@
*/
package io.github.ratul.topactivity.utils;
-import android.content.Context;
-import android.content.SharedPreferences;
import io.github.ratul.topactivity.App;
/**
@@ -25,64 +23,55 @@
* Refactored by Ratul on 04/05/2022.
*/
public class DatabaseUtil {
- private static SharedPreferences sp = App.getApp().getSharedPreferences("io.github.ratul.topactivity", 0);
-
- public static int getDisplayWidth() {
- return sp.getInt("width", 720);
- }
-
- public static void setDisplayWidth(int width) {
- sp.edit().putInt("width", width).apply();
- }
-
- public static boolean isShowWindow() {
- return sp.getBoolean("is_show_window", false);
- }
-
- public static boolean hasBattery() {
- return sp.getBoolean("hasBattery", false);
- }
-
- public static void setHasBattery(boolean bool) {
- sp.edit().putBoolean("hasBattery", bool).apply();
- }
-
- public static void setIsShowWindow(boolean isShow) {
- sp.edit().putBoolean("is_show_window", isShow).apply();
- }
-
- public static boolean appInitiated() {
- return sp.getBoolean("app_init", false);
- }
-
- public static void setAppInitiated(boolean added) {
- sp.edit().putBoolean("app_init", added).apply();
- }
-
- public static boolean hasAccess() {
- return sp.getBoolean("has_access", true);
- }
-
- public static void setHasAccess(boolean added) {
- sp.edit().putBoolean("has_access", added).apply();
- }
-
- public static boolean hasQSTileAdded() {
- return sp.getBoolean("has_qs_tile_added", false);
- }
-
- public static void setQSTileAdded(boolean added) {
- sp.edit().putBoolean("has_qs_tile_added", added).apply();
- }
-
- public static boolean isNotificationToggleEnabled() {
- if (!hasQSTileAdded()) {
- return true;
- }
- return sp.getBoolean("is_noti_toggle_enabled", true);
- }
-
- public static void setNotificationToggleEnabled(boolean isEnabled) {
- sp.edit().putBoolean("is_noti_toggle_enabled", isEnabled).apply();
- }
+ public static int getDisplayWidth() {
+ return App.getInstance().getSharedPreferences()
+ .getInt("width", 720);
+ }
+
+ public static void setDisplayWidth(int width) {
+ App.getInstance().getSharedPreferences().edit()
+ .putInt("width", width)
+ .apply();
+ }
+
+ public static int getUserWidth() {
+ return App.getInstance().getSharedPreferences()
+ .getInt("user_width", -1);
+ }
+
+ public static void setUserWidth(int width) {
+ App.getInstance().getSharedPreferences().edit()
+ .putInt("user_width", width)
+ .apply();
+ }
+
+ public static boolean isShowingWindow() {
+ return App.getInstance().getSharedPreferences()
+ .getBoolean("is_show_window", false);
+ }
+
+ public static void setShowingWindow(boolean bool) {
+ App.getInstance().getSharedPreferences().edit()
+ .putBoolean("is_show_window", bool).apply();
+ }
+
+ public static boolean useAccessibility() {
+ return App.getInstance().getSharedPreferences()
+ .getBoolean("has_access", false);
+ }
+
+ public static void setUseAccessibility(boolean bool) {
+ App.getInstance().getSharedPreferences().edit()
+ .putBoolean("has_access", bool).apply();
+ }
+
+ public static boolean isShowNotification() {
+ return App.getInstance().getSharedPreferences()
+ .getBoolean("show_notification", false);
+ }
+
+ public static void setShowNotification(boolean bool) {
+ App.getInstance().getSharedPreferences().edit()
+ .putBoolean("show_notification", bool).apply();
+ }
}
diff --git a/app/src/main/java/io/github/ratul/topactivity/utils/NullSafety.java b/app/src/main/java/io/github/ratul/topactivity/utils/NullSafety.java
new file mode 100644
index 0000000..8e7a7f6
--- /dev/null
+++ b/app/src/main/java/io/github/ratul/topactivity/utils/NullSafety.java
@@ -0,0 +1,145 @@
+package io.github.ratul.topactivity.utils;
+
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.util.SparseArray;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Can be used up to Java 8
+ */
+public class NullSafety {
+ @NonNull
+ public static T requireNonNullElse(@Nullable T obj, @NonNull T defaultObj) {
+ return (obj != null) ? obj : Objects.requireNonNull(defaultObj, "defaultObj");
+ }
+
+ /**
+ * Returns {@code true} if the given {@link CharSequence} is {@code null} or
+ * consists only of whitespace characters.
+ */
+ public static boolean isNullOrEmpty(@Nullable CharSequence charSequence) {
+ return charSequence == null || isBlank(charSequence);
+ }
+
+ /**
+ * Returns {@code true} if the given array is {@code null} or has no elements.
+ */
+ public static boolean isNullOrEmpty(@Nullable T[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
+ * Returns {@code true} if the given array is {@code null} or has no elements.
+ */
+ public static boolean isNullOrEmpty(@Nullable int[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
+ * Returns {@code true} if the given array is {@code null} or has no elements.
+ */
+ public static boolean isNullOrEmpty(@Nullable char[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
+ * Returns {@code true} if the given array is {@code null} or has no elements.
+ */
+ public static boolean isNullOrEmpty(@Nullable byte[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
+ * Returns {@code true} if the given array is {@code null} or has no elements.
+ */
+ public static boolean isNullOrEmpty(@Nullable short[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
+ * Returns {@code true} if the given array is {@code null} or has no elements.
+ */
+ public static boolean isNullOrEmpty(@Nullable long[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
+ * Returns {@code true} if the given array is {@code null} or has no elements.
+ */
+ public static boolean isNullOrEmpty(@Nullable double[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
+ * Returns {@code true} if the given array is {@code null} or has no elements.
+ */
+ public static boolean isNullOrEmpty(@Nullable float[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
+ * Returns {@code true} if the given {@link Collection} is {@code null} or empty.
+ */
+ public static boolean isNullOrEmpty(@Nullable Collection collection) {
+ return collection == null || collection.isEmpty();
+ }
+
+ /**
+ * Returns {@code true} if the given {@link SparseArray} is {@code null} or has no
+ * elements.
+ */
+ public static boolean isNullOrEmpty(@Nullable SparseArray sparseArray) {
+ return sparseArray == null || sparseArray.size() == 0;
+ }
+
+ /**
+ * Returns {@code true} if the given {@link Map} is {@code null} or empty.
+ */
+ public static boolean isNullOrEmpty(@Nullable Map map) {
+ return map == null || map.isEmpty();
+ }
+
+ /**
+ * Returns {@code true} if the given {@link Bundle} is {@code null} or empty.
+ */
+ public static boolean isNullOrEmpty(@Nullable Bundle bundle) {
+ return bundle == null || bundle.isEmpty();
+ }
+
+ /**
+ * Returns {@code true} if the given {@link PersistableBundle} is {@code null} or empty.
+ */
+ public static boolean isNullOrEmpty(@Nullable PersistableBundle bundle) {
+ return bundle == null || bundle.isEmpty();
+ }
+
+ /**
+ * Returns {@code true} if the given {@link Cursor} is {@code null}, closed, or has no
+ * rows.
+ */
+ public static boolean isNullOrEmpty(@Nullable Cursor cursor) {
+ return cursor == null || cursor.isClosed() || cursor.getCount() == 0;
+ }
+
+ private static boolean isBlank(@NonNull CharSequence charSequence) {
+ int length = charSequence.length();
+ int left = 0;
+
+ while (left < length) {
+ char ch = charSequence.charAt(left);
+ if (ch != ' ' && ch != '\t' && !Character.isWhitespace(ch)) {
+ return false;
+ }
+ left++;
+ }
+ return true;
+ }
+}
diff --git a/app/src/main/java/io/github/ratul/topactivity/utils/WindowUtil.java b/app/src/main/java/io/github/ratul/topactivity/utils/WindowUtil.java
index e797c92..1c856d1 100644
--- a/app/src/main/java/io/github/ratul/topactivity/utils/WindowUtil.java
+++ b/app/src/main/java/io/github/ratul/topactivity/utils/WindowUtil.java
@@ -16,186 +16,159 @@
*/
package io.github.ratul.topactivity.utils;
+import android.annotation.SuppressLint;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
import android.os.Build;
-import android.view.MotionEvent;
-import android.view.View;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
import android.view.WindowManager;
-import android.content.ClipboardManager;
-import android.content.ClipData;
-import android.widget.Toast;
-import android.widget.LinearLayout;
-import android.graphics.Typeface;
-import android.content.Intent;
-import com.google.android.material.imageview.ShapeableImageView;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import io.github.ratul.topactivity.App;
import io.github.ratul.topactivity.R;
-import io.github.ratul.topactivity.model.NotificationMonitor;
-import com.google.android.material.textview.MaterialTextView;
+import io.github.ratul.topactivity.receivers.NotificationReceiver;
+import io.github.ratul.topactivity.services.QuickSettingsTileService;
import io.github.ratul.topactivity.ui.MainActivity;
-import io.github.ratul.topactivity.ui.BackgroundActivity;
-import io.github.ratul.topactivity.service.QuickSettingsTileService;
-import io.github.ratul.topactivity.service.MonitoringService;
-import io.github.ratul.topactivity.service.AccessibilityMonitoringService;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import io.github.ratul.topactivity.App;
/**
* Created by Ratul on 04/05/2022.
*/
public class WindowUtil {
- private static WindowManager.LayoutParams sWindowParams;
- public static WindowManager sWindowManager;
- private static View sView;
- private static int xInitCord = 0;
- private static int yInitCord = 0;
- private static int xInitMargin = 0;
- private static int yInitMargin = 0;
- private static String text, text1;
- private static MaterialTextView appName, packageName, className;
- private static ClipboardManager clipboard;
- public static boolean viewAdded = false;
-
- public static void init(final Context context) {
- sWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-
- sWindowParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.WRAP_CONTENT,
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
- : WindowManager.LayoutParams.TYPE_PHONE,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
-
- sWindowParams.gravity = Gravity.CENTER;
- sWindowParams.width = (DatabaseUtil.getDisplayWidth() / 2) + 300;
- sWindowParams.windowAnimations = android.R.style.Animation_Toast;
-
- sView = LayoutInflater.from(context).inflate(R.layout.window_tasks, null);
- clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
- appName = sView.findViewById(R.id.text);
- packageName = sView.findViewById(R.id.text1);
- className = sView.findViewById(R.id.text2);
- ShapeableImageView closeBtn = sView.findViewById(R.id.closeBtn);
-
- closeBtn.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- dismiss(context);
- DatabaseUtil.setIsShowWindow(false);
- NotificationMonitor.cancelNotification(context);
- context.sendBroadcast(new Intent(MainActivity.ACTION_STATE_CHANGED));
- }
- });
-
- appName.setOnLongClickListener(new View.OnLongClickListener() {
- public boolean onLongClick(View v) {
- copyString(context, text, "App name copied");
- return true;
- }
- });
-
- packageName.setOnLongClickListener(new View.OnLongClickListener() {
- public boolean onLongClick(View v) {
- copyString(context, text, "Package name copied");
- return true;
- }
- });
-
- className.setOnLongClickListener(new View.OnLongClickListener() {
- public boolean onLongClick(View v) {
- copyString(context, text1, "Class name copied");
- return true;
- }
- });
-
- sView.setOnTouchListener(new View.OnTouchListener() {
- public boolean onTouch(View view, MotionEvent event) {
- WindowManager.LayoutParams layoutParams = sWindowParams;
-
- int xCord = (int) event.getRawX();
- int yCord = (int) event.getRawY();
- int xCordDestination;
- int yCordDestination;
- int action = event.getAction();
-
- if (action == MotionEvent.ACTION_DOWN) {
- xInitCord = xCord;
- yInitCord = yCord;
- xInitMargin = layoutParams.x;
- yInitMargin = layoutParams.y;
- }
- else if (action == MotionEvent.ACTION_MOVE) {
- int xDiffMove = xCord - xInitCord;
- int yDiffMove = yCord - yInitCord;
- xCordDestination = xInitMargin + xDiffMove;
- yCordDestination = yInitMargin + yDiffMove;
-
- layoutParams.x = xCordDestination;
- layoutParams.y = yCordDestination;
- sWindowManager.updateViewLayout(view, layoutParams);
- }
- return true;
- }
- });
- }
-
- private static void copyString(Context context, String str, String msg) {
- if (Build.VERSION.SDK_INT < 29) {
- ClipData clip = ClipData.newPlainText("Current Activity", str);
- clipboard.setPrimaryClip(clip);
- } else {
- context.startActivity(
- new Intent(context, BackgroundActivity.class).putExtra(BackgroundActivity.STRING_COPY, str)
- .putExtra(BackgroundActivity.COPY_MSG, msg).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- }
- App.showToast(msg, 0);
- }
-
- public static String getAppName(Context context, String pkg) {
- try {
- PackageManager pm = context.getPackageManager();
- return pm.getApplicationLabel(pm.getApplicationInfo(pkg, 0)).toString();
- } catch (Exception e) {
- return "Unknown";
- }
- }
-
- public static void show(Context context, String pkg, String clas) {
- if (sWindowManager == null) {
- init(context);
- }
- appName.setText(getAppName(context, pkg));
- packageName.setText(pkg);
- className.setText(clas);
-
- if (!viewAdded) {
- viewAdded = true;
- if (DatabaseUtil.isShowWindow()) {
- sWindowManager.addView(sView, sWindowParams);
- }
- }
-
- if (NotificationMonitor.builder != null) {
- NotificationMonitor.builder.setContentTitle(pkg);
- NotificationMonitor.builder.setContentText(clas);
- NotificationMonitor.notifManager.notify(NotificationMonitor.NOTIFICATION_ID,
- NotificationMonitor.builder.build());
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- QuickSettingsTileService.updateTile(context);
- }
- }
-
- public static void dismiss(Context context) {
- viewAdded = false;
- try {
- sWindowManager.removeView(sView);
- } catch (Exception e) {
- e.printStackTrace();
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- QuickSettingsTileService.updateTile(context);
- }
- }
+ private static WindowManager.LayoutParams layoutParams;
+ private static WindowManager windowManager;
+ private static PackageManager packageManager;
+ private static View baseView;
+ private static int xInitCord = 0;
+ private static int yInitCord = 0;
+ private static int xInitMargin = 0;
+ private static int yInitMargin = 0;
+ private static TextView appName, packageName, className;
+
+ public static void show(
+ @NonNull Context context, @NonNull String pkg, @NonNull String cls) {
+ if (windowManager == null || baseView == null) {
+ init(context.getApplicationContext());
+ }
+
+ if (!isViewVisible()) {
+ int userWidth = DatabaseUtil.getUserWidth();
+ if (userWidth != -1) {
+ layoutParams.width = userWidth;
+ } else {
+ double displaySize = Math.min(1100, DatabaseUtil.getDisplayWidth());
+ layoutParams.width = (int) (displaySize * 0.65);
+ }
+ windowManager.addView(baseView, layoutParams);
+ QuickSettingsTileService.updateTile(context);
+ }
+
+ boolean isPackageChanged = !packageName.getText().toString().equals(pkg);
+ boolean isClassChanged = !className.getText().toString().equals(cls);
+
+ if (isPackageChanged) {
+ appName.setText(getAppName(pkg));
+ packageName.setText(pkg);
+ }
+
+ if (isClassChanged) {
+ className.setText(cls);
+ }
+
+ if (isPackageChanged || isClassChanged) {
+ NotificationReceiver.showNotification(context, pkg, cls);
+ }
+ }
+
+ public static void dismiss(@NonNull Context context) {
+ if (windowManager != null) {
+ windowManager.removeView(baseView);
+ }
+ QuickSettingsTileService.updateTile(context);
+ }
+
+ public static boolean isViewVisible() {
+ return baseView != null && baseView.isAttachedToWindow();
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ private static void init(@NonNull Context context) {
+ windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ packageManager = context.getPackageManager();
+
+ layoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+ : WindowManager.LayoutParams.TYPE_PHONE,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
+
+ layoutParams.gravity = Gravity.CENTER;
+ layoutParams.windowAnimations = android.R.style.Animation_Toast;
+
+ baseView = LayoutInflater.from(context).inflate(R.layout.content_activity_info, null);
+ appName = baseView.findViewById(R.id.app_name);
+ packageName = baseView.findViewById(R.id.package_name);
+ className = baseView.findViewById(R.id.class_name);
+ ImageView closeBtn = baseView.findViewById(R.id.closeBtn);
+
+ View.OnLongClickListener copyListener = v -> {
+ TextView textView = (TextView) v;
+ String label = "";
+
+ if (v.getId() == R.id.app_name) label = "App name";
+ else if (v.getId() == R.id.package_name) label = "Package";
+ else if (v.getId() == R.id.class_name) label = "Class";
+
+ App.copyString(context, textView.getText().toString(), label + " copied");
+ return true;
+ };
+
+ closeBtn.setOnClickListener(v -> {
+ DatabaseUtil.setShowingWindow(false);
+ NotificationReceiver.cancelNotification();
+ dismiss(context);
+ context.sendBroadcast(new Intent(MainActivity.ACTION_STATE_CHANGED));
+ });
+
+ packageName.setOnLongClickListener(copyListener);
+ className.setOnLongClickListener(copyListener);
+
+ baseView.setOnTouchListener((view, event) -> {
+ int xCord = (int) event.getRawX();
+ int yCord = (int) event.getRawY();
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ xInitCord = xCord;
+ yInitCord = yCord;
+ xInitMargin = layoutParams.x;
+ yInitMargin = layoutParams.y;
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ int xDiffMove = xCord - xInitCord;
+ int yDiffMove = yCord - yInitCord;
+ layoutParams.x = xInitMargin + xDiffMove;
+ layoutParams.y = yInitMargin + yDiffMove;
+ windowManager.updateViewLayout(view, layoutParams);
+ return true;
+ }
+ return false;
+ });
+ }
+
+ private static String getAppName(@NonNull String pkg) {
+ try {
+ return packageManager.getApplicationLabel(
+ packageManager.getApplicationInfo(pkg, 0)).toString();
+ } catch (PackageManager.NameNotFoundException ignored) {
+ return "Unknown";
+ }
+ }
}
diff --git a/app/src/main/java/io/github/ratul/topactivity/view/BoldTextView.java b/app/src/main/java/io/github/ratul/topactivity/view/BoldTextView.java
deleted file mode 100644
index 5d87080..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/view/BoldTextView.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.view;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Typeface;
-import android.util.AttributeSet;
-import com.google.android.material.textview.MaterialTextView;
-
-public class BoldTextView extends MaterialTextView {
- public void setBoldFont(Context context) {
- Typeface face = Typeface.createFromAsset(context.getAssets(), "fonts/google_sans_bold.ttf");
- super.setTypeface(face);
- }
-
- public BoldTextView(Context context) {
- super(context);
- setBoldFont(context);
- }
-
- public BoldTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- setBoldFont(context);
- }
-
- public BoldTextView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- setBoldFont(context);
- }
-
- public BoldTextView(Context context, AttributeSet attrs, int defStyle, int res) {
- super(context, attrs, defStyle, res);
- setBoldFont(context);
- }
-
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- }
-}
diff --git a/app/src/main/java/io/github/ratul/topactivity/view/NormalTextView.java b/app/src/main/java/io/github/ratul/topactivity/view/NormalTextView.java
deleted file mode 100644
index 381c8d7..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/view/NormalTextView.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.view;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Typeface;
-import android.util.AttributeSet;
-import com.google.android.material.textview.MaterialTextView;
-
-public class NormalTextView extends MaterialTextView {
- public void setRegularFont(Context context) {
- Typeface face = Typeface.createFromAsset(context.getAssets(), "fonts/google_sans_regular.ttf");
- super.setTypeface(face);
- }
-
- public NormalTextView(Context context) {
- super(context);
- setRegularFont(context);
- }
-
- public NormalTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- setRegularFont(context);
- }
-
- public NormalTextView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- setRegularFont(context);
- }
-
- public NormalTextView(Context context, AttributeSet attrs, int defStyle, int res) {
- super(context, attrs, defStyle, res);
- setRegularFont(context);
- }
-
- protected void onDraw (Canvas canvas) {
- super.onDraw(canvas);
- }
-}
diff --git a/app/src/main/java/io/github/ratul/topactivity/view/RegularTextView.java b/app/src/main/java/io/github/ratul/topactivity/view/RegularTextView.java
deleted file mode 100644
index 8470b9c..0000000
--- a/app/src/main/java/io/github/ratul/topactivity/view/RegularTextView.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2022 Ratul Hasan
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package io.github.ratul.topactivity.view;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Typeface;
-import android.util.AttributeSet;
-import com.google.android.material.textview.MaterialTextView;
-
-public class RegularTextView extends MaterialTextView {
- public void setRegularFont(Context context) {
- Typeface face = Typeface.createFromAsset(context.getAssets(), "fonts/google_sans_regular.ttf");
- super.setTypeface(face, 1);
- }
-
- public RegularTextView(Context context) {
- super(context);
- setRegularFont(context);
- }
-
- public RegularTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- setRegularFont(context);
- }
-
- public RegularTextView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- setRegularFont(context);
- }
-
- public RegularTextView(Context context, AttributeSet attrs, int defStyle, int res) {
- super(context, attrs, defStyle, res);
- setRegularFont(context);
- }
-
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- }
-}
diff --git a/app/src/main/res/anim/bottom_sheet_close.xml b/app/src/main/res/anim/bottom_sheet_close.xml
deleted file mode 100644
index 9dab7af..0000000
--- a/app/src/main/res/anim/bottom_sheet_close.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
diff --git a/app/src/main/res/anim/bottom_sheet_enter.xml b/app/src/main/res/anim/bottom_sheet_enter.xml
deleted file mode 100644
index ee4ef8d..0000000
--- a/app/src/main/res/anim/bottom_sheet_enter.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
diff --git a/app/src/main/res/anim/fancy_animation_enter.xml b/app/src/main/res/anim/fancy_animation_enter.xml
deleted file mode 100644
index e332395..0000000
--- a/app/src/main/res/anim/fancy_animation_enter.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/anim/fancy_animation_exit.xml b/app/src/main/res/anim/fancy_animation_exit.xml
deleted file mode 100644
index 3acb0c5..0000000
--- a/app/src/main/res/anim/fancy_animation_exit.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_cancel.xml b/app/src/main/res/drawable/ic_cancel.xml
new file mode 100644
index 0000000..fe302f5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cancel.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_class.xml b/app/src/main/res/drawable/ic_class.xml
new file mode 100644
index 0000000..86cec13
--- /dev/null
+++ b/app/src/main/res/drawable/ic_class.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_github.xml b/app/src/main/res/drawable/ic_github.xml
index 03381d5..d56a2c9 100644
--- a/app/src/main/res/drawable/ic_github.xml
+++ b/app/src/main/res/drawable/ic_github.xml
@@ -2,10 +2,9 @@
+ android:viewportWidth="24"
+ android:viewportHeight="24">
-
+ android:pathData="M12,2A10,10 0,0 0,2 12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0,0 0,12 2Z" />
diff --git a/app/src/main/res/drawable/ic_launcher.png b/app/src/main/res/drawable/ic_launcher.png
deleted file mode 100644
index e3ce3a3..0000000
Binary files a/app/src/main/res/drawable/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.png b/app/src/main/res/drawable/ic_launcher_foreground.png
deleted file mode 100644
index 645f29e..0000000
Binary files a/app/src/main/res/drawable/ic_launcher_foreground.png and /dev/null differ
diff --git a/app/src/main/res/drawable/ic_launcher_round.png b/app/src/main/res/drawable/ic_launcher_round.png
deleted file mode 100644
index 7b1dffb..0000000
Binary files a/app/src/main/res/drawable/ic_launcher_round.png and /dev/null differ
diff --git a/app/src/main/res/drawable/layers.xml b/app/src/main/res/drawable/ic_logo.xml
similarity index 88%
rename from app/src/main/res/drawable/layers.xml
rename to app/src/main/res/drawable/ic_logo.xml
index b4f637e..ca4573d 100644
--- a/app/src/main/res/drawable/layers.xml
+++ b/app/src/main/res/drawable/ic_logo.xml
@@ -1,28 +1,28 @@
diff --git a/app/src/main/res/drawable/ic_package.xml b/app/src/main/res/drawable/ic_package.xml
new file mode 100644
index 0000000..a1e3029
--- /dev/null
+++ b/app/src/main/res/drawable/ic_package.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_shortcut.xml b/app/src/main/res/drawable/ic_shortcut.xml
deleted file mode 100644
index b27c8af..0000000
--- a/app/src/main/res/drawable/ic_shortcut.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
- -
-
-
-
-
-
-
diff --git a/app/src/main/res/font/product_sans_medium.ttf b/app/src/main/res/font/product_sans_medium.ttf
new file mode 100644
index 0000000..1543660
Binary files /dev/null and b/app/src/main/res/font/product_sans_medium.ttf differ
diff --git a/app/src/main/assets/fonts/google_sans_regular.ttf b/app/src/main/res/font/product_sans_regular.ttf
similarity index 100%
rename from app/src/main/assets/fonts/google_sans_regular.ttf
rename to app/src/main/res/font/product_sans_regular.ttf
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 9629aa1..f222959 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,93 +1,160 @@
-
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
-
+ android:text="Show Floating Window"
+ android:textColor="?editTextColor"
+ android:textSize="16sp" />
-
+ android:layout_height="wrap_content" />
-
+ android:background="?actionBarDivider" />
-
+ android:orientation="vertical">
-
+
+
+
+
+
+ android:layout_height="wrap_content" />
-
+ android:background="?actionBarDivider" />
+
-
+ android:orientation="vertical">
+
+
-
+
+
+
+ android:layout_height="wrap_content"
+ android:visibility="@integer/use_accessibility" />
+
+
-
+ android:background="?actionBarDivider" />
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/content_activity_info.xml b/app/src/main/res/layout/content_activity_info.xml
new file mode 100644
index 0000000..95609fa
--- /dev/null
+++ b/app/src/main/res/layout/content_activity_info.xml
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/content_configure_width.xml b/app/src/main/res/layout/content_configure_width.xml
new file mode 100644
index 0000000..d34a5de
--- /dev/null
+++ b/app/src/main/res/layout/content_configure_width.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/crash_view.xml b/app/src/main/res/layout/crash_view.xml
deleted file mode 100644
index 3bf0639..0000000
--- a/app/src/main/res/layout/crash_view.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/window_tasks.xml b/app/src/main/res/layout/window_tasks.xml
deleted file mode 100644
index 67758ca..0000000
--- a/app/src/main/res/layout/window_tasks.xml
+++ /dev/null
@@ -1,123 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..79d695e
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..79d695e
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..968c430
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..3f13bac
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9dcc7ce
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..492616f
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..602be1f
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..6b23ff6
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..4b77186
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..537faf6
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..8707b48
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
deleted file mode 100644
index 27e9567..0000000
--- a/app/src/main/res/values-night/colors.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- #FFA8A8A8
- #FFBDC1C6
- #FF3C4042
-
diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml
deleted file mode 100644
index 87d0148..0000000
--- a/app/src/main/res/values-night/styles.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/values-v24/bools.xml b/app/src/main/res/values-v24/bools.xml
deleted file mode 100644
index f93b670..0000000
--- a/app/src/main/res/values-v24/bools.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- true
-
diff --git a/app/src/main/res/values/bools.xml b/app/src/main/res/values/bools.xml
deleted file mode 100644
index 72fc307..0000000
--- a/app/src/main/res/values/bools.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- false
-
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 85f5214..33aa166 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,13 +1,4 @@
- #FFFAFAFA
- #FF5E7C87
- #FF202124
- #FF202124
- #FFBDC1C6
- #FF202124
- #FF3C4042
- #FF3C4042
- #888888
- #FF505153
-
+ #1e1e1e
+
\ No newline at end of file
diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml
new file mode 100644
index 0000000..caffa71
--- /dev/null
+++ b/app/src/main/res/values/integers.xml
@@ -0,0 +1,5 @@
+
+
+ 2
+ 0
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 661e53d..7e2d536 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,11 +1,4 @@
Current Activity
- Show Window
- We use accessibility permission to monitor window change events. So that we can show the current activity info.
- Show floating window
- Don\'t show notification
- %s is running
- Touch for settings.
- Stop
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
deleted file mode 100644
index 61caf1b..0000000
--- a/app/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..76b2f28
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml-v25/app_shortcuts.xml b/app/src/main/res/xml-v25/app_shortcuts.xml
deleted file mode 100644
index 72f1548..0000000
--- a/app/src/main/res/xml-v25/app_shortcuts.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/xml/accessibility.xml b/app/src/main/res/xml/accessibility.xml
deleted file mode 100644
index 291139d..0000000
--- a/app/src/main/res/xml/accessibility.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
diff --git a/build.gradle b/build.gradle
index 78a6726..182b248 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.11.1'
+ classpath 'com.android.tools.build:gradle:8.13.0'
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 0156b72..3ec3649 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Mon Aug 30 15:20:07 CST 2021
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME