Add support for AndroidX fragments (#957)

Summary:
Fix https://github.com/facebook/flipper/issues/931

This is not how I would *like* to fix this, but it should do the job.
When the switch over to AndroidX was made, the overall abstraction
started to leak and we really need to remodel this in its entirety.
There's also the question of whether we want to support both support
fragments and AndroidX fragments or not. Right now it's kinda-sorta
supported but only under some circumstances, which is not great.

I also added some more defensive try/catches as there's some unsafe casting
involved and future changes may break this causing the entire layout to disappear.

Change Log: Fix support for AndroidX fragments in Layout Inspector.

Pull Request resolved: https://github.com/facebook/flipper/pull/957

Test Plan:
Changed the sample app to include some AndroidX fragments and they
now show up (again) in the view hierarchy:

![Screenshot 2020-04-01 13 40 53](https://user-images.githubusercontent.com/9906/78138910-915fbc00-741f-11ea-8386-4eeca9b7f932.png)

Tested internally that FB4A fragments show up again, too:

{F233098198}

Reviewed By: mweststrate

Differential Revision: D20792503

Pulled By: passy

fbshipit-source-id: 7030b897ab547d1e8803b7f0d7aaa34263cfaed2
This commit is contained in:
Pascal Hartig
2020-04-03 09:19:05 -07:00
committed by Facebook GitHub Bot
parent 492b1076d2
commit 4be1b4d491
3 changed files with 37 additions and 15 deletions

View File

@@ -8,6 +8,7 @@
package com.facebook.flipper.plugins.inspector.descriptors;
import android.app.Activity;
import android.util.Log;
import android.view.Window;
import com.facebook.flipper.core.FlipperDynamic;
import com.facebook.flipper.core.FlipperObject;
@@ -25,6 +26,8 @@ import javax.annotation.Nullable;
public class ActivityDescriptor extends NodeDescriptor<Activity> {
private static final String TAG = "ActivityDescriptor";
@Override
public void init(Activity node) {}
@@ -112,9 +115,14 @@ public class ActivityDescriptor extends NodeDescriptor<Activity> {
}
FragmentManagerAccessor fragmentManagerAccessor = compat.forFragmentManager();
List<Object> addedFragments = fragmentManagerAccessor.getAddedFragments(fragmentManager);
List<Object> addedFragments = null;
try {
addedFragments = fragmentManagerAccessor.getAddedFragments(fragmentManager);
} catch (Exception e) {
Log.e(TAG, "Failed to obtain list of fragments.", e);
}
if (addedFragments == null) {
return Collections.EMPTY_LIST;
return Collections.emptyList();
}
final List<Object> dialogFragments = new ArrayList<>();

View File

@@ -9,7 +9,9 @@ package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies;
import android.app.Activity;
import android.os.Build;
import androidx.fragment.app.FragmentManager;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
@@ -37,7 +39,7 @@ public abstract class FragmentCompat<
static {
sHasSupportFragment =
ReflectionUtil.tryGetClassForName("android.support.v4.app.Fragment") != null;
ReflectionUtil.tryGetClassForName("androidx.fragment.app.Fragment") != null;
}
@Nullable
@@ -82,20 +84,22 @@ public abstract class FragmentCompat<
@Nullable
@Override
public List<FRAGMENT> getAddedFragments(FRAGMENT_MANAGER fragmentManager) {
// This field is actually sitting on FragmentManagerImpl, which derives from FragmentManager.
if (mFieldMAdded == null) {
Field fieldMAdded =
ReflectionUtil.tryGetDeclaredField(fragmentManager.getClass(), "mAdded");
if (fragmentManager instanceof android.app.FragmentManager) {
// This field is actually sitting on FragmentManagerImpl, which derives from
// FragmentManager.
if (mFieldMAdded == null) {
Field fieldMAdded =
ReflectionUtil.tryGetDeclaredField(fragmentManager.getClass(), "mAdded");
if (fieldMAdded != null) {
fieldMAdded.setAccessible(true);
mFieldMAdded = fieldMAdded;
if (fieldMAdded != null) {
fieldMAdded.setAccessible(true);
mFieldMAdded = fieldMAdded;
}
}
} else if (fragmentManager instanceof androidx.fragment.app.FragmentManager) {
return (List<FRAGMENT>) ((FragmentManager) fragmentManager).getFragments();
}
return (mFieldMAdded != null)
? (List<FRAGMENT>) ReflectionUtil.getFieldValue(mFieldMAdded, fragmentManager)
: null;
return Collections.emptyList();
}
}
}

View File

@@ -8,11 +8,15 @@
package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
public final class FragmentCompatUtil {
private static final String TAG = "FragmentCompatUtil";
private FragmentCompatUtil() {}
public static boolean isDialogFragment(Object fragment) {
@@ -79,7 +83,13 @@ public final class FragmentCompatUtil {
@Nullable
private static Object findFragmentForViewInFragmentManager(
FragmentCompat compat, Object fragmentManager, View view) {
List<?> fragments = compat.forFragmentManager().getAddedFragments(fragmentManager);
List<?> fragments;
try {
fragments = compat.forFragmentManager().getAddedFragments(fragmentManager);
} catch (Exception e) {
fragments = Collections.emptyList();
Log.e(TAG, "Failed to obtain list of fragments.", e);
}
if (fragments != null) {
for (int i = 0, N = fragments.size(); i < N; ++i) {