Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Refactored `DeviceSelectorAction` and add rich icons to different platform devices (#8475)
- Fix DTD freezes when opening projects, and EDT freezes when the theme is changed and opening embedded DevTools (#8477)
- Fix `DeviceSelectorAction` `NoSuchElementException` in the toolbar layout (#8515)
- Fix `DeviceSelectorAction`'s concurrent modification exception. (#8550)

## 87.1.0

Expand Down
38 changes: 26 additions & 12 deletions src/io/flutter/actions/DeviceSelectorAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
Expand All @@ -28,6 +29,7 @@
import com.intellij.util.ui.JBUI;
import icons.FlutterIcons;
import io.flutter.FlutterBundle;
import io.flutter.logging.PluginLogger;
import io.flutter.run.FlutterDevice;
import io.flutter.run.daemon.DeviceService;
import io.flutter.sdk.AndroidEmulatorManager;
Expand All @@ -43,14 +45,16 @@
import java.util.List;

public class DeviceSelectorAction extends AnAction implements CustomComponentAction, DumbAware {
private static final @NotNull Logger LOG = PluginLogger.createLogger(DeviceSelectorAction.class);

private static final Key<JButton> CUSTOM_COMPONENT_KEY = Key.create("customComponent");
private static final Key<JBLabel> ICON_LABEL_KEY = Key.create("iconLabel");
private static final Key<JBLabel> TEXT_LABEL_KEY = Key.create("textLabel");
private static final Key<JBLabel> ARROW_LABEL_KEY = Key.create("arrowLabel");
private static final @NotNull Icon DEFAULT_DEVICE_ICON = FlutterIcons.Mobile;
private static final @NotNull Icon DEFAULT_ARROW_ICON = IconUtil.scale(AllIcons.General.ChevronDown, null, 1.2f);

private final List<AnAction> actions = new ArrayList<>();
private volatile @NotNull List<AnAction> actions = new ArrayList<>();
private final List<Project> knownProjects = Collections.synchronizedList(new ArrayList<>());

private @Nullable SelectDeviceAction selectedDeviceAction;
Expand Down Expand Up @@ -386,7 +390,11 @@ private static boolean isSelectorVisible(@Nullable Project project) {
}

private void updateActions(@NotNull Project project, @NotNull Presentation presentation) {
actions.clear();
final String projectName = project.getName();
LOG.debug("[" + projectName + "] Building device selector actions");

// Create a new list instead of modifying the existing one
final List<AnAction> newActions = new ArrayList<>();

final DeviceService deviceService = DeviceService.getInstance(project);

Expand All @@ -399,7 +407,8 @@ private void updateActions(@NotNull Project project, @NotNull Presentation prese
if (device == null) continue;

final SelectDeviceAction deviceAction = new SelectDeviceAction(device, devices);
actions.add(deviceAction);
newActions.add(deviceAction);
LOG.debug("[" + projectName + "] Device action added for " + device);

if (Objects.equals(device, selectedDevice)) {
selectedDeviceAction = deviceAction;
Expand All @@ -411,37 +420,42 @@ private void updateActions(@NotNull Project project, @NotNull Presentation prese
// Show the 'Open iOS Simulator' action.
if (SystemInfo.isMac) {
boolean simulatorOpen = false;
for (AnAction action : actions) {
for (AnAction action : newActions) {
if (action instanceof SelectDeviceAction deviceAction) {
final FlutterDevice device = deviceAction.device;
if (device.isIOS() && device.emulator()) {
simulatorOpen = true;
break;
}
}
}

actions.add(new Separator());
actions.add(new OpenSimulatorAction(!simulatorOpen));
newActions.add(new Separator());
newActions.add(new OpenSimulatorAction(!simulatorOpen));
LOG.debug("[" + projectName + "] 'Open iOS Simulator' action added");
}

// Add Open Android emulators actions.
final List<OpenEmulatorAction> emulatorActions = OpenEmulatorAction.getEmulatorActions(project);
if (emulatorActions != null && !emulatorActions.isEmpty()) {
actions.add(new Separator());
actions.addAll(emulatorActions);
newActions.add(new Separator());
newActions.addAll(emulatorActions);
LOG.debug("[" + projectName + "] Emulator action added: " + emulatorActions);
}
if (!FlutterModuleUtils.hasInternalDartSdkPath(project)) {
actions.add(new Separator());
actions.add(RestartFlutterDaemonAction.forDeviceSelector());
newActions.add(new Separator());
newActions.add(RestartFlutterDaemonAction.forDeviceSelector());
}

// Atomically replace the action list
LOG.debug("[" + projectName + "] Replacing device selector actions");
this.actions = newActions;

var tracker = ActivityTracker.getInstance();
if (tracker != null) {
tracker.inc();
}
}


private static class SelectDeviceAction extends AnAction {
@NotNull private final FlutterDevice device;

Expand Down