aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Lake <ilake@google.com>2019-03-19 10:14:07 -0700
committerIan Lake <ilake@google.com>2019-03-25 14:48:35 -0700
commit2f634eec2143acd6a4ea19a375a6e3877cdcc2ed (patch)
tree657eaa105401a9b7ccd6b3b84e89844443423974
parent8044e8eb9ac9af014d38e655cd3170828f5bd635 (diff)
downloadsupport-2f634eec2143acd6a4ea19a375a6e3877cdcc2ed.tar.gz
Convert @ContentView to constructor annotation
As layout IDs are non-final in library modules (and in the future, app modules), change @ContentView to only be a marker annotation for a constructor which developers can call with a specific layout ID. Fixes: 128352521 Fixes: 127531658 Test: updated tests Change-Id: I15e2edee8cbd68180991f89fbc3b04e12c961ede
-rw-r--r--activity/api/1.0.0-alpha06.txt1
-rw-r--r--activity/api/current.txt1
-rw-r--r--activity/src/androidTest/java/androidx/activity/ContentViewTest.kt4
-rw-r--r--activity/src/main/java/androidx/activity/ComponentActivity.java41
-rw-r--r--annotations/api/1.1.0-beta01.txt3
-rw-r--r--annotations/api/current.txt3
-rw-r--r--annotations/src/main/java/androidx/annotation/ContentView.java30
-rw-r--r--appcompat/api/1.1.0-alpha04.txt1
-rw-r--r--appcompat/api/current.txt1
-rw-r--r--appcompat/src/main/java/androidx/appcompat/app/AppCompatActivity.java25
-rw-r--r--fragment/api/1.1.0-alpha06.txt2
-rw-r--r--fragment/api/current.txt2
-rw-r--r--fragment/src/androidTest/java/androidx/fragment/app/FragmentFactoryTest.kt4
-rw-r--r--fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.kt8
-rw-r--r--fragment/src/androidTest/java/androidx/fragment/app/FragmentTest.kt10
-rw-r--r--fragment/src/androidTest/java/androidx/fragment/app/FragmentTransactionTest.kt9
-rw-r--r--fragment/src/androidTest/java/androidx/fragment/app/FragmentViewTest.kt9
-rw-r--r--fragment/src/androidTest/java/androidx/fragment/app/NestedInflatedFragmentTest.kt7
-rw-r--r--fragment/src/androidTest/java/androidx/fragment/app/StrictFragment.kt3
-rw-r--r--fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt11
-rw-r--r--fragment/src/androidTest/java/androidx/fragment/app/test/FragmentTestActivity.kt4
-rw-r--r--fragment/src/androidTest/java/androidx/fragment/app/test/LoaderActivity.kt10
-rw-r--r--fragment/src/main/java/androidx/fragment/app/Fragment.java53
-rw-r--r--fragment/src/main/java/androidx/fragment/app/FragmentActivity.java27
-rw-r--r--testutils/src/main/java/androidx/testutils/RecreatedActivity.kt5
25 files changed, 160 insertions, 114 deletions
diff --git a/activity/api/1.0.0-alpha06.txt b/activity/api/1.0.0-alpha06.txt
index c46e0cdedea..16e509e06b1 100644
--- a/activity/api/1.0.0-alpha06.txt
+++ b/activity/api/1.0.0-alpha06.txt
@@ -3,6 +3,7 @@ package androidx.activity {
public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
ctor public ComponentActivity();
+ ctor @ContentView public ComponentActivity(@LayoutRes int);
method @Deprecated public void addOnBackPressedCallback(androidx.activity.OnBackPressedCallback);
method @Deprecated public void addOnBackPressedCallback(androidx.lifecycle.LifecycleOwner, androidx.activity.OnBackPressedCallback);
method @Deprecated public Object? getLastCustomNonConfigurationInstance();
diff --git a/activity/api/current.txt b/activity/api/current.txt
index c46e0cdedea..16e509e06b1 100644
--- a/activity/api/current.txt
+++ b/activity/api/current.txt
@@ -3,6 +3,7 @@ package androidx.activity {
public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
ctor public ComponentActivity();
+ ctor @ContentView public ComponentActivity(@LayoutRes int);
method @Deprecated public void addOnBackPressedCallback(androidx.activity.OnBackPressedCallback);
method @Deprecated public void addOnBackPressedCallback(androidx.lifecycle.LifecycleOwner, androidx.activity.OnBackPressedCallback);
method @Deprecated public Object? getLastCustomNonConfigurationInstance();
diff --git a/activity/src/androidTest/java/androidx/activity/ContentViewTest.kt b/activity/src/androidTest/java/androidx/activity/ContentViewTest.kt
index be432311bdd..eff215cfa2b 100644
--- a/activity/src/androidTest/java/androidx/activity/ContentViewTest.kt
+++ b/activity/src/androidTest/java/androidx/activity/ContentViewTest.kt
@@ -18,7 +18,6 @@ package androidx.activity
import android.widget.TextView
import androidx.activity.test.R
-import androidx.annotation.ContentView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.rule.ActivityTestRule
@@ -43,5 +42,4 @@ class ContentViewTest {
}
}
-@ContentView(R.layout.activity_inflates_res)
-class ContentViewActivity : ComponentActivity()
+class ContentViewActivity : ComponentActivity(R.layout.activity_inflates_res)
diff --git a/activity/src/main/java/androidx/activity/ComponentActivity.java b/activity/src/main/java/androidx/activity/ComponentActivity.java
index 1619920c1d3..2a3e3e9d453 100644
--- a/activity/src/main/java/androidx/activity/ComponentActivity.java
+++ b/activity/src/main/java/androidx/activity/ComponentActivity.java
@@ -25,6 +25,7 @@ import android.view.Window;
import androidx.annotation.CallSuper;
import androidx.annotation.ContentView;
+import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.arch.core.util.Cancellable;
@@ -39,7 +40,6 @@ import androidx.savedstate.SavedStateRegistry;
import androidx.savedstate.SavedStateRegistryController;
import androidx.savedstate.SavedStateRegistryOwner;
-import java.util.HashMap;
import java.util.WeakHashMap;
/**
@@ -73,9 +73,14 @@ public class ComponentActivity extends androidx.core.app.ComponentActivity imple
private final WeakHashMap<OnBackPressedCallback, Cancellable>
mOnBackPressedCallbackCancellables = new WeakHashMap<>();
- // Cache the ContentView layoutIds for Activities.
- private static final HashMap<Class, Integer> sAnnotationIds = new HashMap<>();
+ @LayoutRes
+ private int mContentLayoutId;
+ /**
+ * Default constructor for ComponentActivity. All Activities must have a default constructor
+ * for API 27 and lower devices or when using the default
+ * {@link android.app.AppComponentFactory}.
+ */
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
//noinspection ConstantConditions
@@ -116,6 +121,22 @@ public class ComponentActivity extends androidx.core.app.ComponentActivity imple
}
/**
+ * Alternate constructor that can be used to provide a default layout
+ * that will be inflated as part of <code>super.onCreate(savedInstanceState)</code>.
+ *
+ * <p>This should generally be called from your constructor that takes no parameters,
+ * as is required for API 27 and lower or when using the default
+ * {@link android.app.AppComponentFactory}.
+ *
+ * @see #ComponentActivity()
+ */
+ @ContentView
+ public ComponentActivity(@LayoutRes int contentLayoutId) {
+ this();
+ mContentLayoutId = contentLayoutId;
+ }
+
+ /**
* {@inheritDoc}
*
* If your ComponentActivity is annotated with {@link ContentView}, this will
@@ -126,18 +147,8 @@ public class ComponentActivity extends androidx.core.app.ComponentActivity imple
super.onCreate(savedInstanceState);
mSavedStateRegistryController.performRestore(savedInstanceState);
ReportFragment.injectIfNeededIn(this);
- Class<? extends ComponentActivity> clazz = getClass();
- if (!sAnnotationIds.containsKey(clazz)) {
- ContentView annotation = clazz.getAnnotation(ContentView.class);
- if (annotation != null) {
- sAnnotationIds.put(clazz, annotation.value());
- } else {
- sAnnotationIds.put(clazz, null);
- }
- }
- Integer layoutId = sAnnotationIds.get(clazz);
- if (layoutId != null && layoutId != 0) {
- setContentView(layoutId);
+ if (mContentLayoutId != 0) {
+ setContentView(mContentLayoutId);
}
}
diff --git a/annotations/api/1.1.0-beta01.txt b/annotations/api/1.1.0-beta01.txt
index 491ffea2f98..7e260ad643c 100644
--- a/annotations/api/1.1.0-beta01.txt
+++ b/annotations/api/1.1.0-beta01.txt
@@ -41,8 +41,7 @@ package androidx.annotation {
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface ColorRes {
}
- @java.lang.annotation.Inherited @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public @interface ContentView {
- method @LayoutRes public abstract int value();
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.CONSTRUCTOR}) public @interface ContentView {
}
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface DimenRes {
diff --git a/annotations/api/current.txt b/annotations/api/current.txt
index 491ffea2f98..7e260ad643c 100644
--- a/annotations/api/current.txt
+++ b/annotations/api/current.txt
@@ -41,8 +41,7 @@ package androidx.annotation {
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface ColorRes {
}
- @java.lang.annotation.Inherited @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public @interface ContentView {
- method @LayoutRes public abstract int value();
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.CONSTRUCTOR}) public @interface ContentView {
}
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface DimenRes {
diff --git a/annotations/src/main/java/androidx/annotation/ContentView.java b/annotations/src/main/java/androidx/annotation/ContentView.java
index eee0d2f63bf..f2f098dea4a 100644
--- a/annotations/src/main/java/androidx/annotation/ContentView.java
+++ b/annotations/src/main/java/androidx/annotation/ContentView.java
@@ -16,31 +16,31 @@
package androidx.annotation;
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.RetentionPolicy.CLASS;
-import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
- * Annotation that can be attached to a component such as an
- * androidx.activity.ComponentActivity or {@link androidx.fragment.app.Fragment}
+ * Annotation that can be attached to a constructor with a single {@link LayoutRes} parameter
* to denote what layout the component intends to inflate and set as its content.
* <p>
- * This annotation is marked as {@link Inherited} and will therefore apply to subclasses
- * automatically.
- * <p>
* It is strongly recommended that components that support this annotation specifically call
* it out in their documentation.
+ * <pre>
+ * public class MainFragment extends Fragment {
+ * public MainFragment() {
+ * // This constructor is annotated with @ContentView
+ * super(R.layout.main);
+ * }
+ * }
+ * </pre>
*
- * @see androidx.activity.ComponentActivity#onCreate(android.os.Bundle)
- * @see androidx.fragment.app.Fragment#onCreateView
+ * @see androidx.activity.ComponentActivity#ComponentActivity(int)
+ * @see androidx.fragment.app.Fragment#Fragment(int)
*/
-@Retention(RUNTIME)
-@Target({TYPE})
-@Inherited
+@Retention(CLASS)
+@Target({CONSTRUCTOR})
public @interface ContentView {
- @LayoutRes
- int value();
}
diff --git a/appcompat/api/1.1.0-alpha04.txt b/appcompat/api/1.1.0-alpha04.txt
index fdf6fc553a0..03862a1c660 100644
--- a/appcompat/api/1.1.0-alpha04.txt
+++ b/appcompat/api/1.1.0-alpha04.txt
@@ -218,6 +218,7 @@ package androidx.appcompat.app {
public class AppCompatActivity extends androidx.fragment.app.FragmentActivity implements androidx.appcompat.app.ActionBarDrawerToggle.DelegateProvider androidx.appcompat.app.AppCompatCallback androidx.core.app.TaskStackBuilder.SupportParentable {
ctor public AppCompatActivity();
+ ctor @ContentView public AppCompatActivity(@LayoutRes int);
method public androidx.appcompat.app.AppCompatDelegate getDelegate();
method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
method public androidx.appcompat.app.ActionBar? getSupportActionBar();
diff --git a/appcompat/api/current.txt b/appcompat/api/current.txt
index fdf6fc553a0..03862a1c660 100644
--- a/appcompat/api/current.txt
+++ b/appcompat/api/current.txt
@@ -218,6 +218,7 @@ package androidx.appcompat.app {
public class AppCompatActivity extends androidx.fragment.app.FragmentActivity implements androidx.appcompat.app.ActionBarDrawerToggle.DelegateProvider androidx.appcompat.app.AppCompatCallback androidx.core.app.TaskStackBuilder.SupportParentable {
ctor public AppCompatActivity();
+ ctor @ContentView public AppCompatActivity(@LayoutRes int);
method public androidx.appcompat.app.AppCompatDelegate getDelegate();
method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
method public androidx.appcompat.app.ActionBar? getSupportActionBar();
diff --git a/appcompat/src/main/java/androidx/appcompat/app/AppCompatActivity.java b/appcompat/src/main/java/androidx/appcompat/app/AppCompatActivity.java
index 23c9a09012a..8e7c7fd9c25 100644
--- a/appcompat/src/main/java/androidx/appcompat/app/AppCompatActivity.java
+++ b/appcompat/src/main/java/androidx/appcompat/app/AppCompatActivity.java
@@ -31,6 +31,7 @@ import android.view.ViewGroup;
import android.view.Window;
import androidx.annotation.CallSuper;
+import androidx.annotation.ContentView;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
@@ -67,6 +68,30 @@ public class AppCompatActivity extends FragmentActivity implements AppCompatCall
private AppCompatDelegate mDelegate;
private Resources mResources;
+ /**
+ * Default constructor for AppCompatActivity. All Activities must have a default constructor
+ * for API 27 and lower devices or when using the default
+ * {@link android.app.AppComponentFactory}.
+ */
+ public AppCompatActivity() {
+ super();
+ }
+
+ /**
+ * Alternate constructor that can be used to provide a default layout
+ * that will be inflated as part of <code>super.onCreate(savedInstanceState)</code>.
+ *
+ * <p>This should generally be called from your constructor that takes no parameters,
+ * as is required for API 27 and lower or when using the default
+ * {@link android.app.AppComponentFactory}.
+ *
+ * @see #AppCompatActivity()
+ */
+ @ContentView
+ public AppCompatActivity(@LayoutRes int contentLayoutId) {
+ super(contentLayoutId);
+ }
+
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
diff --git a/fragment/api/1.1.0-alpha06.txt b/fragment/api/1.1.0-alpha06.txt
index 22f79078af1..c945b0b4ff0 100644
--- a/fragment/api/1.1.0-alpha06.txt
+++ b/fragment/api/1.1.0-alpha06.txt
@@ -27,6 +27,7 @@ package androidx.fragment.app {
public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
ctor public Fragment();
+ ctor @ContentView public Fragment(@LayoutRes int);
method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String[]?);
method public final boolean equals(Object?);
method public final androidx.fragment.app.FragmentActivity? getActivity();
@@ -157,6 +158,7 @@ package androidx.fragment.app {
public class FragmentActivity extends androidx.activity.ComponentActivity implements androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback androidx.core.app.ActivityCompat.RequestPermissionsRequestCodeValidator {
ctor public FragmentActivity();
+ ctor @ContentView public FragmentActivity(@LayoutRes int);
method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
method @Deprecated public androidx.loader.app.LoaderManager getSupportLoaderManager();
method public android.content.Context getThemedContext();
diff --git a/fragment/api/current.txt b/fragment/api/current.txt
index 22f79078af1..c945b0b4ff0 100644
--- a/fragment/api/current.txt
+++ b/fragment/api/current.txt
@@ -27,6 +27,7 @@ package androidx.fragment.app {
public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
ctor public Fragment();
+ ctor @ContentView public Fragment(@LayoutRes int);
method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String[]?);
method public final boolean equals(Object?);
method public final androidx.fragment.app.FragmentActivity? getActivity();
@@ -157,6 +158,7 @@ package androidx.fragment.app {
public class FragmentActivity extends androidx.activity.ComponentActivity implements androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback androidx.core.app.ActivityCompat.RequestPermissionsRequestCodeValidator {
ctor public FragmentActivity();
+ ctor @ContentView public FragmentActivity(@LayoutRes int);
method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
method @Deprecated public androidx.loader.app.LoaderManager getSupportLoaderManager();
method public android.content.Context getThemedContext();
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/FragmentFactoryTest.kt b/fragment/src/androidTest/java/androidx/fragment/app/FragmentFactoryTest.kt
index 1e12a684487..a90cccf97b4 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/FragmentFactoryTest.kt
+++ b/fragment/src/androidTest/java/androidx/fragment/app/FragmentFactoryTest.kt
@@ -17,7 +17,6 @@
package androidx.fragment.app
import android.os.Bundle
-import androidx.annotation.ContentView
import androidx.fragment.app.test.EmptyFragmentTestActivity
import androidx.fragment.test.R
import androidx.test.annotation.UiThreadTest
@@ -87,8 +86,7 @@ class FragmentFactoryTest {
}
}
-@ContentView(R.layout.nested_inflated_fragment_parent)
-class ParentFragment : Fragment() {
+class ParentFragment : Fragment(R.layout.nested_inflated_fragment_parent) {
var factory: FragmentFactory? = null
override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.kt b/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.kt
index d27247cf918..42d18fc4de2 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.kt
+++ b/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.kt
@@ -25,7 +25,6 @@ import android.view.LayoutInflater
import android.view.Menu
import android.view.ViewGroup
import android.widget.TextView
-import androidx.annotation.ContentView
import androidx.core.view.ViewCompat
import androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks
import androidx.fragment.app.FragmentTestUtil.HostCallbacks
@@ -868,11 +867,10 @@ class FragmentLifecycleTest {
}
}
- @ContentView(R.layout.nested_retained_inflated_fragment_parent)
- class RetainedInflatedParentFragment : Fragment()
+ class RetainedInflatedParentFragment :
+ Fragment(R.layout.nested_retained_inflated_fragment_parent)
- @ContentView(R.layout.nested_inflated_fragment_child)
- class RetainedInflatedChildFragment : Fragment() {
+ class RetainedInflatedChildFragment : Fragment(R.layout.nested_inflated_fragment_child) {
internal var mOnInflateCount = 0
override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/FragmentTest.kt b/fragment/src/androidTest/java/androidx/fragment/app/FragmentTest.kt
index 8bf7bbdc3c2..85adb6c7ad6 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/FragmentTest.kt
+++ b/fragment/src/androidTest/java/androidx/fragment/app/FragmentTest.kt
@@ -20,7 +20,6 @@ import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
-import androidx.annotation.ContentView
import androidx.annotation.RequiresApi
import androidx.fragment.app.test.FragmentTestActivity
import androidx.fragment.test.R
@@ -309,12 +308,9 @@ class FragmentTest {
}
}
- @ContentView(R.layout.fragment_a)
- class FragmentA : Fragment()
+ class FragmentA : Fragment(R.layout.fragment_a)
- @ContentView(R.layout.fragment_b)
- class FragmentB : Fragment()
+ class FragmentB : Fragment(R.layout.fragment_b)
- @ContentView(R.layout.fragment_c)
- class FragmentC : Fragment()
+ class FragmentC : Fragment(R.layout.fragment_c)
}
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransactionTest.kt b/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransactionTest.kt
index 0c1e3fbac37..b66c15fed23 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransactionTest.kt
+++ b/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransactionTest.kt
@@ -15,14 +15,10 @@
*/
package androidx.fragment.app.test
-import org.junit.Assert.fail
-
import android.content.Intent
import android.os.Bundle
import android.os.SystemClock
import android.view.LayoutInflater
-
-import androidx.annotation.ContentView
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTestUtil
@@ -34,8 +30,8 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-
import org.junit.After
+import org.junit.Assert.fail
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -407,8 +403,7 @@ class FragmentTransactionTest {
private inner class NonStaticFragment : Fragment()
- @ContentView(R.layout.fragment_a)
- class OnGetLayoutInflaterFragment : Fragment() {
+ class OnGetLayoutInflaterFragment : Fragment(R.layout.fragment_a) {
var onGetLayoutInflaterCalls = 0
lateinit var baseLayoutInflater: LayoutInflater
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewTest.kt b/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewTest.kt
index c1d4d40154a..4b2ff74461d 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewTest.kt
+++ b/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewTest.kt
@@ -15,14 +15,10 @@
*/
package androidx.fragment.app
-import org.junit.Assert.fail
-
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-
-import androidx.annotation.ContentView
import androidx.fragment.app.test.FragmentTestActivity
import androidx.fragment.test.R
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -31,7 +27,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-
+import org.junit.Assert.fail
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -1029,8 +1025,7 @@ class FragmentViewTest {
}
}
- @ContentView(R.layout.fragment_a)
- class SimpleViewFragment : Fragment() {
+ class SimpleViewFragment : Fragment(R.layout.fragment_a) {
var onCreateViewCount: Int = 0
override fun onCreateView(
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/NestedInflatedFragmentTest.kt b/fragment/src/androidTest/java/androidx/fragment/app/NestedInflatedFragmentTest.kt
index af65c8c2448..59c3d43507f 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/NestedInflatedFragmentTest.kt
+++ b/fragment/src/androidTest/java/androidx/fragment/app/NestedInflatedFragmentTest.kt
@@ -20,7 +20,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
-import androidx.annotation.ContentView
import androidx.fragment.app.test.FragmentTestActivity
import androidx.fragment.test.R
import androidx.test.annotation.UiThreadTest
@@ -83,8 +82,7 @@ class NestedInflatedFragmentTest {
fm.executePendingTransactions()
}
- @ContentView(R.layout.nested_inflated_fragment_parent)
- open class ParentFragment : Fragment()
+ open class ParentFragment : Fragment(R.layout.nested_inflated_fragment_parent)
class UserVisibleHintParentFragment : ParentFragment() {
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
@@ -102,8 +100,7 @@ class NestedInflatedFragmentTest {
}
}
- @ContentView(R.layout.nested_inflated_fragment_child)
- class InflatedChildFragment : Fragment()
+ class InflatedChildFragment : Fragment(R.layout.nested_inflated_fragment_child)
class SimpleFragment : Fragment() {
override fun onCreateView(
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/StrictFragment.kt b/fragment/src/androidTest/java/androidx/fragment/app/StrictFragment.kt
index b8faca2d4f1..61d74b5811d 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/StrictFragment.kt
+++ b/fragment/src/androidTest/java/androidx/fragment/app/StrictFragment.kt
@@ -18,13 +18,14 @@ package androidx.fragment.app
import android.content.Context
import android.os.Bundle
+import androidx.annotation.LayoutRes
import com.google.common.truth.Truth.assertWithMessage
/**
* This fragment watches its primary lifecycle events and throws IllegalStateException
* if any of them are called out of order or from a bad/unexpected state.
*/
-open class StrictFragment : Fragment() {
+open class StrictFragment(@LayoutRes contentLayoutId: Int = 0) : Fragment(contentLayoutId) {
var currentState: Int = 0
var calledOnAttach: Boolean = false
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt b/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt
index 5b367e46301..86062b96aee 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt
+++ b/fragment/src/androidTest/java/androidx/fragment/app/StrictViewFragment.kt
@@ -25,8 +25,8 @@ import androidx.fragment.test.R
import com.google.common.truth.Truth.assertWithMessage
open class StrictViewFragment(
- @LayoutRes val contentLayoutId: Int = R.layout.strict_view_fragment
-) : StrictFragment() {
+ @LayoutRes contentLayoutId: Int = R.layout.strict_view_fragment
+) : StrictFragment(contentLayoutId) {
internal var onCreateViewCalled: Boolean = false
internal var onViewCreatedCalled: Boolean = false
@@ -39,12 +39,9 @@ open class StrictViewFragment(
): View? {
checkGetActivity()
checkState("onCreateView", StrictFragment.CREATED)
- var result = super.onCreateView(inflater, container, savedInstanceState)
- if (result == null) {
- result = inflater.inflate(contentLayoutId, container, false)
+ return super.onCreateView(inflater, container, savedInstanceState).also {
+ onCreateViewCalled = true
}
- onCreateViewCalled = true
- return result
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/test/FragmentTestActivity.kt b/fragment/src/androidTest/java/androidx/fragment/app/test/FragmentTestActivity.kt
index 6ab2b107657..64dbc8d186a 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/test/FragmentTestActivity.kt
+++ b/fragment/src/androidTest/java/androidx/fragment/app/test/FragmentTestActivity.kt
@@ -18,7 +18,6 @@ package androidx.fragment.app.test
import android.content.Context
import android.content.Intent
import android.os.Bundle
-import androidx.annotation.ContentView
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.test.R
@@ -26,8 +25,7 @@ import androidx.fragment.test.R
/**
* A simple activity used for Fragment Transitions and lifecycle event ordering
*/
-@ContentView(R.layout.activity_content)
-class FragmentTestActivity : FragmentActivity() {
+class FragmentTestActivity : FragmentActivity(R.layout.activity_content) {
class ParentFragment : Fragment() {
var wasAttachedInTime: Boolean = false
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/test/LoaderActivity.kt b/fragment/src/androidTest/java/androidx/fragment/app/test/LoaderActivity.kt
index ea44bf90e2b..9baf280fc5d 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/test/LoaderActivity.kt
+++ b/fragment/src/androidTest/java/androidx/fragment/app/test/LoaderActivity.kt
@@ -20,8 +20,6 @@ import android.content.Context
import android.os.Bundle
import android.view.View
import android.widget.TextView
-
-import androidx.annotation.ContentView
import androidx.fragment.app.Fragment
import androidx.fragment.test.R
import androidx.loader.app.LoaderManager
@@ -29,8 +27,8 @@ import androidx.loader.content.AsyncTaskLoader
import androidx.loader.content.Loader
import androidx.testutils.RecreatedActivity
-@ContentView(R.layout.activity_loader)
-class LoaderActivity : RecreatedActivity(), LoaderManager.LoaderCallbacks<String> {
+class LoaderActivity : RecreatedActivity(R.layout.activity_loader),
+ LoaderManager.LoaderCallbacks<String> {
lateinit var textView: TextView
lateinit var textViewB: TextView
@@ -75,8 +73,8 @@ class LoaderActivity : RecreatedActivity(), LoaderManager.LoaderCallbacks<String
}
}
- @ContentView(R.layout.fragment_c)
- class TextLoaderFragment : Fragment(), LoaderManager.LoaderCallbacks<String> {
+ class TextLoaderFragment : Fragment(R.layout.fragment_c),
+ LoaderManager.LoaderCallbacks<String> {
lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/fragment/src/main/java/androidx/fragment/app/Fragment.java b/fragment/src/main/java/androidx/fragment/app/Fragment.java
index de3963dcf3f..89cf832443f 100644
--- a/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -48,6 +48,7 @@ import android.widget.AdapterView;
import androidx.annotation.CallSuper;
import androidx.annotation.ContentView;
+import androidx.annotation.LayoutRes;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -72,7 +73,6 @@ import androidx.savedstate.SavedStateRegistryOwner;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
-import java.util.HashMap;
import java.util.UUID;
/**
@@ -250,8 +250,8 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
SavedStateRegistryController mSavedStateRegistryController;
- // Cache the ContentView layoutIds for Fragments.
- private static final HashMap<Class, Integer> sAnnotationIds = new HashMap<>();
+ @LayoutRes
+ private int mContentLayoutId;
/**
* {@inheritDoc}
@@ -414,13 +414,14 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
}
/**
- * Default constructor. <strong>Every</strong> fragment must have an
- * empty constructor, so it can be instantiated when restoring its
- * activity's state. It is strongly recommended that subclasses do not
- * have other constructors with parameters, since these constructors
- * will not be called when the fragment is re-instantiated; instead,
- * arguments can be supplied by the caller with {@link #setArguments}
- * and later retrieved by the Fragment with {@link #getArguments}.
+ * Constructor used by the default {@link FragmentFactory}. You must
+ * {@link FragmentManager#setFragmentFactory(FragmentFactory) set a custom FragmentFactory}
+ * if you want to use a non-default constructor to ensure that your constructor
+ * is called when the fragment is re-instantiated.
+ *
+ * <p>It is strongly recommended to supply arguments with {@link #setArguments}
+ * and later retrieved by the Fragment with {@link #getArguments}. These arguments
+ * are automatically saved and restored alongside the Fragment.
*
* <p>Applications should generally not implement a constructor. Prefer
* {@link #onAttach(Context)} instead. It is the first place application code can run where
@@ -432,6 +433,19 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
initLifecycle();
}
+ /**
+ * Alternate constructor that can be used to provide a default layout
+ * that will be inflated by {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
+ *
+ * @see #Fragment()
+ * @see #onCreateView(LayoutInflater, ViewGroup, Bundle)
+ */
+ @ContentView
+ public Fragment(@LayoutRes int contentLayoutId) {
+ this();
+ mContentLayoutId = contentLayoutId;
+ }
+
private void initLifecycle() {
mLifecycleRegistry = new LifecycleRegistry(this);
mSavedStateRegistryController = SavedStateRegistryController.create(this);
@@ -1616,9 +1630,8 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
* Called to have the fragment instantiate its user interface view.
* This is optional, and non-graphical fragments can return null. This will be called between
* {@link #onCreate(Bundle)} and {@link #onActivityCreated(Bundle)}.
- * <p>The default implementation looks for an {@link ContentView} annotation, inflating
- * and returning that layout. If the annotation is not found or has an invalid layout resource
- * id, this method returns null.
+ * <p>A default View can be returned by calling {@link #Fragment(int)} in your
+ * constructor. Otherwise, this method returns null.
*
* <p>It is recommended to <strong>only</strong> inflate the layout in this method and move
* logic that operates on the returned View to {@link #onViewCreated(View, Bundle)}.
@@ -1639,18 +1652,8 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
@Nullable
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
- Class<? extends Fragment> clazz = getClass();
- if (!sAnnotationIds.containsKey(clazz)) {
- ContentView annotation = clazz.getAnnotation(ContentView.class);
- if (annotation != null) {
- sAnnotationIds.put(clazz, annotation.value());
- } else {
- sAnnotationIds.put(clazz, null);
- }
- }
- Integer layoutId = sAnnotationIds.get(clazz);
- if (layoutId != null && layoutId != 0) {
- return inflater.inflate(layoutId, container, false);
+ if (mContentLayoutId != 0) {
+ return inflater.inflate(mContentLayoutId, container, false);
}
return null;
}
diff --git a/fragment/src/main/java/androidx/fragment/app/FragmentActivity.java b/fragment/src/main/java/androidx/fragment/app/FragmentActivity.java
index 4a4a1f10e84..6cee60b608b 100644
--- a/fragment/src/main/java/androidx/fragment/app/FragmentActivity.java
+++ b/fragment/src/main/java/androidx/fragment/app/FragmentActivity.java
@@ -37,6 +37,8 @@ import android.view.Window;
import androidx.activity.ComponentActivity;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.CallSuper;
+import androidx.annotation.ContentView;
+import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
@@ -107,8 +109,33 @@ public class FragmentActivity extends ComponentActivity implements
// for startActivityForResult calls where a result has not yet been delivered.
SparseArrayCompat<String> mPendingFragmentActivityResults;
+ /**
+ * Default constructor for FragmentActivity. All Activities must have a default constructor
+ * for API 27 and lower devices or when using the default
+ * {@link android.app.AppComponentFactory}.
+ */
public FragmentActivity() {
super();
+ init();
+ }
+
+ /**
+ * Alternate constructor that can be used to provide a default layout
+ * that will be inflated as part of <code>super.onCreate(savedInstanceState)</code>.
+ *
+ * <p>This should generally be called from your constructor that takes no parameters,
+ * as is required for API 27 and lower or when using the default
+ * {@link android.app.AppComponentFactory}.
+ *
+ * @see #FragmentActivity()
+ */
+ @ContentView
+ public FragmentActivity(@LayoutRes int contentLayoutId) {
+ super(contentLayoutId);
+ init();
+ }
+
+ private void init() {
// Route onBackPressed() callbacks to the FragmentManager
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback() {
@Override
diff --git a/testutils/src/main/java/androidx/testutils/RecreatedActivity.kt b/testutils/src/main/java/androidx/testutils/RecreatedActivity.kt
index 22b74aa9e9a..1463395f37f 100644
--- a/testutils/src/main/java/androidx/testutils/RecreatedActivity.kt
+++ b/testutils/src/main/java/androidx/testutils/RecreatedActivity.kt
@@ -17,6 +17,7 @@
package androidx.testutils
import android.os.Bundle
+import androidx.annotation.LayoutRes
import androidx.fragment.app.FragmentActivity
import java.util.concurrent.CountDownLatch
@@ -25,7 +26,9 @@ import java.util.concurrent.CountDownLatch
* In order to use this class, have your activity extend it and call
* [FragmentActivityUtils.recreateActivity] API.
*/
-open class RecreatedActivity : FragmentActivity() {
+open class RecreatedActivity(
+ @LayoutRes contentLayoutId: Int = 0
+) : FragmentActivity(contentLayoutId) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)