-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathflake.nix
More file actions
616 lines (531 loc) · 23.8 KB
/
Copy pathflake.nix
File metadata and controls
616 lines (531 loc) · 23.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
{
description = "Flutter + Android SDK Dev Shell with writable SDK, automatic licenses, NDK, cmdline-tools, emulator, and system image";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
flake-utils.url = "github:numtide/flake-utils";
android-nixpkgs = {
url = "github:tadfisher/android-nixpkgs/main";
};
};
outputs = { self, nixpkgs, flake-utils, android-nixpkgs }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
config = {
allowUnfree = true;
android_sdk.accept_license = true;
};
};
androidEnv = android-nixpkgs.sdk.${system} (sdkPkgs: with sdkPkgs; [
cmdline-tools-latest
build-tools-36-0-0
platform-tools
platforms-android-36
emulator
ndk-27-0-12077973
# include system image inside SDK instead of relying on sdkmanager. This ensures emulator functionality.
system-images-android-36-google-apis-playstore-x86-64
]) // {
# Override the emulator to include the missing Qt/X11 dependencies
buildInputs = (androidEnv.buildInputs or []) ++ (with pkgs; [
xcb-util-cursor
libXcursor
libX11
libxcb
qt6.qtbase
qt6.qtsvg
]);
};
wrappedEmulator = pkgs.writeShellScriptBin "run-emulator" ''
#!/usr/bin/env bash
echo "Launching emulator ..."
echo "🔐 Granting local X access with xhost for DISPLAY=$DISPLAY..."
# CRITICAL: Define the explicit path to the Nix-built xhost
NIX_XHOST="${pkgs.xhost}/bin/xhost"
if [ -x "$NIX_XHOST" ] && [ -n "$DISPLAY" ]; then
# *** USE NIX-PROVIDED XHOST DIRECTLY ***
"$NIX_XHOST" +local: > /dev/null
if [ $? -eq 0 ]; then
XHOST_CLEANUP=true
echo "✅ X access granted successfully via Nix-xhost."
else
echo "❌ Nix-xhost command failed. Authorization may still be blocked."
XHOST_CLEANUP=false
fi
else
echo "⚠️ Warning: Nix-xhost binary not found at $NIX_XHOST or DISPLAY not set. Fix skipped."
XHOST_CLEANUP=false
fi
# ---------------------------------------------
# FHS LIBRARY PREPENDING
# Solves: Qt XCB plugin crash during -gpu host initialization.
# Ensures core libraries are found via FHS symlinks.
# ---------------------------------------------
# Use the FHS_LIB path provided by your shell's environment
if [ -n "$FHS_LIB" ]; then
export LD_LIBRARY_PATH="$FHS_LIB/usr/lib:$LD_LIBRARY_PATH"
export ANDROID_HOME="$PWD/.android/sdk"
export ANDROID_SDK_ROOT="$ANDROID_HOME"
export PATH="$ANDROID_HOME/bin:$ANDROID_HOME/platform-tools:$PATH"
else
echo "❌ Error: FHS_LIB is not set. Library path fix will not work correctly."
fi
# ----------------------------
# Detect display server
# ----------------------------
if [ -n "$WAYLAND_DISPLAY" ]; then
echo "🌿 Wayland detected: $WAYLAND_DISPLAY"
USE_WAYLAND=true
if [ -z "$DISPLAY" ]; then
export DISPLAY=:0
fi
export QT_QPA_PLATFORM=xcb #Force XWayland
else
echo "🖥️ X11 detected: ${DISPLAY:-:0}"
USE_WAYLAND=false
export QT_QPA_PLATFORM=xcb
fi
# ----------------------------
# Qt environment
# ----------------------------
export QT_QPA_PLATFORM=xcb
export QT_QPA_PLATFORM_PLUGIN_PATH="${pkgs.qt6.qtbase}/lib/qt-6/plugins"
export QT_PLUGIN_PATH="${pkgs.qt6.qtbase}/lib/qt-6/plugins"
export QML2_IMPORT_PATH="${pkgs.qt6.qtbase}/lib/qt-6/qml"
export QTWEBENGINE_DISABLE_SANDBOX=1
export QT_OPENGL=desktop
export QT_QPA_PLATFORMTHEME=gtk3
# Input/accessibility fixes - CRITICAL FOR KEYBOARD INPUT
export QT_ACCESSIBILITY=1
export QT_IM_MODULE=compose
export XMODIFIERS=@im=none
export GTK_IM_MODULE=gtk-im-context-simple
# Enhanced input device configuration
export QT_LOGGING_RULES="qt.qpa.input=true;qt.qpa.input.events=true"
export QT_QPA_GENERIC_PLUGINS=""
# Allow Qt to use system input instead of forcing evdev
export QT_QPA_ENABLE_TERMINAL_KEYBOARD=1
# X11 input settings
export SDL_VIDEODRIVER=x11
export XKB_DEFAULT_LAYOUT=us
# ----------------------------
# Graphics / OpenGL driver
# ----------------------------
LD_PATH_BASE="$FHS_LIB/usr/lib"
if [ -d "/run/opengl-driver" ]; then
echo "✅ NVIDIA/OpenGL driver detected"
LD_PATH_BASE="$FHS_LIB/usr/lib"
export LD_LIBRARY_PATH="/run/opengl-driver/lib:$FHS_LIB/usr/lib:$LD_LIBRARY_PATH"
export LIBGL_DRIVERS_PATH="/run/opengl-driver/lib/dri"
export MESA_LOADER_DRIVER_OVERRIDE=""
else
echo "⚠️ NVIDIA driver not found, using Mesa fallback"
LD_PATH_BASE="$FHS_LIB/usr/lib"
export LD_LIBRARY_PATH="${pkgs.mesa}/lib:${pkgs.libdrm}/lib:${pkgs.vulkan-loader}/lib:$LD_LIBRARY_PATH"
export LIBGL_DRIVERS_PATH="${pkgs.mesa}/lib/dri"
export MESA_LOADER_DRIVER_OVERRIDE=i965
fi
if [ -d "/run/opengl-driver" ]; then
echo "⚠️ Forcing Vulkan/Mesa device selection for NVIDIA"
# This environment variable helps the Vulkan loader find the correct driver JSON file.
export MESA_VULKAN_DEVICE_SELECT="${pkgs.vulkan-loader}/etc/vulkan/icd.d/nvidia_icd.json"
fi
# This is a fix to force the emulator to use the host's GL/Vulkan stack
export QEMU_GL_ENABLE=1
export QEMU_VULKAN_ENABLE=1
export LD_PRELOAD="${pkgs.libglvnd}/lib/libGL.so.1" # Only preload libGL.so.1 # ---------------------------------------------
# Physical Keyboard & side panel Functionality
# ---------------------------------------------
# Check home directory AVD
HOME_EMULATOR_CONFIG_DIR="$HOME/.android/avd/android_emulator.avd"
if [ -d "$HOME_EMULATOR_CONFIG_DIR" ]; then
echo "📝 Found AVD in home directory: $HOME_EMULATOR_CONFIG_DIR"
# Update home directory config.ini
if [ -f "$HOME_EMULATOR_CONFIG_DIR/config.ini" ]; then
if grep -q "^hw\.keyboard\s*=" "$HOME_EMULATOR_CONFIG_DIR/config.ini"; then
sed -i 's/^hw\.keyboard\s*=.*/hw.keyboard=yes/' "$HOME_EMULATOR_CONFIG_DIR/config.ini"
else
echo "hw.keyboard=yes" >> "$HOME_EMULATOR_CONFIG_DIR/config.ini"
fi
if grep -q "^hw\.mainKeys\s*=" "$HOME_EMULATOR_CONFIG_DIR/config.ini"; then
sed -i 's/^hw\.mainKeys\s*=.*/hw.mainKeys=yes/' "$HOME_EMULATOR_CONFIG_DIR/config.ini"
else
echo "hw.mainKeys=yes" >> "$HOME_EMULATOR_CONFIG_DIR/config.ini"
fi
if grep -q "^hw\.dPad\s*=" "$HOME_EMULATOR_CONFIG_DIR/config.ini"; then
sed -i 's/^hw\.dPad\s*=.*/hw.dPad=yes/' "$HOME_EMULATOR_CONFIG_DIR/config.ini"
else
echo "hw.dPad=yes" >> "$HOME_EMULATOR_CONFIG_DIR/config.ini"
fi
echo "✅ Updated home directory emulator configuration"
fi
fi
# ---------------------------------------------
# Run the emulator with Cleanup Trap
# ---------------------------------------------
# Set a trap to ensure xhost is cleaned up even if the script is aborted
if $XHOST_CLEANUP; then
trap "xhost -local: > /dev/null; echo '✅ X access revoked.'" EXIT
fi
exec emulator -avd android_emulator \
-gpu host \
-no-snapshot \
-no-snapshot-load \
-no-snapshot-save \
-port 5554 \
-grpc 8554 \
-qemu -enable-kvm \
'';
# Patched Flutter derivation.
patchedFlutter = pkgs.flutter.overrideAttrs (oldAttrs: {
# This patchPhase runs during the package's build time.
patchPhase = ''
runHook prePatch
# Patch FlutterTask.kt - this handles the main cmake/ninja paths
substituteInPlace $FLUTTER_ROOT/packages/flutter_tools/gradle/src/main/kotlin/FlutterTask.kt \
--replace 'val cmakeExecutable = project.file(cmakePath).absolutePath' 'val cmakeExecutable = "cmake"' \
--replace 'val ninjaExecutable = project.file(ninjaPath).absolutePath' 'val ninjaExecutable = "ninja"'
# Also patch any Gradle build scripts that reference cmake directly
find $FLUTTER_ROOT -name "*.gradle" -o -name "*.gradle.kts" | xargs -I {} \
sed -i 's|cmake/[^/]*/bin/cmake|cmake|g' {} 2>/dev/null || true
# Patch any other cmake references in Flutter tools
find $FLUTTER_ROOT/packages/flutter_tools -name "*.dart" | xargs -I {} \
sed -i 's|/cmake/[^/]*/bin/cmake|cmake|g' {} 2>/dev/null || true
runHook postPatch
'';
});
# >>> PIN FOR COMPATIBILITY >>>
minSdkVersion = "24";
kotlinVersion = "2.0.21";
agpVersion = "9.0.0"; # Android Gradle Plugin
ndkVersion = "27.0.12077973";
in
{
devShells.default = (pkgs.buildFHSEnv {
name = "FHS flutter-android-dev-env";
targetPkgs = pkgs: with pkgs; [
# Basic development tools for the shell
bashInteractive
git
cmake
ninja
python3
jdk17
nix-ld
gradle
patchedFlutter
wrappedEmulator
# Android SDK components and environment
androidEnv
patchelf
# Core runtime libraries
glibc
zlib
ncurses5
stdenv.cc.cc.lib
# Qt 5 & 6 and X11 GUI dependencies
libsForQt5.qt5.qtbase
libsForQt5.qt5.qtsvg
libsForQt5.qt5.qtwayland
libsForQt5.qt5.qttools
libsForQt5.qt5.qtdeclarative
qt6.qt5compat
libsForQt5.qt5ct
qt6.qtbase
qt6.qtsvg
qt6.qtwayland
qt6.qt5compat
libX11
libXext
libXfixes
libXi
libXrandr
libXrender
libxcb
xcbutil
xcbutilwm
xcbutilimage
xcbutilkeysyms
xcbutilrenderutil
libxkbcommon
# Graphics and font rendering
mesa
libdrm
vulkan-loader
fontconfig
freetype
mesa-demos
linuxPackages.nvidia_x11
libglvnd
# System services and input handling
dbus
libevdev
libpulseaudio
pipewire
udev
libinput
libinput-gestures
at-spi2-atk
at-spi2-core
# Additional GUI/Qt dependencies for emulator button functionality
gtk3
gdk-pixbuf
cairo
pango
harfbuzz
glib
gsettings-desktop-schemas
# Critical X11 and cursor dependencies for Qt xcb platform
xcb-util-cursor
libXcursor
setxkbmap
xauth
xhost
xset
];
multiPkgs = pkgs: with pkgs; [
zlib
ncurses5
mesa
];
profile = ''
echo "FHS shell is active. Setting up Flutter+Android environment..."
# Critical nix-ld environment variables for dynamic linking compatibility
export PATH="$FHS_LIB/usr/bin:$PATH"
export NIX_LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath [
# Core runtime libraries
pkgs.glibc
pkgs.zlib
pkgs.ncurses5
pkgs.stdenv.cc.cc.lib
# Qt 5/6 and X11 GUI dependencies
pkgs.libsForQt5.qt5.qtbase
pkgs.libsForQt5.qt5.qtsvg
pkgs.libsForQt5.qt5.qtwayland
pkgs.libsForQt5.qt5.qttools
pkgs.libsForQt5.qt5.qtdeclarative
pkgs.qt6.qt5compat
pkgs.libsForQt5.qt5ct
pkgs.qt6.qtbase
pkgs.qt6.qtsvg
pkgs.qt6.qtwayland
pkgs.qt6.qt5compat
pkgs.libX11
pkgs.libXext
pkgs.libXfixes
pkgs.libXi
pkgs.libXrandr
pkgs.libXrender
pkgs.libxcb
pkgs.xcbutil
pkgs.xcbutilwm
pkgs.xcbutilimage
pkgs.xcbutilkeysyms
pkgs.xcbutilrenderutil
pkgs.libxkbcommon
# Graphics and font rendering
pkgs.mesa
pkgs.libdrm
pkgs.vulkan-loader
pkgs.libglvnd
pkgs.linuxPackages.nvidia_x11
pkgs.fontconfig
pkgs.freetype
# System services and input handling
pkgs.dbus
pkgs.libpulseaudio
pkgs.pipewire
pkgs.udev
pkgs.libinput
pkgs.libevdev
pkgs.libinput-gestures
pkgs.at-spi2-atk
pkgs.at-spi2-core
# Additional GUI dependencies
pkgs.gtk3
pkgs.gdk-pixbuf
pkgs.cairo
pkgs.pango
pkgs.harfbuzz
pkgs.glib
pkgs.gsettings-desktop-schemas
# Critical X11 and cursor dependencies for Qt xcb platform
pkgs.xcb-util-cursor
pkgs.libXcursor
pkgs.setxkbmap
pkgs.xauth
pkgs.xhost
pkgs.xset
]}"
export LD_LIBRARY_PATH="$NIX_LD_LIBRARY_PATH:$LD_LIBRARY_PATH"
echo "✅ FHS /usr/lib graphics mock initialized at $FHS_LIB/usr/lib"
# Fast path for subsequent shell entries
if [ -f "$PWD/.flutter_env_ready" ] && [ -d "$PWD/.android/sdk" ]; then
export ANDROID_HOME="$PWD/.android/sdk"
export ANDROID_SDK_ROOT="$ANDROID_HOME"
export JAVA_HOME="${pkgs.jdk17}"
export PATH="${pkgs.cmake}/bin:${pkgs.ninja}/bin:$PATH"
# Prepend to LD_LIBRARY_PATH so emulator sees these first
export LD_LIBRARY_PATH="$FHS_LIB/usr/lib:$LD_LIBRARY_PATH"
export ANDROID_EMULATOR_HOME="$PWD/.android"
echo "✅ FHS graphics symlinks initialized at $FHS_LIB"
echo "⚡ Fast shell entry - Flutter environment ready!"
echo "👉 To launch the emulator, run:"
echo " run-emulator"
echo ""
echo "👉 To build your app, run:"
echo " flutter build apk --release"
else
# Full setup (first time or missing setup)
echo "Performing full environment setup..."
echo "Stopping any existing ADB server..."
"${androidEnv}/share/android-sdk/platform-tools/adb" kill-server &> /dev/null || true
mkdir -p "$PWD/.android/sdk"
export ANDROID_HOME="$PWD/.android/sdk"
export ANDROID_SDK_ROOT="$ANDROID_HOME"
export JAVA_HOME="${pkgs.jdk17}"
# Verify Gradle + Java setup
gradle --version
echo "🔧 Using Java:"
"$JAVA_HOME/bin/java" -version
# Ensure all SDK directories exist
mkdir -p "$ANDROID_HOME/licenses" "$ANDROID_HOME/avd" "$ANDROID_HOME/bin"
# Copy over SDK parts including system-images now in androidEnv
cp -LR ${androidEnv}/share/android-sdk/* "$ANDROID_HOME/" || true
# Copy essential binaries
for bin in adb avdmanager emulator sdkmanager; do
cp -LR ${androidEnv}/bin/$bin "$ANDROID_HOME/bin/" || true
done
rm -rf "$ANDROID_HOME/cmake"
# Create the cmake directory structure that Gradle expects
mkdir -p "$ANDROID_HOME/cmake/3.22.1/bin"
# Create symlinks to our Nix cmake and ninja
ln -sf "${pkgs.cmake}/bin/cmake" "$ANDROID_HOME/cmake/3.22.1/bin/cmake"
ln -sf "${pkgs.ninja}/bin/ninja" "$ANDROID_HOME/cmake/3.22.1/bin/ninja"
echo "Created cmake symlink: $ANDROID_HOME/cmake/3.22.1/bin/cmake -> ${pkgs.cmake}/bin/cmake)"
chmod -R u+w "$ANDROID_HOME"
find "$ANDROID_HOME/bin" "$ANDROID_HOME/platform-tools" \
"$ANDROID_HOME/emulator" "$ANDROID_HOME/cmdline-tools/latest/bin" \
"$ANDROID_HOME/build-tools" "$ANDROID_HOME/platforms" \
"$ANDROID_HOME/ndk" -type f -exec chmod +x {} \; 2>/dev/null || true
# Accept SDK licenses
for license in android-sdk-license android-sdk-preview-license googletv-license; do
touch "$ANDROID_HOME/licenses/$license"
done
yes | flutter doctor --android-licenses || true
flutter config --android-sdk "$ANDROID_HOME"
# Create flutter project in root directory if one doesnt exist.
if [ ! -f pubspec.yaml ]; then
echo "No Flutter project found. Creating a new one..."
flutter create .
echo ".android/sdk" >> .gitignore
fi
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "No Git repository detected. Initializing..."
git init
git add .
git commit -m "Initial Commit done by Flake for Flutter Android dev shell"
echo "✅ Git repository initialized and initial commit created."
else
echo "Git repository already exists. Skipping initialization."
fi
mkdir -p android/app/src/main/{kotlin,java}
mkdir -p android/app/src/debug/{kotlin,java}
mkdir -p android/app/src/profile/{kotlin,java}
mkdir -p android/app/src/release/{kotlin,java}
# Ensure android directories exist (avoid sed failures)
mkdir -p android app
if [ -d android ]; then
# Create gradle.properties if it doesn't exist
touch android/gradle.properties
# Use sed to remove existing properties to avoid duplicates
sed -i '/^android\.cmake\.path=/d' android/gradle.properties
sed -i '/^android\.ninja\.path=/d' android/gradle.properties
sed -i '/^android\.cmake\.version=/d' android/gradle.properties
# Append new properties (preserves any other existing config)
echo "android.cmake.path=${pkgs.cmake}/bin" >> android/gradle.properties
echo "android.ninja.path=${pkgs.ninja}/bin" >> android/gradle.properties
echo "android.cmake.version=" >> android/gradle.properties
echo "android.cmake.makeProgram=${pkgs.ninja}/bin/ninja" >> android/gradle.properties
fi
# Only patch if gradle.kts files exist
if [ -f "android/build.gradle.kts" ]; then
echo "⚙️ Pinning Android build tool versions in Kotlin DSL..."
sed -i -e "s/id(\"com.android.application\") version \"[0-9.]*\"/id(\"com.android.application\") version \"${agpVersion}\"/g" android/build.gradle.kts
sed -i -e "s/id(\"org.jetbrains.kotlin.android\") version \"[0-9.]*\"/id(\"org.jetbrains.kotlin.android\") version \"${kotlinVersion}\"/g" android/build.gradle.kts
fi
if [ -f "android/app/build.gradle.kts" ]; then
sed -i -e "s/minSdk = [0-9a-zA-Z._]*/minSdk = ${minSdkVersion}/g" android/app/build.gradle.kts
fi
#Support for traditional Groovy build files
if [ -f "android/build.gradle" ]; then
echo "⚙️ Pinning Android build tool versions in Groovy DSL..."
sed -i -e "s/com.android.application.*version.*'[0-9.]*'/com.android.application' version '${agpVersion}'/g" android/build.gradle
sed -i -e "s/org.jetbrains.kotlin.android.*version.*'[0-9.]*'/org.jetbrains.kotlin.android' version '${kotlinVersion}'/g" android/build.gradle
sed -i -e "s/minSdkVersion [0-9]*/minSdkVersion ${minSdkVersion}/g" android/app/build.gradle
fi
#Pin NDK version in Gradle build files
if [ -f "android/app/build.gradle.kts" ]; then
sed -i '/ndkVersion\s*=/d' android/app/build.gradle.kts
if grep -q "android\s*{" android/app/build.gradle.kts; then
sed -i "/android\s*{/a \ ndkVersion = \"${ndkVersion}\"" android/app/build.gradle.kts
else
echo -e "\nandroid {\n ndkVersion = \"${ndkVersion}\"\n}" >> android/app/build.gradle.kts
fi
fi
# Create AVD if missing
if ! avdmanager list avd | grep -q 'android_emulator'; then
echo "Creating default AVD: android_emulator"
yes | avdmanager create avd \
--name "android_emulator" \
--package "system-images;android-36;google_apis_playstore;x86_64" \
--device "pixel" \
--abi "x86_64" \
--tag "google_apis_playstore" \
--force
fi
# Configure AVD hardware settings (also runs on existing AVDs)
HOME_AVD_CONFIG="$HOME/.android/avd/android_emulator.avd/config.ini"
if [ -f "$HOME_AVD_CONFIG" ]; then
echo "Configuring home directory AVD..."
if grep -q "^hw\.keyboard\s*=" "$HOME_AVD_CONFIG"; then
sed -i 's/^hw\.keyboard\s*=.*/hw.keyboard=yes/' "$HOME_AVD_CONFIG"
else
echo "hw.keyboard=yes" >> "$HOME_AVD_CONFIG"
fi
if grep -q "^hw\.mainKeys\s*=" "$HOME_AVD_CONFIG"; then
sed -i 's/^hw\.mainKeys\s*=.*/hw.mainKeys=yes/' "$HOME_AVD_CONFIG"
else
echo "hw.mainKeys=yes" >> "$HOME_AVD_CONFIG"
fi
if grep -q "^hw\.dPad\s*=" "$HOME_AVD_CONFIG"; then
sed -i 's/^hw\.dPad\s*=.*/hw.dPad=yes/' "$HOME_AVD_CONFIG"
else
echo "hw.dPad=yes" >> "$HOME_AVD_CONFIG"
fi
echo "✅ Home directory AVD configured for full emulator functionality"
fi
# PATH and tool verification
export PATH="${pkgs.cmake}/bin:${pkgs.ninja}/bin:$PATH"
# Verify our tools are accessible
echo "🔧 Using CMake: ${pkgs.cmake}/bin/cmake ($(${pkgs.cmake}/bin/cmake --version | head -1))"
echo "🔧 Using Ninja: ${pkgs.ninja}/bin/ninja ($(${pkgs.ninja}/bin/ninja --version))"
flutter doctor --quiet
echo "✅ Flutter + Android dev shell ready."
# Mark environment as ready for fast path next time
touch "$PWD/.flutter_env_ready"
if ! grep -qxF ".flutter_env_ready" .gitignore; then
echo ".flutter_env_ready" >> .gitignore
fi
echo "👉 To launch the emulator, run:"
echo " run-emulator"
echo ""
echo "👉 To build your app, run:"
echo " flutter build apk --release"
fi
'';
runScript = "bash";
}).env;
});
}