diff --git a/app/build.gradle b/app/build.gradle index 71d3e8b78..db496e311 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -440,7 +440,6 @@ dependencies { implementation libs.androidx.recyclerview implementation libs.material.material implementation libs.androidx.legacy.support - implementation libs.androidx.cardview implementation libs.androidx.preference implementation libs.androidx.legacy.preference implementation libs.androidx.gridlayout diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ClippedCardView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/ClippedCardView.kt index 08effd2e8..d3030626b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ClippedCardView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ClippedCardView.kt @@ -6,8 +6,8 @@ import android.graphics.Path import android.graphics.Rect import android.graphics.RectF import android.util.AttributeSet -import androidx.cardview.widget.CardView import androidx.core.graphics.withClip +import com.google.android.material.card.MaterialCardView /** * Adds manual clipping around the card. This ensures that software rendering @@ -16,7 +16,7 @@ import androidx.core.graphics.withClip class ClippedCardView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null -) : CardView(context, attrs) { +) : MaterialCardView(context, attrs) { private val bounds = Rect() private val boundsF = RectF() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/RotatableGradientDrawable.java b/app/src/main/java/org/thoughtcrime/securesms/components/RotatableGradientDrawable.java index b5160e4fe..b7cd65486 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/RotatableGradientDrawable.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/RotatableGradientDrawable.java @@ -28,7 +28,7 @@ import kotlin.jvm.functions.Function2; * fill the bounds with a gradient. * * If you wish to apply clipping to this drawable, it is recommended to either use it with - * a CardView or utilize {@link org.thoughtcrime.securesms.util.CustomDrawWrapperKt#customizeOnDraw(Drawable, Function2)} + * a MaterialCardView or utilize {@link org.thoughtcrime.securesms.util.CustomDrawWrapperKt#customizeOnDraw(Drawable, Function2)} */ public final class RotatableGradientDrawable extends Drawable { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsLayout.java index 0c3928c74..a6d2835e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsLayout.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsLayout.java @@ -7,10 +7,10 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.cardview.widget.CardView; import com.google.android.flexbox.AlignItems; import com.google.android.flexbox.FlexboxLayout; +import com.google.android.material.card.MaterialCardView; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.events.CallParticipant; @@ -116,7 +116,7 @@ public class CallParticipantsLayout extends FlexboxLayout { private void update(int index, int count, @NonNull CallParticipant participant) { View view = getChildAt(index); - CardView cardView = view.findViewById(R.id.group_call_participant_card_wrapper); + MaterialCardView cardView = view.findViewById(R.id.group_call_participant_card_wrapper); CallParticipantView callParticipantView = view.findViewById(R.id.group_call_participant); callParticipantView.setCallParticipant(participant); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java index 7040f9311..4f075e6cb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java @@ -29,7 +29,6 @@ import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.cardview.widget.CardView; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; @@ -39,6 +38,7 @@ import com.bumptech.glide.load.Transformation; import com.bumptech.glide.load.resource.bitmap.CenterCrop; import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.transition.Transition; +import com.google.android.material.card.MaterialCardView; import org.signal.core.util.Stopwatch; import org.signal.core.util.logging.Log; @@ -360,9 +360,9 @@ public class Camera1Fragment extends LoggingFragment implements CameraFragment, } private void initializeViewFinderAndControlsPositioning() { - CardView cameraCard = requireView().findViewById(R.id.camera_preview_parent); - View controls = requireView().findViewById(R.id.camera_controls_container); - CameraDisplay cameraDisplay = CameraDisplay.getDisplay(requireActivity()); + MaterialCardView cameraCard = requireView().findViewById(R.id.camera_preview_parent); + View controls = requireView().findViewById(R.id.camera_controls_container); + CameraDisplay cameraDisplay = CameraDisplay.getDisplay(requireActivity()); if (!cameraDisplay.getRoundViewFinderCorners()) { cameraCard.setRadius(0f); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java index a97d69567..f5bc3c96f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java @@ -26,7 +26,6 @@ import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.camera.core.CameraSelector; import androidx.camera.core.ImageCapture; import androidx.camera.core.ImageCaptureException; @@ -35,13 +34,13 @@ import androidx.camera.view.CameraController; import androidx.camera.view.LifecycleCameraController; import androidx.camera.view.PreviewView; import androidx.camera.view.video.ExperimentalVideo; -import androidx.cardview.widget.CardView; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; import androidx.core.content.ContextCompat; import com.bumptech.glide.Glide; import com.bumptech.glide.util.Executors; +import com.google.android.material.card.MaterialCardView; import org.signal.core.util.Stopwatch; import org.signal.core.util.concurrent.SimpleTask; @@ -293,9 +292,9 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment { } private void initializeViewFinderAndControlsPositioning() { - CardView cameraCard = requireView().findViewById(R.id.camerax_camera_parent); - View controls = requireView().findViewById(R.id.camerax_controls_container); - CameraDisplay cameraDisplay = CameraDisplay.getDisplay(requireActivity()); + MaterialCardView cameraCard = requireView().findViewById(R.id.camerax_camera_parent); + View controls = requireView().findViewById(R.id.camerax_controls_container); + CameraDisplay cameraDisplay = CameraDisplay.getDisplay(requireActivity()); if (!cameraDisplay.getRoundViewFinderCorners()) { cameraCard.setRadius(0f); diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt index 0ceeca9e2..581494412 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt @@ -18,7 +18,6 @@ import android.view.View import android.view.animation.Interpolator import android.widget.FrameLayout import android.widget.TextView -import androidx.cardview.widget.CardView import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.core.content.ContextCompat @@ -30,6 +29,7 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import com.google.android.material.button.MaterialButton +import com.google.android.material.card.MaterialCardView import com.google.android.material.progressindicator.CircularProgressIndicatorSpec import com.google.android.material.progressindicator.IndeterminateDrawable import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers @@ -176,7 +176,7 @@ class StoryViewerPageFragment : val moreButton: View = view.findViewById(R.id.more) val distributionList: TextView = view.findViewById(R.id.distribution_list) val cardWrapper: TouchInterceptingFrameLayout = view.findViewById(R.id.story_content_card_touch_interceptor) - val card: CardView = view.findViewById(R.id.story_content_card) + val card: MaterialCardView = view.findViewById(R.id.story_content_card) val caption: TextView = view.findViewById(R.id.story_caption) val largeCaption: TextView = view.findViewById(R.id.story_large_caption) val largeCaptionOverlay: View = view.findViewById(R.id.story_large_caption_overlay) @@ -610,7 +610,7 @@ class StoryViewerPageFragment : private fun adjustConstraintsForScreenDimensions( viewsAndReplies: View, cardWrapper: View, - card: CardView + card: MaterialCardView ) { val constraintSet = ConstraintSet() constraintSet.clone(storyPageContainer) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/StoriesSharedElementCrossFaderView.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/StoriesSharedElementCrossFaderView.kt index 701df9a79..74a9b52f6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/StoriesSharedElementCrossFaderView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/StoriesSharedElementCrossFaderView.kt @@ -6,11 +6,11 @@ import android.graphics.drawable.Drawable import android.net.Uri import android.util.AttributeSet import android.widget.ImageView -import androidx.cardview.widget.CardView import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target +import com.google.android.material.card.MaterialCardView import org.signal.core.util.DimensionUnit import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.animation.transitions.CrossfaderTransition @@ -24,7 +24,7 @@ import kotlin.reflect.KProperty class StoriesSharedElementCrossFaderView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null -) : CardView(context, attrs), CrossfaderTransition.Crossfadeable { +) : MaterialCardView(context, attrs), CrossfaderTransition.Crossfadeable { companion object { val CORNER_RADIUS_START = DimensionUnit.DP.toPixels(12f) diff --git a/app/src/main/res/layout/camera_fragment.xml b/app/src/main/res/layout/camera_fragment.xml index fb1603914..ec0787af7 100644 --- a/app/src/main/res/layout/camera_fragment.xml +++ b/app/src/main/res/layout/camera_fragment.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - + - - + - - + \ No newline at end of file diff --git a/app/src/main/res/layout/custom_chat_color_creator_fragment_page.xml b/app/src/main/res/layout/custom_chat_color_creator_fragment_page.xml index 7c0bc3d74..0a95d3668 100644 --- a/app/src/main/res/layout/custom_chat_color_creator_fragment_page.xml +++ b/app/src/main/res/layout/custom_chat_color_creator_fragment_page.xml @@ -12,7 +12,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - + - - + \ No newline at end of file diff --git a/app/src/main/res/layout/group_call_participant_item.xml b/app/src/main/res/layout/group_call_participant_item.xml index 50f69075d..b836458c1 100644 --- a/app/src/main/res/layout/group_call_participant_item.xml +++ b/app/src/main/res/layout/group_call_participant_item.xml @@ -9,7 +9,7 @@ tools:layout_height="match_parent" tools:layout_width="match_parent"> - - + \ No newline at end of file diff --git a/app/src/main/res/layout/group_pending_member_invites_fragment.xml b/app/src/main/res/layout/group_pending_member_invites_fragment.xml index 322018957..e87843f8a 100644 --- a/app/src/main/res/layout/group_pending_member_invites_fragment.xml +++ b/app/src/main/res/layout/group_pending_member_invites_fragment.xml @@ -15,7 +15,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - + - - + diff --git a/app/src/main/res/layout/group_requesting_member_fragment.xml b/app/src/main/res/layout/group_requesting_member_fragment.xml index fd86744cf..c15704762 100644 --- a/app/src/main/res/layout/group_requesting_member_fragment.xml +++ b/app/src/main/res/layout/group_requesting_member_fragment.xml @@ -14,7 +14,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - + diff --git a/app/src/main/res/layout/message_details_recipient.xml b/app/src/main/res/layout/message_details_recipient.xml index d44fcfbda..a4e855ac0 100644 --- a/app/src/main/res/layout/message_details_recipient.xml +++ b/app/src/main/res/layout/message_details_recipient.xml @@ -1,14 +1,14 @@ - + app:cardElevation="0dp" + tools:viewBindingIgnore="true"> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/message_details_recipient_header.xml b/app/src/main/res/layout/message_details_recipient_header.xml index 9c0234af0..3f5d2c2d2 100644 --- a/app/src/main/res/layout/message_details_recipient_header.xml +++ b/app/src/main/res/layout/message_details_recipient_header.xml @@ -1,14 +1,14 @@ - + app:cardElevation="0dp" + tools:viewBindingIgnore="true"> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/popup_megaphone_view.xml b/app/src/main/res/layout/popup_megaphone_view.xml index 13c9a376d..bbeb014e7 100644 --- a/app/src/main/res/layout/popup_megaphone_view.xml +++ b/app/src/main/res/layout/popup_megaphone_view.xml @@ -6,7 +6,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> - - + \ No newline at end of file diff --git a/app/src/main/res/layout/processing_payment_dialog.xml b/app/src/main/res/layout/processing_payment_dialog.xml index b358c306c..caacf2131 100644 --- a/app/src/main/res/layout/processing_payment_dialog.xml +++ b/app/src/main/res/layout/processing_payment_dialog.xml @@ -1,5 +1,5 @@ - - + diff --git a/app/src/main/res/layout/redeeming_gift_dialog.xml b/app/src/main/res/layout/redeeming_gift_dialog.xml index 808e5f431..6f81bc897 100644 --- a/app/src/main/res/layout/redeeming_gift_dialog.xml +++ b/app/src/main/res/layout/redeeming_gift_dialog.xml @@ -1,5 +1,5 @@ - - + diff --git a/app/src/main/res/layout/stories_link_popup.xml b/app/src/main/res/layout/stories_link_popup.xml index 5ab58ed1a..6038696f9 100644 --- a/app/src/main/res/layout/stories_link_popup.xml +++ b/app/src/main/res/layout/stories_link_popup.xml @@ -21,7 +21,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/bubble" /> - + tools:parentTag="com.google.android.material.card.MaterialCardView"> - - + - - + \ No newline at end of file diff --git a/app/src/main/res/layout/stories_viewer_fragment_page.xml b/app/src/main/res/layout/stories_viewer_fragment_page.xml index 2611a9cd1..f8bf5b370 100644 --- a/app/src/main/res/layout/stories_viewer_fragment_page.xml +++ b/app/src/main/res/layout/stories_viewer_fragment_page.xml @@ -23,7 +23,7 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0"> - - + - - + diff --git a/app/src/main/res/layout/webrtc_call_participant_recycler_item.xml b/app/src/main/res/layout/webrtc_call_participant_recycler_item.xml index ac9c34fec..dfda46744 100644 --- a/app/src/main/res/layout/webrtc_call_participant_recycler_item.xml +++ b/app/src/main/res/layout/webrtc_call_participant_recycler_item.xml @@ -1,5 +1,5 @@ - - + diff --git a/app/src/main/res/layout/webrtc_call_view.xml b/app/src/main/res/layout/webrtc_call_view.xml index 565a47fe2..37f636e27 100644 --- a/app/src/main/res/layout/webrtc_call_view.xml +++ b/app/src/main/res/layout/webrtc_call_view.xml @@ -176,7 +176,7 @@ app:layout_constraintTop_toTopOf="parent" tools:visibility="gone"> - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/contacts/app/src/main/res/layout/parent_item.xml b/contacts/app/src/main/res/layout/parent_item.xml index e6552ceed..f735f901d 100644 --- a/contacts/app/src/main/res/layout/parent_item.xml +++ b/contacts/app/src/main/res/layout/parent_item.xml @@ -9,7 +9,7 @@ android:clipToPadding="false" android:clipChildren="false"> - - + \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index 0550a5438..497c6b107 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -50,7 +50,6 @@ dependencyResolutionManagement { alias('androidx-recyclerview').to('androidx.recyclerview:recyclerview:1.2.1') alias('androidx-legacy-support').to('androidx.legacy:legacy-support-v13:1.0.0') alias('androidx-legacy-preference').to('androidx.legacy:legacy-preference-v14:1.0.0') - alias('androidx-cardview').to('androidx.cardview:cardview:1.0.0') alias('androidx-preference').to('androidx.preference:preference:1.0.0') alias('androidx-gridlayout').to('androidx.gridlayout:gridlayout:1.0.0') alias('androidx-exifinterface').to('androidx.exifinterface:exifinterface:1.3.3') diff --git a/lintchecks/src/main/java/org/signal/lint/CardViewDetector.java b/lintchecks/src/main/java/org/signal/lint/CardViewDetector.java new file mode 100644 index 000000000..84ad79754 --- /dev/null +++ b/lintchecks/src/main/java/org/signal/lint/CardViewDetector.java @@ -0,0 +1,82 @@ +package org.signal.lint; + +import com.android.tools.lint.client.api.JavaEvaluator; +import com.android.tools.lint.detector.api.Category; +import com.android.tools.lint.detector.api.Detector; +import com.android.tools.lint.detector.api.Implementation; +import com.android.tools.lint.detector.api.Issue; +import com.android.tools.lint.detector.api.JavaContext; +import com.android.tools.lint.detector.api.LintFix; +import com.android.tools.lint.detector.api.Scope; +import com.android.tools.lint.detector.api.Severity; +import com.intellij.psi.PsiMethod; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.uast.UCallExpression; +import org.jetbrains.uast.UExpression; + +import java.util.Collections; +import java.util.List; + +@SuppressWarnings("UnstableApiUsage") +public final class CardViewDetector extends Detector implements Detector.UastScanner { + + static final Issue CARD_VIEW_USAGE = Issue.create("CardViewUsage", + "Utilizing CardView instead of MaterialCardView subclass", + "Signal utilizes MaterialCardView for more consistent and pleasant CardViews.", + Category.MESSAGES, + 5, + Severity.WARNING, + new Implementation(CardViewDetector.class, Scope.JAVA_FILE_SCOPE)); + + @Override + public @Nullable List getApplicableConstructorTypes() { + return Collections.singletonList("androidx.cardview.widget.CardView"); + } + + @Override + public void visitConstructor(JavaContext context, @NotNull UCallExpression call, @NotNull PsiMethod method) { + JavaEvaluator evaluator = context.getEvaluator(); + + if (evaluator.isMemberInClass(method, "androidx.cardview.widget.CardView")) { + LintFix fix = quickFixIssueAlertDialogBuilder(call); + context.report(CARD_VIEW_USAGE, + call, + context.getLocation(call), + "Using 'androidx.cardview.widget.CardView' instead of com.google.android.material.card.MaterialCardView", + fix); + } + } + + private LintFix quickFixIssueAlertDialogBuilder(@NotNull UCallExpression alertBuilderCall) { + List arguments = alertBuilderCall.getValueArguments(); + UExpression context = arguments.get(0); + + String fixSource = "new com.google.android.material.card.MaterialCardView"; + + //Context context, AttributeSet attrs, int defStyleAttr + switch (arguments.size()) { + case 1: + fixSource += String.format("(%s)", context); + break; + case 2: + UExpression attrs = arguments.get(1); + fixSource += String.format("(%s, %s)", context, attrs); + break; + case 3: + UExpression attributes = arguments.get(1); + UExpression defStyleAttr = arguments.get(2); + fixSource += String.format("(%s, %s, %s)", context, attributes, defStyleAttr); + break; + + default: + throw new IllegalStateException("MaterialAlertDialogBuilder overloads should have 1 or 2 arguments"); + } + + String builderCallSource = alertBuilderCall.asSourceString(); + LintFix.GroupBuilder fixGrouper = fix().group(); + fixGrouper.add(fix().replace().text(builderCallSource).shortenNames().reformat(true).with(fixSource).build()); + return fixGrouper.build(); + } +} \ No newline at end of file diff --git a/lintchecks/src/main/java/org/signal/lint/Registry.java b/lintchecks/src/main/java/org/signal/lint/Registry.java index 6152e3a5a..9fe531473 100644 --- a/lintchecks/src/main/java/org/signal/lint/Registry.java +++ b/lintchecks/src/main/java/org/signal/lint/Registry.java @@ -28,7 +28,8 @@ public final class Registry extends IssueRegistry { BlockingGetDetector.UNSAFE_BLOCKING_GET, RecipientIdDatabaseDetector.RECIPIENT_ID_DATABASE_REFERENCE_ISSUE, ThreadIdDatabaseDetector.THREAD_ID_DATABASE_REFERENCE_ISSUE, - StartForegroundServiceDetector.START_FOREGROUND_SERVICE_ISSUE); + StartForegroundServiceDetector.START_FOREGROUND_SERVICE_ISSUE, + CardViewDetector.CARD_VIEW_USAGE); } @Override diff --git a/lintchecks/src/test/java/org/signal/lint/CardViewDetectorTest.java b/lintchecks/src/test/java/org/signal/lint/CardViewDetectorTest.java new file mode 100644 index 000000000..a6dd6924d --- /dev/null +++ b/lintchecks/src/test/java/org/signal/lint/CardViewDetectorTest.java @@ -0,0 +1,100 @@ +package org.signal.lint; + +import com.android.tools.lint.checks.infrastructure.TestFile; + +import org.junit.Test; + +import java.io.InputStream; +import java.util.Scanner; + +import static com.android.tools.lint.checks.infrastructure.TestFiles.java; +import static com.android.tools.lint.checks.infrastructure.TestLintTask.lint; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@SuppressWarnings("UnstableApiUsage") +public final class CardViewDetectorTest { + + private static final TestFile cardViewStub = java(readResourceAsString("CardViewStub.java")); + + @Test + public void cardViewUsed_LogCardViewUsage_1_arg() { + lint() + .files(cardViewStub, + java("package foo;\n" + + "import androidx.cardview.widget.CardView;\n" + + "public class Example {\n" + + " public void buildDialog() {\n" + + " new CardView(context);\n" + + " }\n" + + "}") + ) + .issues(AlertDialogBuilderDetector.ALERT_DIALOG_BUILDER_USAGE) + .run() + .expect("src/foo/Example.java:5: Warning: Using 'androidx.cardview.widget.CardView' instead of com.google.android.material.card.MaterialCardView [CardViewUsage]\n" + + " new CardView(context);\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 1 warnings") + .expectFixDiffs("Fix for src/foo/Example.java line 5: Replace with new com.google.android.material.card.MaterialCardView(context):\n" + + "@@ -5 +5\n" + + "- new CardView(context);\n" + + "+ new com.google.android.material.card.MaterialCardView(context);"); + } + + @Test + public void cardViewUsed_LogCardViewUsage_2_arg() { + lint() + .files(cardViewStub, + java("package foo;\n" + + "import androidx.cardview.widget.CardView;\n" + + "public class Example {\n" + + " public void buildDialog() {\n" + + " new CardView(context, attrs);\n" + + " }\n" + + "}") + ) + .issues(AlertDialogBuilderDetector.ALERT_DIALOG_BUILDER_USAGE) + .run() + .expect("src/foo/Example.java:5: Warning: Using 'androidx.cardview.widget.CardView' instead of com.google.android.material.card.MaterialCardView [CardViewUsage]\n" + + " new CardView(context, attrs);\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 1 warnings") + .expectFixDiffs("Fix for src/foo/Example.java line 5: Replace with new com.google.android.material.card.MaterialCardView(context, attrs):\n" + + "@@ -5 +5\n" + + "- new CardView(context, attrs);\n" + + "+ new com.google.android.material.card.MaterialCardView(context, attrs);"); + } + + @Test + public void cardViewUsed_withAssignment_LogCardViewUsage_1_arg() { + lint() + .files(cardViewStub, + java("package foo;\n" + + "import androidx.cardview.widget.CardView;\n" + + "public class Example {\n" + + " public void buildDialog() {\n" + + " CardView cardView = new CardView(context)\n" + + " ;\n" + + " }\n" + + "}") + ) + .issues(AlertDialogBuilderDetector.ALERT_DIALOG_BUILDER_USAGE) + .run() + .expect("src/foo/Example.java:5: Warning: Using 'androidx.cardview.widget.CardView' instead of com.google.android.material.card.MaterialCardView [CardViewUsage]\n" + + " CardView cardView = new CardView(context)\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "0 errors, 1 warnings") + .expectFixDiffs("Fix for src/foo/Example.java line 5: Replace with new com.google.android.material.card.MaterialCardView(context):\n" + + "@@ -5 +5\n" + + "- CardView cardView = new CardView(context)\n" + + "+ CardView cardView = new com.google.android.material.card.MaterialCardView(context)"); + } + + private static String readResourceAsString(String resourceName) { + InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(resourceName); + assertNotNull(inputStream); + Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"); + assertTrue(scanner.hasNext()); + return scanner.next(); + } +} diff --git a/lintchecks/src/test/resources/CardViewStub.java b/lintchecks/src/test/resources/CardViewStub.java new file mode 100644 index 000000000..97860064a --- /dev/null +++ b/lintchecks/src/test/resources/CardViewStub.java @@ -0,0 +1,16 @@ +package androidx.appcompat.app; + +public class CardView { + + public CardView(Context context) { + + } + + public CardView(Context context, AttributeSet attrs) { + + } + + public CardView(Context context, AttributeSet attrs, int defStyleAttr) { + + } +} diff --git a/paging/app/src/main/res/layout/item.xml b/paging/app/src/main/res/layout/item.xml index a0cd66dbd..3c2e797f4 100644 --- a/paging/app/src/main/res/layout/item.xml +++ b/paging/app/src/main/res/layout/item.xml @@ -9,7 +9,7 @@ android:clipToPadding="false" android:clipChildren="false"> - - + \ No newline at end of file diff --git a/spinner/app/src/main/res/layout/item.xml b/spinner/app/src/main/res/layout/item.xml index a0cd66dbd..3c2e797f4 100644 --- a/spinner/app/src/main/res/layout/item.xml +++ b/spinner/app/src/main/res/layout/item.xml @@ -9,7 +9,7 @@ android:clipToPadding="false" android:clipChildren="false"> - - + \ No newline at end of file