/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.stetho.common.android;
import android.app.Activity;
import android.os.Build;
import com.facebook.stetho.common.ReflectionUtil;
import com.facebook.stetho.common.Util;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import java.lang.reflect.Field;
import java.util.List;
/**
* Compatibility abstraction which allows us to generalize access to both the
* support library's fragments and the built-in framework version. Note: both versions
* can be live at the same time in a single application and even on a single object
* instance.
* <p/>
* Type safety is enforced via generics internal to the implementation but treated
* as opaque from the outside.
*
* @param <FRAGMENT>
* @param <DIALOG_FRAGMENT>
* @param <FRAGMENT_MANAGER>
* @param <FRAGMENT_ACTIVITY>
*/
@NotThreadSafe
public abstract class FragmentCompat<
FRAGMENT,
DIALOG_FRAGMENT,
FRAGMENT_MANAGER,
FRAGMENT_ACTIVITY extends Activity> {
private static FragmentCompat sFrameworkInstance;
private static FragmentCompat sSupportInstance;
private static final boolean sHasSupportFragment;
static {
sHasSupportFragment = ReflectionUtil.tryGetClassForName(
"android.support.v4.app.Fragment") != null;
}
@Nullable
public static FragmentCompat getFrameworkInstance() {
if (sFrameworkInstance == null &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
sFrameworkInstance = new FragmentCompatFramework();
}
return sFrameworkInstance;
}
@Nullable
public static FragmentCompat getSupportLibInstance() {
if (sSupportInstance == null &&
sHasSupportFragment) {
sSupportInstance = new FragmentCompatSupportLib();
}
return sSupportInstance;
}
FragmentCompat() {
}
public abstract Class<FRAGMENT> getFragmentClass();
public abstract Class<DIALOG_FRAGMENT> getDialogFragmentClass();
public abstract Class<FRAGMENT_ACTIVITY> getFragmentActivityClass();
public abstract FragmentAccessor<FRAGMENT, FRAGMENT_MANAGER> forFragment();
public abstract DialogFragmentAccessor<DIALOG_FRAGMENT, FRAGMENT, FRAGMENT_MANAGER>
forDialogFragment();
public abstract FragmentManagerAccessor<FRAGMENT_MANAGER, FRAGMENT> forFragmentManager();
public abstract FragmentActivityAccessor<FRAGMENT_ACTIVITY, FRAGMENT_MANAGER> forFragmentActivity();
static class FragmentManagerAccessorViaReflection<FRAGMENT_MANAGER, FRAGMENT>
implements FragmentManagerAccessor<FRAGMENT_MANAGER, FRAGMENT> {
@Nullable
private Field mFieldMAdded;
@SuppressWarnings("unchecked")
@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 (fieldMAdded != null) {
fieldMAdded.setAccessible(true);
mFieldMAdded = fieldMAdded;
}
}
return (mFieldMAdded != null)
? (List<FRAGMENT>)ReflectionUtil.getFieldValue(mFieldMAdded, fragmentManager)
: null;
}
}
}