-
Notifications
You must be signed in to change notification settings - Fork 330
Support co-editing Flutter and Android in a single project #3850
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
This is ready for review. @devoncarew @pq @jacob314 for review or suggestions of reviewers. @alexander-doroshko there's a solution for https://youtrack.jetbrains.com/issue/WEB-40082 embedded in this PR. @xster this handles the modules that are added using @DaveShuckerow and I will be looking at ways to move some of this out of IntelliJ and into a shareable service so VS Code can take advantage of it. Android Q sources are out now but I will do the above-mentioned cleanup in another PR. |
/* | ||
Add-to-app notes: | ||
- The Flutter module must be added to the Android app via manual editing as in the add-to-app docs, | ||
or by using the forthcoming tool in Android Studio that generates a module and does the editing. |
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 I understand what this is potentially referring to, this is already possible via the new section in the wiki https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps#1-depend-on-the-android-archive-aar. Though tracing back to the source of the AAR is indeed going to be a challenge.
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.
Thanks for taking a look, @xster.
I can't review the plugin part but the concept LGTM. |
Great to see this PR! @DaveShuckerow, can you review? Apart from Steve, I think you're closest to the add2app story. |
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.
Comments and questions. This first round of review, I'm trying to get a better picture of what's happening and why.
Thanks!
MessageBusConnection connection = project.getMessageBus().connect(project); | ||
connection.subscribe(ProjectTopics.MODULES, new ModuleListener() { | ||
@Override | ||
public void moduleAdded(@NotNull Project proj, @NotNull Module mod) { |
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 module listener logic looks like it could be refactored out and shared with the occurrence in FlutterStudioStartupActivity.runActivity.
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.
That's the plan, once the consolidation is possible. When I was working on this there were significant differences in IntelliJ and Android Studio. Now that Android Q is public I hope to be able to clean up some duplicated code, but that's not happening for a few weeks. The two code blocks you refer to have TODOs outlining the eventual changes. Note that FlutterInitializer runs in all IntelliJ-based IDEs but FlutterStudioStartupActivity only runs in Android Studio.
} | ||
|
||
public static void addGradleListeners(@NotNull Project project) { | ||
if (!FlutterUtils.isAndroidStudio()) { |
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.
Why is this not needed in Android Studio?
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.
Android Studio has its own notion of how Gradle runs and broadcasts its events. The two are not compatible, at least today. It is possible that IntelliJ will adopt the Android Studio protocol in the future (and if they do they will likely find a way to not break existing clients).
}); | ||
} | ||
|
||
private static boolean isVanillaAddToApp(@NotNull Project project, @Nullable VirtualFile file, @NotNull String name) { |
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.
What does 'vanilla' add to app mean?
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 flavorings have been added, like files that have been edited or added. This method examines the structure of a project to ensure that 1) it looks like a Flutter android module and 2) has not previously been modified to run as an add-to-app module.
// BuildModelContext.create(project).getOrCreateSettingsFile(project); | ||
boolean isAndroidStudio = FlutterUtils.isAndroidStudio(); | ||
VirtualFile projectDir = requireNonNull(FlutterUtils.getProjectRoot(project)); | ||
Object param = isAndroidStudio ? projectDir.findChild(SdkConstants.FN_SETTINGS_GRADLE) : project; |
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 a very interesting way of effectively creating a union type.
Is this equivalent to
if isAndroidStudio {
VirtualFile param = projectDir.findChild(...);
return BuildModelContext.getOrCreateSettingsFile(param);
} else {
return BuildModelContext.getOrCreateSettingsFile(BuildModelContext.create(project));
}
I'm trying to understand what's happening 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.
It would be equivalent, if that code could be compiled. The API skew between IntelliJ and Android Studio means we have to use reflection here.
|
||
private static void addCoeditTransformedProject(@NotNull Project project) { | ||
if (!project.isDisposed()) { | ||
COEDIT_TRANSFORMED_PROJECTS.add(project); |
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.
Do we ever remove a project from this list?
I can see it is a WeakList, which I assume means that when the projects are disposed, the list will remove them. Let me know if that is not correct.
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.
Correct, the weak reference allows a disposed project to get GC'd, and the garbage collector removes it from the collection. This will be an infrequent occurrence.
|
||
// Copied from org.jetbrains.plugins.gradle.execution.GradleRunAnythingProvider. | ||
@NotNull | ||
@SuppressWarnings("DuplicatedCode") |
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.
What is the difference between this and the gradle plugin code?
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.
Accessibility. It happens a lot. Perfectly usable code is marked private
and is unusable elsewhere.
return null; | ||
} | ||
|
||
private static class CoEditHelper { |
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.
My understanding is that the logic from here to the end of the file is focused on rewriting files and is something that we might want to share with the VS Code plugin, which is where we would write code in Dart as we discussed yesterday.
The places that dependencies couple this with the rest of the file are:
parseSettings()
findInclude()
uses the Psi tree.
We'll need to understand the requirements for a VS Code application of the logic to identify how to prune, or if it's worth doing.
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.
Correct. We'd also want to move the functionality of isVanillaAddToApp()
into the external service. Once we've done that there won't be any dependencies left, since both parseSettings()
and findInclude()
are only used by isVanillaAddToApp()
and enableCoediting()
.
Thanks @DaveShuckerow for taking a look at this. I know it isn't easy to understand. It was hard to write, too. It has to compile (and eventually run) in both IntelliJ and Android Studio, for both source and non-source dev environs, and across all supported products (in product-matrix.json). Up to now we've been lucky and haven't had too deep an integration with code that differs between AS and IJ but with the Gradle stuff we're jumping into the deep end, so to speak. |
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 LGTM. Please include a comment near the reflection code explaining why it's necessary so that if we have to change it in the future it's a little clearer.
Sure, I'll add that to be included with the next PR. Thanks! |
Thanks, @stevemessick! |
Replacement for #3817, due to git merge error.
Caveat: only works for Android Studio; IntelliJ support will be added later.
TODO: Find a better way to manage Maven repositories in IntelliJ. The project structure dialog is way too slow.
TODO: Clean up module dependencies after Android Q is released.
There is a comment in AndroidUtils about Add-to-app notes. It will be deleted before the co-edit work is finalized (after Android Q) but I want it to stay for now. Likewise, #triggerSyncIfFlutterModuleFoundAfterBuild has been commented-out and will probably be removed, but there's a (rare?) use case that might require it.