Enhance widget integration, build stability, and MLKit fallback - #72
Conversation
Build fixes: - Raise compileSdk 35→36 to match targetSdk=36; eliminates targetSdk > compileSdk mismatch that caused AGP lint failures - Update android.suppressUnsupportedCompileSdk=36 accordingly - Add ProGuard keep rules for com.google.mlkit.genai.**, Guava ListenableFuture, and Kotlin coroutines internals to prevent R8 from stripping reflection-loaded classes that caused corrupt/crashing release packages Widget screen (integrated flavor): - Replace static grayscale preview bitmaps with live AppWidgetHostView instances embedded via AndroidView; touch events pass directly to each widget so PendingIntents fire natively (opens the app) - Add BIND_APPWIDGET permission to AndroidManifest.xml - Add widgetAllocatedIds: Set<String> to Prefs.kt to persist the provider→appWidgetId mapping across launches - WidgetsViewModel now owns AppWidgetHost (WIDGET_HOST_ID=1024); binds each widget on pin via bindAppWidgetIdIfAllowed(); releases IDs on unpin via deleteAppWidgetId(); migrates orphaned pins from the old version by dropping those without allocated IDs - WidgetsScreen uses DisposableEffect for startListening/stopListening lifecycle, LazyColumn for full-screen scrollable widget list, and a per-widget × button instead of the long-press resize/remove dialog - AndroidView factory wrapped in try-catch so a removed provider degrades to a blank FrameLayout rather than crashing AiSummarizer (integrated flavor): - fallbackSummarize() (Tier 3 — no on-device AI) now produces minimum-token output: "Title: body[0..70]" or truncates at 80 chars instead of dumping full notification text verbatim Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: paromitaslg98-source <paromitaslg98@gmail.com>
Remove the old app module sources and resources (activities, fragments, adapters, helpers, layouts, drawables, menus, navigation, etc.) and clean up related Kotlin/Java files. Update README to document building integrated/disintegrated debug APK flavors and their output locations. Also include build-system adjustments (Gradle properties, libs, daemon JVM props, proguard) and updates to AndroidManifests / NotificationService implementations to align with the integrated/disintegrated flavor structure.
Add feature gating and disintegrated stubs, consolidate helpers, and refactor major UI screens. Introduces FeatureAvailability to gate integrated vs disintegrated features and adds an AiSummarizer stub for the disintegrated flavor. Moves the integrated PrivateSpaceHelper into src/main and guards it with FeatureAvailability; removes the disintegrated PrivateSpaceHelper and several disintegrated notification/summary screens. Adds a project knowledge base (.gemini/KNOWLEDGE.md). Refactors AppDrawerScreen by extracting search bar, app list and item composables, improving private-space handling (immediate load on unlock), safer broadcast registration, stable list keys (including pinned shortcuts), and UI/behavior cleanups. Renames disintegrated WidgetsScreen to a generic FeatureUnavailableScreen and centralizes messaging for unavailable features. HomeScreen receives a large rewrite: consolidated gesture handling, improved drag-to-reorder logic, global dragging state, app picker sheet, and date click now opens the calendar via helper. MainActivity updated to import openCalendar and to navigate/handle notification panel behavior changes.
There was a problem hiding this comment.
Code Review
This pull request enhances build stability by aligning SDK versions and adding ProGuard rules to prevent MLKit class stripping. A major highlight is the complete rewrite of the widget screen, which now hosts live, interactive system widgets instead of static previews. Feedback identifies a critical bug in widget height calculation where raw pixel values are used instead of DP. Furthermore, the reviewer suggests extending widget discovery and binding logic to support cross-profile scenarios, such as Private Space and Work Profiles, to ensure full compatibility with modern Android features.
| val widgetHeightDp = remember(widget.provider) { | ||
| maxOf(widget.provider.minHeight, 100).dp | ||
| } |
There was a problem hiding this comment.
The AppWidgetProviderInfo.minHeight field is defined in pixels at runtime, not DP. Direct conversion using .dp will result in incorrect widget sizing on devices with different screen densities. You should use LocalDensity to convert the pixel value to DP correctly.
| val widgetHeightDp = remember(widget.provider) { | |
| maxOf(widget.provider.minHeight, 100).dp | |
| } | |
| val density = LocalDensity.current | |
| val widgetHeightDp = remember(widget.provider, density) { | |
| with(density) { maxOf(widget.provider.minHeight.toDp(), 100.dp) } | |
| } |
| val all = manager.installedProviders.mapNotNull { info -> | ||
| try { | ||
| val label = info.loadLabel(pm) | ||
| val appName = try { | ||
| pm.getApplicationLabel(pm.getApplicationInfo(info.provider.packageName, 0)).toString() | ||
| } catch (_: Exception) { info.provider.packageName } | ||
| WidgetInfo(provider = info, label = label, previewImage = null, appName = appName) | ||
| } catch (_: Exception) { null } | ||
| }.sortedBy { it.appName } | ||
| _allWidgets.value = all | ||
| refreshPinnedAndIds(all) |
There was a problem hiding this comment.
The current implementation of loadWidgets only queries widget providers for the current user profile. To fully support Private Space (Android 15) and Work Profiles, you should iterate through all available profiles using LauncherApps.getProfiles() and query AppWidgetManager.getInstalledProvidersForProfile(UserHandle) for each.
| val appWidgetId = appWidgetHost.allocateAppWidgetId() | ||
|
|
||
| return try { | ||
| val bound = manager.bindAppWidgetIdIfAllowed(appWidgetId, widget.provider.provider) |
There was a problem hiding this comment.
This call to bindAppWidgetIdIfAllowed does not specify a UserHandle. This will fail for widgets belonging to other profiles (like Private Space or Work Profile). Since the project targets SDK 36 and supports Private Space, you should use the overload that accepts a UserHandle to ensure cross-profile widgets can be bound correctly.
No description provided.