Diagnose and fix Android foldable screen adaptation issues — Activity recreation, state loss, fragment reference invalidation, and layout breakage on fold/unfold.
From android-dev-toolsnpx claudepluginhub adzcsx2/android-claude-skills --plugin android-dev-toolsThis skill uses the workspace's default tool permissions.
README.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Executes pre-written implementation plans: critically reviews, follows bite-sized steps exactly, runs verifications, tracks progress with checkpoints, uses git worktrees, stops on blockers.
中文环境要求
本技能运行在中文环境下,请遵循以下约定:
- 面向用户的回复、注释、提示信息必须使用中文
- AI 内部处理过程可以使用英文
- 所有生成的文件必须使用 UTF-8 编码
Fix issues caused by foldable screen fold/unfold events. These events trigger configuration changes (screenSize, smallestScreenSize, screenLayout) that destroy and recreate Activities unless handled.
This skill supports automatic updates. When solving a new foldable screen issue:
/android-dev-tools:update-remote-plugins to sync changesAndroidManifest.xml entry for that Activity.android:configChanges includes foldable-relevant values:
screenSizesmallestScreenSizescreenLayoutconfigChanges value.If the Activity lacks configChanges, it gets destroyed and recreated on fold/unfold. Check for:
onCreate reset visibility of containers based on intent.action without checking savedInstanceState? This causes search results, loaded content, or navigation state to disappear.WeakReference or SparseArray references to Fragments (common with ViewPager2 + FragmentStateAdapter), these references become stale after recreation because FragmentStateAdapter restores Fragments via FragmentManager without calling createFragment().After Activity recreation:
forEach on the reference collection does nothing → button clicks appear to have no effect.searchLock) that prevents re-entry but never gets reset.lifecycleScope coroutines launched before destruction may have been cancelled, and new ones aren't started.Add android:configChanges to the Activity in AndroidManifest.xml:
<activity
android:name=".ui.YourActivity"
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|screenLayout"
... />
Copy the exact configChanges value from other Activities in the same project that already handle foldable screens (e.g., HomeActivity, WebActivity).
Even with Pattern A, add state saving as a fallback (system can still kill the Activity in low-memory situations):
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString(KEY_SEARCH_CONTENT, contentSearch)
outState.putBoolean(KEY_HAS_RESULTS, binding.container.visibility == View.VISIBLE)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val restored = savedInstanceState?.getBoolean(KEY_HAS_RESULTS, false) == true
if (restored) {
// Restore UI state from savedInstanceState
contentSearch = savedInstanceState?.getString(KEY_SEARCH_CONTENT, "") ?: ""
binding.container.visibility = View.VISIBLE
// ...
} else {
// Normal intent-based initialization
}
}
Never rely on a manually maintained SparseArray<WeakReference<Fragment>> to find Fragments after recreation. Use FragmentManager instead:
// BAD: stale after recreation
fragments.forEach { key, value ->
value.get()?.doSomething()
}
// GOOD: always finds current Fragment instances
supportFragmentManager.fragments
.filterIsInstance<YourFragment>()
.forEach { it.doSomething() }
For FragmentStateAdapter hosted in Activity, Fragments are in supportFragmentManager.
For FragmentStateAdapter hosted in Fragment, Fragments are in that Fragment's childFragmentManager.
Before marking the fix complete, verify:
AndroidManifest.xml: Target Activity has configChanges with screenSize|smallestScreenSize|screenLayoutonSaveInstanceState: Key UI state and data fields are savedonCreate: savedInstanceState is checked before intent.action to restore stateFragmentManager.fragments instead of manual collectionssearchLock) are properly reset on recreation./gradlew :module:assembleDebugSymptoms: After searching on the search page, folding/unfolding the screen causes search results to disappear. After re-searching, subsequent search button clicks have no effect.
Root Cause: SearchActivity lacked configChanges → Activity recreated → onCreate hid the results container → fragments SparseArray emptied → search() method's forEach iterated over nothing.
Fix Applied: Pattern A + B + C (all three). Files changed:
AndroidManifest.xml — added configChangesSearchActivity.kt — added onSaveInstanceState/restore + replaced fragments.forEach with supportFragmentManager.fragments.filterIsInstance<SearchFragment>()Symptoms: After placing the widget on the launcher, running adb shell pm clear com.vertu.personalizationwidget, then folding or unfolding the device causes the launcher widget to show "无法加载微件" and become non-clickable.
Root Cause: The widget provider rebuilt fallback data after pm clear, but logo rendering still relied on FileProvider URIs pointing to app-private files under files/logos. During launcher host rebind on fold/unfold, those file-backed URIs could become stale or invalid after the app data clear, causing the host to fail while applying RemoteViews.
Fix Applied: Restore default widget state synchronously after pm clear, ensure built-in logo assets are recreated if missing, and replace setImageViewUri(...) with setImageViewBitmap(...) so widget rendering no longer depends on app-private file URIs surviving host-side rebinds. Files changed:
WidgetEngraving/src/main/java/com/vertu/personalizationwidget/EngravingAppWidgetProvider.kt — persist fallback widget entity, restore built-in logos, replace URI-based logo rendering with bitmap-based rendering