Test Yourself!

These ten questions cover the project files, the Android build and the bridge, and each turns on a detail that is easy to get wrong: version codes, version ranges, merged configuration, sticky events, service names, the CSP and the bridge secret. Read each block, write down what it prints and why, and only then check Appendix H, which gives the real output, the reason and the section it comes from.

Questions 1 to 5 change files, so run them in a throwaway copy of the finished BookNest, in order:

A scratch copy of BookNest for questions 1-5Shell
git clone ~/booknest-cordova booknest-quiz
cd booknest-quiz
npm ci
cordova prepare android

npm 2,036 ci restores the exact plugin and platform versions from package-lock.json, and cordova prepare regenerates platforms/ and plugins/, which Git 1,932 does not track (package.json and Versions and Exporting Projects). Questions 6 to 10 run in the WebView console (Remote Debugging) of the debug build on an emulator, with the app freshly launched:

Where to run each question
Questions Where they run Sections
1-5 A terminal in the scratch copy 1.3-1.8
6-8 The console of BookNest's debug build 1.4, 1.5, 1.11
9 BookNest, then its Capacitor 243,123 build 1.4, 1.10, 1.14
10 BookNest, last, since it disables the bridge 1.4

Questions

Questions 1-3: versions and the merged config.xmlXML
# 1. Three releases in a row, then an explicit preference: which versionCode is each?
for v in 1.2.100 1.3.0 1.10.0; do
  sed -i "s/version=\"[0-9.]*\"/version=\"$v\"/" config.xml
  cordova prepare android > /dev/null
  grep -o 'versionCode="[0-9]*"' platforms/android/app/src/main/AndroidManifest.xml
done
sed -i '/<content /a <preference name="android-versionCode" value="10301" />' config.xml
cordova prepare android > /dev/null
grep -o 'versionCode="[0-9]*"' platforms/android/app/src/main/AndroidManifest.xml
# 2. Which of these versions does each range accept?
npx semver -r "~15.1.0" 15.0.9 15.1.0 15.1.7 15.2.0 16.0.0
npx semver -r "^15.1.0" 15.0.9 15.1.0 15.1.7 15.2.0 16.0.0
# 3. Which <allow-intent> entries reach Android's merged config.xml, and how many plugin
#    <feature> elements does it declare?
grep -o '<allow-intent href="[^"]*"' platforms/android/app/src/main/res/xml/config.xml
grep -c '<feature' platforms/android/app/src/main/res/xml/config.xml
Questions 4-5: a removed edit-config, and iOS on Linux
# 4. Delete the <edit-config> block and prepare again. What does the manifest say?
#    Then remove and re-add the platform. What does it say now?
sed -i '/<edit-config/,/<\/edit-config>/d' config.xml
cordova prepare android > /dev/null
grep -o 'android:allowBackup="[a-z]*"' platforms/android/app/src/main/AndroidManifest.xml
cordova platform rm android > /dev/null && cordova platform add android > /dev/null
grep -c allowBackup platforms/android/app/src/main/AndroidManifest.xml
# 5. On this Linux machine, does adding iOS fail? And building it?
cordova platform add ios > /dev/null && ls platforms
cordova build ios 2>&1 | tail -1
Questions 6-8: deviceready, service names and the CSPJavaScript
// 6. deviceready fired seconds ago. Which new listeners run, and in which order?
const order = [];
window.addEventListener('deviceready', () => order.push('window listener'));
document.addEventListener('deviceready', () => order.push('document listener'));
order.push('after addEventListener');
await new Promise((done) => setTimeout(done, 100));
order;
// 7. Three calls that differ only in letter case: what does each return?
const call = (service, action) =>
  new Promise((done) => cordova.exec(done, done, service, action, []));
[await call('booknest', 'info'), await call('BookNest', 'Info'),
  (await call('BookNest', 'info')).sdkInt];
// 8. Which of these requests does BookNest's Content-Security-Policy allow?
const status = (url) => fetch(url).then((res) => res.status, (err) => err.message);
[await status('data/books.json'), await status('https://localhost/data/books.json'),
  await status('http://localhost/data/books.json'), await status('data:application/json,{}')];
Questions 9-10: Cordova or Capacitor, and the bridge secret
// 9. Run this in BookNest, then in the Capacitor build of Section 1.14.3. What differs?
[location.origin, cordova.platformId, cordova.platformVersion, device.cordova, typeof Capacitor];
// 10. Knock on the bridge with a guessed secret. Does the next plugin call still work?
try { _cordovaNative.exec(12345, 'BookNest', 'info', 'guess', '[]'); } catch (e) { }
await BookNest.info().then(() => 'answered', (err) => 'rejected: ' + err);

Going further

Three exercises have no printed answer, because your results will differ. Give cordova-plugin-booknest a share action that opens Android's share sheet, and call it from the book detail view. Add a pause listener that saves the cart to localStorage and a resume listener that restores it, then background and reopen the app with adb shell input keyevent KEYCODE_HOME. Finally, build a release bundle with the release script from Build Batch Files and compare its size and debuggable flag with the debug APK's using apkanalyzer.