Show a warning for projects with kts build files Android Studio is able to open projects with kts Gradle build files but some functionality is not available for them. This CL causes a warning to appear every time a project with kts build files is opened or the first time it is synced and it contains a kts file. Test: New tests added to confirm warning is shown when appropriate. Bug: 130226195 Change-Id: Id2e3bd6103364499e8351ca083a623b36fb384f5
diff --git a/android/src/META-INF/android-plugin.xml b/android/src/META-INF/android-plugin.xml index 567a2db..a782204 100644 --- a/android/src/META-INF/android-plugin.xml +++ b/android/src/META-INF/android-plugin.xml
@@ -576,6 +576,7 @@ <projectService serviceImplementation="com.android.tools.idea.gradle.project.sync.GradleFiles"/> <projectService serviceImplementation="com.android.tools.idea.gradle.project.build.GradleBuildState"/> <projectService serviceImplementation="com.android.tools.idea.project.AndroidNotification"/> + <projectService serviceImplementation="com.android.tools.idea.project.AndroidKtsSupportNotification"/> <projectService serviceImplementation="com.android.tools.idea.project.IndexingSuspender"/> <projectService serviceImplementation="com.android.tools.idea.gradle.project.sync.setup.module.common.DependencySetupIssues"/> <projectService serviceImplementation="com.android.tools.idea.gradle.project.sync.messages.GradleSyncMessages"/> @@ -856,6 +857,7 @@ <postSyncProjectSetupStep implementation="com.android.tools.idea.gradle.project.sync.setup.post.project.SdkToolsVersionSetupStep"/> <postSyncProjectSetupStep implementation="com.android.tools.idea.gradle.project.sync.setup.post.project.ExpiredPreviewBuildSetupStep"/> <postSyncProjectSetupStep implementation="com.android.tools.idea.gradle.project.sync.setup.post.project.IgnoredBuildScriptSetupStep"/> + <postSyncProjectSetupStep implementation="com.android.tools.idea.gradle.project.sync.setup.post.project.GradleKtsBuildFilesWarningStep"/> <postSyncModuleSetupStep implementation="com.android.tools.idea.gradle.project.sync.setup.post.module.TestArtifactSearchScopeSetupStep"/> <postSyncModuleSetupStep implementation="com.android.tools.idea.gradle.project.sync.setup.post.module.AndroidRunConfigurationSetupStep"/>
diff --git a/android/src/com/android/tools/idea/gradle/project/sync/setup/post/project/GradleKtsBuildFilesWarningStep.java b/android/src/com/android/tools/idea/gradle/project/sync/setup/post/project/GradleKtsBuildFilesWarningStep.java new file mode 100644 index 0000000..9da01a4 --- /dev/null +++ b/android/src/com/android/tools/idea/gradle/project/sync/setup/post/project/GradleKtsBuildFilesWarningStep.java
@@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tools.idea.gradle.project.sync.setup.post.project; + +import com.android.tools.idea.gradle.project.sync.setup.post.ProjectSetupStep; +import com.android.tools.idea.gradle.util.GradleUtil; +import com.android.tools.idea.project.AndroidKtsSupportNotification; +import com.google.common.annotations.VisibleForTesting; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class GradleKtsBuildFilesWarningStep extends ProjectSetupStep { + @Override + public void setUpProject(@NotNull Project project, @Nullable ProgressIndicator indicator) { + doSetUpProject(project, GradleUtil.hasKtsBuildFiles(project)); + } + + @VisibleForTesting + void doSetUpProject(@NotNull Project project, boolean hasKts) { + if (hasKts) { + AndroidKtsSupportNotification.getInstance(project).showWarningIfNotShown(); + } + } +}
diff --git a/android/src/com/android/tools/idea/gradle/util/GradleUtil.java b/android/src/com/android/tools/idea/gradle/util/GradleUtil.java index 87a40bb..f57fdfd 100644 --- a/android/src/com/android/tools/idea/gradle/util/GradleUtil.java +++ b/android/src/com/android/tools/idea/gradle/util/GradleUtil.java
@@ -1030,6 +1030,10 @@ return result; } + public static boolean hasKtsBuildFiles(@NotNull Project project) { + return projectBuildFilesTypes(project).contains(DOT_KTS); + } + private static void addBuildFileType(@NotNull HashSet<String> result, @Nullable VirtualFile buildFile) { if (buildFile != null) { String buildFileExtension = buildFile.getExtension();
diff --git a/android/src/com/android/tools/idea/project/AndroidKtsSupportNotification.java b/android/src/com/android/tools/idea/project/AndroidKtsSupportNotification.java new file mode 100644 index 0000000..1fdd0f0 --- /dev/null +++ b/android/src/com/android/tools/idea/project/AndroidKtsSupportNotification.java
@@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tools.idea.project; + +import static com.intellij.notification.NotificationDisplayType.NONE; +import static com.intellij.notification.NotificationType.WARNING; + +import com.android.tools.idea.project.hyperlink.NotificationHyperlink; +import com.intellij.notification.NotificationGroup; +import com.intellij.notification.NotificationsConfiguration; +import com.intellij.notification.impl.NotificationSettings; +import com.intellij.notification.impl.NotificationsConfigurationImpl; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +public class AndroidKtsSupportNotification { + public static final String KTS_WARNING_MSG = "This project uses Gradle KTS build files which are not fully supported. Some functions may be affected."; + public static final String KTS_WARNING_TITLE = "Gradle KTS Build Files"; + public static final NotificationGroup KTS_NOTIFICATION_GROUP = NotificationGroup.balloonGroup("Gradle KTS build files"); + + @NotNull private final Project myProject; + private boolean alreadyShown; + + @NotNull + public static AndroidKtsSupportNotification getInstance(@NotNull Project project) { + return ServiceManager.getService(project, AndroidKtsSupportNotification.class); + } + + public AndroidKtsSupportNotification(@NotNull Project project) { + myProject = project; + alreadyShown = false; + } + + public void showWarningIfNotShown() { + if (!alreadyShown) { + AndroidNotification.getInstance(myProject).showBalloon(KTS_WARNING_TITLE, KTS_WARNING_MSG, WARNING, KTS_NOTIFICATION_GROUP, + new DisableAndroidKtsNotificationHyperlink()); + // Make sure that it was displayed, otherwise notification will not show until project is reopened. + NotificationSettings settings = NotificationsConfigurationImpl.getSettings(KTS_NOTIFICATION_GROUP.getDisplayId()); + alreadyShown = settings.getDisplayType() != NONE || settings.isShouldLog(); + } + } + + public static class DisableAndroidKtsNotificationHyperlink extends NotificationHyperlink { + + protected DisableAndroidKtsNotificationHyperlink() { + super("disableKtsNotification", "Disable this warning"); + } + + @Override + protected void execute(@NotNull Project project) { + NotificationsConfiguration.getNotificationsConfiguration().changeSettings(KTS_NOTIFICATION_GROUP.getDisplayId(), NONE, false, false); + } + } +}
diff --git a/android/testSrc/com/android/tools/idea/gradle/project/sync/setup/post/project/GradleKtsBuildFilesWarningStepTest.java b/android/testSrc/com/android/tools/idea/gradle/project/sync/setup/post/project/GradleKtsBuildFilesWarningStepTest.java new file mode 100644 index 0000000..8841a04 --- /dev/null +++ b/android/testSrc/com/android/tools/idea/gradle/project/sync/setup/post/project/GradleKtsBuildFilesWarningStepTest.java
@@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tools.idea.gradle.project.sync.setup.post.project; + +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.android.tools.idea.project.AndroidKtsSupportNotification; +import com.android.tools.idea.testing.IdeComponents; +import com.intellij.testFramework.IdeaTestCase; +import org.jetbrains.annotations.NotNull; +import org.mockito.Mock; + +/** + * Tests for {@link GradleKtsBuildFilesWarningStep} + */ +public class GradleKtsBuildFilesWarningStepTest extends IdeaTestCase { + + @Mock private AndroidKtsSupportNotification myNotification; + @NotNull private GradleKtsBuildFilesWarningStep myWarningStep = new GradleKtsBuildFilesWarningStep(); + + @Override + public void setUp() throws Exception { + super.setUp(); + initMocks(this); + new IdeComponents(myProject).replaceProjectService(AndroidKtsSupportNotification.class, myNotification); + } + + /** + * Check that a warning is created when the project uses kts build files + */ + public void testDoSetUpProjectWithKts() { + myWarningStep.doSetUpProject(myProject, true); + verify(myNotification).showWarningIfNotShown(); + } + + /** + * Check that a warning is *NOT* created when the project does not use kts build files + */ + public void testDoSetUpProjectWithoutKts() { + myWarningStep.doSetUpProject(myProject, false); + verify(myNotification, never()).showWarningIfNotShown(); + } +}
diff --git a/android/testSrc/com/android/tools/idea/project/AndroidKtsSupportNotificationTest.java b/android/testSrc/com/android/tools/idea/project/AndroidKtsSupportNotificationTest.java new file mode 100644 index 0000000..a44f3db --- /dev/null +++ b/android/testSrc/com/android/tools/idea/project/AndroidKtsSupportNotificationTest.java
@@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tools.idea.project; + +import static com.android.tools.idea.project.AndroidKtsSupportNotification.KTS_NOTIFICATION_GROUP; +import static com.android.tools.idea.project.AndroidKtsSupportNotification.KTS_WARNING_MSG; +import static com.android.tools.idea.project.AndroidKtsSupportNotification.KTS_WARNING_TITLE; +import static com.google.common.truth.Truth.assertThat; +import static com.intellij.notification.NotificationType.WARNING; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.android.tools.idea.project.AndroidKtsSupportNotification.DisableAndroidKtsNotificationHyperlink; +import com.android.tools.idea.project.hyperlink.NotificationHyperlink; +import com.android.tools.idea.testing.IdeComponents; +import com.intellij.testFramework.IdeaTestCase; +import java.util.List; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; + +/** + * Tests for {@link AndroidKtsSupportNotification} + */ +public class AndroidKtsSupportNotificationTest extends IdeaTestCase { + @Mock private AndroidNotification myAndroidNotification; + + @Override + public void setUp() throws Exception { + super.setUp(); + initMocks(this); + new IdeComponents(myProject).replaceProjectService(AndroidNotification.class, myAndroidNotification); + } + + /** + * Verify an AndroidNotification is created with the expected parameters + */ + public void testShowWarningIfNotShown() { + AndroidKtsSupportNotification myKtsNotification = new AndroidKtsSupportNotification(myProject); + myKtsNotification.showWarningIfNotShown(); + verifyBalloon(); + } + + /** + * Verify that warning is generated only once even when called multiple times + */ + public void testShowWarningIfNotShownTwice() { + AndroidKtsSupportNotification myKtsNotification = new AndroidKtsSupportNotification(myProject); + myKtsNotification.showWarningIfNotShown(); + myKtsNotification.showWarningIfNotShown(); + verifyBalloon(); + } + + private void verifyBalloon() { + ArgumentCaptor<NotificationHyperlink> hyperlinkCaptor = ArgumentCaptor.forClass(NotificationHyperlink.class); + verify(myAndroidNotification, times(1)).showBalloon(same(KTS_WARNING_TITLE), same(KTS_WARNING_MSG), same(WARNING), same(KTS_NOTIFICATION_GROUP), hyperlinkCaptor.capture()); + List<NotificationHyperlink> hyperlinks = hyperlinkCaptor.getAllValues(); + assertThat(hyperlinks).hasSize(1); + assertThat(hyperlinks.get(0)).isInstanceOf(DisableAndroidKtsNotificationHyperlink.class); + } +}