-
Notifications
You must be signed in to change notification settings - Fork 331
Add Android module libraries to Flutter project #2474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Awesome! Looking now... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Several questions :)
import javax.swing.*; | ||
|
||
public class AndroidModuleLibraryType extends LibraryType<AndroidModuleLibraryProperties> { | ||
public static final String LIBRARY_NAME = "Android Libraries"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will this show up in the UI? As the category in the Project view? If so, perhaps something which indicates that it comes from Flutter (and/or is automanaged)? Flutter Plugin Libraries
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the latest is Android Libraries
. It could still be useful to give a hint where these libraries come from (the Flutter plugin dependencies), but YMMV.
//return gradleProjectSystem.getAvailableDependency(coordinate, includePreview); | ||
Method finders = ReflectionUtil.getMethod(gradleProjectSystem.getClass(), "getAvailableDependency"); | ||
if (finders == null) { | ||
LOG.error("No method found: GradleProjectSystem.getAvailableDependency()"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this an error we want to show to the user, or something we just want to log (at a warn level)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops! I forgot to change that. Done.
return (GradleCoordinate) finders.invoke(gradleProjectSystem, coordinate, includePreview); | ||
} | ||
catch (IllegalAccessException | InvocationTargetException e) { | ||
LOG.error(e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this is a totally unexpected error? Does GradleProjectSystem.getAvailableDependency()
itself declare any exceptions? If so, we should like declare them in this method, then instanceof check for them here, and if that's what the invoke threw, re-throw them here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of the exceptions will be thrown. The checking is all there to satisfy compilers and style guides. :) The method isn't used in 3.1 and exists in 3.2.
@@ -85,7 +85,20 @@ public ProjectSystemSyncManager getSyncManager() { | |||
@SuppressWarnings("override") | |||
@Nullable | |||
public GradleCoordinate getAvailableDependency(@NotNull GradleCoordinate coordinate, boolean includePreview) { | |||
return null; | |||
//return gradleProjectSystem.getAvailableDependency(coordinate, includePreview); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When will we be able to remove the reflection, and can you add a todo: comment for us to come back then and remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need that reflection as long as we build for 3.1. I added a TODO.
@@ -40,5 +41,7 @@ public void runActivity(@NotNull Project project) { | |||
// Unset this flag for all projects, mainly to ease the upgrade from 3.0.1 to 3.1. | |||
// TODO(messick) Delete once 3.0.x has 0 7DA's. | |||
FlutterProjectCreator.disableUserConfig(project); | |||
// Monitor Android dependencies. | |||
AndroidModuleLibraryManager.startWatching(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this generally solid, or should we have an experimental flag to try it out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a flag.
doGradleSync(getProject(), (Project x) -> updateAndroidLibraryContent(x)); | ||
} | ||
|
||
private Void updateAndroidLibraryContent(Project androidProject) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
void
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting rid of type-checking warnings was a hassle. If there's a better way to do it I'd like to know. Using "void" will cause a compilation error now, though.
doGradleSync(getProject(), (Project x) -> updateAndroidLibraryContent(x)); | ||
} | ||
|
||
private Void updateAndroidLibraryContent(Project androidProject) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have androidProject
be @Nonnullable
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, missed that one. Done.
LibraryTable androidProjectLibraryTable = LibraryTablesRegistrar.getInstance().getLibraryTable(androidProject); | ||
Library[] androidProjectLibraries = androidProjectLibraryTable.getLibraries(); | ||
if (androidProjectLibraries.length == 0) { | ||
LOG.error("Gradle sync was incomplete -- no Android libraries found"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do want this to be a user-facing error, we should use the toast notifications (from the FlutterMessages
utility class). If not, we should log at a warn level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
} | ||
|
||
private static void fileChanged(@NotNull final Project project, @NotNull final VirtualFile file) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for my own edification, it looks like we watch the build.gradle
(in any subdirectory?), and schedule a gradle sync when it changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, there are two of them (at least) in an Android module. We might want to watch settings.gradle
too.
return ServiceManager.getService(project, AndroidModuleLibraryManager.class); | ||
} | ||
|
||
public static void startWatching() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this will only watch projects that are open at IntelliJ launch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This runs each time a project is opened.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're iterating over all open projects below, so we may want to instead just process the project that was opened (which, I think this information is available from the StartupActivity
interface).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is one of those "What was I thinking?" moments. I removed the loop from startWatching()
and added the Project as a parameter. Thanks for catching that!
I made a few changes, PTAL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Three main comments (also in-line):
- we could choose to expose as a user-facing preference (defaulting to off) instead of a system property, if we think we're ready
- we should evaluate the workflow that we want with this, esp. wrt when we should drive flutter_tools, and when it should drive gradle
- we're iterating over all the projects when a project is opened, and we should likely just examine the specific one that was opened
import io.flutter.project.FlutterProjectCreator; | ||
import io.flutter.utils.FlutterModuleUtils; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
import java.util.MissingResourceException; | ||
|
||
public class FlutterStudioStartupActivity implements StartupActivity { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if we implement void runActivity(@NotNull Project project);
here we can get the reference to the project that is being opened.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, done.
@@ -40,5 +44,10 @@ public void runActivity(@NotNull Project project) { | |||
// Unset this flag for all projects, mainly to ease the upgrade from 3.0.1 to 3.1. | |||
// TODO(messick) Delete once 3.0.x has 0 7DA's. | |||
FlutterProjectCreator.disableUserConfig(project); | |||
// Monitor Android dependencies. | |||
if (System.getProperty("flutter.android.library.sync", null) != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option here is to add a user facing preference in the preferences (likely defaulting to off initially).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could do; but later, I think.
} | ||
|
||
public void update() { | ||
doGradleSync(getProject(), (Project x) -> updateAndroidLibraryContent(x)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, one thing to consider:
gradle is being run for these projects, just not explicitly by the user. flutter run
will drive gradle as necessary to build the app. flutter packages get
already does som set up in terms of maintaining flutter plugin metadata (including editing gradle files?). So, we have options for having some portions of this being driven from the CLI, and us triggering the flutter CLI when appropriate.
return ServiceManager.getService(project, AndroidModuleLibraryManager.class); | ||
} | ||
|
||
public static void startWatching() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're iterating over all open projects below, so we may want to instead just process the project that was opened (which, I think this information is available from the StartupActivity
interface).
import javax.swing.*; | ||
|
||
public class AndroidModuleLibraryType extends LibraryType<AndroidModuleLibraryProperties> { | ||
public static final String LIBRARY_NAME = "Android Libraries"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the latest is Android Libraries
. It could still be useful to give a hint where these libraries come from (the Flutter plugin dependencies), but YMMV.
|
||
@Override | ||
public void syncSucceeded(@NotNull Project project) { | ||
callback.apply(androidProject); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to put the flag update in a finally? Something like:
try {
callback.apply(androidProject);
} finally {
isUpdating.set(false);
}
EDIT: I'm probably over-thinking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that doesn't work too well because the callback runs async. Effectively removes the flag. Speaking from experience here :)
I made a slight modification to the code, in response to @devoncarew's comment about looping over projects. I checked it out and the smoke tests still work as expected so I'll submit without further review. Thanks for the comments! |
@devoncarew @pq
Analyzes the Android module and adds all libraries required to compile its code to the top-level Flutter project. This should fix problems related to not having indexed all the code in the Android module (i.e. completion, navigation, etc.)
It probably still needs work but I'm not sure what the issues are. It takes noticeably more time to open a project since a Gradle sync is required, but that is no worse than what Android Studio does for an Android project.