├── .gitignore ├── LICENSE ├── README.md ├── activity-manager ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── publish.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── io.xiandan.utils │ │ ├── ActivityLifecycleManager.java │ │ └── SimpleActivityLifecycleCallbacks.java │ └── res │ └── values │ └── strings.xml ├── build.gradle ├── example ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── io │ │ └── xiandan │ │ └── am │ │ └── example │ │ ├── AActivity.java │ │ ├── BActivity.java │ │ ├── CActivity.java │ │ ├── DActivity.java │ │ ├── ExampleActivity.java │ │ ├── ExampleApplication.java │ │ ├── MainActivity.java │ │ ├── MenuActivity.java │ │ └── WindowLog.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_example.xml │ ├── activity_main.xml │ └── window_log.xml │ ├── menu │ └── menu.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/dictionaries 41 | .idea/libraries 42 | 43 | # Keystore files 44 | *.jks 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | .externalNativeBuild 48 | 49 | # Google Services (e.g. APIs or Firebase) 50 | google-services.json 51 | 52 | # Freeline 53 | freeline.py 54 | freeline/ 55 | freeline_project_description.json 56 | .idea 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 xiandanin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ActivityManager 2 | 一个管理所有Activity的库,可以在任意处关闭任意Activity 3 | 4 | ### __Gradle引入__ 5 | ``` 6 | compile 'com.dyhdyh:activity-manager:1.0.0' 7 | ``` 8 | 9 | ### __在Application注册__ 10 | ``` 11 | public class ExampleApplication extends Application { 12 | 13 | @Override 14 | public void onCreate() { 15 | super.onCreate(); 16 | //注册管理器 17 | ActivityManager.getInstance().register(this); 18 | } 19 | ``` 20 | 21 | ### __示例__ 22 | ``` 23 | //关闭栈顶Activity 24 | ActivityManager.getInstance().finishTopActivity(); 25 | 26 | //关闭所有BActivity,CActivity 27 | ActivityManager.getInstance().finishActivity(BActivity.class, CActivity.class); 28 | 29 | //保留DActivity,其余全部关闭 30 | ActivityManager.getInstance().finishAllActivityByWhitelist(DActivity.class); 31 | 32 | //关闭所有Activity 33 | ActivityManager.getInstance().finishAllActivity(); 34 | ``` 35 | 36 | ### __示例apk__ 37 | [点击下载](https://github.com/xiandanin/ActivityManager/releases/download/1.0.0/ActivityManager-example.apk) 38 | 39 | ![](image/qrcode.png) -------------------------------------------------------------------------------- /activity-manager/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /activity-manager/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | compileSdkVersion 31 7 | buildToolsVersion "31.0.3" 8 | 9 | defaultConfig { 10 | minSdkVersion 21 11 | targetSdkVersion 31 12 | versionCode 7 13 | versionName "1.1.0" 14 | 15 | consumerProguardFiles "consumer-rules.pro" 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | } 29 | 30 | dependencies { 31 | 32 | } 33 | 34 | 35 | ext { 36 | groupId = 'in.xiandan' 37 | description = '全局耗时计算器,可以在任何地方计算耗时' 38 | gitUrl = 'https://github.com/xiandanin/AnyCost' 39 | authorEmail = 'denghahae@gmail.com' 40 | license = 'MIT' 41 | } 42 | apply from: 'publish.gradle' -------------------------------------------------------------------------------- /activity-manager/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /activity-manager/publish.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven-publish' 2 | apply plugin: 'signing' 3 | 4 | Properties localProperties = new Properties() 5 | localProperties.load(project.rootProject.file('local.properties').newDataInputStream()) 6 | localProperties.each { name, value -> 7 | project.ext[name] = value 8 | } 9 | 10 | def mavenUsername = localProperties.getProperty("sonatype.username") 11 | def mavenPassword = localProperties.getProperty("sonatype.password") 12 | def projectGroupId = project.ext.groupId 13 | def projectArtifactId = project.getName() 14 | def projectVersionName = project.ext.has('version') ? project.ext.getProperty('version') : project.extensions.findByName("android")["defaultConfig"].versionName 15 | def projectDescription = project.ext.has('description') ? project.ext.getProperty('description') : null 16 | def projectGitUrl = project.ext.has('gitUrl') ? project.ext.getProperty('gitUrl') : null 17 | def projectLicense = project.ext.has('license') ? project.ext.getProperty('license') : null 18 | def projectLicenseUrl = projectLicense ? "https://opensource.org/licenses/${projectLicense.toString().replace(" ", "-")} " : null 19 | 20 | def developerAuthorId = mavenUsername 21 | def developerAuthorName = mavenUsername 22 | def developerAuthorEmail = project.ext.has('authorEmail') ? project.ext.getProperty('authorEmail') : null 23 | 24 | println("${mavenUsername} ${mavenPassword} - ${projectGroupId}:${projectArtifactId}:${projectVersionName}") 25 | println("${projectLicense} - ${projectLicenseUrl}") 26 | 27 | if (!mavenUsername || !mavenPassword || !projectGroupId || !projectArtifactId || !projectVersionName) { 28 | println('错误:缺少参数') 29 | return 30 | } 31 | if (!projectDescription || !projectGitUrl || !projectLicense || !projectLicenseUrl || !developerAuthorId || !developerAuthorName || !developerAuthorEmail) { 32 | println('警告:缺少可选信息') 33 | } 34 | 35 | def isAndroidProject = project.hasProperty('android') 36 | if (isAndroidProject) { 37 | println("使用Android工程方式发布") 38 | // Android 工程 39 | task androidJavadocs(type: Javadoc) { 40 | source = android.sourceSets.main.java.srcDirs 41 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 42 | } 43 | task javadocsJar(type: Jar, dependsOn: androidJavadocs) { 44 | archiveClassifier.set("javadoc") 45 | from androidJavadocs.destinationDir 46 | } 47 | task sourcesJar(type: Jar) { 48 | archiveClassifier.set("sources") 49 | from android.sourceSets.main.java.srcDirs 50 | } 51 | } else { 52 | println("使用Java工程方式发布") 53 | // Java 工程 54 | task javadocsJar(type: Jar, dependsOn: javadoc) { 55 | archiveClassifier.set("javadoc") 56 | from javadoc.destinationDir 57 | } 58 | task sourcesJar(type: Jar) { 59 | archiveClassifier.set("sources") 60 | from sourceSets.main.allJava 61 | } 62 | } 63 | 64 | tasks.withType(Javadoc).all { 65 | options { 66 | encoding "UTF-8" 67 | charSet 'UTF-8' 68 | author true 69 | version true 70 | links "http://docs.oracle.com/javase/8/docs/api" 71 | if (isAndroidProject) { 72 | linksOffline "http://d.android.com/reference", "${android.sdkDirectory}/docs/reference" 73 | } 74 | failOnError = false 75 | } 76 | enabled = false 77 | } 78 | 79 | artifacts { 80 | archives javadocsJar, sourcesJar 81 | } 82 | 83 | publishing { 84 | publications { 85 | aar(MavenPublication) { 86 | groupId = projectGroupId 87 | artifactId = projectArtifactId 88 | version = projectVersionName 89 | // Tell maven to prepare the generated "*.aar" file for publishing 90 | if (isAndroidProject){ 91 | artifact("$buildDir/outputs/aar/${project.getName()}-release.aar") 92 | }else{ 93 | artifact("$buildDir/libs/${project.getName()}.jar") 94 | } 95 | artifact javadocsJar 96 | artifact sourcesJar 97 | 98 | pom { 99 | name = projectArtifactId 100 | description = projectDescription 101 | // If your project has a dedicated site, use its URL here 102 | url = projectGitUrl 103 | if (projectLicense){ 104 | licenses { 105 | license { 106 | name = projectLicense 107 | url = projectLicenseUrl 108 | } 109 | } 110 | } 111 | developers { 112 | developer { 113 | id = developerAuthorId 114 | name = developerAuthorName 115 | email = developerAuthorEmail 116 | } 117 | } 118 | // Version control info, if you're using GitHub, follow the format as seen here 119 | scm { 120 | connection = "scm:git:${projectGitUrl}" 121 | developerConnection = "scm:git:${projectGitUrl}" 122 | url = projectGitUrl 123 | } 124 | withXml { 125 | def dependenciesNode = asNode().appendNode('dependencies') 126 | configurations.api.allDependencies.each { dependency -> 127 | if (!dependency.hasProperty('dependencyProject')) { 128 | def dependencyNode = dependenciesNode.appendNode('dependency') 129 | dependencyNode.appendNode('groupId', dependency.group) 130 | dependencyNode.appendNode('artifactId', dependency.name) 131 | dependencyNode.appendNode('version', dependency.version) 132 | } 133 | } 134 | } 135 | } 136 | } 137 | } 138 | 139 | repositories { 140 | maven { 141 | name = projectArtifactId 142 | 143 | def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 144 | def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" 145 | // You only need this if you want to publish snapshots, otherwise just set the URL 146 | // to the release repo directly 147 | url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl 148 | 149 | // The username and password we've fetched earlier 150 | credentials { 151 | username mavenUsername 152 | password mavenPassword 153 | } 154 | } 155 | } 156 | } 157 | 158 | signing { 159 | sign publishing.publications 160 | } -------------------------------------------------------------------------------- /activity-manager/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /activity-manager/src/main/java/io.xiandan.utils/ActivityLifecycleManager.java: -------------------------------------------------------------------------------- 1 | package io.xiandan.utils; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.text.TextUtils; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.Collection; 10 | import java.util.List; 11 | import java.util.Stack; 12 | 13 | /** 14 | * Created by xiandanin on 2022-01-20 23:45 15 | */ 16 | public class ActivityLifecycleManager { 17 | 18 | private static class ActivityLifecycleManagerHolder { 19 | private static final ActivityLifecycleManager instance = new ActivityLifecycleManager(); 20 | } 21 | 22 | private ActivityLifecycleManager() { 23 | } 24 | 25 | public static ActivityLifecycleManager getInstance() { 26 | return ActivityLifecycleManagerHolder.instance; 27 | } 28 | 29 | private final Stack stack = new Stack<>(); 30 | 31 | public void register(Application application) { 32 | application.registerActivityLifecycleCallbacks(new SimpleActivityLifecycleCallbacks() { 33 | @Override 34 | public void onActivityCreated(Activity activity) { 35 | pushActivity(activity); 36 | } 37 | 38 | @Override 39 | public void onActivityDestroyed(Activity activity) { 40 | removeActivity(activity); 41 | } 42 | }); 43 | } 44 | 45 | public void pushActivity(Activity activity) { 46 | synchronized (this) { 47 | stack.push(activity); 48 | } 49 | } 50 | 51 | public void removeActivity(Activity activity) { 52 | synchronized (this) { 53 | stack.remove(activity); 54 | } 55 | } 56 | 57 | public void removeActivities(Collection activities) { 58 | synchronized (this) { 59 | stack.removeAll(activities); 60 | } 61 | } 62 | 63 | private void finishActivity(Activity activity) { 64 | if (activity != null && !activity.isFinishing()) { 65 | activity.finish(); 66 | } 67 | } 68 | 69 | /** 70 | * 返回栈顶的Activity但不移除 71 | */ 72 | public Activity peekActivity() { 73 | synchronized (this) { 74 | return stack != null && !stack.isEmpty() ? stack.peek() : null; 75 | } 76 | } 77 | 78 | /** 79 | * 返回栈顶的Activity并移除 80 | */ 81 | public Activity popActivity() { 82 | synchronized (this) { 83 | return stack != null && !stack.isEmpty() ? stack.pop() : null; 84 | } 85 | } 86 | 87 | /** 88 | * 关闭栈顶Activity 89 | */ 90 | public void finishTopActivity() { 91 | finishActivity(popActivity()); 92 | } 93 | 94 | /** 95 | * 关闭栈顶多个Activity 96 | */ 97 | public void finishTopActivity(int count) { 98 | for (int i = 0; i < count; i++) { 99 | finishActivity(popActivity()); 100 | } 101 | } 102 | 103 | /** 104 | * 根据class关闭activity并移除 105 | * 106 | * @param classes 107 | */ 108 | public final void finishActivityByClass(Class... classes) { 109 | String[] classNames = new String[classes.length]; 110 | for (int i = 0; i < classes.length; i++) { 111 | classNames[i] = classes[i].getName(); 112 | } 113 | finishActivityByClassName(classNames); 114 | } 115 | 116 | public void finishActivityByClassName(String... className) { 117 | finishActivityByPredicate(new Predicate() { 118 | @Override 119 | public boolean test(Activity activity) { 120 | for (String s : className) { 121 | if (activity.getClass().getName().equals(s)) { 122 | return true; 123 | } 124 | } 125 | return false; 126 | } 127 | }); 128 | } 129 | 130 | /** 131 | * 自定义条件关闭activity并移除 132 | * 133 | * @param predicate 134 | */ 135 | public void finishActivityByPredicate(Predicate predicate) { 136 | List activities = new ArrayList<>(); 137 | for (int i = 0; i < stack.size(); i++) { 138 | Activity activity = stack.get(i); 139 | if (predicate.test(activity)) { 140 | activities.add(activity); 141 | finishActivity(activity); 142 | } 143 | } 144 | removeActivities(activities); 145 | } 146 | 147 | public void finishOutsideActivityTheClass(Class... classes) { 148 | String[] classNames = new String[classes.length]; 149 | for (int i = 0; i < classes.length; i++) { 150 | classNames[i] = classes[i].getName(); 151 | } 152 | finishOutsideActivityTheClassName(classNames); 153 | } 154 | 155 | /** 156 | * 关闭并移除白名单以外的activity 157 | */ 158 | public void finishOutsideActivityTheClassName(String... className) { 159 | finishActivityByPredicate(new Predicate() { 160 | @Override 161 | public boolean test(Activity activity) { 162 | for (String s : className) { 163 | if (!activity.getClass().getName().equals(s)) { 164 | return true; 165 | } 166 | } 167 | return false; 168 | } 169 | }); 170 | } 171 | 172 | /** 173 | * 关闭并移除所有activity 174 | */ 175 | public void finishAllActivity() { 176 | for (Activity activity : stack) { 177 | finishActivity(activity); 178 | } 179 | stack.clear(); 180 | } 181 | 182 | public Stack getActivityStack() { 183 | return stack; 184 | } 185 | 186 | public List getActivities() { 187 | return new ArrayList<>(stack); 188 | } 189 | 190 | public interface Predicate { 191 | boolean test(Activity activity); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /activity-manager/src/main/java/io.xiandan.utils/SimpleActivityLifecycleCallbacks.java: -------------------------------------------------------------------------------- 1 | package io.xiandan.utils; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | 7 | /** 8 | * Created by xiandanin on 2022-01-20 23:44 9 | */ 10 | public abstract class SimpleActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { 11 | @Override 12 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 13 | 14 | } 15 | 16 | @Override 17 | public void onActivityStarted(Activity activity) { 18 | 19 | } 20 | 21 | @Override 22 | public void onActivityResumed(Activity activity) { 23 | 24 | } 25 | 26 | @Override 27 | public void onActivityPaused(Activity activity) { 28 | 29 | } 30 | 31 | @Override 32 | public void onActivityStopped(Activity activity) { 33 | 34 | } 35 | 36 | @Override 37 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 38 | 39 | } 40 | 41 | @Override 42 | public void onActivityDestroyed(Activity activity) { 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /activity-manager/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | activity-manager 3 | 4 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext.kotlin_version = "1.4.32" 4 | repositories { 5 | mavenCentral() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.0.3' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | mavenCentral() 19 | google() 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /example/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id 'kotlin-android-extensions' 5 | } 6 | 7 | android { 8 | compileSdkVersion 31 9 | buildToolsVersion "31.0.3" 10 | 11 | defaultConfig { 12 | applicationId "io.xiandan.am.example" 13 | minSdkVersion 21 14 | targetSdkVersion 31 15 | versionCode 1 16 | versionName "1.0.0" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | kotlinOptions { 30 | jvmTarget = '1.8' 31 | } 32 | } 33 | 34 | dependencies { 35 | 36 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 37 | implementation 'androidx.appcompat:appcompat:1.4.1' 38 | implementation 'com.google.android.material:material:1.5.0' 39 | implementation project(path: ':activity-manager') 40 | } -------------------------------------------------------------------------------- /example/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /example/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/src/main/java/io/xiandan/am/example/AActivity.java: -------------------------------------------------------------------------------- 1 | package io.xiandan.am.example; 2 | 3 | /** 4 | * @author xiandanin 5 | * created 2017/12/8 14:19 6 | */ 7 | public class AActivity extends ExampleActivity { 8 | 9 | @Override 10 | protected String getTag() { 11 | return "A"; 12 | } 13 | } -------------------------------------------------------------------------------- /example/src/main/java/io/xiandan/am/example/BActivity.java: -------------------------------------------------------------------------------- 1 | package io.xiandan.am.example; 2 | 3 | /** 4 | * @author xiandanin 5 | * created 2017/12/8 14:19 6 | */ 7 | public class BActivity extends ExampleActivity { 8 | 9 | @Override 10 | protected String getTag() { 11 | return "B"; 12 | } 13 | } -------------------------------------------------------------------------------- /example/src/main/java/io/xiandan/am/example/CActivity.java: -------------------------------------------------------------------------------- 1 | package io.xiandan.am.example; 2 | 3 | /** 4 | * @author xiandanin 5 | * created 2017/12/8 14:19 6 | */ 7 | public class CActivity extends ExampleActivity { 8 | 9 | @Override 10 | protected String getTag() { 11 | return "C"; 12 | } 13 | } -------------------------------------------------------------------------------- /example/src/main/java/io/xiandan/am/example/DActivity.java: -------------------------------------------------------------------------------- 1 | package io.xiandan.am.example; 2 | 3 | /** 4 | * @author xiandanin 5 | * created 2017/12/8 14:19 6 | */ 7 | public class DActivity extends ExampleActivity { 8 | 9 | @Override 10 | protected String getTag() { 11 | return "D"; 12 | } 13 | } -------------------------------------------------------------------------------- /example/src/main/java/io/xiandan/am/example/ExampleActivity.java: -------------------------------------------------------------------------------- 1 | package io.xiandan.am.example; 2 | 3 | import android.os.Bundle; 4 | import android.widget.TextView; 5 | 6 | import com.dyhdyh.manager.example.R; 7 | 8 | /** 9 | * @author xiandanin 10 | * created 2017/12/8 14:21 11 | */ 12 | public abstract class ExampleActivity extends MenuActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_example); 18 | 19 | TextView tv = findViewById(R.id.tv); 20 | tv.setText(getTag()); 21 | } 22 | 23 | protected abstract String getTag(); 24 | 25 | 26 | } -------------------------------------------------------------------------------- /example/src/main/java/io/xiandan/am/example/ExampleApplication.java: -------------------------------------------------------------------------------- 1 | package io.xiandan.am.example; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | 7 | 8 | import io.xiandan.utils.ActivityLifecycleManager; 9 | import io.xiandan.utils.SimpleActivityLifecycleCallbacks; 10 | 11 | /** 12 | * @author xiandanin 13 | * created 2017/12/8 14:48 14 | */ 15 | public class ExampleApplication extends Application { 16 | 17 | @Override 18 | public void onCreate() { 19 | super.onCreate(); 20 | WindowLog.create(this); 21 | 22 | //注册管理器 23 | ActivityLifecycleManager.getInstance().register(this); 24 | 25 | registerActivityLifecycleCallbacks(new SimpleActivityLifecycleCallbacks() { 26 | @Override 27 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 28 | WindowLog.updateLog(); 29 | } 30 | 31 | @Override 32 | public void onActivityDestroyed(Activity activity) { 33 | WindowLog.updateLog(); 34 | } 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example/src/main/java/io/xiandan/am/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.xiandan.am.example; 2 | 3 | import android.app.AppOpsManager; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.net.Uri; 7 | import android.os.Binder; 8 | import android.os.Bundle; 9 | import android.support.v4.content.ContextCompat; 10 | import android.view.View; 11 | import android.widget.TextView; 12 | 13 | import com.dyhdyh.manager.example.R; 14 | 15 | import java.lang.reflect.Method; 16 | 17 | public class MainActivity extends MenuActivity { 18 | TextView tv; 19 | View btnSetting; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_main); 25 | 26 | tv = findViewById(R.id.tv); 27 | btnSetting = findViewById(R.id.btn_setting); 28 | 29 | } 30 | 31 | public void clickStartSetting(View v) { 32 | Intent localIntent = new Intent(); 33 | localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 34 | localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); 35 | localIntent.setData(Uri.fromParts("package", getPackageName(), null)); 36 | startActivity(localIntent); 37 | } 38 | 39 | @Override 40 | protected void onResume() { 41 | super.onResume(); 42 | if (tv == null) { 43 | return; 44 | } 45 | 46 | if (!checkWindowPermission(this)) { 47 | tv.setText("需要打开“悬浮窗”权限"); 48 | btnSetting.setVisibility(View.VISIBLE); 49 | } else { 50 | tv.setText("点击右上角菜单"); 51 | btnSetting.setVisibility(View.GONE); 52 | } 53 | } 54 | 55 | @Override 56 | public void onBackPressed() { 57 | super.onBackPressed(); 58 | System.exit(0); 59 | } 60 | 61 | /** 62 | * 判断 悬浮窗口权限是否打开 63 | * 64 | * @param context 65 | * @return true 允许 false禁止 66 | */ 67 | public static boolean checkWindowPermission(Context context) { 68 | try { 69 | Object object = context.getSystemService("appops"); 70 | if (object == null) { 71 | return false; 72 | } 73 | Class localClass = object.getClass(); 74 | Class[] arrayOfClass = new Class[3]; 75 | arrayOfClass[0] = Integer.TYPE; 76 | arrayOfClass[1] = Integer.TYPE; 77 | arrayOfClass[2] = String.class; 78 | Method method = localClass.getMethod("checkOp", arrayOfClass); 79 | if (method == null) { 80 | return false; 81 | } 82 | Object[] arrayOfObject1 = new Object[3]; 83 | arrayOfObject1[0] = Integer.valueOf(24); 84 | arrayOfObject1[1] = Integer.valueOf(Binder.getCallingUid()); 85 | arrayOfObject1[2] = context.getPackageName(); 86 | int m = ((Integer) method.invoke(object, arrayOfObject1)).intValue(); 87 | return m == AppOpsManager.MODE_ALLOWED; 88 | } catch (Exception ex) { 89 | 90 | } 91 | return false; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /example/src/main/java/io/xiandan/am/example/MenuActivity.java: -------------------------------------------------------------------------------- 1 | package io.xiandan.am.example; 2 | 3 | import android.app.ActivityManager; 4 | import android.content.Intent; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | 8 | 9 | import androidx.appcompat.app.AppCompatActivity; 10 | 11 | import java.util.Random; 12 | 13 | import io.xiandan.utils.ActivityLifecycleManager; 14 | 15 | /** 16 | * @author xiandanin 17 | * created 2017/12/8 17:08 18 | */ 19 | public class MenuActivity extends AppCompatActivity { 20 | private final Class[] cls = new Class[]{AActivity.class, BActivity.class, CActivity.class, DActivity.class}; 21 | 22 | public void clickRandomActivity(MenuItem menuItem) { 23 | int index = new Random().nextInt(cls.length); 24 | startActivity(new Intent(this, cls[index])); 25 | } 26 | 27 | public void clickFinishTop(MenuItem menuItem) { 28 | //关闭栈顶Activity 29 | ActivityLifecycleManager.getInstance().finishTopActivity(); 30 | } 31 | 32 | public void clickFinishAllA(MenuItem menuItem) { 33 | ActivityLifecycleManager.getInstance().finishActivityByClass(AActivity.class); 34 | } 35 | 36 | public void clickFinishAllCD(MenuItem menuItem) { 37 | //关闭所有BActivity,CActivity 38 | ActivityLifecycleManager.getInstance().finishActivityByClass(BActivity.class, CActivity.class); 39 | } 40 | 41 | public void clickFinishByWhitelist(MenuItem menuItem) { 42 | //保留DActivity,其余全部关闭 43 | ActivityLifecycleManager.getInstance().finishOutsideActivityTheClass(DActivity.class); 44 | } 45 | 46 | public void clickFinishAll(MenuItem menuItem) { 47 | //关闭所有Activity 48 | ActivityLifecycleManager.getInstance().finishAllActivity(); 49 | } 50 | 51 | 52 | @Override 53 | public boolean onCreateOptionsMenu(Menu menu) { 54 | getMenuInflater().inflate(R.menu.menu, menu); 55 | return super.onCreateOptionsMenu(menu); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /example/src/main/java/io/xiandan/am/example/WindowLog.java: -------------------------------------------------------------------------------- 1 | package io.xiandan.am.example; 2 | 3 | import android.app.Activity; 4 | import android.app.ActivityManager; 5 | import android.content.Context; 6 | import android.graphics.PixelFormat; 7 | import android.util.TypedValue; 8 | import android.view.Gravity; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.WindowManager; 12 | import android.widget.TextView; 13 | 14 | import com.dyhdyh.manager.example.R; 15 | 16 | import java.util.Stack; 17 | 18 | /** 19 | * @author xiandanin 20 | * created 2017/12/8 14:45 21 | */ 22 | public class WindowLog { 23 | private static View layout; 24 | 25 | 26 | public static void create(Context context) { 27 | //赋值WindowManager&LayoutParam. 28 | WindowManager.LayoutParams params = new WindowManager.LayoutParams( 29 | WindowManager.LayoutParams.WRAP_CONTENT, 30 | WindowManager.LayoutParams.WRAP_CONTENT, 31 | WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, 32 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, 33 | PixelFormat.TRANSLUCENT); 34 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 35 | 36 | int actionBarSize = 0; 37 | TypedValue typedValue = new TypedValue(); 38 | if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, typedValue, true)) { 39 | actionBarSize = TypedValue.complexToDimensionPixelSize(typedValue.data, context.getResources().getDisplayMetrics()); 40 | } 41 | params.y = actionBarSize; 42 | 43 | params.gravity = Gravity.LEFT | Gravity.TOP; 44 | LayoutInflater inflater = LayoutInflater.from(context); 45 | layout = inflater.inflate(R.layout.window_log, null); 46 | windowManager.addView(layout, params); 47 | } 48 | 49 | public static void setVisibility(boolean visible) { 50 | if (layout == null) { 51 | return; 52 | } 53 | layout.setVisibility(visible ? View.VISIBLE : View.GONE); 54 | } 55 | 56 | public static void updateLog() { 57 | TextView tv = layout.findViewById(R.id.tv_log); 58 | StringBuffer sb = new StringBuffer(); 59 | sb.append("当前打开Activity:"); 60 | sb.append(ActivityManager.getInstance().getActivityCount()); 61 | sb.append("个"); 62 | sb.append("\n"); 63 | sb.append("栈顶Activity:"); 64 | Activity peek = ActivityManager.getInstance().getActivityStack().peek(); 65 | sb.append(peek.getClass().getSimpleName() + "@" + Integer.toHexString(peek.hashCode())); 66 | sb.append("\n"); 67 | sb.append("\n"); 68 | 69 | Stack stack = ActivityManager.getInstance().getActivityStack(); 70 | for (int i = stack.size() - 1; i >= 0; i--) { 71 | Activity activity = stack.get(i); 72 | sb.append(i); 73 | sb.append(" - "); 74 | sb.append(activity.getClass().getSimpleName() + "@" + Integer.toHexString(activity.hashCode())); 75 | sb.append("\n"); 76 | } 77 | tv.setText(sb.toString()); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /example/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /example/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /example/src/main/res/layout/activity_example.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /example/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 |