Skip to content

Commit 4ea0525

Browse files
committed
Add detection for <fragment> tag usage
1 parent 44de0e9 commit 4ea0525

File tree

6 files changed

+71
-0
lines changed

6 files changed

+71
-0
lines changed

fragment/fragment/api/public_plus_experimental_current.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ package androidx.fragment.app.strictmode {
461461

462462
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public final class FragmentStrictMode {
463463
method public static androidx.fragment.app.strictmode.FragmentStrictMode.Policy getDefaultPolicy();
464+
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onFragmentTagUsage(androidx.fragment.app.Fragment);
464465
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onRetainInstanceUsage(androidx.fragment.app.Fragment);
465466
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onSetUserVisibleHint(androidx.fragment.app.Fragment);
466467
method public static void setDefaultPolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy);
@@ -477,13 +478,18 @@ package androidx.fragment.app.strictmode {
477478
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static final class FragmentStrictMode.Policy.Builder {
478479
ctor public FragmentStrictMode.Policy.Builder();
479480
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy build();
481+
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentTagUsage();
480482
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectRetainInstanceUsage();
481483
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectSetUserVisibleHint();
482484
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyDeath();
483485
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyListener(androidx.fragment.app.strictmode.FragmentStrictMode.OnViolationListener);
484486
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyLog();
485487
}
486488

489+
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public final class FragmentTagUsageViolation extends androidx.fragment.app.strictmode.Violation {
490+
ctor public FragmentTagUsageViolation();
491+
}
492+
487493
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public final class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
488494
ctor public RetainInstanceUsageViolation();
489495
}

fragment/fragment/api/restricted_current.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ package androidx.fragment.app.strictmode {
487487

488488
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public final class FragmentStrictMode {
489489
method public static androidx.fragment.app.strictmode.FragmentStrictMode.Policy getDefaultPolicy();
490+
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onFragmentTagUsage(androidx.fragment.app.Fragment);
490491
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onRetainInstanceUsage(androidx.fragment.app.Fragment);
491492
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static void onSetUserVisibleHint(androidx.fragment.app.Fragment);
492493
method public static void setDefaultPolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy);
@@ -503,13 +504,18 @@ package androidx.fragment.app.strictmode {
503504
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public static final class FragmentStrictMode.Policy.Builder {
504505
ctor public FragmentStrictMode.Policy.Builder();
505506
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy build();
507+
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentTagUsage();
506508
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectRetainInstanceUsage();
507509
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectSetUserVisibleHint();
508510
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyDeath();
509511
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyListener(androidx.fragment.app.strictmode.FragmentStrictMode.OnViolationListener);
510512
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyLog();
511513
}
512514

515+
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public final class FragmentTagUsageViolation extends androidx.fragment.app.strictmode.Violation {
516+
ctor public FragmentTagUsageViolation();
517+
}
518+
513519
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY) public final class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
514520
ctor public RetainInstanceUsageViolation();
515521
}

fragment/fragment/src/androidTest/java/androidx/fragment/app/strictmode/FragmentStrictModeTest.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import android.os.Looper
2020
import androidx.fragment.app.StrictFragment
2121
import androidx.fragment.app.executePendingTransactions
2222
import androidx.fragment.app.test.FragmentTestActivity
23+
import androidx.fragment.test.R
2324
import androidx.test.core.app.ActivityScenario
2425
import androidx.test.ext.junit.runners.AndroidJUnit4
2526
import androidx.test.filters.MediumTest
@@ -128,6 +129,21 @@ public class FragmentStrictModeTest {
128129
}
129130
}
130131

132+
@Test
133+
public fun detectFragmentTagUsage() {
134+
var violation: Violation? = null
135+
val policy = FragmentStrictMode.Policy.Builder()
136+
.detectFragmentTagUsage()
137+
.penaltyListener { violation = it }
138+
.build()
139+
FragmentStrictMode.setDefaultPolicy(policy)
140+
141+
with(ActivityScenario.launch(FragmentTestActivity::class.java)) {
142+
withActivity { setContentView(R.layout.activity_inflated_fragment) }
143+
assertThat(violation).isInstanceOf(FragmentTagUsageViolation::class.java)
144+
}
145+
}
146+
131147
@Test
132148
public fun detectRetainInstanceUsage() {
133149
var violation: Violation? = null

fragment/fragment/src/main/java/androidx/fragment/app/FragmentLayoutInflaterFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import androidx.annotation.NonNull;
2828
import androidx.annotation.Nullable;
2929
import androidx.fragment.R;
30+
import androidx.fragment.app.strictmode.FragmentStrictMode;
3031

3132
class FragmentLayoutInflaterFactory implements LayoutInflater.Factory2 {
3233
private static final String TAG = FragmentManager.TAG;
@@ -130,6 +131,7 @@ public View onCreateView(@Nullable final View parent, @NonNull String name,
130131
+ "re-attached via the <fragment> tag: id=0x" + Integer.toHexString(id));
131132
}
132133
}
134+
FragmentStrictMode.onFragmentTagUsage(fragment);
133135

134136
// Explicitly set the container for the fragment as we already know
135137
// the parent that the fragment will be added to by the LayoutInflater

fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentStrictMode.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ private enum Flag {
5151
PENALTY_LOG,
5252
PENALTY_DEATH,
5353

54+
DETECT_FRAGMENT_TAG_USAGE,
5455
DETECT_RETAIN_INSTANCE_USAGE,
5556
DETECT_SET_USER_VISIBLE_HINT
5657
}
@@ -143,6 +144,14 @@ public Builder penaltyListener(@NonNull OnViolationListener listener) {
143144
return this;
144145
}
145146

147+
/** Detects usage of the &lt;fragment&gt; tag inside XML layouts. */
148+
@NonNull
149+
@SuppressLint("BuilderSetStyle")
150+
public Builder detectFragmentTagUsage() {
151+
flags.add(Flag.DETECT_FRAGMENT_TAG_USAGE);
152+
return this;
153+
}
154+
146155
/**
147156
* Detects calls to #{@link Fragment#setRetainInstance} and
148157
* #{@link Fragment#getRetainInstance()}.
@@ -207,6 +216,14 @@ private static Policy getNearestPolicy(@Nullable Fragment fragment) {
207216
return defaultPolicy;
208217
}
209218

219+
@RestrictTo(RestrictTo.Scope.LIBRARY)
220+
public static void onFragmentTagUsage(@NonNull Fragment fragment) {
221+
Policy policy = getNearestPolicy(fragment);
222+
if (policy.flags.contains(Flag.DETECT_FRAGMENT_TAG_USAGE)) {
223+
handlePolicyViolation(fragment, policy, new FragmentTagUsageViolation());
224+
}
225+
}
226+
210227
@RestrictTo(RestrictTo.Scope.LIBRARY)
211228
public static void onRetainInstanceUsage(@NonNull Fragment fragment) {
212229
Policy policy = getNearestPolicy(fragment);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2021 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package androidx.fragment.app.strictmode;
18+
19+
import androidx.annotation.RestrictTo;
20+
21+
/** See #{@link FragmentStrictMode.Policy.Builder#detectFragmentTagUsage()}. */
22+
@RestrictTo(RestrictTo.Scope.LIBRARY) // TODO: Make API public as soon as we have a few checks
23+
public final class FragmentTagUsageViolation extends Violation {
24+
}

0 commit comments

Comments
 (0)