diff --git a/build.gradle b/build.gradle index 0f3efbb08e..aad7c69d1d 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,7 @@ buildscript { runner : 'androidx.test:runner:1.1.0', ], ], + android_support: 'com.android.support:support-v4:28.0.0', junit : 'junit:junit:4.12', kotlin : [ gradlePlugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}", @@ -42,7 +43,7 @@ buildscript { } dependencies { classpath deps.kotlin.gradlePlugin - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.2' classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.16' classpath 'com.github.ben-manes:gradle-versions-plugin:0.20.0' classpath "org.jetbrains.dokka:dokka-gradle-plugin:0.9.18" diff --git a/leakcanary-android-core/build.gradle b/leakcanary-android-core/build.gradle index 26d64a86f7..387dfe7d27 100644 --- a/leakcanary-android-core/build.gradle +++ b/leakcanary-android-core/build.gradle @@ -5,6 +5,7 @@ dependencies { api project(':shark-android') api project(':leakcanary-object-watcher-android') api project(':leakcanary-object-watcher-android-androidx') + api project(':leakcanary-object-watcher-android-support-fragments') implementation deps.kotlin.stdlib diff --git a/leakcanary-object-watcher-android-support-fragments/build.gradle b/leakcanary-object-watcher-android-support-fragments/build.gradle new file mode 100644 index 0000000000..43cae05ff4 --- /dev/null +++ b/leakcanary-object-watcher-android-support-fragments/build.gradle @@ -0,0 +1,25 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +dependencies { + api project(':leakcanary-object-watcher-android') + + implementation deps.kotlin.stdlib + // Optional dependency + compileOnly deps.android_support +} + +android { + compileSdkVersion versions.compileSdk + defaultConfig { + minSdkVersion versions.minSdk + consumerProguardFiles 'consumer-proguard-rules.pro' + } + lintOptions { + disable 'GoogleAppIndexingWarning' + error 'ObsoleteSdkInt' + check 'Interoperability' + } +} + +apply from: rootProject.file('gradle/gradle-mvn-push.gradle') diff --git a/leakcanary-object-watcher-android-support-fragments/consumer-proguard-rules.pro b/leakcanary-object-watcher-android-support-fragments/consumer-proguard-rules.pro new file mode 100644 index 0000000000..636103e326 --- /dev/null +++ b/leakcanary-object-watcher-android-support-fragments/consumer-proguard-rules.pro @@ -0,0 +1,2 @@ +# AndroidSupportFragmentDestroyWatcher is loaded via reflection +-keep class leakcanary.internal.AndroidSupportFragmentDestroyWatcher { *; } diff --git a/leakcanary-object-watcher-android-support-fragments/gradle.properties b/leakcanary-object-watcher-android-support-fragments/gradle.properties new file mode 100644 index 0000000000..d78acb64f2 --- /dev/null +++ b/leakcanary-object-watcher-android-support-fragments/gradle.properties @@ -0,0 +1,3 @@ +POM_ARTIFACT_ID=leakcanary-object-watcher-android-support-fragments +POM_NAME=LeakCanary Object Watcher for Android extension: Android support library fragments support +POM_PACKAGING=aar diff --git a/leakcanary-object-watcher-android-support-fragments/src/main/AndroidManifest.xml b/leakcanary-object-watcher-android-support-fragments/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..c44760c2a2 --- /dev/null +++ b/leakcanary-object-watcher-android-support-fragments/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/leakcanary-object-watcher-android-support-fragments/src/main/java/leakcanary/internal/AndroidSupportFragmentDestroyWatcher.kt b/leakcanary-object-watcher-android-support-fragments/src/main/java/leakcanary/internal/AndroidSupportFragmentDestroyWatcher.kt new file mode 100644 index 0000000000..38ebd830fd --- /dev/null +++ b/leakcanary-object-watcher-android-support-fragments/src/main/java/leakcanary/internal/AndroidSupportFragmentDestroyWatcher.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package leakcanary.internal + +import android.app.Activity +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentActivity +import android.support.v4.app.FragmentManager +import leakcanary.AppWatcher.Config +import leakcanary.ObjectWatcher + +internal class AndroidSupportFragmentDestroyWatcher( + private val objectWatcher: ObjectWatcher, + private val configProvider: () -> Config +) : (Activity) -> Unit { + + private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { + + override fun onFragmentViewDestroyed( + fm: FragmentManager, + fragment: Fragment + ) { + val view = fragment.view + if (view != null && configProvider().watchFragmentViews) { + objectWatcher.watch(view) + } + } + + override fun onFragmentDestroyed( + fm: FragmentManager, + fragment: Fragment + ) { + if (configProvider().watchFragments) { + objectWatcher.watch(fragment) + } + } + } + + override fun invoke(activity: Activity) { + if (activity is FragmentActivity) { + val supportFragmentManager = activity.supportFragmentManager + supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true) + } + } +} diff --git a/leakcanary-object-watcher-android/src/main/java/leakcanary/internal/FragmentDestroyWatcher.kt b/leakcanary-object-watcher-android/src/main/java/leakcanary/internal/FragmentDestroyWatcher.kt index 9b4059c27e..7d12431c2e 100644 --- a/leakcanary-object-watcher-android/src/main/java/leakcanary/internal/FragmentDestroyWatcher.kt +++ b/leakcanary-object-watcher-android/src/main/java/leakcanary/internal/FragmentDestroyWatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Square, Inc. + * Copyright (C) 2019 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,10 @@ internal object FragmentDestroyWatcher { private const val ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME = "leakcanary.internal.AndroidXFragmentDestroyWatcher" + private const val ANDROID_SUPPORT_FRAGMENT_CLASS_NAME = "android.support.v4.app.Fragment" + private const val ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME = + "leakcanary.internal.AndroidSupportFragmentDestroyWatcher" + fun install( application: Application, objectWatcher: ObjectWatcher, @@ -46,15 +50,22 @@ internal object FragmentDestroyWatcher { ) } - if (classAvailable(ANDROIDX_FRAGMENT_CLASS_NAME) && - classAvailable(ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME) - ) { - val watcherConstructor = Class.forName(ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME) - .getDeclaredConstructor(ObjectWatcher::class.java, Function0::class.java) - @kotlin.Suppress("UNCHECKED_CAST") - fragmentDestroyWatchers.add( - watcherConstructor.newInstance(objectWatcher, configProvider) as (Activity) -> Unit - ) + getWatcherIfAvailable( + ANDROIDX_FRAGMENT_CLASS_NAME, + ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, + objectWatcher, + configProvider + )?.let { + fragmentDestroyWatchers.add(it) + } + + getWatcherIfAvailable( + ANDROID_SUPPORT_FRAGMENT_CLASS_NAME, + ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, + objectWatcher, + configProvider + )?.let { + fragmentDestroyWatchers.add(it) } if (fragmentDestroyWatchers.size == 0) { @@ -73,6 +84,26 @@ internal object FragmentDestroyWatcher { }) } + private fun getWatcherIfAvailable( + fragmentClassName: String, + watcherClassName: String, + objectWatcher: ObjectWatcher, + configProvider: () -> AppWatcher.Config + ): ((Activity) -> Unit)? { + + return if (classAvailable(fragmentClassName) && + classAvailable(watcherClassName) + ) { + val watcherConstructor = Class.forName(watcherClassName) + .getDeclaredConstructor(ObjectWatcher::class.java, Function0::class.java) + @Suppress("UNCHECKED_CAST") + watcherConstructor.newInstance(objectWatcher, configProvider) as (Activity) -> Unit + + } else { + null + } + } + private fun classAvailable(className: String): Boolean { return try { Class.forName(className) diff --git a/settings.gradle b/settings.gradle index 51e7d44d82..7eddf2ad9e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,6 +6,7 @@ include ':leakcanary-android-sample' include ':leakcanary-object-watcher' include ':leakcanary-object-watcher-android' include ':leakcanary-object-watcher-android-androidx' +include ':leakcanary-object-watcher-android-support-fragments' include ':shark' include ':shark-android' include ':shark-cli'