
Crime fiction for your commute with Pixel Fold

ChromeOS M115 Stable
The Stable channel is being updated to OS version: 15474.84.0 Browser version: 115.0.5790.182 for most ChromeOS devices.
If you find new issues, please let us know one of the following ways
Interested in switching channels? Find out how.
You can find the release notes here.
Security Fixes and Rewards
Note: Access to bug details and links may be kept restricted until a majority of users are updated with a fix. We will also retain restrictions if the bug exists in a third party library that other projects similarly depend on, but haven’t yet fixed.
[NA][NA] High Fixes for CVE-2023-4211 on impacted platforms
We would also like to thank all security researchers that worked with us during the development cycle to prevent security bugs from ever reaching the stable channel.
Daniel Gagnon,
Google ChromeOS
A new LTC-114 version, 114.0.5735.329 (Platform Version: 15437.66.0), is being rolled out for most ChromeOS devices.
The Beta channel has been updated to 116.0.5845.96 for Windows, Mac and Linux.
A partial list of changes is available in the Git log. Interested in switching release channels? Find out how. If you find a new issue, please let us know by filing a bug. The community help forum is also a great place to reach out for help or learn about common issues.
Daniel Yip
Google Chrome
The Stable channel has been updated to 116.0.5845.96 for Windows and Mac as part of our early stable release to a small percentage of users. A full list of changes in this build is available in the log.
You can find more details about early Stable releases here.
Interested in switching release channels? Find out how here. If you find a new issue, please let us know by filing a bug. The community help forum is also a great place to reach out for help or learn about common issues.
Daniel Yip
Google Chrome
The Indian startup ecosystem is thriving, with new startups being founded every day. The country has a large pool of talented engineers and entrepreneurs, and a growing number of investors, policy makers and new age enterprises are looking to back Indian startups.
Google Developer Groups (GDGs) in 50 key Indian cities with varying tech ecosystems across India have seen a healthy mix of developers from the startup ecosystem participating in local meetups. As a result, GDGs have created a platform in collaboration with Google to help early-stage startups accelerate their growth. GDGs across India are increasingly playing a vital role in assisting startup founders and their teams with content, networking opportunities, hackathons, bootcamps, demo days, and more.
We are pleased to announce Startup Success Days with the goal of strengthening how developer communities interact with startup founders, VCs, and Googlers to discuss, share, and learn about the latest trends like Generative AI, Google Cloud, Google Maps, and Keras.Startup Success Days will be held in 15 cities across India, starting with 8 cities in August and September: Ahmedabad, Bangalore, Hyderabad, Indore, Chennai, New Delhi, Mumbai, and Pune.
The next event will be hosted at Bangalore on August 12, 2023 at Google Office. The events will be free to attend and will be open to all startups, regardless of stage or industry. The events will cover technical topics, focused on Google technologies, and will provide opportunities for startups to receive mentorship from industry experts, network with other startups, and meet VCs to receive feedback on their business models.
Learn more and register for Startup Success Days on our website.
We look forward to seeing you there!
Harsh Dattani Program Manager, Developer Ecosystem at Google
The upcoming stable release of Android 14 is fast approaching. Now is a great time to test your app with this new release’s changes if you haven’t done so already. With Platform Stability, you can even submit apps targeting SDK 34 to the Google Play Store.
Android 14 introduces a new feature called Selected Photos Access, allowing users to grant apps access to specific images and videos in their library, rather than granting access to all media of a given type. This is a great way for users to feel more comfortable sharing media with apps, and it's also a great way for developers to build apps that respect user privacy.
To ease the migration for apps that currently use storage permissions, apps will run in a compatibility mode. In this mode, if a user chooses “Select photos and videos” the permission will appear to be granted, but the app will only be able to access the selected photos. The permission will be revoked when your app process is killed or in the background for a certain time (similar to one time permissions). When the permission is once again requested by your app, users can select a different set of pictures or videos if they wish. Instead of letting the system manage this re-selection, it’s recommended for apps to handle this process to have a better user experience.Even when your app correctly manages media re-selection, we believe that for the vast majority of apps, the permissionless photo picker that we introduced last year will be the best media selection solution for both user experience and privacy. Most apps allow users to choose media to do tasks such as attaching to an email, changing a profile picture, sharing with friends, and the Android photo picker's familiar UI gives users a consistent, high-quality experience that helps users grant access in confidence, allowing you to focus on the differentiating features of your app. If you absolutely need a more tightly integrated solution, integrating with MediaStore can be considered as an alternative to the photo picker.
To use the photo picker in your app, you only need to register an activity result:
// Using Jetpack Compose, you should use rememberLauncherForActivityResult instead of registerForActivityResult
// Registers a photo picker activity launcher in single-select mode
val pickMedia = registerForActivityResult(PickVisualMedia()) { uri ->
// Callback is invoked after the user selects a media item or closes the photo picker
if (uri != null) {
Log.d("PhotoPicker", "Selected URI: $uri")
} else {
Log.d("PhotoPicker", "No media selected")
}
} |
The photo picker allows customization of media type selection between photos, videos, or a specific mime type when launched:
// Launch the photo picker and let the user choose images and videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))
// Launch the photo picker and let the user choose only images.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))
// Launch the photo picker and let the user choose only videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.VideoOnly))
// Launch the photo picker and let the user choose only images/videos of a
// specific MIME type, like GIFs.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.SingleMimeType("image/gif"))) |
You can set a maximum limit when allowing multiple selections:
// Registers a photo picker activity launcher in multi-select mode.
// In this example, the app lets the user select up to 5 media files.
val pickMultipleMedia = registerForActivityResult(PickMultipleVisualMedia(5)) { uris ->
// Callback is invoked after the user selects media items or closes the
// photo picker.
if (uris.isNotEmpty()) {
Log.d("PhotoPicker", "Number of items selected: ${uris.size}")
} else {
Log.d("PhotoPicker", "No media selected")
}
} |
Lastly, you can enable the photo picker support on older devices from Android KitKat onwards (API 19+) using Google Play services, by adding this entry to your AndroidManifest.xml file:
<!-- Prompt Google Play services to install the backported photo picker module -->
<service android:name="com.google.android.gms.metadata.ModuleDependencies"
android:enabled="false"
android:exported="false"
tools:ignore="MissingClass">
<intent-filter>
<action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
</intent-filter>
<meta-data android:name="photopicker_activity:0:required" android:value="" />
</service> |
In less than 20 lines of code you have a well-integrated photo/video picker within your app that doesn’t require any permissions!
Creating your own gallery picker requires extensive development and maintenance, and the app needs to request storage permissions to get explicit user consent, which users can deny, or, as of Android 14, limit access to selected media.
First, request the correct storage permissions in the Android manifest depending on the OS version:
<!-- Devices running up to Android 12L -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- Devices running Android 13+ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!-- To handle the reselection within the app on Android 14+ (when targeting API 33+) -->
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" /> |
Then, the app needs to request the correct runtime permissions, also depending on the OS version:
val requestPermissions = registerForActivityResult(RequestMultiplePermissions()) { results ->
// Handle permission requests results
// See the permission example in the Android platform samples: https://github.com/android/platform-samples
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
requestPermissions.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_VISUAL_USER_SELECTED))
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissions.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO))
} else {
requestPermissions.launch(arrayOf(READ_EXTERNAL_STORAGE))
} |
With the Selected Photos Access feature in Android 14, your app should adopt the new READ_MEDIA_VISUAL_USER_SELECTED permission to control media re-selection, and update your app’s UX to let users grant your app access to a different set of images and videos.
When opening the selection dialog, photos and/or videos will be shown depending on the permissions requested: if you're requesting the READ_MEDIA_VIDEO permission without the READ_MEDIA_IMAGES permission, only videos would appear in the UI for users to select files.
// Allowing the user to select only videos
requestPermissions.launch(arrayOf(READ_MEDIA_VIDEO, READ_MEDIA_VISUAL_USER_SELECTED)) |
You can check if your app has full, partial or denied access to the device’s photo library and update your UX accordingly. It's even more important now to request these permissions when the app needs storage access, instead of at startup. Keep in mind that the permission grant can be changed between the onStart and onResume lifecycle callbacks, as the user can change the access in the settings without closing your app.
if (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
(
ContextCompat.checkSelfPermission(context, READ_MEDIA_IMAGES) == PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(context, READ_MEDIA_VIDEO) == PERMISSION_GRANTED
)
) {
// Full access on Android 13+
} else if (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
ContextCompat.checkSelfPermission(context, READ_MEDIA_VISUAL_USER_SELECTED) == PERMISSION_GRANTED
) {
// Partial access on Android 14+
} else if (ContextCompat.checkSelfPermission(context, READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
// Full access up to Android 12
} else {
// Access denied
} |
Once you verified you have access to the right storage permissions, you can interact with MediaStore to query the device library (whether the granted access is partial or full):
data class Media(
val uri: Uri,
val name: String,
val size: Long,
val mimeType: String,
val dateTaken: Long
)
// We run our querying logic in a coroutine outside of the main thread to keep the app responsive.
// Keep in mind that this code snippet is querying all the images of the shared storage
suspend fun getImages(contentResolver: ContentResolver): List<Media> = withContext(Dispatchers.IO) {
val projection = arrayOf(
Images.Media._ID,
Images.Media.DISPLAY_NAME,
Images.Media.SIZE,
Images.Media.MIME_TYPE,
)
val collectionUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// This allows us to query all the device storage volumes instead of the primary only
Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else {
Images.Media.EXTERNAL_CONTENT_URI
}
val images = mutableListOf<Media>()
contentResolver.query(
collectionUri,
projection,
null,
null,
"${Images.Media.DATE_ADDED} DESC"
)?.use { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(Images.Media._ID)
val displayNameColumn = cursor.getColumnIndexOrThrow(Images.Media.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndexOrThrow(Images.Media.SIZE)
val mimeTypeColumn = cursor.getColumnIndexOrThrow(Images.Media.MIME_TYPE)
while (cursor.moveToNext()) {
val uri = ContentUris.withAppendedId(collectionUri, cursor.getLong(idColumn))
val name = cursor.getString(displayNameColumn)
val size = cursor.getLong(sizeColumn)
val mimeType = cursor.getString(mimeTypeColumn)
val dateTaken = cursor.getLong(4)
val image = Media(uri, name, size, mimeType, dateTaken)
images.add(image)
}
}
return@withContext images
} |
The code snippet above is simplified to illustrate how to interact with MediaStore. In a proper production app, you should consider using pagination with something like the Paging library to ensure good performance.
As of Android 10 (API 29), you no longer need storage permissions to add files to shared storage. This means that you can add images to the gallery, record videos and save them to shared storage, or download PDF invoices without having to request storage permissions. If your app only adds files to shared storage and does not query images or videos, you should stop requesting storage permissions and set a maxSdkVersion of API 28 in your AndroidManifest.xml:
<!-- No permission is needed to add files from Android 10 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> |
In our last storage blog post, we announced that we’ll be rolling out a behavior change whenever ACTION_GET_CONTENT intent is launched with an image and/or video mime type. If you haven’t tested yet this change, you can enable it manually on your device:
adb shell device_config put storage_native_boot take_over_get_content true |
That covers how to offer visual media selection in your app with the privacy-preserving changes we've made across multiple Android releases.If you have any feedback or suggestions, submit tickets to our issue tracker.