Firecracker Vocabulary Flashcards:自動化 Google Play 商店截圖更新流程
I Got Tired of Updating Play Store Screenshots. So I Automated It!
Firecracker Vocabulary Flashcards 的開發者以 Playwright 和 Sharp 自動化 Google Play 商店宣傳截圖的擷取與製作,省去介面更新後手動截圖、用 Canva 編輯及輸出不同尺寸的步驟。
A few years ago, I published an Android app called Firecracker Vocabulary Flashcards.
Since then, I have added new features, redesigned several screens, improved the user experience, and released numerous updates.
But guess what? The screenshots on the Google Play Store were still showing the old version of the app!
The reason was pretty simple. I was too lazy to go through the tedious process of taking screenshots, editing them in Canva, adding promotional text, and exporting them in different dimensions.
Every time I thought about updating those images, I ended up finding something more interesting to work on.
One fine day, I thought, why am I still doing this manually? I'm a software engineer, after all. Can't I automate the whole thing?
Well, that thought led me to an interesting experiment. Let's dive in!
The Boring Task
Updating Play Store screenshots sounds pretty straightforward.
- Open the app on a phone or emulator.
- Navigate to the screens worth showcasing.
- Take screenshots.
- Open Canva and add some fancy backgrounds, headlines, and decorations.
- Repeat the process for different device sizes.
- Download everything and upload the images to Google Play.
Nothing technically complicated, right?
But the problem is repetition. Every time the UI changes, I have to repeat these steps.
On top of that, Google Play supports different screenshots for phones, 7-inch tablets, and 10-inch tablets.
Imagine maintaining seven promotional screenshots for three device categories. In my case, that's potentially 21 images to prepare!
I wanted a solution that would allow me to update the screenshots without touching Canva again.
The Idea
My app is built using Ionic with Vue.js and Capacitor.
Although I publish it as a native Android app, the user interface is essentially a web application running inside a native container.
That gave me an idea.
What if I could open the web version of my app in an automated browser, navigate to specific pages, take screenshots, and programmatically add the promotional graphics?
After exploring a few alternatives, I decided to use two libraries:
- Playwright — To automate browser interactions and capture screenshots.
- Sharp — To process images and generate promotional graphics.
The idea was to divide the entire process into two independent tasks.
Capture: Open the app, navigate to different screens, and save raw screenshots.
Render: Take the raw screenshots, add backgrounds and headlines, and generate Play Store-ready images.
Pretty simple, ha?
Well, there were a few challenges.
Challenge 1: Authentication
My app uses Google Sign-In for authentication.
Most of the interesting pages are accessible only after signing in. Therefore, simply opening a URL using Playwright wouldn't work.
I could automate Google authentication, but that would introduce unnecessary complexity. The automation would depend on Google authentication, valid credentials, and potentially interactive login flows.
Do I really need all of that just to take screenshots?
Of course not!
Instead, I introduced a dedicated screenshot mode that creates a demo session before the Vue application starts.
The idea looks like this:
async function bootstrap(): Promise<void> {
if (isStoreScreenshotMode()) {
await startStoreScreenshotSession();
}
const app = createApp(App)
.use(IonicVue, { mode: 'md' })
.use(router);
await router.isReady();
app.mount('#app');
}
The startStoreScreenshotSession() method prepares a fictional user, authentication token, and vocabulary preferences in local storage.
I can enable this mode using an environment variable:
VUE_APP_STORE_SCREENSHOTS=true
The regular app doesn't need to use this demo session. It is intended only for the screenshot workflow, so care must be taken not to enable it in production builds.
Now Playwright can navigate directly to the authenticated pages without going through Google Sign-In.
One problem solved!
Challenge 2: Making the Data Predictable
Authentication was only half the problem.
The app fetches vocabulary, flashcards, guessing games, and collaborative groups from a backend API.
If I capture screenshots using my actual account, the content could change every time. There might be different words, empty lists, or unexpected loading states.
On top of that, I didn't want my screenshot generation to depend on a running backend service.
Fortunately, Playwright provides a beautiful feature called network interception.
We can intercept API requests and return predefined responses.
For example, I configured Playwright to intercept requests targeting my backend:
await context.route(
'**/rest/ielts-service/**',
handleApiRequest
);
Inside handleApiRequest, I examine the requested endpoint and return the corresponding fixture data.
The fixtures contain sample vocabularies, meanings, Leitner box counts, collaborative group members, and guessing-game data.
For example:
const leitnerCounts = {
1: 24,
2: 12,
3: 8,
4: 5,
5: 3
};
The benefit?
Every screenshot can use the same predictable content without exposing real users' data.
It also makes the process reproducible. At least that's the goal.
Challenge 3: Capturing the Right Screens
Now comes the interesting part.
I could write separate Playwright scripts for every page, but that would make future maintenance unnecessarily difficult.
Instead, I created a configuration file called screens.json.
It describes which pages to capture, what headline to display, and which UI element to wait for.
For example:
{
"id": "01-build-your-dictionary",
"route": "/vocabularies",
"headline": "Build Your Own Dictionary",
"waitFor": ".vocab-count"
}
Another example:
{
"id": "07-dark-mode",
"route": "/vocabularies",
"headline": "Easy on the Eyes, Day or Night",
"waitFor": ".vocab-count",
"colorScheme": "dark"
}
Notice the waitFor property.
Taking a screenshot immediately after navigating to a page might capture a loading spinner instead of the actual content.
Therefore, Playwright waits for the expected element to appear before taking the screenshot.
The configuration also supports actions such as clicking buttons, filling input fields, and waiting for modal windows.
That was particularly useful for my word-guessing game. I wanted to capture the game in an interesting state rather than just show an untouched screen.
With this approach, adding a new screenshot mostly means adding another configuration entry.
Challenge 4: Different Device Sizes
Next, I needed to generate screenshots for different device categories.
Playwright allows us to create browser contexts with custom viewport dimensions and device scale factors.
I configured three device profiles.
| Device | Final image dimensions |
|---|---|
| Phone | 1080 × 1920 |
| 7-inch tablet | 1440 × 2560 |
| 10-inch tablet | 2160 × 3840 |
For each device, the capture script launches the page using the configured viewport and takes screenshots of the selected screens.
With seven screens and three device profiles, the pipeline is configured to generate 21 final images.
However, there is an important distinction here. These are browser-rendered screenshots with simulated device dimensions, not screenshots captured directly from an Android emulator.
Since Ionic uses web technologies, this is a practical starting point. Still, visual differences between the browser and the native Android app need to be checked.
The Fun Part: Replacing Canva!
Finally, I reached the part I wanted to automate the most.
Taking screenshots wasn't enough. I wanted them to look like proper promotional images with consistent branding.
This is where Sharp came to the rescue.
Sharp is a Node.js image-processing library that can resize, crop, composite, and convert images programmatically.
For the background and headlines, I used SVG.
The rendering script takes each screenshot and combines it with:
- A branded orange gradient background, inspired by the vibrant orange colour of the Firecracker flower (Crossandra infundibuliformis).
- A headline describing the feature.
- Rounded corners around the app screenshot.
- A subtle border and shadow.
- A footer containing the app's tagline.
The colour, text, and dimensions are driven by screens.json, rather than being hardcoded separately for every image.
For example, here is part of my branding configuration:
{
"brand": {
"background": "#F5821F",
"backgroundAccent": "#FB9A45",
"backgroundAccentDeep": "#E1700C",
"headlineColor": "#FFFFFF"
},
"footer": "Memorize Vocabulary Effectively"
}
Sharp composites the generated SVG background with the resized application screenshot. The renderer also removes the alpha channel before exporting the PNG.
This last step matters because Google Play has specific image-format requirements.
The complete implementation, including headline wrapping, layout calculations, rounded corners, and shadows, is available in render.cjs.
And that's it! No more manually moving screenshots around in Canva.
Putting Everything Together
I added a few npm scripts to make the process easier.
{
"scripts": {
"store:serve": "VUE_APP_STORE_SCREENSHOTS=true vue-cli-service serve --port 8090",
"store:capture": "node scripts/play-store/capture.cjs",
"store:render": "node scripts/play-store/render.cjs",
"store:images": "npm run store:capture && npm run store:render"
}
}
Now I can start the application in screenshot mode:
npm run store:serve
And in another terminal:
npm run store:images
That's all I need to invoke the capture and rendering pipeline.
The first script handles browser automation and saves the raw screenshots. The second processes those images and generates the final promotional graphics.
The generated files are organised by device type:
play-store/
raw/
phone/
tablet-7-inch/
tablet-10-inch/
rendered/
phone/
tablet-7-inch/
tablet-10-inch/
Whenever I modify the UI or want to highlight a different feature, I can update the configuration and regenerate the images.
The full implementation can be found in this GitHub pull request.
Is Everything Automated?
Not entirely!
The current implementation automates screenshot capture and image generation. Uploading the images to Google Play is still a manual step.
I could automate that using Fastlane and the Google Play Developer API. But for now, I prefer reviewing the generated graphics before publishing them.
Of course, browser screenshots are not a perfect replacement for validating the UI on actual Android devices.
Nevertheless, the code now handles the most repetitive part of the process.
The code is available on GitHub. Feel free to explore it and adapt it to your project.
Happy automating!
Cover image generated using ChatGPT.
來源:DEV Community · dev.to