CINXE.COM

Android Developers Blog: Jetpack

<!DOCTYPE html> <html class="v2 list-page" dir="ltr" itemscope="" itemtype="http://schema.org/Blog" lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:b="http://www.google.com/2005/gml/b" xmlns:data="http://www.google.com/2005/gml/data" xmlns:expr="http://www.google.com/2005/gml/expr"> <head><script type="text/javascript" src="/_static/js/bundle-playback.js?v=HxkREWBo" charset="utf-8"></script> <script type="text/javascript" src="/_static/js/wombat.js?v=txqj7nKC" charset="utf-8"></script> <script>window.RufflePlayer=window.RufflePlayer||{};window.RufflePlayer.config={"autoplay":"on","unmuteOverlay":"hidden"};</script> <script type="text/javascript" src="/_static/js/ruffle/ruffle.js"></script> <script type="text/javascript"> __wm.init("https://web.archive.org/web"); __wm.wombat("https://android-developers.googleblog.com/search/label/Jetpack","20230213032228","https://web.archive.org/","web","/_static/", "1676258548"); </script> <link rel="stylesheet" type="text/css" href="/_static/css/banner-styles.css?v=S1zqJCYt" /> <link rel="stylesheet" type="text/css" href="/_static/css/iconochive.css?v=3PDvdIFv" /> <!-- End Wayback Rewrite JS Include --> <link href="https://web.archive.org/web/20230213032228cs_/https://www.blogger.com/static/v1/widgets/2975350028-css_bundle_v2.css" rel="stylesheet" type="text/css"/> <!-- Google Tag Manager --> <script> (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://web.archive.org/web/20230213032228/https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-TB2PQP2'); </script> <!-- End Google Tag Manager --> <title>Android Developers Blog: Jetpack</title> <meta content="width=device-width, height=device-height, minimum-scale=1.0, initial-scale=1.0, user-scalable=0" name="viewport"/> <meta content="IE=Edge" http-equiv="X-UA-Compatible"/> <meta content="Android Developers Blog" property="og:title"/> <meta content="https://web.archive.org/web/20230213032228im_/https://2.bp.blogspot.com/-kptChpiuaf0/VeSjz-zMUzI/AAAAAAAAAxE/REIiZ4qVI2s/s1600/150824_GoogleBlog_Share.png" property="og:image"/> <meta content="News and insights on the Android platform, developer tools, and events." property="og:description"/> <meta content="en_US" property="og:locale"/> <meta content="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack" property="og:url"/> <meta content="Android Developers Blog" property="og:site_name"/> <!--START Twitter Card --> <meta content="summary_large_image" name="twitter:card"/> <meta content="@AndroidDev" name="twitter:site"/> <meta content="@AndroidDev" name="twitter:creator"/> <meta content="https://android-developers.googleblog.com/" name="twitter:domain"/> <meta content="https://web.archive.org/web/20230213032228im_/https://android-developers.googleblog.com/" name="twitter:url"/> <meta content="Android Developers Blog: Jetpack" name="twitter:title"/> <meta content="https://web.archive.org/web/20230213032228im_/https://2.bp.blogspot.com/-SyYsE6lCBK4/WpbnmkKnvjI/AAAAAAAAFG4/iALBir1-WU0NzVTf-83eo3MB0kvaHZliQCLcBGAs/s1600/ad_logo_twitter_card.png" name="twitter:image"/> <meta content="News and insights on the Android platform, developer tools, and events." name="twitter:description"/> <!--END Twitter Card --> <link href="https://web.archive.org/web/20230213032228cs_/https://fonts.googleapis.com/css?family=Roboto:300,400italic,400,500,500italic,700,700italic" rel="stylesheet" type="text/css"/> <link href="https://web.archive.org/web/20230213032228cs_/https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/> <script src="https://web.archive.org/web/20230213032228js_/https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js" type="text/javascript"></script> <script src="https://web.archive.org/web/20230213032228js_/https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js" type="text/javascript"></script> <script src="https://web.archive.org/web/20230213032228js_/https://www.google.com/js/gweb/analytics/autotrack.js" type="text/javascript"></script> <script src="https://web.archive.org/web/20230213032228js_/https://apis.google.com/js/plusone.js" type="text/javascript"></script> <script type="text/javascript"> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//web.archive.org/web/20230213032228/https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-961555-37', 'auto'); ga('send', 'pageview'); </script> <!-- End --> <style id="page-skin-1" type="text/css"><!-- h3 { 115%; } #imgFull { display: block; margin: 10px auto; width: 90%; border: 0; padding: 0; } #imgHalf { display: block; margin: 10px auto; width: 50%; border: 0; padding: 0; } #flexParent { display: flex; width: 100%; justify-content: space-around; align-items: center; } #flexImg { width: 90%; margin: 0; padding: 0; border: 0; } #flexImg img { margin: 0 auto; display: block; padding: 0; width: 100%; } #imgCaption { text-align: center; font-style: italic; font-size: 85%; margin: -5px 0 10px 0; padding: 0; border: 0; } #floatRight { float: right; width: 45%; margin: 5px 0 5px 5px; padding: 0; border:0; } #floatLeft { float: left; width: 45%; margin: 5px 5px 5px 0; padding: 0; border:0; } --></style> <style id="template-skin-1" type="text/css"><!-- --></style> <!-- start all head --> <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/> <meta content="blogger" name="generator"/> <link href="https://web.archive.org/web/20230213032228im_/https://android-developers.googleblog.com/favicon.ico" rel="icon" type="image/x-icon"/> <link href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack" rel="canonical"/> <link rel="alternate" type="application/atom+xml" title="Android Developers Blog - Atom" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/feeds/posts/default"/> <link rel="alternate" type="application/rss+xml" title="Android Developers Blog - RSS" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/feeds/posts/default?alt=rss"/> <link rel="service.post" type="application/atom+xml" title="Android Developers Blog - Atom" href="https://www.blogger.com/feeds/6755709643044947179/posts/default"/> <!--Can't find substitution for tag [blog.ieCssRetrofitLinks]--> <meta content="News and insights on the Android platform, developer tools, and events." name="description"/> <meta content="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack" property="og:url"/> <meta content="Android Developers Blog" property="og:title"/> <meta content="News and insights on the Android platform, developer tools, and events." property="og:description"/> <!-- end all head --> <base target="_self"/> <style> /** Blogger default styles **/ html, .Label h2, #sidebar .rss a, .BlogArchive h2, .FollowByEmail h2.title, .widget .post h2 { font-family: Roboto, sans-serif; } .plusfollowers h2.title, .post h2.title, .widget h2.title { font-family: Roboto, sans-serif; } pre, .post-content pre.prettyprint { background-color: #f7f7f7; border: 1px solid #ddd; margin: 0 0 1em 0; padding: 1em; overflow: auto; } pre, code { font-size: 9pt; line-height: 125%; font-family: monospace; } pre, code { color: #060; font: 13px/18px Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace; -webkit-font-smoothing: subpixel-antialiased; -moz-osx-font-smoothing: auto; } /* Styles for https://github.com/google/code-prettify */ .com { color: #060; } .kwd { color: #008; } .typ { color: #606; } .lit { color: #066; } .pun { color: #660; } .pln { color: #000; } .fb-custom img, .twitter-custom img, .gplus-share img, .email-custom img { cursor: pointer; opacity: 0.54; } .twitter-custom, .gplus-share, .fb-custom { margin-right: 12px; } /** Blogger theme styles **/ .cols-wrapper, .header-inner { max-width: 978px; margin-left: auto; margin-right: auto; } .cols-wrapper { margin: auto; clear: both; margin-top: 60px; margin-bottom: 60px; overflow: hidden; } .col-main-wrapper { margin-top: 20%; float: left; width: 100%; } .col-main { margin-right: 278px; max-width: 660px; } .col-right { float: right; width: 248px; margin-left: -278px; } /** Blogger custom theme **/ .adb-header, .searchBox input { box-sizing: border-box; } .popout-nav { background-color: white; width: 280px; height: 100%; position: fixed; z-index: 6; left: -280px; transition: left 0.2s; } .popout-search { background-color: white; width: 280px; position: fixed; z-index: 6; right: -280px; transition: right 0.2s; top: 0; } .popout-search .widget { margin: 0; } .popout-search #BlogSearch2_form .popout-options { padding-right: 24px; width: calc(100% - 48px); } .dropdown-nav { display: none; background-color: white; width: 104px; position: fixed; padding: 4px 0px 3px 0px; z-index: 4; left: 352px; top: 156px; transition: top 0.2s; box-shadow: 0 0px 2px 0 rgba(60,64,67,.3), 0 0px 6px 2px rgba(60,64,67,.15), inset 0 0px 6px -4px rgba(154,160,166,.5); } .dropdown-options { align-items: center; display: flex; flex-wrap: wrap; width: 94px; height: 48px; padding-left: 10px; } .dropdown-options:hover { background-color: #f7f9fa; } .dropdown-options span { color: #80868b; } .popout-options { padding-right: 8px; padding-left: 24px; padding-top: 16px; padding-bottom: 15px; display: inline-block; width: calc(100% - 32px); border-top: 1px solid #ddd; font-size: 13px; font-weight: 700; color: #80868b !important; } .popout-options:last-of-type { border-bottom: 1px solid #ddd; } .popout-options:first-of-type { border-top: 0; } #close-popout { padding: 12px 12px 11px 20px; } #close-search-popout { padding: 12px 20px 11px 12px; text-align: right; } .popout-search-overlay{ display: none; opacity: 0; background-color: black; width: 100%; height: 100%; position: fixed; transition: opacity 0.2s; } .popout-overlay{ display: none; opacity: 0; background-color: black; width: 100%; height: 100%; position: fixed; z-index: 5; transition: opacity 0.2s; } html { background-color: #fff; font-family: Roboto, sans-serif; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; } body { width: 100%; position: absolute; padding: 0; /* This ensures that the scroll bar is always present, which is needed */ /* because content render happens after page load; otherwise the header */ /* would 'bounce' in-between states. */ min-height: 150%; } h2 { font-size: 130%; } h1, h2, h3, h4, h5 { line-height: 1.5em; margin-bottom: 1em; } html, h4, h5, h6 { font-size: 14px; } a, a:visited { color: #039be5; text-decoration: none; } pre code { display: block; } .cols-wrapper { margin-top: 56px; } .header-outer, .cols-wrapper { padding: 0 60px; } html, .header-inner a { color: #212121; color: rgba(0, 0, 0, .87); } /** Archive widget. **/ .BlogArchive { font-size: 13px; font-weight: normal; } .BlogArchive .widget-content { display: none; } .BlogArchive h2, .Label h2 { color: #039be5; text-decoration: none; } /* Specificity needed here to override widget CSS defaults. */ /* Months */ .Label { font-size: 13px; font-weight: normal; } .sidebar-icon { display: inline-block; width: auto; vertical-align: middle; height: 24px; margin-right: 24px; } .sidebar-social-links { align-items: center; } .sidebar-social-links .android-logo, .android-logo { height: 48px; } .sidebar-social-links a:last-child img { margin-right: 0; } .Label .widget-content { display: none; } .searchBox input { border: 1px solid #eee; color: #212121; color: rgba(0, 0, 0, .87); font-size: 14px; padding: 8px 8px 8px 40px; width: 100%; font-family: Roboto, sans-serif; background: url('https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/1x/search_grey600_24dp.png') 8px center no-repeat; } .searchBox ::-webkit-input-placeholder { /* WebKit, Blink, Edge */ color: rgba(0, 0, 0, .54); } .section { margin: 0; padding: 0; } #sidebar-top { border: 1px solid #eee; } #sidebar-top > div { margin: 16px 0; } /*main post*/ .post { margin-bottom: 30px; } #main .post .title { margin: 0; } #main .post .title a { color: #212121; color: rgba(0, 0, 0, .87); font-weight: normal; font-size: 24px; } .message, #main .post .post-header { margin: 0; padding: 0; } #main .post .post-header .caption, #main .post .post-header .labels-caption, #main .post .post-footer .caption, #main .post .post-footer .labels-caption { color: #444; font-weight: 500; } #main .post .post-header .published { font-size: 11px; font-weight: bold; } .post-header .publishdate { font-size: 16px; font-weight: normal; color: #757575; color: rgba(0, 0, 0, .54); } #main .post .post-footer { font-size: 12px; padding-bottom: 21px; } .label-footer { margin-bottom: 12px; margin-top: 12px; } #main .post .post-header .published { margin-bottom: 16px; } .post .post-content { color: #545454; color: rgba(0, 0, 0, .67); font-size: 16px; margin: 16px 0 36px 0; line-height: 24px; word-wrap: break-word; } .post-summary { display: none; } /* Override all post images/videos to left align. */ @media (max-width: 712px) { .post-content img { max-width: 100%; height: auto; width: auto; } } .post-content .tr-caption-container img { margin-bottom: 12px; } .post-content iframe, .post-content embed { margin-left: auto !important; margin-right: auto !important; max-width: 100%; } .post-content .carousel-container { margin-bottom: 48px; } #main .post-content b { font-weight: 500; } /* These are the main paragraph spacing tweaks. */ #main .post-content br { content: ''; display: block; padding: 4px; } .post-content .space { display: block; height: 8px; } .post-content iframe+.space, .post-content iframe+br { padding: 0 !important; } #main .post .jump-link { margin-bottom: 10px; } .post-content img, .post-content iframe { margin: 30px 0 20px 0; } .post-content > img:first-child, .post-content > iframe:first-child { margin-top: 0; } .col-right .section { padding: 0 16px; } #aside { background: #fff; border: 1px solid #eee; border-top: 0; z-index: 2; } #aside .widget { margin: 0; } #aside .widget h2, #ArchiveList .toggle+a.post-count-link { color: #212121; color: rgba(0, 0, 0, .87); font-weight: 400 !important; margin: 0; } #ArchiveList .toggle { float: right; } #ArchiveList .toggle .material-icons { padding-top: 4px; } #sidebar-top, #sidebar { background: #fff; } #sidebar .tab { cursor: pointer; } #sidebar .tab .arrow { display: inline-block; float: right; } #sidebar .tab .icon { display: inline-block; vertical-align: top; height: 24px; width: 24px; margin-right: 13px; margin-left: -1px; margin-top: 1px; color: #757575; color: rgba(0, 0, 0, .54); } #sidebar .widget-content > :first-child { padding-top: 8px; } #sidebar .active .tab .arrow { -ms-transform: rotate(180deg); transform: rotate(180deg); } #sidebar .arrow { color: #757575; color: rgba(0, 0, 0, .54); } #sidebar .widget h2 { font-size: 14px; line-height: 24px; display: inline-block; } #sidebar .widget .BlogArchive { padding-bottom: 8px; } #sidebar .widget { border-bottom: 1px solid #eee; box-shadow: 0px 1px 0 white; margin-bottom: 0; padding: 14px 0; min-height: 20px; } #sidebar .widget:last-child { border-bottom: none; box-shadow: none; margin-bottom: 0; } #sidebar ul { margin: 0; padding: 0; } #sidebar ul li { list-style: none; padding: 0; } #sidebar ul li a { line-height: 32px; } #sidebar ul ul ul li a { line-height: 24px; } #sidebar .archive { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAYCAYAAADzoH0MAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAE1JREFUeNpiNDY23s9AAWBioBCwYBM8c+YMVsUmJibEGYBNMS5DaeMFfDYSZQA2v9I3FrB5AZeriI4FmnrBccCT8mhmGs1MwyAzAQQYAKEWG9zm9QFEAAAAAElFTkSuQmCC'); height: 24px; line-height: 24px; padding-left: 30px; } #sidebar .labels { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAUxJREFUeNpiNDY23s9AAMycOfM7UF05kHkZmzwTMkdSUhKrIcXFxZy3bt3qBjIN8RrS09PDsHnzZjCNDr58+cKQlpbGDjSoHcg1w2oIyAUODg5gARCNzUVIBrUCuVYYhjx//pzhwIEDYAEQDeJjA1CDWIAGNQK59jBxRuSABbkAlwHIgIeHh2HWrFn/1NTU2oDcvSgBS4wBSC5iArqoCsj1YGIgEyAZVMoEchqlBjEB/cZAiUHg2AEGznpKDAImxOeM////B4VLKtBvEUCngZ1ILKivr3/u6+ubBzJAGZQ9gC5aQoqLgAY8BhkAZL4BuQQkxgXE34A4BuiiZEIuAhrwEGhAEZD5DpzYoIaA2UAM4kQADUrHZRDUgAIg8wO2XAwzbQXQa5OweQ1owB10AyA6gS7BgX1u3ry5397eHow3bdo0EyjGi00tQIABANPgyAH1q1eaAAAAAElFTkSuQmCC'); height: 20px; line-height: 20px; padding-left: 30px; } #sidebar .rss a { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAX5JREFUeNqsVDGSgkAQHL2rIiIikohIc/EBRkbwAIwuwgfwAXiAD9AHSI7kEkECRCb6AIyINDLx7K0aa6kT7uq0q7YYtnZ7umdnt7darXbr9Zpegeu61DNNc0dvwCcH4/GYJpMJnc9nOhwOVJbl/4hAAokMECZJQtvt9k+kH7qufyEYDAakqqqYxFdRFBqNRmTbNg2HQ0rTlK7XayvR0xqBdDqdkuM4dE/0ULhYLOh4PHYrknG5XGi/31MYhuL/nkwonM1mlGUZ1XXdrsiyLGEDhY7juJEZ1u5tIixDGdYhmYw+B7CAzPP5nDabjdgIAgCksMX1832/3drtdqPT6SQWapomiGEFNkDEdpDMMAzK81ys/7XYy+XyoQgq2WoURSIJ2iIIgp/WZCCTvFm2wgeAU31aI3Q2GhIDMeB53qPYPIcm5VrxXIOIOxsDMStjVawAc1VViRgN22lNBiuQN3GR+SY07hpOoStmFQAKXRRFY93bnpG+fONfedi+BRgAbkS8Fxp7QQIAAAAASUVORK5CYII='); } #sidebar .subscription a { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAALCAYAAACZIGYHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAUBJREFUeNrMkSGLAlEUhb+ZB4JFi8mx2cz+ApvhRUGTcUCrNqNJDYIi+DO0GUwmQXDK2DSIoGgZcSaIjDrzwrK4ssvChj1w0733O+fdp+m6PozH4yQSCfb7Pa7r8pOi0SjJZBLP8zgej4gAIMvlMuPxmADIYrHger1+C6lUKmo+NJ/NZojb7SZDWiwWo1qtks1msW2bw+HwZdkwDHq9HvV6nel0SqvVYrvdIh6Ph3Qch+VyqRYLhQJSSjRNw7IsfN9XgGKxSLfbJZfL0e/3aTabrFYr7vc7IujLcOh8PqunrNdr0uk0pVKJVCpFJBJRgEajweVyod1uMxgM2O12BAGUgRbU8DV2JpOhVquRz+cRQii3+XxOp9NRN3jVR5LPOp1OjEYjlSL8hclkgmmabDabt4d+m+S30vkD/R/IU4ABAPTZgnZdmG/PAAAAAElFTkSuQmCC'); } #sidebar-bottom { background: #f5f5f5; border-top: 1px solid #eee; } #sidebar-bottom .widget { border-bottom: 1px solid #e0e0e0; padding: 15px 0; text-align: center; } #sidebar-bottom > div:last-child { border-bottom: 0; } #sidebar-bottom .text { line-height: 20px; } /* Home, forward, and backward pagination. */ .blog-pager { border-top: 1px #e0e0e0 solid; padding-top: 10px; margin-top: 15px; text-align: right !important; } #blog-pager { padding: 40px 0 0; } #blog-pager a { display: inline-block; } .blog-pager i.disabled { opacity: 0.2 !important; } .blog-pager i { color: black; margin-left: 16px; opacity: 0.54; } .blog-pager i:hover, .blog-pager i:active { opacity: 0.87; } #blog-pager-older-link, #blog-pager-newer-link { float: none; } .gplus-profile { background-color: #fafafa; border: 1px solid #eee; overflow: hidden; width: 212px; } .gplus-profile-inner { margin-left: -1px; margin-top: -1px; } /* Sidebar follow buttons. */ .followgooglewrapper { padding: 12px 0 0 0; } .loading { visibility: hidden; } .detail-page .post-footer .cmt_iframe_holder { padding-top: 40px !important; } /* ADB */ .adb-hero-area { min-height: 104px; background-color: #073042; box-shadow: 0 1px 2px 0 rgba(60,64,67,.3), 0 2px 6px 2px rgba(60,64,67,.15); z-index: 3; position: fixed; width: 100%; } .adb-hero-logo { height: 80px; position: absolute; top: 12px; left: 16px; } .adb-footer-btm { display: flex; margin: 0 94px 40px; align-items: center; justify-content: space-between; flex-wrap: wrap; } .subscribe-btn { color: black; background-color: #3DDC84; border-radius: 100px; font: 500 16px/24px Roboto,sans-serif; padding: 12px 24px; border: 0; } .adb-footer-info { display: flex; padding: 0; margin: 9px 0 0 -8px; font-size: 13px; } .footer-list { list-style: none; padding: 0 8px; } .subscribe-info { list-style: none; padding: 0 8px 0 0; font-size: 13px; color: black; } .footer-newsletter { display: flex; align-items: center; } .adb-header-inner { display: flex; align-items: center; } .adb-header { background-color: white; color: #fff; height: 48px; left: 0; overflow: hidden; padding: 36px 24px; right: 0; top: 104px; z-index: 4; display: flex; align-items: center; position: fixed; border-bottom: solid 1px #DADCE0; } .adb-shadow { margin-top: 48px; height: 1px; width: 100%; position: fixed; background: white; z-index: 2; box-shadow: 0 1px 2px 0 rgba(60,64,67,.3), 0 2px 6px 2px rgba(60,64,67,.15); } .adb-logo-box { display:inline; } .adb-burger-box { display: none; margin: 0 16px 0 0; } .adb-search-box { display: none; margin: 0 0 0 16px; } .adb-htag-box { display: flex; align-items: center; } .adb-header-tags { padding: 0 24px 0 0; } .adb-header-tags ~ .atb-header-tags { padding: 0 24px; } .adb-header-tags a { font-weight: 600; font-size: 16px; line-height: 20px; color: #5F6368; } .adb-header-tags a:hover { font-weight: 600; font-size: 16px; line-height: 20px; color: #073042; } .more-dropdown { display: none; } .dropdown-icon { transition: all 0.3s; } .adb-header-logo { display: inline-block; vertical-align: -11px; width: 134px; height: 32px; } .adb-header-title { color: #fff; display: inline-block; font-size: 20px; font-weight: normal; } .adb-header-links { border-left: 1px solid rgba(0, 0, 0, .1); display: block; left: 250px; list-style: none; margin: 0; padding: 12px 0 12px 18px; position: absolute; top: 0; } .adb-header-link-item { display: inline-block; } .adb-header-link, .adb-header-link:visited { color: #fff; display: inline-block; font-weight: 500; opacity: .7; padding: 12px 9px; text-transform: uppercase; vertical-align: 2px; } .adb-header-link:hover, .adb-header-link:focus { color: #fff; opacity: 1; } .adb-header-console, .adb-header-console:visited { border-radius: 3px; box-shadow: 0 1px 4px 0 rgba(0, 0, 0, .2); color: #fff; float: right; font-size: 14px; font-weight: 500; line-height: 28px; padding: 4px 10px; position: relative; text-transform: uppercase; transition: box-shadow .2s; z-index: 60; } .adb-header-console:hover { box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .3); color: #fff; } .adb-header-console:focus { background-color: rgba(63, 81, 181, .1); color: #fff; } .adb-header-console-image { vertical-align: -5px; } .adb-hero { background-color: #dcedc8; margin-top: 64px; } .adb-hero-back, .adb-hero-back:link, .adb-hero-back:visited { color: #414141; display: inline-block; font-weight: 500; margin: 25px 25px 0; } .adb-hero-inner { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAc4AAADZCAMAAACenCAVAAAAvVBMVEXc7cjY68PT6bvR57fQ57bY6sPZ68TU6bvR6LfP5rTQ57XR57jW6r7b7cfa7MbS6LnS6Lrb7cbQ6Lfb7MbX6r/Z68PY6sLV6b7U6b3U6r3T6LnX68Da7MXP57Tb7Mfc7cfW6b/P57XS57nY68LY68HU6L3W6r/W6b7T6brX6sDV6rzQ57fa7MTV6b3T6Lra68TR57bX68HW6sDS57jU6bzO5rTP5rXZ7MXR6LbZ68XX6sHT6LvV6bzS6LjZ7MILrTuqAAAIyklEQVR4AezXhXEkUQwE0B6+Xhg8MzND/smZ4UsLBSb9Kr0UhI1vl6RZnqUFlnBl9Y+j8WQK4+qGr9oOi7iKr0YFTOt6vhsPmM81/FDAspafUszl/vNTXsOuFYZWMYfrRgyswa51hjLM4TYYGsOuTQpbgNO2dyjAroxCvgunpZT2YFZDaQKnlFRg1z6lgxpOOqS0CbtqKutwwhGVYxh2QqWGC/WUMlhWH1BK4QKnVFZh2hmVEh/cXk7pHLbt5pQafHCT6Jr9gsoR3riayiXMyyhd4Y27jDDHJVQu8MJdU7lBBBpKoz08c7eU8gERuKNyhiduhcopolBRqeGAMaV7xKE7eGzvThQTVZooAJdC1AM2qGg0BhdCrpnciVE0i9dZ3v+t/n2ZMBtJGpsu6nuCrNJddar4/gAnMmuP/GPk3JFwrL2QKw3FD26GyGmQNXbIGVHNKdfmYraDl/ZUcxO8NNtIW888bafDC7tP5R2qtQfL724N5AypxjbIGdseWHMV1VfP+rrnZvbTerMEhHb2Py4eA7KX9AwD5LSppi5ZdPTHEhv6cd5mSjZaeMW/Dc66TEpkO+Q0qIbUE5es6ke85FMN/YGXZs9yQLdXYGVASGJDBaMZXsQouzau/fzfitNsKvpUL1O8dFBksb+Q80C1coaco7RtLeYzO9tHCV7qUY0ckXPGribSoNpQB3Z1MbWtby1hiZwWw35CRjUReRx7Sg5ecqgmJiyHOxrIWVEtPDPNY/TMx4bmczq5T3jpSRELm1NWodVmlGY364tP4ZXvdw5u8jjDf8yQJJ7j+2HYfBh3d+l5HFB5RmyzjM3yh8iDUbb6M9y7j3idgxM+3BzTuPzyXmdOTARJaUc8tUmX7ekB7+aF7dV1a0G6pIx3CkxKmBBUreMfvS0086aTnZZf6kfG53mlN/0UnS2bW5Socz9OA737eM6JkZWu4uUiXfVcnIT34eZM6Yq9hcRKR0OpL7i9cHBi/udjrCOUuiFWsnc2/oKsfYAhXvM2lsj4S/7b2/L9y4cODDu0j8HbBzqSPvutOksqpDH+gorw1+dvHLcaEzv3r4+0BdmnBJXi9bL+G6qaEbFz98rF4sEwnKGKrrrxK3sOuzoMlM9i+ql47KPC/HX8inTxljgKUHBYI1g6qDx/HBct76XETfHF4v3hHpZwVgH9APc0TfFSyTwLZ7DJ9FYxmZvTv1h89DmBdbyHEX0rcuuTRB38fNuQ2vmw1GAYMSjv6ZwQbD0ksFlz9LPW7gMxNv3RBVtlDqz3cbWo32rmEXLGFExcsJBMnilGzk29pleT5gx8fLiq2XzyM2rllpiboEYGxF2UoD6+EntL1Ma+TkOP/LVqNJLM3yeqgayDmnjqKmIu3aNGDhlx1pqiZvyUuAqaqKGwRRwt1gnqqR0QO5ce6ms5J1ZGIWqtkxIf0QNqb7ohJjIXgFjPiYF4j38R2wbZTk1m+C/xqU9WSzv4hkgyVkcg0QvIUg0XEHleRjaK/gQb8g/a2EL8hHcky6zxC+JTQBa5c/BL4nBO1lgl+B2xJjv0QxQgnA1ZoOGhGHFNldedoShxMadKU/d4BeEEVGF3Ll5FJF+psm7Bh5xwHyDeoKeogvp7iDfpbKhyWgeIN/JSqpjbBOLtlqwq7qJNFdKGeKdQUUUEIcS7Df6iSngeQAPhtqgCWi60EElKxjUSsCFriK5nYEMuLDtoJSZk0BiaiaYsZGPlXnEqHoipIhOaKIW4WnD63xThgtP/pthHdFqfUCIRRvK/KZ+3VX1uiulc7puc3EstSOpD9d7+LYGTFfiQenyKkxE3VLLGI9iQkcH4CackzqlEwRYnJbwNlUb5ODHhBVSWHk5O+JFMLkh5qFYxLxnoPZ9BmJGRdrELNuTddNEAwhg34NSvFnvSqgth1ANpNEIZkhkYSg4dt9rHocCFXvvxWUD/ELR2TQ98hMOWon+Ks7YLrZI70iWETu6yT99Qt3uw4A4j+lY6hU4dVcloUFdR3tkA1kt29J1zHxr1SIsGNAoDnvGjMCj/CLmr3INzQj9x5sFm41Ns933c0PtNoc+O0bJNA9+Xryp14+zSL9x5PH6beS2vQrfP0exkQcMG08BdCn1SepeFc8KPijWs5MxPdzN4CipzR2nQb8wdnu0O5UKbkN7h/LQZ/a9Ms+pZNYp9kQt9Yvo9H3Yx8H15QSXm/qZUQMY0ybOCPtNKlIMuqYAogW1SKkBBoxW9SXSARoqKCGGbBRVxBY0C85V3h2mb/MrASGxIb3BnYlaxwXRmLwUMFxMc6NSlQvpM6nt5MXTylOl00JGKYTqxp6DVBb1SkECrlOmvs0HFGA7efjA0dwrLfKVC5tDLp1dpGHrGKFjm0tCZYEWvMTA0NrOBZVZUSMtoLn4I3T5RISnTZTGXJrea9BPotmU6QrqlQi6gXWx05V5MRTiwTaD/4aW5FN8y9pCJYZ2usSPBNRUzNfapNIZ1fGM7KQdUSMNYJyl6Ajh2yAKTBUYHpfCZruJ0jB3wCpVur1GSIf3G8yOPlG1ePDN46d2iJEmLR+86L4lNBRQTZXK/6UAxunMWDxC3Da6omXsoz5XiOa8/NfV9JYHRXVBXiudG8r0y9Jmzpl87oFSDFv3Qog2rDTb0Q6qJckX0K7coWTKkHxh1YLmkSz/Q6JjNKnVQOj+lnM0nMPDxmnLuPhjej5rhFLbjmP4nOF6BCXfdov8JLkPj+xcdnMjh03p3nWXLBwesuB/+GGbHXbc5wIkkSt7CwEmXU1lGbPX3OYVBt5y2Yoov9EN/QVhpJK9xZL/7P4Kw1LO8iIH7qxx8CEu5zG8pcldpQ1hrSjnBI4S9YnrpBsJiEzkIcfIkByFWUqkIcdKjb7kQdos49a3Fjv7vE4TlfPoflYAPuXoeIaw3pv+6h7CeQ/8RPYIP6XpegwGxknMtwxcqK7AgAqkhcDKkf3oAI9LE9sCDWGhf4S8M70peggnxQERTMCFcIjUDF+KOUrAhhvQANkT4dwEsPvJYWz4QAAAAAElFTkSuQmCC'); background-position: bottom right 70px; background-repeat: no-repeat; background-size: auto 100%; margin: 0 auto; max-width: 980px; padding: 0 60px; position: relative; } .adb-hero-back-icon { display: inline-block; margin-right: 5px; vertical-align: middle; } .adb-hero-title { font-size: 22px; line-height: 28px; padding: 24px 24px 0px 112px; } .adb-hero-title a { color: #FFFFFF; font-weight: 500; } .adb-hero-summary { font-size: 16px; line-height: 24px; padding: 8px 24px 8px 112px; } .adb-hero-summary a { color: rgba(255, 255, 255, 0.9); } .adb-hero-description { color: #6f7172; font-size: 16px; margin: 0; max-width: 440px; padding-bottom: 32px; } .adb-hero-search { position: absolute; top: 19.35%; right: 1.94%; } .adb-hero-search .searchBox input { border: 1px solid #80868B; border-radius: 4px; color: white; } .adb-hero-search .searchBox input::placeholder { color: #F1F3F4; font-weight: 500; font-size: 16px; line-height: 24px; letter-spacing: 0.5px; padding-left: 12px; } .adb-hero-link { position: absolute; right: 1.67%; top: 64.52%; } .adb-hero-link a { font-weight: 600; font-size: 16px; line-height: 20px; color: #3DDC84; letter-spacing: 1.4px; } .adb-hero-arrow { font-size: 24px; } .adb-socials-box { display: flex; position: absolute; right: 1.67%; } .adb-socials-category, .adb-socials-divider { display: flex; align-items: center; } .adb-socials-divider { color: #DADCE0; font-size: 40px; font-weight: 100; margin: 0 20px; } .adb-socials-category img, .adb-footer-social-links img { height: 24px; } .adb-footer-social-links-img { margin-right: 20px; } .adb-hero-subscribe, .adb-hero-subscribe:visited { background-color: #90c653; color: #fff; display: inline-block; border-radius: 3px; margin: 25px 0 60px; padding: 13px 32px; text-transform: uppercase; } .adb-hero-subscribe:hover, .adb-hero-subscribe:focus { background-color: #333; } .adb-footer { margin: 32px auto 64px; } .adb-footer-more { border-bottom: 1px solid #f0f0f0; border-top: 1px solid #f0f0f0; padding: 30px 0; } .adb-footer-more a { color: #000; display: inline-block; font-size: 20px; font-weight: 300; margin-right: 16px; } .adb-footer-social { float: right; margin-top: -62px; } .adb-footer-social a { background-color: #ccc; background-position: center; background-repeat: no-repeat; background-size: auto 16px; border-radius: 50%; display: inline-block; height: 36px; margin-left: 16px; transition: background-color .1s; width: 36px; } .adb-footer-social-youtube { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAQAAAAxWje/AAABLUlEQVQ4y6WUvUoDYRBFr42gCdhK8AV8BwsVC0FQfIQUgr5DIKJoGy1CfAaDq1hIGn8K2xDSaBc1wUJJEYmbkDXssdi1Mrvfst9tZ+bAzNwZSZLEAmvsUKTCBQ80eaWLyxiAMS5dXmhyT5UyBfIsM68/McM1aXRDJgCUSKsTSWQZpAa4ZMUmNtoSRxGhbd4TAI7F1eSIxBwVfAPgUtSjAJLEEk+xgLroxAEkpikyigS0RS8eIEks8hgB6ImhGSAxxS5fE9JGwksCkCRyOP/SfPFjC0jawl5UC9ZDbBvXuB+zxjezkZ5NRnJirHxmtLIjDq2O6UBsWJ3zut1D+WZWEqepAaVg0hlqqcpr4VMNbbpKngJlqtzRoMUnfTx8fDz6fNCiwS3n4VtfIRdU/gLTrSb7DGWLcQAAAABJRU5ErkJggg'); } .adb-footer-social-youtube:hover { background-color: #f44336; } .adb-footer-social-gplus { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAUCAQAAACpWpz6AAABJUlEQVQ4EZXBTUoCYQDH4X9hTS3Cja0Kyb2X6BCJoLlr1aKwK1QHCMMzWLiqTcuWXaAWQWIlU1DQh4QJ+Wt4X95x0hGn55FCeGzSpMUXXW45ocSC/oMiHUZ1KCgZZqkzyZaSoM7QgCd8BlgXLGo6ijgv7JJRgAxV3jjDUwwMOXh0sK5ZUQRZ5hQLQw4VrA/WlBCGHJpY+0oMQw5trLxCjNtTgFiij5VSiHFHChBL9LFSCjGurgCxRBsrrxA+Pj4+Pl2sQ0VgyKGJdaAYNLAqisCQQxnrk5xGkKOHlVUEhhw8HrFuWFUEaa6wLvUHhoYo4LxSZVkBlihzh7OuaagR9YzPD0PHmo5ZakzSIKVk2OCBUe/sMKPkmKfEKS16fHPPOduklcAvR50zMVwhTI4AAAAASUVORK5CYII='); } .adb-footer-social-gplus:hover { background-color: #f44336; } .adb-footer-social-twitter { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAWCAQAAAB0gUQ+AAABI0lEQVQ4EY3BQShDAQDH4f+zTSyJuLgoLlwclYOTHCwHu9ouiLRScpUmNwduDkpCSTs4LAeJhINoOTo4ctHkskyJtf20vWbe3tvb+z7JFUGibLHPMj0qoYEWSYzRKkdMkKbsm3m6WSRFuyQeuKNNNoTJU61ABL8kssAjvbKgiTR2V+ywJol3ijJMY+gP4zhLEpDEGWUpwvhUQgwnh/hURIT/3thjjmGWcBKViSCXeBWSiWdO+MGbPpm4watPfDIRxasLVXCANwuqwCDGK/V80akKGjnmmgLutmXFOvV80CUrAiRxNys7DCY5J4OzXdVCP7c4OcIvO5oJkSCHXZ5VDFkxwilP5HB2z6Cc0EGcF6plSTAqNxgMMEWcTTZYYYYhAqrpF0uh1zgYi1hVAAAAAElFTkSuQmCC'); } .adb-footer-social-twitter:hover { background-color: #55acee; } .adb-footer-social span { display: none; } .adb-footer-related { padding-top: 8px; } .adb-footer-related a { color: #999; font-size: 12px; white-space: nowrap; } .adb-footer-related a:not(:last-child)::after { content: '|'; cursor: default; margin: 0 7px 0 10px; } .adb-footer-section { margin: 48px auto 48px 94px; max-width: 840px; } .adb-footer-title { font-weight: 500; font-size: 14px; line-height: 20px; letter-spacing: 1.5px; text-transform: uppercase; color: #3C4043; margin-bottom: 24px; } .adb-footer-columns { display: flex; justify-content: space-between; } .adb-other-blogs-title img { margin-right: 8px; vertical-align: -12px; } .adb-footer-social-label { display: flex !important; align-items: center; } .adb-footer-column a, .adb-footer-social-label, #footer-subscribe .tab h2 { color: #000; font-size: 14px; font-weight: 400; line-height: 20px; letter-spacing: 0.25px; padding: .5em 0 .85em 0; -webkit-column-break-inside: avoid; break-inside: avoid-column; display: table; margin: 0; page-break-inside: avoid; } .adb-footer-btm a { color: #000; font-size: 14px; font-weight: 400; letter-spacing: 0.25px; opacity: .7; transition: opacity .1s; -webkit-column-break-inside: avoid; break-inside: avoid-column; display: table; margin: 0; page-break-inside: avoid; } .adb-footer-column a { opacity: .7; transition: opacity .1s; } .adb-footer-column a:hover { opacity: 1; } .adb-footer-social-links { margin: 0 0 20px; } #footer-subscribe .widget { margin: 0; } #footer-subscribe .tab { display: flex; align-items: center; } #footer-subscribe .tab img { height: 16px; } .adb-label-view-more-toggle { cursor: pointer; } .adb-label-view-more-toggle::after { content: '\00e5c5'; float: right; font: 2em / .7 Material Icons; opacity: .6; } .adb-label-view-more-toggle.active::after { transform: rotate(180deg); } /** Desktop **/ @media (max-width: 900px) { .col-right { display: none; } .col-main { margin-right: 0; min-width: initial; } .cols-wrapper { min-width: initial; } .adb-hero-inner { background-image: none; } } /** ADB header breakpoint **/ @media (max-width: 768px) { .adb-header-links { display: none; } .dropdown-nav { display: none; } .adb-header { padding: 12px; text-align: center; } } @media (max-width: 1196px) { .breakpoint-hide { display:none; } .more-dropdown { height: 50px; display: flex; align-items: center; } } @media (max-width: 915px) { .adb-header { height: 0; margin: 0; padding: 0; display: none; } .adb-header-tags { display:none; } .more-dropdown { display:none; } .dropdown-nav { display: none; } .adb-hero-summary, .adb-hero-link, .adb-socials-category { display: none; } .adb-hero-title { padding-left: 152px; } .adb-hero-logo { height: 52px; top: 12px; left: 120px; } .adb-footer-section { margin: 0 24px; } .adb-footer-btm { margin: 0 24px 40px; } .adb-hero-area { min-height: 76px; } .adb-burger-box { display:inline; position: absolute; top: 17px; margin-left: 24px; font-size: 32px; color: white; } .adb-hero-title a{ margin-left: 40px; } } /** Tablet **/ @media (max-width: 712px) { .header-outer, .cols-wrapper { padding: 0 40px; } /* ADB */ .adb-hero-inner { padding: 0 40px; } .adb-hero-title { font-size: 16px; padding-left: 132px; } .adb-hero-logo { left: 100px; } .adb-hero-back, .adb-hero-back:link, .adb-hero-back:visited { margin-left: 36px; } .adb-other-blogs-list { columns: auto; } .adb-other-blogs-title { font-size: 20px; } .adb-other-blogs-title img { height: auto; vertical-align: -6px; width: 32px; } .adb-footer-more { padding-bottom: 75px; } .adb-footer-social { float: none; margin-bottom: 35px } .adb-footer-social .adb-footer-social-youtube { margin-left: 0; } } /** Small tablet **/ @media (max-width: 600px) { .header-inner .google-logo { top: 32px; margin-top: 0; } .header-inner .google-logo img { height: 56px; width: auto; /* height: auto; width: 188px; */ /* Override any optical adjustments at desktop size. */ top: 0 !important; } .header-left { left: 0; top: inherit; bottom: 24px; } .adb-footer-columns { display: block; } .adb-footer-column { margin-bottom: 48px; } .adb-footer-column a { opacity: 1; } .adb-footer-social-label { padding: 0; } .adb-footer-column .widget a { padding: 0; } .footer-newsletter { margin-top: 16px; } .subscribe-btn { width: 100%; } .adb-hero-search { display: none; } .adb-search-box { display:inline; position: absolute; top: 24px; right: 24px; font-size: 24px; color: white; } } /** Mobile **/ @media (max-width: 480px) { .header-outer, .cols-wrapper { padding: 0 16px; } .adb-hero-logo { height: 28px; top: 24px; left: 76px; } .adb-hero-back, .adb-hero-back:link, .adb-hero-back:visited { margin-left: 16px; } .cols-wrapper { margin-top: 0; overflow-x: auto; } .post-header .publishdate, .post .post-content { font-size: 16px; } .post .post-content { line-height: 28px; margin-bottom: 30px; } .post { margin-top: 30px; } .byline-author { display: block; font-size: 12px; line-height: 24px; margin-top: 6px; } #main .post .title a { font-weight: 500; color: #4c4c4c; color: rgba(0, 0, 0, .70); } #main .post .post-header { padding-bottom: 12px; } #main .post .post-header .published { margin-bottom: -8px; margin-top: 3px; } .post .read-more { display: block; margin-top: 14px; } .post .tr-caption { font-size: 12px; } #main .post .title a { font-size: 20px; line-height: 30px; } .post-content iframe { /* iframe won't keep aspect ratio when scaled down. */ max-height: 240px; } .post-content .separator img, .post-content .tr-caption-container img { margin-left: -16px; max-width: 100%; } .post-content table { table-layout: fixed; width: 100%; } /** List page tweaks. **/ .list-page .post-original { display: none; } .list-page .post-summary { display: block; } .list-page .comment-container { display: none; } .list-page .label-footer { display: none; } .list-page #main .post .post-footer { border-bottom: 1px solid #eee; margin: -16px 0 0 0; padding: 0 0 20px 0; } .list-page .post .share { display: none; } /** Detail page tweaks. **/ .detail-page .post-footer .cmt_iframe_holder { padding-top: 32px !important; } .detail-page .label-footer { margin-bottom: 0; } .detail-page #main .post .post-footer { padding-bottom: 0; } .detail-page #comments { display: none; } /* ADB */ .adb-header-console { display: none; } .adb-hero-inner { padding: 0 16px; } .adb-hero-title { color: #4c4c4c; color: rgba(0, 0, 0, .70); font-weight: 500; line-height: 30px; padding-left: 80px; } } /* Fixed-position sidebar for larger screens */ @media screen and (min-height: 700px) and (min-width: 1100px) { .col-right .section { max-height: calc(100vh - 197px); overflow: auto; } .col-right.scroll-start { left: 50%; margin-left: 241px; position: fixed; top: 96px; } .col-right.scroll-end { bottom: 485px; position: absolute; top: auto; } } [data-about-pullquote], [data-is-preview], [data-about-syndication] { display: none; } </style> <noscript> <style> .loading { visibility: visible; } </style> </noscript> <script type="text/javascript"> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://web.archive.org/web/20230213032228/https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-5831155-1', 'auto', 'blogger'); ga('blogger.send', 'pageview'); </script> <link href="https://web.archive.org/web/20230213032228cs_/https://www.blogger.com/dyn-css/authorization.css?targetBlogID=6755709643044947179&amp;zx=07a2ef05-5fc0-47e6-aa99-36154fcf157a" media="none" onload="if(media!='all')media='all'" rel="stylesheet"/><noscript><link href="https://web.archive.org/web/20230213032228cs_/https://www.blogger.com/dyn-css/authorization.css?targetBlogID=6755709643044947179&amp;zx=07a2ef05-5fc0-47e6-aa99-36154fcf157a" rel="stylesheet"/></noscript> <meta name="google-adsense-platform-account" content="ca-host-pub-1556223355139109"/> <meta name="google-adsense-platform-domain" content="blogspot.com"/> </head> <body> <!-- Google Tag Manager (noscript) --> <noscript><iframe height="0" src="https://web.archive.org/web/20230213032228if_/https://www.googletagmanager.com/ns.html?id=GTM-TB2PQP2" style="display: none; visibility: hidden" width="0"></iframe></noscript> <!-- End Google Tag Manager (noscript) --> <script type="text/javascript"> //<![CDATA[ var axel = Math.random() + ""; var a = axel * 10000000000000; document.write( '<iframe src="https://web.archive.org/web/20230213032228/https://2542116.fls.doubleclick.net/activityi;src=2542116;type=gblog;cat=googl0;ord=ord=' + a + '?" width="1" height="1" frameborder="0" style="display:none"></iframe>' ); //]]> </script> <noscript> <img alt="" height="1" src="https://web.archive.org/web/20230213032228im_/https://ad.doubleclick.net/ddm/activity/src=2542116;type=gblog;cat=googl0;ord=1" width="1"/> </noscript> <div class="adb-hero-area"> <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/"><img alt="hero android logo" class="adb-hero-logo" src="https://web.archive.org/web/20230213032228im_/https://developer.android.com/static/images/logos/android.svg"/></a> <span class="adb-burger-box"> &#9776; </span> <div class="adb-hero-title"> <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/">Android Developers Blog</a> </div> <div class="adb-hero-summary"> <a>The latest Android and Google Play news for app and game developers.</a> </div> <span class="adb-search-box"> 🔍 </span> <div class="popout-search"> <span class="popout-options" id="close-search-popout"><img height="22px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAQAAABLCVATAAAAoklEQVR4Ae2UtQEDMQDE5LHCsE26nyBQfenuS1dZM8yscGW5M8h8nJPJ9Ag8ItDFoWBBeqAKJOYMENosNiVxj7TrUUcodx0rblHtWksc4l3VXhNxzlTxLY0MiqoRVSnbPaL3M5ILUMJeZU/CCUxPNOETUXLRH7e210yY+GHj1+8f53cPUga5yj+t//13Y2SC0DnXeLBZ1CJRW3wn/PucksksATXJlBc3KdoMAAAAAElFTkSuQmCC" width="22px"/></span> <div class="section" id="header-popout-search"><div class="widget BlogSearch" data-version="1" id="BlogSearch2"> <div class="widget-content"> <div id="BlogSearch2_form"> <div class="searchBox popout-options"> <input autocomplete="off" placeholder="Search blog" title="Search blog" type="text" value=""/> </div> </div> </div> </div></div> </div> <div class="adb-hero-search"> <div class="section" id="header-search"><div class="widget BlogSearch" data-version="1" id="BlogSearch1"> <div class="widget-content"> <div id="BlogSearch1_form"> <div class="searchBox"> <input autocomplete="off" placeholder="Search blog" title="Search blog" type="text" value=""/> </div> </div> </div> </div></div> </div> <div class="adb-hero-link"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/" target="_blank">Android Developers <span class="adb-hero-arrow">&#8594;</span></a> </div> </div> <div class="dropdown-nav"> <a class="dropdown-options" href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack"> <span>Jetpack</span> </a> <a class="dropdown-options" href="https://web.archive.org/web/20230213032228/https://developer.android.com/kotlin"> <span>Kotlin</span> </a> <a class="dropdown-options" href="https://web.archive.org/web/20230213032228/https://developer.android.com/docs"> <span>Docs</span> </a> <a class="dropdown-options" href="https://web.archive.org/web/20230213032228/https://developer.android.com/news"> <span>News</span> </a> </div> <div class="popout-nav"> <span class="popout-options" id="close-popout"><img height="22px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAQAAABLCVATAAAAoklEQVR4Ae2UtQEDMQDE5LHCsE26nyBQfenuS1dZM8yscGW5M8h8nJPJ9Ag8ItDFoWBBeqAKJOYMENosNiVxj7TrUUcodx0rblHtWksc4l3VXhNxzlTxLY0MiqoRVSnbPaL3M5ILUMJeZU/CCUxPNOETUXLRH7e210yY+GHj1+8f53cPUga5yj+t//13Y2SC0DnXeLBZ1CJRW3wn/PucksksATXJlBc3KdoMAAAAAElFTkSuQmCC" width="22px"/></span> <a class="popout-options" href="https://web.archive.org/web/20230213032228/https://developer.android.com/about"> <span>Platform</span> </a> <a class="popout-options" href="https://web.archive.org/web/20230213032228/https://developer.android.com/studio"> <span>Android Studio</span> </a> <a class="popout-options" href="https://web.archive.org/web/20230213032228/https://developer.android.com/distribute"> <span>Google Play</span> </a> <a class="popout-options" href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack"> <span>Jetpack</span> </a> <a class="popout-options" href="https://web.archive.org/web/20230213032228/https://developer.android.com/kotlin"> <span>Kotlin</span> </a> <a class="popout-options" href="https://web.archive.org/web/20230213032228/https://developer.android.com/docs"> <span>Docs</span> </a> <a class="popout-options" href="https://web.archive.org/web/20230213032228/https://developer.android.com/news"> <span>News</span> </a> <div class="popout-options"> <div class="sidebar-social-links" style="display: flex"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/" title="Android Developers Site"> <img alt="Android Developers Site" class="sidebar-icon android-logo" src="https://web.archive.org/web/20230213032228im_/https://developer.android.com/static/images/logos/android.svg"/> </a> <a href="https://web.archive.org/web/20230213032228/https://www.youtube.com/user/androiddevelopers" title="Android Developers on YouTube"> <img alt="Android Developers on YouTube" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/video_youtube_grey600_24dp.png"/> </a> <a href="https://web.archive.org/web/20230213032228/https://www.linkedin.com/showcase/androiddev/" title="Android Developers on LinkedIn"> <img alt="Android Developers on LinkedIn" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://www.pagetraffic.com/blog/wp-content/uploads/2022/09/linkedin-black-white-logo.png"/> </a> <a href="https://web.archive.org/web/20230213032228/https://medium.com/androiddevelopers" title="Android Developers on Medium"> <img alt="Android Developers on Medium" class="sidebar-icon" src="data:image/png;base64,R0lGODlhMAAwAPcAAHJxcHFwb4iGhYODgre2tnNxcLi4t+jo6LCvr3BubW1ram5tbLOzsm1sa3BvbmtqaWppaG5ta2poZ2dmZoB/fmhnZ3Jxb3Jwb5CPjqqpqWtram9ubWxras7OzmloaGpoaHh3d8XExPj4+GhnZmhoZ/39/fv7+2pqaWtqaObm5r69vK2trIWEg+np6X59fIOCgYiHhoaFg93d3ba1tJKRj66trdDQz2lnZrm5uIKCge7t7WpqarW1tKampW1ra9ra2t3d3PPy8uvr67W1s8LCwZ2cm5WUk2ppafX19auqqpKRkff393RzcXRycbSzso+OjbW0s8vLyqalpODf3/f39srKymdnZ+Xl5IKCgLa2tXRzcnZ0c/7+/ru7upuamrS0s7y8u5GPjpuamNPS0nZ1dJqZmLu6ufHx8X18e+zs7O7u7fDw8Obl5dXU1JaVlLSzs52dm9DOzpOSkWtoZ2dnZoCAf+jn525sa9bW1r28vOfn556enbKysamop9nZ2Hl3dnFvbuXl5cTEw399fMHAv6Cenb6+vayrqsrKya+urdHR0fr5+W1tbHp5eNfW1uno6ODg4OLi4qqpqPr6+omIhn59fXh3dmZmZv///3FwbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMTEgNzkuMTU4MzI1LCAyMDE1LzA5LzEwLTAxOjEwOjIwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCQzNFMDRFOTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCQzNFMDRFQTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkJDM0UwNEU3OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkJDM0UwNEU4OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAAAAAAAsAAAAADAAMAAACP8AKVgaSLCgwYMIEyq0RCGTw4cQI0qcSLHiQ0sWM2rcOHCjx48QO4IcqVEkyZMTTaJc6VAlS5QuX5J0CcDBpZs4cY4AYBFAgpxAM/EMiRHiAiYYBChdqrRJg6ETN6BgwXQphgILIrp80EANpq9gv465FIFigEsCwoYNBOGB1qIPz7JQG/bFJagQAV0KQRdTGhAeArydeElOX0xVLiWYWCHAJLoyGF0qIDFmgJ+HDlu6JBgigEtS+rqYnBIuxAANLnXoa+iSg4gcLl0RfYliTIefV/TlssVK50wXLj05PKB26YqXCBwmYNzhz9V9i9s2HTE5JjYl1C5p4OEhHTKYRAj/oSv9OEXrPWzQ7XPJQiYLlxJh6hKFfPPB55UboURXxwS3D1yyBiaDtGHfdMgpl8ElSNAlxmSXYIAJEJekcKB5hCnHwyVD0BXJJRpcoggmZVzSwoWVUQeRdW9c0khfYVzyByYmQDDBASjilyEmDNwEXVh+XOIEJipcwgGOapWXYoI83hQDXSWAcGIdlzSAZFhK6iiRdT1GcMkPdD2CCSSXjKDAlWBlSRSTPX5Gw2GFXDLHmTmumV+TmcRmoVpULECCBHQmeZ+dhGWBCR+1fbYHXYTUBiiaX6l5kYoOFXCJbgtSdgIJZ6gVA2eP1jnpRAB8cIkjmOBxyQcAwCdJWFNc06JAJhI8oAddWAw6akSoXSIIWCGQBQAEEAQBFhx3BXDDJXbQpcVdGDp02SVJqLWgA8Eh8JUIC1RwwQaXeGECXVCAiFdL1J2lRF92FdDYIpjEoZhNRRzW5ALnujRBDvZWUkEmlxCBiRuuXfKFvV8hQpaWmZwAAw4IRCwxAmDAcEQAGqBRQwI+bLDDDCoYIPLII5uRxwKzElopBEDlhAJlBUxwyR0FBGBpyy0DkC+lMn10W88e/Qx0STwPnZHQRleEdNLRMk200z5DDZJAC1VttdUUBAQAOw=="/> </a> <a href="https://web.archive.org/web/20230213032228/https://twitter.com/androiddev" title="Follow Android Developers on Twitter"> <img alt="Follow Android Developers on Twitter" class="sidebar-icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABgUExURUxpcWdnZ2ZmZmNjY2dnZ2ZmZmZmZmdnZ2dnZ2ZmZmZmZmZmZmZmZmZmZmdnZ2VlZWdnZ2ZmZmdnZ2ZmZmdnZ2ZmZmdnZ2dnZ2dnZ2ZmZmZmZmZmZmdnZ2dnZ2dnZ2dnZ/SZ370AAAAfdFJOUwCRmwjzDdwc+rM+LaykVRXqZsVfhs8mc9RLvIA1i0NM81npAAABVklEQVRIx+1VyZaDIBAEBBXjEnVwQ9P//5ejGNQgKLnOm7ooUAXd1SwI/XlEfSvrMfClJxUoxBNbO8pcferIzs9gA03mdi5gXPoZVFZ+BQfQKaUAcl0HIL2eXyNDaEmHzL/Did+f+RIxXs9D6dLozDzEiR9OFYSBFgCUH/wH2KD4KqQFojkIahtfrO62WwffJT8WPtHVjPe+KmdOQRxZDYy5TALl9QmbIKLGCA0HYVmhePMDhyUmKNMRYTRSD0GnI5rrMHQegvTKQRva66KekW9F4l78vQyo8RLw6618kYI6vLF/Fd5owxsB+dz9L5Z3vilrZ6+jwsaBxDcBPQvzzN+kUJ4uiYb61kCDVd8EpOD0KX44b9/Gmkrivq7xV/yCWON3xNPU3NOfggwEZ8KRbm2Z++WuGXa8VRF+2i5eydzuBL2hCeXr/sFMyinjPCUyHyP0jyN+Abu6gf9XJNj2AAAAAElFTkSuQmCC"/> </a> </div> </div> <div class="popout-options"> <div class="sidebar-social-links" style="display: flex"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/distribute/" title="Google Play Site"> <img alt="Google Play Site" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://developer.android.com/static/images/logos/google-play.svg"/> </a> <a href="https://web.archive.org/web/20230213032228/https://www.linkedin.com/company/googleplaybiz/" title="Google Play Apps &amp; Games on LinkedIn"> <img alt="Google Play Apps &amp; Games on LinkedIn" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://www.pagetraffic.com/blog/wp-content/uploads/2022/09/linkedin-black-white-logo.png"/> </a> <a href="https://web.archive.org/web/20230213032228/https://medium.com/googleplaydev" title="Google Play Apps &amp; Games on Medium"> <img alt="Google Play Apps &amp; Games on Medium" class="sidebar-icon" src="data:image/png;base64,R0lGODlhMAAwAPcAAHJxcHFwb4iGhYODgre2tnNxcLi4t+jo6LCvr3BubW1ram5tbLOzsm1sa3BvbmtqaWppaG5ta2poZ2dmZoB/fmhnZ3Jxb3Jwb5CPjqqpqWtram9ubWxras7OzmloaGpoaHh3d8XExPj4+GhnZmhoZ/39/fv7+2pqaWtqaObm5r69vK2trIWEg+np6X59fIOCgYiHhoaFg93d3ba1tJKRj66trdDQz2lnZrm5uIKCge7t7WpqarW1tKampW1ra9ra2t3d3PPy8uvr67W1s8LCwZ2cm5WUk2ppafX19auqqpKRkff393RzcXRycbSzso+OjbW0s8vLyqalpODf3/f39srKymdnZ+Xl5IKCgLa2tXRzcnZ0c/7+/ru7upuamrS0s7y8u5GPjpuamNPS0nZ1dJqZmLu6ufHx8X18e+zs7O7u7fDw8Obl5dXU1JaVlLSzs52dm9DOzpOSkWtoZ2dnZoCAf+jn525sa9bW1r28vOfn556enbKysamop9nZ2Hl3dnFvbuXl5cTEw399fMHAv6Cenb6+vayrqsrKya+urdHR0fr5+W1tbHp5eNfW1uno6ODg4OLi4qqpqPr6+omIhn59fXh3dmZmZv///3FwbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMTEgNzkuMTU4MzI1LCAyMDE1LzA5LzEwLTAxOjEwOjIwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCQzNFMDRFOTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCQzNFMDRFQTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkJDM0UwNEU3OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkJDM0UwNEU4OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAAAAAAAsAAAAADAAMAAACP8AKVgaSLCgwYMIEyq0RCGTw4cQI0qcSLHiQ0sWM2rcOHCjx48QO4IcqVEkyZMTTaJc6VAlS5QuX5J0CcDBpZs4cY4AYBFAgpxAM/EMiRHiAiYYBChdqrRJg6ETN6BgwXQphgILIrp80EANpq9gv465FIFigEsCwoYNBOGB1qIPz7JQG/bFJagQAV0KQRdTGhAeArydeElOX0xVLiWYWCHAJLoyGF0qIDFmgJ+HDlu6JBgigEtS+rqYnBIuxAANLnXoa+iSg4gcLl0RfYliTIefV/TlssVK50wXLj05PKB26YqXCBwmYNzhz9V9i9s2HTE5JjYl1C5p4OEhHTKYRAj/oSv9OEXrPWzQ7XPJQiYLlxJh6hKFfPPB55UboURXxwS3D1yyBiaDtGHfdMgpl8ElSNAlxmSXYIAJEJekcKB5hCnHwyVD0BXJJRpcoggmZVzSwoWVUQeRdW9c0khfYVzyByYmQDDBASjilyEmDNwEXVh+XOIEJipcwgGOapWXYoI83hQDXSWAcGIdlzSAZFhK6iiRdT1GcMkPdD2CCSSXjKDAlWBlSRSTPX5Gw2GFXDLHmTmumV+TmcRmoVpULECCBHQmeZ+dhGWBCR+1fbYHXYTUBiiaX6l5kYoOFXCJbgtSdgIJZ6gVA2eP1jnpRAB8cIkjmOBxyQcAwCdJWFNc06JAJhI8oAddWAw6akSoXSIIWCGQBQAEEAQBFhx3BXDDJXbQpcVdGDp02SVJqLWgA8Eh8JUIC1RwwQaXeGECXVCAiFdL1J2lRF92FdDYIpjEoZhNRRzW5ALnujRBDvZWUkEmlxCBiRuuXfKFvV8hQpaWmZwAAw4IRCwxAmDAcEQAGqBRQwI+bLDDDCoYIPLII5uRxwKzElopBEDlhAJlBUxwyR0FBGBpyy0DkC+lMn10W88e/Qx0STwPnZHQRleEdNLRMk200z5DDZJAC1VttdUUBAQAOw=="/> </a> <a href="https://web.archive.org/web/20230213032228/https://twitter.com/GooglePlayBiz" title="Follow GooglePlaydBiz on Twitter"> <img alt="Follow GooglePlayBiz on Twitter" class="sidebar-icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABgUExURUxpcWdnZ2ZmZmNjY2dnZ2ZmZmZmZmdnZ2dnZ2ZmZmZmZmZmZmZmZmZmZmdnZ2VlZWdnZ2ZmZmdnZ2ZmZmdnZ2ZmZmdnZ2dnZ2dnZ2ZmZmZmZmZmZmdnZ2dnZ2dnZ2dnZ/SZ370AAAAfdFJOUwCRmwjzDdwc+rM+LaykVRXqZsVfhs8mc9RLvIA1i0NM81npAAABVklEQVRIx+1VyZaDIBAEBBXjEnVwQ9P//5ejGNQgKLnOm7ooUAXd1SwI/XlEfSvrMfClJxUoxBNbO8pcferIzs9gA03mdi5gXPoZVFZ+BQfQKaUAcl0HIL2eXyNDaEmHzL/Did+f+RIxXs9D6dLozDzEiR9OFYSBFgCUH/wH2KD4KqQFojkIahtfrO62WwffJT8WPtHVjPe+KmdOQRxZDYy5TALl9QmbIKLGCA0HYVmhePMDhyUmKNMRYTRSD0GnI5rrMHQegvTKQRva66KekW9F4l78vQyo8RLw6618kYI6vLF/Fd5owxsB+dz9L5Z3vilrZ6+jwsaBxDcBPQvzzN+kUJ4uiYb61kCDVd8EpOD0KX44b9/Gmkrivq7xV/yCWON3xNPU3NOfggwEZ8KRbm2Z++WuGXa8VRF+2i5eydzuBL2hCeXr/sFMyinjPCUyHyP0jyN+Abu6gf9XJNj2AAAAAElFTkSuQmCC"/> </a> </div> </div> </div> <div class="popout-overlay"></div> <div class="popout-search-overlay"></div> <div class="adb-header"> <div class="adb-header-inner"> <div class="adb-htag-box"> <span class="adb-header-tags"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/about">Platform</a> </span> <span class="adb-header-tags"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/studio">Android Studio</a> </span> <span class="adb-header-tags"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/distribute">Google Play</a> </span> <span class="adb-header-tags breakpoint-hide"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack">Jetpack</a> </span> <span class="adb-header-tags breakpoint-hide"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/kotlin">Kotlin</a> </span> <span class="adb-header-tags breakpoint-hide"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/docs">Docs</a> </span> <span class="adb-header-tags breakpoint-hide"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/news">News</a> </span> <span class="adb-header-tags more-dropdown" status="inactive"> <a>More</a> <img class="dropdown-icon" height="24px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAAR0lEQVR4Ae3OMREAIBDEQCQgBf8qkIID+P4qqnyRnUmfIUmS1NesdnU/O9VqMJUz5FTMAFPcTE7xMzHFz8QUPxNT/IwkSUoPu7I4F2kEFg8AAAAASUVORK5CYII= " width="24px"/> </span> </div> <div class="adb-socials-box"> <div class="adb-socials-category"> <div class="sidebar-social-links" style="display: flex; justify-content: center; padding: 0 4px"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/" title="Android Developers Site"> <img alt="Android Developers Site" class="sidebar-icon android-logo" src="https://web.archive.org/web/20230213032228im_/https://developer.android.com/static/images/logos/android.svg"/> </a> <a href="https://web.archive.org/web/20230213032228/https://www.youtube.com/user/androiddevelopers" title="Android Developers on YouTube"> <img alt="Android Developers on YouTube" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/video_youtube_grey600_24dp.png"/> </a> <a href="https://web.archive.org/web/20230213032228/https://www.linkedin.com/showcase/androiddev/" title="Android Developers on LinkedIn"> <img alt="Android Developers on LinkedIn" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://www.pagetraffic.com/blog/wp-content/uploads/2022/09/linkedin-black-white-logo.png"/> </a> <a href="https://web.archive.org/web/20230213032228/https://medium.com/androiddevelopers" title="Android Developers on Medium"> <img alt="Android Developers on Medium" class="sidebar-icon" src="data:image/png;base64,R0lGODlhMAAwAPcAAHJxcHFwb4iGhYODgre2tnNxcLi4t+jo6LCvr3BubW1ram5tbLOzsm1sa3BvbmtqaWppaG5ta2poZ2dmZoB/fmhnZ3Jxb3Jwb5CPjqqpqWtram9ubWxras7OzmloaGpoaHh3d8XExPj4+GhnZmhoZ/39/fv7+2pqaWtqaObm5r69vK2trIWEg+np6X59fIOCgYiHhoaFg93d3ba1tJKRj66trdDQz2lnZrm5uIKCge7t7WpqarW1tKampW1ra9ra2t3d3PPy8uvr67W1s8LCwZ2cm5WUk2ppafX19auqqpKRkff393RzcXRycbSzso+OjbW0s8vLyqalpODf3/f39srKymdnZ+Xl5IKCgLa2tXRzcnZ0c/7+/ru7upuamrS0s7y8u5GPjpuamNPS0nZ1dJqZmLu6ufHx8X18e+zs7O7u7fDw8Obl5dXU1JaVlLSzs52dm9DOzpOSkWtoZ2dnZoCAf+jn525sa9bW1r28vOfn556enbKysamop9nZ2Hl3dnFvbuXl5cTEw399fMHAv6Cenb6+vayrqsrKya+urdHR0fr5+W1tbHp5eNfW1uno6ODg4OLi4qqpqPr6+omIhn59fXh3dmZmZv///3FwbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMTEgNzkuMTU4MzI1LCAyMDE1LzA5LzEwLTAxOjEwOjIwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCQzNFMDRFOTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCQzNFMDRFQTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkJDM0UwNEU3OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkJDM0UwNEU4OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAAAAAAAsAAAAADAAMAAACP8AKVgaSLCgwYMIEyq0RCGTw4cQI0qcSLHiQ0sWM2rcOHCjx48QO4IcqVEkyZMTTaJc6VAlS5QuX5J0CcDBpZs4cY4AYBFAgpxAM/EMiRHiAiYYBChdqrRJg6ETN6BgwXQphgILIrp80EANpq9gv465FIFigEsCwoYNBOGB1qIPz7JQG/bFJagQAV0KQRdTGhAeArydeElOX0xVLiWYWCHAJLoyGF0qIDFmgJ+HDlu6JBgigEtS+rqYnBIuxAANLnXoa+iSg4gcLl0RfYliTIefV/TlssVK50wXLj05PKB26YqXCBwmYNzhz9V9i9s2HTE5JjYl1C5p4OEhHTKYRAj/oSv9OEXrPWzQ7XPJQiYLlxJh6hKFfPPB55UboURXxwS3D1yyBiaDtGHfdMgpl8ElSNAlxmSXYIAJEJekcKB5hCnHwyVD0BXJJRpcoggmZVzSwoWVUQeRdW9c0khfYVzyByYmQDDBASjilyEmDNwEXVh+XOIEJipcwgGOapWXYoI83hQDXSWAcGIdlzSAZFhK6iiRdT1GcMkPdD2CCSSXjKDAlWBlSRSTPX5Gw2GFXDLHmTmumV+TmcRmoVpULECCBHQmeZ+dhGWBCR+1fbYHXYTUBiiaX6l5kYoOFXCJbgtSdgIJZ6gVA2eP1jnpRAB8cIkjmOBxyQcAwCdJWFNc06JAJhI8oAddWAw6akSoXSIIWCGQBQAEEAQBFhx3BXDDJXbQpcVdGDp02SVJqLWgA8Eh8JUIC1RwwQaXeGECXVCAiFdL1J2lRF92FdDYIpjEoZhNRRzW5ALnujRBDvZWUkEmlxCBiRuuXfKFvV8hQpaWmZwAAw4IRCwxAmDAcEQAGqBRQwI+bLDDDCoYIPLII5uRxwKzElopBEDlhAJlBUxwyR0FBGBpyy0DkC+lMn10W88e/Qx0STwPnZHQRleEdNLRMk200z5DDZJAC1VttdUUBAQAOw=="/> </a> <a href="https://web.archive.org/web/20230213032228/https://twitter.com/androiddev" title="Follow Android Developers on Twitter"> <img alt="Follow Android Developers on Twitter" class="sidebar-icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABgUExURUxpcWdnZ2ZmZmNjY2dnZ2ZmZmZmZmdnZ2dnZ2ZmZmZmZmZmZmZmZmZmZmdnZ2VlZWdnZ2ZmZmdnZ2ZmZmdnZ2ZmZmdnZ2dnZ2dnZ2ZmZmZmZmZmZmdnZ2dnZ2dnZ2dnZ/SZ370AAAAfdFJOUwCRmwjzDdwc+rM+LaykVRXqZsVfhs8mc9RLvIA1i0NM81npAAABVklEQVRIx+1VyZaDIBAEBBXjEnVwQ9P//5ejGNQgKLnOm7ooUAXd1SwI/XlEfSvrMfClJxUoxBNbO8pcferIzs9gA03mdi5gXPoZVFZ+BQfQKaUAcl0HIL2eXyNDaEmHzL/Did+f+RIxXs9D6dLozDzEiR9OFYSBFgCUH/wH2KD4KqQFojkIahtfrO62WwffJT8WPtHVjPe+KmdOQRxZDYy5TALl9QmbIKLGCA0HYVmhePMDhyUmKNMRYTRSD0GnI5rrMHQegvTKQRva66KekW9F4l78vQyo8RLw6618kYI6vLF/Fd5owxsB+dz9L5Z3vilrZ6+jwsaBxDcBPQvzzN+kUJ4uiYb61kCDVd8EpOD0KX44b9/Gmkrivq7xV/yCWON3xNPU3NOfggwEZ8KRbm2Z++WuGXa8VRF+2i5eydzuBL2hCeXr/sFMyinjPCUyHyP0jyN+Abu6gf9XJNj2AAAAAElFTkSuQmCC"/> </a> </div> </div> <div class="adb-socials-divider">|</div> <div class="adb-socials-category"> <div class="sidebar-social-links" style="display: flex; justify-content: center; padding: 0 4px"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/distribute/" title="Google Play Site"> <img alt="Google Play Site" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://developer.android.com/static/images/logos/google-play.svg"/> </a> <a href="https://web.archive.org/web/20230213032228/https://www.linkedin.com/company/googleplaybiz/" title="Google Play Apps &amp; Games on LinkedIn"> <img alt="Google Play Apps &amp; Games on LinkedIn" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://www.pagetraffic.com/blog/wp-content/uploads/2022/09/linkedin-black-white-logo.png"/> </a> <a href="https://web.archive.org/web/20230213032228/https://medium.com/googleplaydev" title="Google Play Apps &amp; Games on Medium"> <img alt="Google Play Apps &amp; Games on Medium" class="sidebar-icon" src="data:image/png;base64,R0lGODlhMAAwAPcAAHJxcHFwb4iGhYODgre2tnNxcLi4t+jo6LCvr3BubW1ram5tbLOzsm1sa3BvbmtqaWppaG5ta2poZ2dmZoB/fmhnZ3Jxb3Jwb5CPjqqpqWtram9ubWxras7OzmloaGpoaHh3d8XExPj4+GhnZmhoZ/39/fv7+2pqaWtqaObm5r69vK2trIWEg+np6X59fIOCgYiHhoaFg93d3ba1tJKRj66trdDQz2lnZrm5uIKCge7t7WpqarW1tKampW1ra9ra2t3d3PPy8uvr67W1s8LCwZ2cm5WUk2ppafX19auqqpKRkff393RzcXRycbSzso+OjbW0s8vLyqalpODf3/f39srKymdnZ+Xl5IKCgLa2tXRzcnZ0c/7+/ru7upuamrS0s7y8u5GPjpuamNPS0nZ1dJqZmLu6ufHx8X18e+zs7O7u7fDw8Obl5dXU1JaVlLSzs52dm9DOzpOSkWtoZ2dnZoCAf+jn525sa9bW1r28vOfn556enbKysamop9nZ2Hl3dnFvbuXl5cTEw399fMHAv6Cenb6+vayrqsrKya+urdHR0fr5+W1tbHp5eNfW1uno6ODg4OLi4qqpqPr6+omIhn59fXh3dmZmZv///3FwbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMTEgNzkuMTU4MzI1LCAyMDE1LzA5LzEwLTAxOjEwOjIwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCQzNFMDRFOTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCQzNFMDRFQTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkJDM0UwNEU3OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkJDM0UwNEU4OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAAAAAAAsAAAAADAAMAAACP8AKVgaSLCgwYMIEyq0RCGTw4cQI0qcSLHiQ0sWM2rcOHCjx48QO4IcqVEkyZMTTaJc6VAlS5QuX5J0CcDBpZs4cY4AYBFAgpxAM/EMiRHiAiYYBChdqrRJg6ETN6BgwXQphgILIrp80EANpq9gv465FIFigEsCwoYNBOGB1qIPz7JQG/bFJagQAV0KQRdTGhAeArydeElOX0xVLiWYWCHAJLoyGF0qIDFmgJ+HDlu6JBgigEtS+rqYnBIuxAANLnXoa+iSg4gcLl0RfYliTIefV/TlssVK50wXLj05PKB26YqXCBwmYNzhz9V9i9s2HTE5JjYl1C5p4OEhHTKYRAj/oSv9OEXrPWzQ7XPJQiYLlxJh6hKFfPPB55UboURXxwS3D1yyBiaDtGHfdMgpl8ElSNAlxmSXYIAJEJekcKB5hCnHwyVD0BXJJRpcoggmZVzSwoWVUQeRdW9c0khfYVzyByYmQDDBASjilyEmDNwEXVh+XOIEJipcwgGOapWXYoI83hQDXSWAcGIdlzSAZFhK6iiRdT1GcMkPdD2CCSSXjKDAlWBlSRSTPX5Gw2GFXDLHmTmumV+TmcRmoVpULECCBHQmeZ+dhGWBCR+1fbYHXYTUBiiaX6l5kYoOFXCJbgtSdgIJZ6gVA2eP1jnpRAB8cIkjmOBxyQcAwCdJWFNc06JAJhI8oAddWAw6akSoXSIIWCGQBQAEEAQBFhx3BXDDJXbQpcVdGDp02SVJqLWgA8Eh8JUIC1RwwQaXeGECXVCAiFdL1J2lRF92FdDYIpjEoZhNRRzW5ALnujRBDvZWUkEmlxCBiRuuXfKFvV8hQpaWmZwAAw4IRCwxAmDAcEQAGqBRQwI+bLDDDCoYIPLII5uRxwKzElopBEDlhAJlBUxwyR0FBGBpyy0DkC+lMn10W88e/Qx0STwPnZHQRleEdNLRMk200z5DDZJAC1VttdUUBAQAOw=="/> </a> <a href="https://web.archive.org/web/20230213032228/https://twitter.com/GooglePlayBiz" title="Follow GooglePlaydBiz on Twitter"> <img alt="Follow GooglePlayBiz on Twitter" class="sidebar-icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABgUExURUxpcWdnZ2ZmZmNjY2dnZ2ZmZmZmZmdnZ2dnZ2ZmZmZmZmZmZmZmZmZmZmdnZ2VlZWdnZ2ZmZmdnZ2ZmZmdnZ2ZmZmdnZ2dnZ2dnZ2ZmZmZmZmZmZmdnZ2dnZ2dnZ2dnZ/SZ370AAAAfdFJOUwCRmwjzDdwc+rM+LaykVRXqZsVfhs8mc9RLvIA1i0NM81npAAABVklEQVRIx+1VyZaDIBAEBBXjEnVwQ9P//5ejGNQgKLnOm7ooUAXd1SwI/XlEfSvrMfClJxUoxBNbO8pcferIzs9gA03mdi5gXPoZVFZ+BQfQKaUAcl0HIL2eXyNDaEmHzL/Did+f+RIxXs9D6dLozDzEiR9OFYSBFgCUH/wH2KD4KqQFojkIahtfrO62WwffJT8WPtHVjPe+KmdOQRxZDYy5TALl9QmbIKLGCA0HYVmhePMDhyUmKNMRYTRSD0GnI5rrMHQegvTKQRva66KekW9F4l78vQyo8RLw6618kYI6vLF/Fd5owxsB+dz9L5Z3vilrZ6+jwsaBxDcBPQvzzN+kUJ4uiYb61kCDVd8EpOD0KX44b9/Gmkrivq7xV/yCWON3xNPU3NOfggwEZ8KRbm2Z++WuGXa8VRF+2i5eydzuBL2hCeXr/sFMyinjPCUyHyP0jyN+Abu6gf9XJNj2AAAAAElFTkSuQmCC"/> </a> </div> </div> </div> </div> </div> <!-- all content wrapper start --> <div class="cols-wrapper loading"> <div class="col-main-wrapper"> <div class="col-main"> <div class="section" id="main"><div class="widget Blog" data-version="1" id="Blog1"> <div class="post" data-id="4033776724941094921" itemscope="" itemtype="http://schema.org/BlogPosting"> <h2 class="title" itemprop="name"> <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/12/compose-for-wear-os-11-stable.html" itemprop="url" title="Compose for Wear OS 1.1 is now stable: check out new features!"> Compose for Wear OS 1.1 is now stable: check out new features! </a> </h2> <div class="post-header"> <div class="published"> <span class="publishdate" itemprop="datePublished"> 07 December 2022 </span> </div> </div> <div class="post-body"> <div class="post-content" itemprop="articleBody"> <script type="text/template"> <meta content="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEn0M6bhucNqVTl_bkmKz-iRcbja5UAnc_l7npwajB91k3CsM6sXQr4xrE8SHA64d0MY9ybssfMU1eraSqxwrtKE1km-s6FxTt8jjckjN1kXgpulmBfxmPecXbLLkfmiHwG4fgPFG-GefKmTndXkshDz5_sC-S3IHz-sKad-Z_3peH-7nbILWQfpOD/s1600/image5.png" name="twitter:image"></meta> <img src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEn0M6bhucNqVTl_bkmKz-iRcbja5UAnc_l7npwajB91k3CsM6sXQr4xrE8SHA64d0MY9ybssfMU1eraSqxwrtKE1km-s6FxTt8jjckjN1kXgpulmBfxmPecXbLLkfmiHwG4fgPFG-GefKmTndXkshDz5_sC-S3IHz-sKad-Z_3peH-7nbILWQfpOD/s1600/image5.png" style="display: none;" /> <p><em>Posted by <a href="https://web.archive.org/web/20230213032228/https://twitter.com/kseniiaS" target="_blank">Kseniia Shumelchyk</a>, Android Developer Relations Engineer</em></p><p> <a href="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnaOlNifFoeziIpIYC9xaIUjKDPaW0Nz2r8joGDzZ-n9SF_2asQ3gz4HNh0q2uq1zGv8JTEyYMCbqg1y5DC9s5yp956B1OqI4-KPkN_v2XQ6vXnbC_KDS32wmj1BdiIb8vaZTdgSTMkgo0SkgtQUe0-HB4FN8ng7ZXOlWJrAMDWDGt67L0mm2F6NQp/s1600/image4.png"><img border="0" data-original-height="800" data-original-width="1058" height="202" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnaOlNifFoeziIpIYC9xaIUjKDPaW0Nz2r8joGDzZ-n9SF_2asQ3gz4HNh0q2uq1zGv8JTEyYMCbqg1y5DC9s5yp956B1OqI4-KPkN_v2XQ6vXnbC_KDS32wmj1BdiIb8vaZTdgSTMkgo0SkgtQUe0-HB4FN8ng7ZXOlWJrAMDWDGt67L0mm2F6NQp/w677-h202/image4.png" width="677" /></a> </p><p> Today we&#8217;re releasing version 1.1 of <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/wear-compose" target="_blank">Compose for Wear OS</a>, our modern declarative UI toolkit to help developers build beautiful, responsive apps for Wear OS. </p> <p> Since <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/07/compose-for-wear-os-10-stable.html" target="_blank">the first stable release</a> earlier this year, we have seen many developers taking advantage of the powerful tools and intuitive APIs to make building their app simpler and more efficient. Todoist and Outdooractive are some of the developers that rebuilt their Wear apps with Compose and accelerated the delivery of a new, functional user experience. </p> <p> <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/todoist-adopted-compose-for-wear-os-and.html" target="_blank">Todoist increased its growth rate by 50%</a> since rebuilding their app for Wear 3 and <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/09/outdooractive-boosts-user-experience-on-wearable-devices-using-compose-for-wear-os.html" target="_blank">Outdooractive reduced development time by 30%</a> and saw a significant boost in developer productivity and better design/developer collaboration: </p> <p> <em>&#8220;Compose makes the UI code more intuitive to write and read, allowing us to prototype faster in the design phase and also collaborate better on the code. What would have taken us days now takes us hours.&#8221;</em> </p> <p> The Compose for Wear OS 1.1 release contains new features and brings improvements to existing components, focusing on UX and accessibility. We&#8217;ve already updated our <a href="https://web.archive.org/web/20230213032228/https://github.com/android/wear-os-samples" target="_blank">samples</a>, <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/codelabs/compose-for-wear-os" target="_blank">codelab</a>, and <a href="https://web.archive.org/web/20230213032228/https://github.com/google/horologist" target="_blank">Horologist libraries</a> to work with Compose for Wear OS 1.1. </p><div style="text-align: left;"><br /></div> <h2 style="text-align: left;">New features and APIs</h2> <p> The Compose for Wear OS 1.1 release includes the following new functionality (baseline profiles already added for new components): </p> <h3 style="text-align: left;">Outlined style for Chips and Buttons</h3> <div style="text-align: left;"> To give you additional ability to customize the user interface, we added outlined styles for Chips and Buttons. New <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#OutlinedChip(kotlin.Function1,kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Function1,kotlin.Function1,androidx.wear.compose.material.ChipColors,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape,androidx.wear.compose.material.ChipBorder)" target="_blank">OutlinedChip</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#OutlinedButton(kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.wear.compose.material.ButtonColors,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.ui.graphics.Shape,androidx.wear.compose.material.ButtonBorder,kotlin.Function1)" target="_blank">OutlinedButton</a> composables provide a transparent component with a thin border that can be used for medium-emphasis actions. Also available for compact versions: <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#OutlinedCompactChip(kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Function1,kotlin.Function1,androidx.wear.compose.material.ChipColors,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape,androidx.wear.compose.material.ChipBorder)" target="_blank">OutlinedCompactChip</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#OutlinedCompactButton(kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.wear.compose.material.ButtonColors,androidx.compose.ui.unit.Dp,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.ui.graphics.Shape,androidx.wear.compose.material.ButtonBorder,kotlin.Function1)" target="_blank">OutlinedCompactButton</a>.</div> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Demonstration of OutlinedChip and OutlinedButton composables on a round watch face" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6eaxO7_ZUqDyRebR0dvlBZYV91rNeWjHRNT4SIdI7MF24lz_c_32ny4C9pkhSDNujeCMuIiKcPwkh1gt6CTZuxWLZCNkMtMXom5FU3GMJ-UUygwNZxNp58TdtbWRyAXBPPU1SLvgWcP8SEHGiqRlsP5h_iZoyK2lJSaJLjbws6IbLRPpJ28ohq63k/s551/Compose%20for%20Wear%20OS%201.1%20.png" style="width: 100%;" td="" /></center></td></tr> <tr><td class="tr-caption" style="text-align: center;"><i>OutlinedChip and OutlinedButton composables</i></td></tr></tbody></table></div> <h3 style="text-align: left;">Modifying Chip and Button shapes</h3> <div style="text-align: left;"> Starting from version 1.1, you can also modify shapes for <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#Chip(kotlin.Function0,androidx.wear.compose.material.ChipColors,androidx.wear.compose.material.ChipBorder,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.ui.semantics.Role,kotlin.Function1)" target="_blank">Chip</a>/<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#ToggleChip(kotlin.Boolean,kotlin.Function1,kotlin.Function1,kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Function1,kotlin.Function1,androidx.wear.compose.material.ToggleChipColors,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape)" target="_blank">ToggleChip</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#Button(kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.wear.compose.material.ButtonColors,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.ui.graphics.Shape,androidx.wear.compose.material.ButtonBorder,kotlin.Function1)" target="_blank">Button</a>/<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#ToggleButton(kotlin.Boolean,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.wear.compose.material.ToggleButtonColors,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.ui.graphics.Shape,kotlin.Function1)" target="_blank">ToggleButton</a> components using new functions overloads. <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Demonstration of Different Chip and Button shapes on a round watch face" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZBGfjpO7YBW7rNaZueNmS__FJ9V2FBB7uUTDB0oIYtnxN2lLJHiK25VL9mhdlv9U04aHh9Zds4cR_eaFF_ZN0N1SZujH52UhrMwKpfySR2hbpIMo6o1on3HgQz-hb0rGkPOBz-pj_IKHwJWKMVIX3oQHniKZJFsY-TJd1SdA0ShabnTLNZp-ogu_-/s572/Compose%20for%20Wear%20OS%201.1%20-%202%20.png" style="width: 100%;" td="" /></center></td></tr> <tr><td class="tr-caption" style="text-align: center;"><i>Different Chip and Button shapes</i></td></tr></tbody></table></div> <h3 style="text-align: left;">Placeholder API</h3> <p> A new experimental API has been added to implement placeholder support. This can be used to achieve three distinct visual effects separately or all together: </p> <ul> <li>A placeholder background brush effect used in containers such as Chip and Cards to draw over the normal background when waiting for content to load. </li><li>A <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#(androidx.compose.ui.Modifier).placeholder(androidx.wear.compose.material.PlaceholderState,androidx.compose.ui.graphics.Shape,androidx.compose.ui.graphics.Color)" target="_blank">Modifier.placeholder()</a> to draw a stadium shaped placeholder widget over the top of content that is being loaded. </li><li>A <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#(androidx.compose.ui.Modifier).placeholderShimmer(androidx.wear.compose.material.PlaceholderState,androidx.compose.ui.graphics.Shape,androidx.compose.ui.graphics.Color)" target="_blank">Modifier.placeholderShimmer()</a> for gradient/shimmer effect that is drawn over the top of the other effects to indicate to users that the current state is waiting for data to load. </li> </ul> <div style="text-align: left;"> These effects are designed to be coordinated and shimmer and wipe-off in an orchestrated fashion. </div> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Moving demonstration of Placeholder API usage examples on a round watch face" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcsrF8Vw0tliKNe8M4peOjdQ92_8fhe0RCvNlWcTxPYaWdxd6HeWLIp-Th92aSfQOlOBnkMetql0zcHYdENiWXlE6nX-G-LKqk9O98cCxLDaC1-zcrJB6adGWtBDl-WbwloNL6sxkyQB2HzV8OSQNCEQWS8_XAMxTqyTlAFPiAoUtXg_PYV2JwxAf_/s1600/image6.gif" style="width: 65%;" td="" /></center></td></tr> <tr><td class="tr-caption" style="text-align: center;"><i>Placeholder API usage examples</i></td></tr></tbody></table></div> <p> Check out the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#(androidx.compose.ui.Modifier).placeholder(androidx.wear.compose.material.PlaceholderState,androidx.compose.ui.graphics.Shape,androidx.compose.ui.graphics.Color)" target="_blank">reference docs</a> and <a href="https://web.archive.org/web/20230213032228/https://github.com/google/horologist/pull/709" target="_blank">sample in Horologist</a> to see how to apply the placeholder to common use cases, such as a Chip with icon and a label that puts placeholder over individual content slots and draws a placeholder shimmer on top while waiting for data to load. </p> <h3 style="text-align: left;">Modifier.scrollAway</h3> <p> Horologist&#8217;s <a href="https://web.archive.org/web/20230213032228/https://google.github.io/horologist/compose-layout/#fade-away-modifier" target="_blank">fadeAway modifier</a> has been graduated to <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#(androidx.compose.ui.Modifier).scrollAway(androidx.compose.foundation.ScrollState,androidx.compose.ui.unit.Dp)" target="_blank">scrollAway modifier</a> in version 1.1. Modifier.scrollAway scrolls an item vertically in and out of view, based on the scroll state, and already has overloads to work with Column, LazyColumn and ScalingLazyColumn. </p> <div style="text-align: left;"> Use this modifier to make <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/training/wearables/compose/time-text#anatomy" target="_blank">TimeText fade out</a> of the view as the user starts to scroll a list of items upwards. </div> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Moving demonstration of ScrollAway modifier usage with TimeText on a round watch face" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM1oqqGJxjYtYdRaL1R_xCtVSSebLxM_mLA0C2XGpRYNuAljwBDrMOjjKfnviuGPwXj9B1iuXBsm5nJtBZPXB0Aq_os1fOCXx7_5p4oodin2IGvCX-8X3195cAy-hyV1Q5HGo4iC_gZyf8_cT9rKoD54UdxyjyN8bqwr9Nf4wkaK6A21oZ8MRF-lhz/s1600/image7.gif" style="width: 65%;" td="" /></center></td></tr> <tr><td class="tr-caption" style="text-align: center;"><i>ScrollAway modifier usage with TimeText</i></td></tr></tbody></table></div> <h3 style="text-align: left;">Additional parameters in CurvedTextStyle</h3> <p> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/foundation/CurvedTextStyle#CurvedTextStyle(androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.unit.TextUnit,androidx.compose.ui.text.font.FontFamily,androidx.compose.ui.text.font.FontWeight,androidx.compose.ui.text.font.FontStyle,androidx.compose.ui.text.font.FontSynthesis)" target="_blank">CurvedTextStyle</a> now supports additional parameters (fontFamily, fontWeight, fontStyle, fontSynthesis) to specify font details when creating a curved text style. Extended curved text style can be used on both <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#(androidx.wear.compose.foundation.CurvedScope).curvedText(kotlin.String,androidx.wear.compose.foundation.CurvedModifier,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.unit.TextUnit,androidx.compose.ui.text.font.FontFamily,androidx.compose.ui.text.font.FontWeight,androidx.compose.ui.text.font.FontStyle,androidx.compose.ui.text.font.FontSynthesis,androidx.wear.compose.foundation.CurvedTextStyle,androidx.wear.compose.foundation.CurvedDirection.Angular,androidx.compose.ui.text.style.TextOverflow)" target="_blank">curvedText</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/foundation/package-summary#(androidx.wear.compose.foundation.CurvedScope).basicCurvedText(kotlin.String,androidx.wear.compose.foundation.CurvedModifier,androidx.wear.compose.foundation.CurvedDirection.Angular,androidx.compose.ui.text.style.TextOverflow,kotlin.Function0)" target="_blank">basicCurvedText</a>. </p> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Demonstration of applying different font to curved text on a round watch face" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9bIl808TYNGjuJm10iFLO61wm2Q_s9nK6-X6OQ81pqpIoNyC2Il5HBt2X65VrwnLh_G1CCMJ3Od7BtjzrKu2wuc-YZ5A_K4LtLgMvFX18U6rWfEcBMRxsYrucZ_6H_ovyLhimb23Gta5aqSRv12q9vaV7nEL9FCnUh-V5UzX63eKPVqF_SSaMVMUn/s1600/image8.png" style="width: 65%;" td="" /></center></td></tr> <tr><td class="tr-caption" style="text-align: center;"><i>Applying different font to curved text</i></td></tr></tbody></table></div> <h3 style="text-align: left;"><span style="font-weight: bold;">UX and accessibility improvements</span></h3> <p> The 1.1 release also focuses on bringing a refined user experience, improvements for <a href="https://web.archive.org/web/20230213032228/https://support.google.com/wearos/answer/7313945?hl=en-GB" target="_blank">TalkBack</a> support and overall better <a href="https://web.archive.org/web/20230213032228/https://m2.material.io/design/usability/accessibility.html#understanding-accessibility" target="_blank">accessibility</a>: </p> <ul> <li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#ToggleChip(kotlin.Boolean,kotlin.Function1,kotlin.Function1,kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Function1,kotlin.Function1,androidx.wear.compose.material.ToggleChipColors,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape)" target="_blank">ToggleChip</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#SplitToggleChip(kotlin.Boolean,kotlin.Function1,kotlin.Function1,kotlin.Function0,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1,androidx.wear.compose.material.SplitToggleChipColors,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape)" target="_blank">SplitToggleChip</a> support usage of animated toggle controls (Checkbox, Switch and RadioButton) that can be used instead of the static icons provided by ToggleChipDefaults. </li><li>Default gradient colors for Chip/ToggleChip and Cards were adjusted to match the latest UX specification. </li><li>Updated a number of the default colors in the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#MaterialTheme(androidx.wear.compose.material.Colors,androidx.wear.compose.material.Typography,androidx.wear.compose.material.Shapes,kotlin.Function0)" target="_blank">MaterialTheme</a> to improve accessibility as the original colors did not have sufficient contrast. </li><li>Accessibility improvements to <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#Picker(androidx.wear.compose.material.PickerState,kotlin.String,androidx.compose.ui.Modifier,kotlin.Boolean,kotlin.Function1,kotlin.Function0,androidx.wear.compose.material.ScalingParams,androidx.compose.ui.unit.Dp,kotlin.Float,androidx.compose.ui.graphics.Color,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Function2)" target="_blank">Picker</a> so that multi-picker screens are navigable with screen readers and the content description is accessible. </li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#InlineSlider(kotlin.Float,kotlin.Function1,kotlin.Int,kotlin.Function0,kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Boolean,kotlin.ranges.ClosedFloatingPointRange,kotlin.Boolean,androidx.wear.compose.material.InlineSliderColors)" target="_blank">InlineSlider</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#Stepper(kotlin.Float,kotlin.Function1,kotlin.Int,kotlin.Function0,kotlin.Function0,androidx.compose.ui.Modifier,kotlin.ranges.ClosedFloatingPointRange,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,kotlin.Function1)" target="_blank">Stepper</a> now have button roles, so that TalkBack can recognize them as buttons. </li><li>The PositionIndicator in <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#Scaffold(androidx.compose.ui.Modifier,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0)" target="_blank">Scaffold</a> is now positioned and sized so that it only takes the space needed. This is useful when semantic information is added to it, so TalkBack gets the correct bounds of the PositionIndicator on screen.</li></ul><br /><ul> </ul> <h2 style="text-align: left;">It&#8217;s time &#8986; to bring your app to the wrist!</h2> <h3 style="text-align: left;">Get started</h3> <p> To begin developing with Compose for Wear OS, get started with hands-on experience trying our <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/codelabs/compose-for-wear-os" target="_blank">codelab</a>, and make sure to check out the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/training/wearables/compose" target="_blank">documentation</a> and <a href="https://web.archive.org/web/20230213032228/https://github.com/android/wear-os-samples" target="_blank">samples</a>. Visit <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/wear-compose#1.1.0-rc01" target="_blank">Compose for Wear OS release notes</a> for full list of changes available in version 1.1. </p> <p> Note that using version 1.1 of Compose for Wear OS requires using the version 1.3 of <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/compose" target="_blank">androidx.compose</a> libraries and therefore Kotlin 1.7.10. Check out the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/compose-kotlin" target="_blank">Compose to Kotlin Compatibility Map</a> for more information. </p> <h3 style="text-align: left;">Provide feedback</h3> <p> Compose for Wear OS continues to evolve with the features you&#8217;ve been asking for. Please do continue providing us feedback on the <a href="https://web.archive.org/web/20230213032228/https://b.corp.google.com/issues/new?component=1077552&amp;template=1598429" target="_blank">issue tracker</a> and join <a href="https://web.archive.org/web/20230213032228/https://slack-chats.kotlinlang.org/c/compose-wear" target="_blank">Kotlin Slack</a> <strong>#compose-wear</strong> channel to connect with the Google team and dev community. </p> <p> We&#8217;re excited to see a growing number of apps using Compose for Wear OS in production, and we&#8217;re grateful for all issues and requests that help us to make the toolkit better! </p> <h3 style="text-align: left;">Start building for Wear OS now</h3> <p> Discover even more with <a href="https://web.archive.org/web/20230213032228/https://www.youtube.com/playlist?list=PLWz5rJ2EKKc9jBnpl83LH6oZc7nFIVSRq" target="_blank">technical sessions</a> from the <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/form-factors-at-ads-22.html" target="_blank">Android Dev Summit</a> providing guidance on app architecture, testing, handling rotary input, and verticalized sessions for media and fitness. </p> </div> </script> <noscript> <meta content="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEn0M6bhucNqVTl_bkmKz-iRcbja5UAnc_l7npwajB91k3CsM6sXQr4xrE8SHA64d0MY9ybssfMU1eraSqxwrtKE1km-s6FxTt8jjckjN1kXgpulmBfxmPecXbLLkfmiHwG4fgPFG-GefKmTndXkshDz5_sC-S3IHz-sKad-Z_3peH-7nbILWQfpOD/s1600/image5.png" name="twitter:image"></meta> <img src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEn0M6bhucNqVTl_bkmKz-iRcbja5UAnc_l7npwajB91k3CsM6sXQr4xrE8SHA64d0MY9ybssfMU1eraSqxwrtKE1km-s6FxTt8jjckjN1kXgpulmBfxmPecXbLLkfmiHwG4fgPFG-GefKmTndXkshDz5_sC-S3IHz-sKad-Z_3peH-7nbILWQfpOD/s1600/image5.png" style="display: none;"/> <p><em>Posted by <a href="https://web.archive.org/web/20230213032228/https://twitter.com/kseniiaS" target="_blank">Kseniia Shumelchyk</a>, Android Developer Relations Engineer</em></p><p> <a href="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnaOlNifFoeziIpIYC9xaIUjKDPaW0Nz2r8joGDzZ-n9SF_2asQ3gz4HNh0q2uq1zGv8JTEyYMCbqg1y5DC9s5yp956B1OqI4-KPkN_v2XQ6vXnbC_KDS32wmj1BdiIb8vaZTdgSTMkgo0SkgtQUe0-HB4FN8ng7ZXOlWJrAMDWDGt67L0mm2F6NQp/s1600/image4.png"><img border="0" data-original-height="800" data-original-width="1058" height="202" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnaOlNifFoeziIpIYC9xaIUjKDPaW0Nz2r8joGDzZ-n9SF_2asQ3gz4HNh0q2uq1zGv8JTEyYMCbqg1y5DC9s5yp956B1OqI4-KPkN_v2XQ6vXnbC_KDS32wmj1BdiIb8vaZTdgSTMkgo0SkgtQUe0-HB4FN8ng7ZXOlWJrAMDWDGt67L0mm2F6NQp/w677-h202/image4.png" width="677"/></a> </p><p> Today we&#8217;re releasing version 1.1 of <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/wear-compose" target="_blank">Compose for Wear OS</a>, our modern declarative UI toolkit to help developers build beautiful, responsive apps for Wear OS. </p> <p> Since <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/07/compose-for-wear-os-10-stable.html" target="_blank">the first stable release</a> earlier this year, we have seen many developers taking advantage of the powerful tools and intuitive APIs to make building their app simpler and more efficient. Todoist and Outdooractive are some of the developers that rebuilt their Wear apps with Compose and accelerated the delivery of a new, functional user experience. </p> <p> <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/todoist-adopted-compose-for-wear-os-and.html" target="_blank">Todoist increased its growth rate by 50%</a> since rebuilding their app for Wear 3 and <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/09/outdooractive-boosts-user-experience-on-wearable-devices-using-compose-for-wear-os.html" target="_blank">Outdooractive reduced development time by 30%</a> and saw a significant boost in developer productivity and better design/developer collaboration: </p> <p> <em>&#8220;Compose makes the UI code more intuitive to write and read, allowing us to prototype faster in the design phase and also collaborate better on the code. What would have taken us days now takes us hours.&#8221;</em> </p> <p> The Compose for Wear OS 1.1 release contains new features and brings improvements to existing components, focusing on UX and accessibility. We&#8217;ve already updated our <a href="https://web.archive.org/web/20230213032228/https://github.com/android/wear-os-samples" target="_blank">samples</a>, <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/codelabs/compose-for-wear-os" target="_blank">codelab</a>, and <a href="https://web.archive.org/web/20230213032228/https://github.com/google/horologist" target="_blank">Horologist libraries</a> to work with Compose for Wear OS 1.1. </p><div style="text-align: left;"><br/></div> <h2 style="text-align: left;">New features and APIs</h2> <p> The Compose for Wear OS 1.1 release includes the following new functionality (baseline profiles already added for new components): </p> <h3 style="text-align: left;">Outlined style for Chips and Buttons</h3> <div style="text-align: left;"> To give you additional ability to customize the user interface, we added outlined styles for Chips and Buttons. New <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#OutlinedChip(kotlin.Function1,kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Function1,kotlin.Function1,androidx.wear.compose.material.ChipColors,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape,androidx.wear.compose.material.ChipBorder)" target="_blank">OutlinedChip</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#OutlinedButton(kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.wear.compose.material.ButtonColors,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.ui.graphics.Shape,androidx.wear.compose.material.ButtonBorder,kotlin.Function1)" target="_blank">OutlinedButton</a> composables provide a transparent component with a thin border that can be used for medium-emphasis actions. Also available for compact versions: <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#OutlinedCompactChip(kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Function1,kotlin.Function1,androidx.wear.compose.material.ChipColors,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape,androidx.wear.compose.material.ChipBorder)" target="_blank">OutlinedCompactChip</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#OutlinedCompactButton(kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.wear.compose.material.ButtonColors,androidx.compose.ui.unit.Dp,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.ui.graphics.Shape,androidx.wear.compose.material.ButtonBorder,kotlin.Function1)" target="_blank">OutlinedCompactButton</a>.</div> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Demonstration of OutlinedChip and OutlinedButton composables on a round watch face" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6eaxO7_ZUqDyRebR0dvlBZYV91rNeWjHRNT4SIdI7MF24lz_c_32ny4C9pkhSDNujeCMuIiKcPwkh1gt6CTZuxWLZCNkMtMXom5FU3GMJ-UUygwNZxNp58TdtbWRyAXBPPU1SLvgWcP8SEHGiqRlsP5h_iZoyK2lJSaJLjbws6IbLRPpJ28ohq63k/s551/Compose%20for%20Wear%20OS%201.1%20.png" style="width: 100%;" td=""/></center></td></tr> <tr><td class="tr-caption" style="text-align: center;"><i>OutlinedChip and OutlinedButton composables</i></td></tr></tbody></table></div> <h3 style="text-align: left;">Modifying Chip and Button shapes</h3> <div style="text-align: left;"> Starting from version 1.1, you can also modify shapes for <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#Chip(kotlin.Function0,androidx.wear.compose.material.ChipColors,androidx.wear.compose.material.ChipBorder,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.ui.semantics.Role,kotlin.Function1)" target="_blank">Chip</a>/<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#ToggleChip(kotlin.Boolean,kotlin.Function1,kotlin.Function1,kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Function1,kotlin.Function1,androidx.wear.compose.material.ToggleChipColors,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape)" target="_blank">ToggleChip</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#Button(kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.wear.compose.material.ButtonColors,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.ui.graphics.Shape,androidx.wear.compose.material.ButtonBorder,kotlin.Function1)" target="_blank">Button</a>/<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#ToggleButton(kotlin.Boolean,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.wear.compose.material.ToggleButtonColors,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.ui.graphics.Shape,kotlin.Function1)" target="_blank">ToggleButton</a> components using new functions overloads. <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Demonstration of Different Chip and Button shapes on a round watch face" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZBGfjpO7YBW7rNaZueNmS__FJ9V2FBB7uUTDB0oIYtnxN2lLJHiK25VL9mhdlv9U04aHh9Zds4cR_eaFF_ZN0N1SZujH52UhrMwKpfySR2hbpIMo6o1on3HgQz-hb0rGkPOBz-pj_IKHwJWKMVIX3oQHniKZJFsY-TJd1SdA0ShabnTLNZp-ogu_-/s572/Compose%20for%20Wear%20OS%201.1%20-%202%20.png" style="width: 100%;" td=""/></center></td></tr> <tr><td class="tr-caption" style="text-align: center;"><i>Different Chip and Button shapes</i></td></tr></tbody></table></div> <h3 style="text-align: left;">Placeholder API</h3> <p> A new experimental API has been added to implement placeholder support. This can be used to achieve three distinct visual effects separately or all together: </p> <ul> <li>A placeholder background brush effect used in containers such as Chip and Cards to draw over the normal background when waiting for content to load. </li><li>A <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#(androidx.compose.ui.Modifier).placeholder(androidx.wear.compose.material.PlaceholderState,androidx.compose.ui.graphics.Shape,androidx.compose.ui.graphics.Color)" target="_blank">Modifier.placeholder()</a> to draw a stadium shaped placeholder widget over the top of content that is being loaded. </li><li>A <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#(androidx.compose.ui.Modifier).placeholderShimmer(androidx.wear.compose.material.PlaceholderState,androidx.compose.ui.graphics.Shape,androidx.compose.ui.graphics.Color)" target="_blank">Modifier.placeholderShimmer()</a> for gradient/shimmer effect that is drawn over the top of the other effects to indicate to users that the current state is waiting for data to load. </li> </ul> <div style="text-align: left;"> These effects are designed to be coordinated and shimmer and wipe-off in an orchestrated fashion. </div> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Moving demonstration of Placeholder API usage examples on a round watch face" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcsrF8Vw0tliKNe8M4peOjdQ92_8fhe0RCvNlWcTxPYaWdxd6HeWLIp-Th92aSfQOlOBnkMetql0zcHYdENiWXlE6nX-G-LKqk9O98cCxLDaC1-zcrJB6adGWtBDl-WbwloNL6sxkyQB2HzV8OSQNCEQWS8_XAMxTqyTlAFPiAoUtXg_PYV2JwxAf_/s1600/image6.gif" style="width: 65%;" td=""/></center></td></tr> <tr><td class="tr-caption" style="text-align: center;"><i>Placeholder API usage examples</i></td></tr></tbody></table></div> <p> Check out the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#(androidx.compose.ui.Modifier).placeholder(androidx.wear.compose.material.PlaceholderState,androidx.compose.ui.graphics.Shape,androidx.compose.ui.graphics.Color)" target="_blank">reference docs</a> and <a href="https://web.archive.org/web/20230213032228/https://github.com/google/horologist/pull/709" target="_blank">sample in Horologist</a> to see how to apply the placeholder to common use cases, such as a Chip with icon and a label that puts placeholder over individual content slots and draws a placeholder shimmer on top while waiting for data to load. </p> <h3 style="text-align: left;">Modifier.scrollAway</h3> <p> Horologist&#8217;s <a href="https://web.archive.org/web/20230213032228/https://google.github.io/horologist/compose-layout/#fade-away-modifier" target="_blank">fadeAway modifier</a> has been graduated to <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#(androidx.compose.ui.Modifier).scrollAway(androidx.compose.foundation.ScrollState,androidx.compose.ui.unit.Dp)" target="_blank">scrollAway modifier</a> in version 1.1. Modifier.scrollAway scrolls an item vertically in and out of view, based on the scroll state, and already has overloads to work with Column, LazyColumn and ScalingLazyColumn. </p> <div style="text-align: left;"> Use this modifier to make <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/training/wearables/compose/time-text#anatomy" target="_blank">TimeText fade out</a> of the view as the user starts to scroll a list of items upwards. </div> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Moving demonstration of ScrollAway modifier usage with TimeText on a round watch face" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM1oqqGJxjYtYdRaL1R_xCtVSSebLxM_mLA0C2XGpRYNuAljwBDrMOjjKfnviuGPwXj9B1iuXBsm5nJtBZPXB0Aq_os1fOCXx7_5p4oodin2IGvCX-8X3195cAy-hyV1Q5HGo4iC_gZyf8_cT9rKoD54UdxyjyN8bqwr9Nf4wkaK6A21oZ8MRF-lhz/s1600/image7.gif" style="width: 65%;" td=""/></center></td></tr> <tr><td class="tr-caption" style="text-align: center;"><i>ScrollAway modifier usage with TimeText</i></td></tr></tbody></table></div> <h3 style="text-align: left;">Additional parameters in CurvedTextStyle</h3> <p> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/foundation/CurvedTextStyle#CurvedTextStyle(androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.unit.TextUnit,androidx.compose.ui.text.font.FontFamily,androidx.compose.ui.text.font.FontWeight,androidx.compose.ui.text.font.FontStyle,androidx.compose.ui.text.font.FontSynthesis)" target="_blank">CurvedTextStyle</a> now supports additional parameters (fontFamily, fontWeight, fontStyle, fontSynthesis) to specify font details when creating a curved text style. Extended curved text style can be used on both <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#(androidx.wear.compose.foundation.CurvedScope).curvedText(kotlin.String,androidx.wear.compose.foundation.CurvedModifier,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.unit.TextUnit,androidx.compose.ui.text.font.FontFamily,androidx.compose.ui.text.font.FontWeight,androidx.compose.ui.text.font.FontStyle,androidx.compose.ui.text.font.FontSynthesis,androidx.wear.compose.foundation.CurvedTextStyle,androidx.wear.compose.foundation.CurvedDirection.Angular,androidx.compose.ui.text.style.TextOverflow)" target="_blank">curvedText</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/foundation/package-summary#(androidx.wear.compose.foundation.CurvedScope).basicCurvedText(kotlin.String,androidx.wear.compose.foundation.CurvedModifier,androidx.wear.compose.foundation.CurvedDirection.Angular,androidx.compose.ui.text.style.TextOverflow,kotlin.Function0)" target="_blank">basicCurvedText</a>. </p> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Demonstration of applying different font to curved text on a round watch face" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9bIl808TYNGjuJm10iFLO61wm2Q_s9nK6-X6OQ81pqpIoNyC2Il5HBt2X65VrwnLh_G1CCMJ3Od7BtjzrKu2wuc-YZ5A_K4LtLgMvFX18U6rWfEcBMRxsYrucZ_6H_ovyLhimb23Gta5aqSRv12q9vaV7nEL9FCnUh-V5UzX63eKPVqF_SSaMVMUn/s1600/image8.png" style="width: 65%;" td=""/></center></td></tr> <tr><td class="tr-caption" style="text-align: center;"><i>Applying different font to curved text</i></td></tr></tbody></table></div> <h3 style="text-align: left;"><span style="font-weight: bold;">UX and accessibility improvements</span></h3> <p> The 1.1 release also focuses on bringing a refined user experience, improvements for <a href="https://web.archive.org/web/20230213032228/https://support.google.com/wearos/answer/7313945?hl=en-GB" target="_blank">TalkBack</a> support and overall better <a href="https://web.archive.org/web/20230213032228/https://m2.material.io/design/usability/accessibility.html#understanding-accessibility" target="_blank">accessibility</a>: </p> <ul> <li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#ToggleChip(kotlin.Boolean,kotlin.Function1,kotlin.Function1,kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Function1,kotlin.Function1,androidx.wear.compose.material.ToggleChipColors,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape)" target="_blank">ToggleChip</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#SplitToggleChip(kotlin.Boolean,kotlin.Function1,kotlin.Function1,kotlin.Function0,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1,androidx.wear.compose.material.SplitToggleChipColors,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.layout.PaddingValues,androidx.compose.ui.graphics.Shape)" target="_blank">SplitToggleChip</a> support usage of animated toggle controls (Checkbox, Switch and RadioButton) that can be used instead of the static icons provided by ToggleChipDefaults. </li><li>Default gradient colors for Chip/ToggleChip and Cards were adjusted to match the latest UX specification. </li><li>Updated a number of the default colors in the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#MaterialTheme(androidx.wear.compose.material.Colors,androidx.wear.compose.material.Typography,androidx.wear.compose.material.Shapes,kotlin.Function0)" target="_blank">MaterialTheme</a> to improve accessibility as the original colors did not have sufficient contrast. </li><li>Accessibility improvements to <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#Picker(androidx.wear.compose.material.PickerState,kotlin.String,androidx.compose.ui.Modifier,kotlin.Boolean,kotlin.Function1,kotlin.Function0,androidx.wear.compose.material.ScalingParams,androidx.compose.ui.unit.Dp,kotlin.Float,androidx.compose.ui.graphics.Color,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Function2)" target="_blank">Picker</a> so that multi-picker screens are navigable with screen readers and the content description is accessible. </li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#InlineSlider(kotlin.Float,kotlin.Function1,kotlin.Int,kotlin.Function0,kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Boolean,kotlin.ranges.ClosedFloatingPointRange,kotlin.Boolean,androidx.wear.compose.material.InlineSliderColors)" target="_blank">InlineSlider</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#Stepper(kotlin.Float,kotlin.Function1,kotlin.Int,kotlin.Function0,kotlin.Function0,androidx.compose.ui.Modifier,kotlin.ranges.ClosedFloatingPointRange,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,kotlin.Function1)" target="_blank">Stepper</a> now have button roles, so that TalkBack can recognize them as buttons. </li><li>The PositionIndicator in <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/wear/compose/material/package-summary#Scaffold(androidx.compose.ui.Modifier,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0)" target="_blank">Scaffold</a> is now positioned and sized so that it only takes the space needed. This is useful when semantic information is added to it, so TalkBack gets the correct bounds of the PositionIndicator on screen.</li></ul><br/><ul> </ul> <h2 style="text-align: left;">It&#8217;s time &#8986; to bring your app to the wrist!</h2> <h3 style="text-align: left;">Get started</h3> <p> To begin developing with Compose for Wear OS, get started with hands-on experience trying our <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/codelabs/compose-for-wear-os" target="_blank">codelab</a>, and make sure to check out the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/training/wearables/compose" target="_blank">documentation</a> and <a href="https://web.archive.org/web/20230213032228/https://github.com/android/wear-os-samples" target="_blank">samples</a>. Visit <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/wear-compose#1.1.0-rc01" target="_blank">Compose for Wear OS release notes</a> for full list of changes available in version 1.1. </p> <p> Note that using version 1.1 of Compose for Wear OS requires using the version 1.3 of <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/compose" target="_blank">androidx.compose</a> libraries and therefore Kotlin 1.7.10. Check out the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/compose-kotlin" target="_blank">Compose to Kotlin Compatibility Map</a> for more information. </p> <h3 style="text-align: left;">Provide feedback</h3> <p> Compose for Wear OS continues to evolve with the features you&#8217;ve been asking for. Please do continue providing us feedback on the <a href="https://web.archive.org/web/20230213032228/https://b.corp.google.com/issues/new?component=1077552&amp;template=1598429" target="_blank">issue tracker</a> and join <a href="https://web.archive.org/web/20230213032228/https://slack-chats.kotlinlang.org/c/compose-wear" target="_blank">Kotlin Slack</a> <strong>#compose-wear</strong> channel to connect with the Google team and dev community. </p> <p> We&#8217;re excited to see a growing number of apps using Compose for Wear OS in production, and we&#8217;re grateful for all issues and requests that help us to make the toolkit better! </p> <h3 style="text-align: left;">Start building for Wear OS now</h3> <p> Discover even more with <a href="https://web.archive.org/web/20230213032228/https://www.youtube.com/playlist?list=PLWz5rJ2EKKc9jBnpl83LH6oZc7nFIVSRq" target="_blank">technical sessions</a> from the <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/form-factors-at-ads-22.html" target="_blank">Android Dev Summit</a> providing guidance on app architecture, testing, handling rotary input, and verticalized sessions for media and fitness. </p> </div> </noscript> </div> </div> <div class="share"> <span class="twitter-custom social-wrapper" data-href="http://twitter.com/share?text=Android Developers Blog:Compose for Wear OS 1.1 is now stable: check out new features!&amp;url=https://android-developers.googleblog.com/2022/12/compose-for-wear-os-11-stable.html&amp;via=google"> <img alt="Share on Twitter" height="24" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png" width="24"/> </span> <span class="fb-custom social-wrapper" data-href="https://www.facebook.com/sharer.php?u=https://android-developers.googleblog.com/2022/12/compose-for-wear-os-11-stable.html"> <img alt="Share on Facebook" height="24" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png" width="24"/> </span> <span class="email-custom social-wrapper" data-href="mailto:?body=https://android-developers.googleblog.com/2022/12/compose-for-wear-os-11-stable.html"> <img alt="Share by email" height="24" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAMFBMVEVMaXEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACbsZG/AAAAEHRSTlMAILDy/y78bg9wzE/jjwyQQ3EYrQAAAMpJREFUeAHN0yHIwmAQxvHng2/IypDZw9AOyuziDiemgb2tCgsGO4LY7cneg73Yl+0dsy8PcuzdsTaGf7D4e3cbbIdh+ptKq0VBmIhpSZhZWBNSCxmhFFv/sDmh0aVUyOF1VNj6cFZYIYEW478BWYVvYaogDmRXgAV38eAhhxdc0VueHkS1+wPggSZwRALEHOkBRmlWhe6HFvA0rzLg5vM+FqKaT2YBQQEL2u+DebUKuQ83Bf/zuZYKtn6he3G6V21vYU4I7DqPMUgfpvKO2fcD4TMAAAAASUVORK5CYII=" target="_blank" width="24"/> </span> </div> <div class="post-footer"> <div class="cmt_iframe_holder" data-href="https://android-developers.googleblog.com/2022/12/compose-for-wear-os-11-stable.html" data-viewtype="FILTERED_POSTMOD"></div> <a href="https://web.archive.org/web/20230213032228/https://plus.google.com/112374322230920073195" rel="author" style="display: none"> Google </a> <div class="label-footer"> <span class="labels-caption"> Labels: </span> <span class="labels"> <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Compose" rel="tag"> Compose </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Featured" rel="tag"> Featured </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Horologist" rel="tag"> Horologist </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack" rel="tag"> Jetpack </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/latest" rel="tag"> latest </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Platform_Update" rel="tag"> Platform_Update </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Wear%20OS" rel="tag"> Wear OS </a> </span> </div> </div> </div> <div class="post" data-id="4950350895031818697" itemscope="" itemtype="http://schema.org/BlogPosting"> <h2 class="title" itemprop="name"> <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/11/power-your-wear-os-fitness-app-with-health-services-latest-version.html" itemprop="url" title="Power your Wear OS fitness app with the latest version of Health Services"> Power your Wear OS fitness app with the latest version of Health Services </a> </h2> <div class="post-header"> <div class="published"> <span class="publishdate" itemprop="datePublished"> 09 November 2022 </span> </div> </div> <div class="post-body"> <div class="post-content" itemprop="articleBody"> <script type="text/template"> <meta content="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXIB_WDnAa8QUGJGU_R9xF1EPCpoa4bnUYzcAaI22z7Htdmqffezl8TYDHcXpq5mc_GCnR5iDQre7Os_sK0Ufo1kK50wkgPZOfaASAIbBb4RrWxlBduvkIdTH3ddlH0pUftnCZaMITJYZwCJwRUZH1jcNIv23dNoU_PU8kDWLjQZs6DPsIEbR1PA-N/s1600/Social%20-%20Android%20-%20Power%20your%20Wear%20OS%20fitness%20app%20with%20the%20latest%20version%20of%20Health%20Services.png" name="twitter:image"></meta> <img src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXIB_WDnAa8QUGJGU_R9xF1EPCpoa4bnUYzcAaI22z7Htdmqffezl8TYDHcXpq5mc_GCnR5iDQre7Os_sK0Ufo1kK50wkgPZOfaASAIbBb4RrWxlBduvkIdTH3ddlH0pUftnCZaMITJYZwCJwRUZH1jcNIv23dNoU_PU8kDWLjQZs6DPsIEbR1PA-N/s1600/Social%20-%20Android%20-%20Power%20your%20Wear%20OS%20fitness%20app%20with%20the%20latest%20version%20of%20Health%20Services.png" style="display: none;" /> <p><em>Posted by Breana Tate, Developer Relations Engineer</em><a href="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih0IqBdpuuWPaDANuDFI11F1WOhmGe5akHZVGYLCW4rD4ooNDWvTFYabuqC4d99FKTDDeCKVBGkdeY9g7scbAAUe2gIJtsXx4G-nkw4apoTRhNoqmxZ8jrHiVK8-B36aGU174sKzGGaedFUzG4-CNNMg9zwIOQnHKJjzL720Y1iCPKWg_sSGlJrSVX/s1600/Header%20-%20Android%20-%20Power%20your%20Wear%20OS%20fitness%20app%20with%20the%20latest%20version%20of%20Health%20Services.png"><img border="0" data-original-height="800" data-original-width="1058" height="201" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih0IqBdpuuWPaDANuDFI11F1WOhmGe5akHZVGYLCW4rD4ooNDWvTFYabuqC4d99FKTDDeCKVBGkdeY9g7scbAAUe2gIJtsXx4G-nkw4apoTRhNoqmxZ8jrHiVK8-B36aGU174sKzGGaedFUzG4-CNNMg9zwIOQnHKJjzL720Y1iCPKWg_sSGlJrSVX/w677-h201/Header%20-%20Android%20-%20Power%20your%20Wear%20OS%20fitness%20app%20with%20the%20latest%20version%20of%20Health%20Services.png" width="677" /></a>The <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/training/wearables/health-services" target="_blank">Health Services API</a> enables developers to use on-device sensor data and related algorithms to provide their apps with high-quality data related to activity, exercise, and health. What&#8217;s more, you don&#8217;t have to choose between conserving battery life and delivering high frequency data&#8211;Health Services makes it possible to do both. Since announcing Health Services Alpha at I/O &#8216;21, we&#8217;ve introduced a number of improvements to the platform aimed at simplifying the development experience. Read on to learn about the exciting features from Health Services Beta in Android Jetpack that your app will be able to take advantage of when you migrate from Alpha.</p> <p style="text-align: left;"><br /></p><h2 style="text-align: left;">Capture more with new metrics</h2> <p> The Health Services Jetpack Beta introduces new data and exercise types, including&nbsp;<span style="font-family: courier; font-size: medium;">DataType.GOLF_SHOT_COUNT, ExerciseType.HORSE_RIDING</span><span style="font-size: medium;">,</span> and&nbsp;<span style="font-family: courier; font-size: medium;">ExerciseType.BACKPACKING</span><code><span style="font-size: medium;">.</span></code> You can review the full list of new exercise and data types <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/health#health-services-client-1.0.0-beta01" target="_blank">here</a>. These supplement the already large library of <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/health/services/client/data/DataType" target="_blank">data</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/health/services/client/data/ExerciseType" target="_blank">exercise</a> types available to developers building Wear OS apps with Health Services. Additionally, we&#8217;ve added the ability to listen for health events, such as fall detection, through&nbsp;<span style="font-family: courier; font-size: medium;">PassiveMonitoringClient.</span></p> <p> In addition to new&nbsp;data types<span style="font-size: medium;">,</span> we&#8217;ve also introduced a new organization model for data in Health Services. This new model makes the Health Services API more type-safe by adding additional classification information to data types and data points, reducing the chance of errors in code. In Beta, all&nbsp;<span style="font-family: courier; font-size: medium;">DataPoint</span>&nbsp;types have their own subclass and are derived from the&nbsp;<span style="font-family: courier; font-size: medium;">DataPoint</span>&nbsp;class. You can choose from:</p><p></p><ul style="text-align: left;"><li><span style="font-family: courier; font-size: medium;">SampleDataPoints&nbsp;</span></li><li><span style="font-family: courier; font-size: medium;">IntervalDataPoints&nbsp;</span></li><li><span style="font-family: courier; font-size: medium;">StatisticalDataPoints</span></li><li><span style="font-family: courier;"><span style="font-size: medium;">CumulativeDataPoints</span>.&nbsp;</span></li></ul><p></p><p><span style="font-family: courier;"><span style="font-size: medium;">DataTypes&nbsp;</span></span>are categorized as&nbsp;<span style="font-family: courier; font-size: medium;">AggregateDataTypes</span>&nbsp;or&nbsp;<span style="font-family: courier; font-size: medium;">DeltaDataTypes</span>.</p> <p> As a result of this change, Health Services can guarantee the correct type at compile time instead of at runtime, reducing errors and improving the developer experience. For example, location data points are now represented as a strongly-typed LocationData object instead of as a DoubleArray. Take a look at the example below: </p> <p> Previously: </p><p><span id="docs-internal-guid-b4a7e5dd-7fff-af2e-b204-1df9364a5c4a"></span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 468pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f0f0f0; border-bottom: solid #ffffff 1pt; border-color: rgb(255, 255, 255); border-left: solid #ffffff 1pt; border-right: solid #ffffff 1pt; border-style: solid; border-top: solid #ffffff 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: courier;"><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">exerciseUpdate.latestMetrics[DataType.LOCATION]?.forEach {</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> loc = it.value.asDoubleArray()</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> lat = loc[DataPoints.LOCATION_DATA_POINT_LATITUDE_INDEX]</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> lon = loc[DataPoints.LOCATION_DATA_POINT_LONGITUDE_INDEX]</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> alt = loc[DataPoints.LOCATION_DATA_POINT_ALTITUDE_INDEX]</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; println(</span><span style="background-color: #f0f0f0; color: #880000; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">"(</span><span style="background-color: #f0f0f0; color: #bc6060; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">$lat</span><span style="background-color: #f0f0f0; color: #880000; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">,</span><span style="background-color: #f0f0f0; color: #bc6060; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">$lon</span><span style="background-color: #f0f0f0; color: #880000; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">,</span><span style="background-color: #f0f0f0; color: #bc6060; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">$alt</span><span style="background-color: #f0f0f0; color: #880000; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">) @ </span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">${it.startDurationFromBoot}</span><span style="background-color: #f0f0f0; color: #880000; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">"</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">)</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span face="Consolas, sans-serif"><br /></span></span></p></td></tr></tbody></table></div><p><span id="docs-internal-guid-28769aef-7fff-c204-6195-147a1dcbb34d"></span></p><div align="left" dir="ltr" style="margin-left: 0pt;">Health Services Beta:</div><div align="left" dir="ltr" style="margin-left: 0pt;"><br /></div> <div><span id="docs-internal-guid-7b961c11-7fff-c1e9-32a9-e8296fbbd883"><div align="left" dir="ltr" style="margin-left: 0pt;"><span id="docs-internal-guid-350194d6-7fff-0957-de06-1ecee44ce94b"><div align="left" dir="ltr" style="margin-left: 0pt;"></div></span></div></span></div><div><span id="docs-internal-guid-35538826-7fff-7601-7c9d-f4e1eb35b13f"><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 468pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f0f0f0; border-bottom: solid #ffffff 1pt; border-color: rgb(255, 255, 255); border-left: solid #ffffff 1pt; border-right: solid #ffffff 1pt; border-style: solid; border-top: solid #ffffff 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: courier;"><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">exerciseUpdate</span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">.latestMetrics.getData</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">(DataType.LOCATION)</span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">.forEach</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> {</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; </span><span style="color: #888888; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">// it.value is of type LocationData</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; val loc = it</span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">.value</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; val </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">time</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> = it</span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">.timeDurationFromBoot</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; println(</span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">"loc = [${loc.latitude}, ${loc.longitude}, ${loc.altitude}] @ $time"</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">}</span></span><span face="Consolas, sans-serif" style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span></p></td></tr></tbody></table></div></span></div><div>As you can see, due to the new approach, Health Services knows that&nbsp;<span style="font-family: courier; font-size: medium;">loc</span>&nbsp;is of type <span style="font-family: courier; font-size: medium;"><b>List&lt;SampleDataPoint&lt;LocationData&gt;&gt;</b></span> because <span style="font-family: courier; font-size: medium;"><b>DataType.LOCATION</b></span> is defined as a <span style="font-family: courier; font-size: medium;"><b>DeltaDataType&lt;LocationData, SampleDataPoint&lt;LocationData&gt;&gt;</b></span>.</div> <p style="text-align: left;"><br /></p><h2 style="text-align: left;">Consolidated exercise end state</h2> <p><span style="font-family: courier; font-size: medium;">ExerciseState</span>&nbsp;is now included within&nbsp;<span style="font-family: courier; font-size: medium;">ExerciseUpdate&#8217;s ExerciseStateInfo&nbsp;</span>property. To give you more control over how your app responds to an ending exercise, we&#8217;ve added new&nbsp;<span style="font-family: courier; font-size: medium;">ExerciseState</span>s called&nbsp;<span style="font-family: courier; font-size: medium;">ExerciseState.ENDED&nbsp;</span>and<code>&nbsp;</code><span style="font-family: courier; font-size: medium;">ExerciseState.ENDING</span>&nbsp;to replace what was previously multiple variations of ended and ending states. These new states also include an&nbsp;<span style="font-family: courier; font-size: medium;">endReason</span>, such as&nbsp;<span style="font-family: courier; font-size: medium;">USER_END, AUTO_END_PREPARE_EXPIRED, and AUTO_END_PERMISSION_LOST.</span></p> <div><p>The following example shows how to check for exercise termination:</p><p><span id="docs-internal-guid-8a055a14-7fff-3b0e-4b94-a0ac4d2d2e86"></span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><span id="docs-internal-guid-0f2452ba-7fff-2ea2-8f1f-02cd0b0fb2c5"><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 468pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f0f0f0; border-bottom: solid #ffffff 1pt; border-color: rgb(255, 255, 255); border-left: solid #ffffff 1pt; border-right: solid #ffffff 1pt; border-style: solid; border-top: solid #ffffff 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: courier;"><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> callback = </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">object</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> : ExerciseUpdateCallback {</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">override</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">fun</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">onExerciseUpdateReceived</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">(update: </span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">ExerciseUpdate</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">) {</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; &nbsp; &nbsp; </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">if</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> (update.exerciseStateInfo.state.isEnded) {</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span style="color: #888888; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">// Workout has either been ended by the user, or otherwise terminated</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> reason = update.exerciseStateInfo.endReason</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; &nbsp; &nbsp; }</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; &nbsp; &nbsp; ...</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; }</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; ...</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">}</span></span></p></td></tr></tbody></table></div></span></div></div><p style="text-align: left;"><br /></p><h2 style="text-align: left;">Improvements to passive monitoring</h2> <p> Health Services Beta also transitions to a new set of passive listener APIs. These changes largely focus on making daily metrics better typed and easier to integrate. For example, we renamed the&nbsp;<span style="font-family: courier;"><span style="font-size: medium;">PassiveListenerConfig&nbsp;</span></span>function&nbsp;<span style="font-family: courier; font-size: medium;">setPassiveGoals</span><span style="font-size: medium;">&nbsp;</span>to<span style="font-size: medium;">&nbsp;</span><span style="font-family: courier; font-size: medium;">setDailyGoals</span>. This change reinforces that Health Services only supports daily passive goals.We&#8217;ve also condensed multiple APIs for registering Passive Listeners into a single registration call. Clients can directly implement the desired overrides for only the data your app needs.</p> <div style="text-align: left;"> Additionally, the Passive Listener <span style="font-family: courier; font-size: medium;"><b>BroadcastReceiver</b></span> was replaced by the <span style="font-family: courier; font-size: medium;"><b>PassiveListenerService</b></span>, which offers stronger typing, along with better reliability and performance. Clients can now register both a service and a callback simultaneously with different requests, making it easier to register a callback for UI updates while reserving the background request for database updates. </div> <p style="text-align: left;"><br /></p><h2 style="text-align: left;">Build for even more devices on Wear OS 3</h2> <p> Health Services is only available for Wear OS 3. The Wear OS 3 ecosystem now includes even more devices, which means your apps can reach even more users. Montblanc, Samsung, and Fossil are just a few of the OEMs that have recently released new devices running Wear OS 3 (with more coming later this year!). The newly released <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/the-new-google-pixel-watch-is-here.html" target="_blank">Pixel Watch</a> also features Fitbit health tracking powered by Health Services. </p> <p> If you haven&#8217;t used Health Services before, now is the time to try it out! And if your app is still using Health Services Alpha, here is why you should consider migrating: </p> <ul> <li>Ongoing Health Services Development: Since Health Services Beta is the newest version, bug fixes and feature improvements are likely to be prioritized over older versions. </li><li>Prepares your app infrastructure for when Health Services goes to stable release </li><li>Improvements to type safety - less chance of error in code! </li><li>Adds additional functionality to make it easier to work with Health Services data </li> </ul> <p> You can view the full list of changes and updated documentation at <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/health#health-services-client-1.0.0-beta01" target="_blank">developer.android.com</a>. </p><p><br /></p> </script> <noscript> <meta content="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXIB_WDnAa8QUGJGU_R9xF1EPCpoa4bnUYzcAaI22z7Htdmqffezl8TYDHcXpq5mc_GCnR5iDQre7Os_sK0Ufo1kK50wkgPZOfaASAIbBb4RrWxlBduvkIdTH3ddlH0pUftnCZaMITJYZwCJwRUZH1jcNIv23dNoU_PU8kDWLjQZs6DPsIEbR1PA-N/s1600/Social%20-%20Android%20-%20Power%20your%20Wear%20OS%20fitness%20app%20with%20the%20latest%20version%20of%20Health%20Services.png" name="twitter:image"></meta> <img src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXIB_WDnAa8QUGJGU_R9xF1EPCpoa4bnUYzcAaI22z7Htdmqffezl8TYDHcXpq5mc_GCnR5iDQre7Os_sK0Ufo1kK50wkgPZOfaASAIbBb4RrWxlBduvkIdTH3ddlH0pUftnCZaMITJYZwCJwRUZH1jcNIv23dNoU_PU8kDWLjQZs6DPsIEbR1PA-N/s1600/Social%20-%20Android%20-%20Power%20your%20Wear%20OS%20fitness%20app%20with%20the%20latest%20version%20of%20Health%20Services.png" style="display: none;"/> <p><em>Posted by Breana Tate, Developer Relations Engineer</em><a href="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih0IqBdpuuWPaDANuDFI11F1WOhmGe5akHZVGYLCW4rD4ooNDWvTFYabuqC4d99FKTDDeCKVBGkdeY9g7scbAAUe2gIJtsXx4G-nkw4apoTRhNoqmxZ8jrHiVK8-B36aGU174sKzGGaedFUzG4-CNNMg9zwIOQnHKJjzL720Y1iCPKWg_sSGlJrSVX/s1600/Header%20-%20Android%20-%20Power%20your%20Wear%20OS%20fitness%20app%20with%20the%20latest%20version%20of%20Health%20Services.png"><img border="0" data-original-height="800" data-original-width="1058" height="201" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih0IqBdpuuWPaDANuDFI11F1WOhmGe5akHZVGYLCW4rD4ooNDWvTFYabuqC4d99FKTDDeCKVBGkdeY9g7scbAAUe2gIJtsXx4G-nkw4apoTRhNoqmxZ8jrHiVK8-B36aGU174sKzGGaedFUzG4-CNNMg9zwIOQnHKJjzL720Y1iCPKWg_sSGlJrSVX/w677-h201/Header%20-%20Android%20-%20Power%20your%20Wear%20OS%20fitness%20app%20with%20the%20latest%20version%20of%20Health%20Services.png" width="677"/></a>The <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/training/wearables/health-services" target="_blank">Health Services API</a> enables developers to use on-device sensor data and related algorithms to provide their apps with high-quality data related to activity, exercise, and health. What&#8217;s more, you don&#8217;t have to choose between conserving battery life and delivering high frequency data&#8211;Health Services makes it possible to do both. Since announcing Health Services Alpha at I/O &#8216;21, we&#8217;ve introduced a number of improvements to the platform aimed at simplifying the development experience. Read on to learn about the exciting features from Health Services Beta in Android Jetpack that your app will be able to take advantage of when you migrate from Alpha.</p> <p style="text-align: left;"><br/></p><h2 style="text-align: left;">Capture more with new metrics</h2> <p> The Health Services Jetpack Beta introduces new data and exercise types, including&nbsp;<span style="font-family: courier; font-size: medium;">DataType.GOLF_SHOT_COUNT, ExerciseType.HORSE_RIDING</span><span style="font-size: medium;">,</span> and&nbsp;<span style="font-family: courier; font-size: medium;">ExerciseType.BACKPACKING</span><code><span style="font-size: medium;">.</span></code> You can review the full list of new exercise and data types <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/health#health-services-client-1.0.0-beta01" target="_blank">here</a>. These supplement the already large library of <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/health/services/client/data/DataType" target="_blank">data</a> and <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/health/services/client/data/ExerciseType" target="_blank">exercise</a> types available to developers building Wear OS apps with Health Services. Additionally, we&#8217;ve added the ability to listen for health events, such as fall detection, through&nbsp;<span style="font-family: courier; font-size: medium;">PassiveMonitoringClient.</span></p> <p> In addition to new&nbsp;data types<span style="font-size: medium;">,</span> we&#8217;ve also introduced a new organization model for data in Health Services. This new model makes the Health Services API more type-safe by adding additional classification information to data types and data points, reducing the chance of errors in code. In Beta, all&nbsp;<span style="font-family: courier; font-size: medium;">DataPoint</span>&nbsp;types have their own subclass and are derived from the&nbsp;<span style="font-family: courier; font-size: medium;">DataPoint</span>&nbsp;class. You can choose from:</p><p></p><ul style="text-align: left;"><li><span style="font-family: courier; font-size: medium;">SampleDataPoints&nbsp;</span></li><li><span style="font-family: courier; font-size: medium;">IntervalDataPoints&nbsp;</span></li><li><span style="font-family: courier; font-size: medium;">StatisticalDataPoints</span></li><li><span style="font-family: courier;"><span style="font-size: medium;">CumulativeDataPoints</span>.&nbsp;</span></li></ul><p></p><p><span style="font-family: courier;"><span style="font-size: medium;">DataTypes&nbsp;</span></span>are categorized as&nbsp;<span style="font-family: courier; font-size: medium;">AggregateDataTypes</span>&nbsp;or&nbsp;<span style="font-family: courier; font-size: medium;">DeltaDataTypes</span>.</p> <p> As a result of this change, Health Services can guarantee the correct type at compile time instead of at runtime, reducing errors and improving the developer experience. For example, location data points are now represented as a strongly-typed LocationData object instead of as a DoubleArray. Take a look at the example below: </p> <p> Previously: </p><p><span id="docs-internal-guid-b4a7e5dd-7fff-af2e-b204-1df9364a5c4a"></span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 468pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f0f0f0; border-bottom: solid #ffffff 1pt; border-color: rgb(255, 255, 255); border-left: solid #ffffff 1pt; border-right: solid #ffffff 1pt; border-style: solid; border-top: solid #ffffff 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: courier;"><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">exerciseUpdate.latestMetrics[DataType.LOCATION]?.forEach {</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> loc = it.value.asDoubleArray()</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> lat = loc[DataPoints.LOCATION_DATA_POINT_LATITUDE_INDEX]</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> lon = loc[DataPoints.LOCATION_DATA_POINT_LONGITUDE_INDEX]</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> alt = loc[DataPoints.LOCATION_DATA_POINT_ALTITUDE_INDEX]</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; println(</span><span style="background-color: #f0f0f0; color: #880000; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">"(</span><span style="background-color: #f0f0f0; color: #bc6060; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">$lat</span><span style="background-color: #f0f0f0; color: #880000; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">,</span><span style="background-color: #f0f0f0; color: #bc6060; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">$lon</span><span style="background-color: #f0f0f0; color: #880000; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">,</span><span style="background-color: #f0f0f0; color: #bc6060; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">$alt</span><span style="background-color: #f0f0f0; color: #880000; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">) @ </span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">${it.startDurationFromBoot}</span><span style="background-color: #f0f0f0; color: #880000; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">"</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">)</span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></span><span style="background-color: #f0f0f0; color: #444444; font-size: 10.5pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span face="Consolas, sans-serif"><br/></span></span></p></td></tr></tbody></table></div><p><span id="docs-internal-guid-28769aef-7fff-c204-6195-147a1dcbb34d"></span></p><div align="left" dir="ltr" style="margin-left: 0pt;">Health Services Beta:</div><div align="left" dir="ltr" style="margin-left: 0pt;"><br/></div> <div><span id="docs-internal-guid-7b961c11-7fff-c1e9-32a9-e8296fbbd883"><div align="left" dir="ltr" style="margin-left: 0pt;"><span id="docs-internal-guid-350194d6-7fff-0957-de06-1ecee44ce94b"><div align="left" dir="ltr" style="margin-left: 0pt;"></div></span></div></span></div><div><span id="docs-internal-guid-35538826-7fff-7601-7c9d-f4e1eb35b13f"><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 468pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f0f0f0; border-bottom: solid #ffffff 1pt; border-color: rgb(255, 255, 255); border-left: solid #ffffff 1pt; border-right: solid #ffffff 1pt; border-style: solid; border-top: solid #ffffff 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: courier;"><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">exerciseUpdate</span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">.latestMetrics.getData</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">(DataType.LOCATION)</span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">.forEach</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> {</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; </span><span style="color: #888888; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">// it.value is of type LocationData</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; val loc = it</span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">.value</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; val </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">time</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> = it</span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">.timeDurationFromBoot</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; println(</span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">"loc = [${loc.latitude}, ${loc.longitude}, ${loc.altitude}] @ $time"</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">)</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">}</span></span><span face="Consolas, sans-serif" style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span></p></td></tr></tbody></table></div></span></div><div>As you can see, due to the new approach, Health Services knows that&nbsp;<span style="font-family: courier; font-size: medium;">loc</span>&nbsp;is of type <span style="font-family: courier; font-size: medium;"><b>List&lt;SampleDataPoint&lt;LocationData&gt;&gt;</b></span> because <span style="font-family: courier; font-size: medium;"><b>DataType.LOCATION</b></span> is defined as a <span style="font-family: courier; font-size: medium;"><b>DeltaDataType&lt;LocationData, SampleDataPoint&lt;LocationData&gt;&gt;</b></span>.</div> <p style="text-align: left;"><br/></p><h2 style="text-align: left;">Consolidated exercise end state</h2> <p><span style="font-family: courier; font-size: medium;">ExerciseState</span>&nbsp;is now included within&nbsp;<span style="font-family: courier; font-size: medium;">ExerciseUpdate&#8217;s ExerciseStateInfo&nbsp;</span>property. To give you more control over how your app responds to an ending exercise, we&#8217;ve added new&nbsp;<span style="font-family: courier; font-size: medium;">ExerciseState</span>s called&nbsp;<span style="font-family: courier; font-size: medium;">ExerciseState.ENDED&nbsp;</span>and<code>&nbsp;</code><span style="font-family: courier; font-size: medium;">ExerciseState.ENDING</span>&nbsp;to replace what was previously multiple variations of ended and ending states. These new states also include an&nbsp;<span style="font-family: courier; font-size: medium;">endReason</span>, such as&nbsp;<span style="font-family: courier; font-size: medium;">USER_END, AUTO_END_PREPARE_EXPIRED, and AUTO_END_PERMISSION_LOST.</span></p> <div><p>The following example shows how to check for exercise termination:</p><p><span id="docs-internal-guid-8a055a14-7fff-3b0e-4b94-a0ac4d2d2e86"></span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><span id="docs-internal-guid-0f2452ba-7fff-2ea2-8f1f-02cd0b0fb2c5"><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none; table-layout: fixed; width: 468pt;"><colgroup><col></col></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f0f0f0; border-bottom: solid #ffffff 1pt; border-color: rgb(255, 255, 255); border-left: solid #ffffff 1pt; border-right: solid #ffffff 1pt; border-style: solid; border-top: solid #ffffff 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: courier;"><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> callback = </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">object</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> : ExerciseUpdateCallback {</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">override</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">fun</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">onExerciseUpdateReceived</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">(update: </span><span style="color: #880000; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">ExerciseUpdate</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">) {</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; &nbsp; &nbsp; </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">if</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> (update.exerciseStateInfo.state.isEnded) {</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span style="color: #888888; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">// Workout has either been ended by the user, or otherwise terminated</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">val</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"> reason = update.exerciseStateInfo.endReason</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; &nbsp; &nbsp; }</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; &nbsp; &nbsp; ...</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; }</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">&nbsp; &nbsp; ...</span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><br/></span><span style="color: #444444; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">}</span></span></p></td></tr></tbody></table></div></span></div></div><p style="text-align: left;"><br/></p><h2 style="text-align: left;">Improvements to passive monitoring</h2> <p> Health Services Beta also transitions to a new set of passive listener APIs. These changes largely focus on making daily metrics better typed and easier to integrate. For example, we renamed the&nbsp;<span style="font-family: courier;"><span style="font-size: medium;">PassiveListenerConfig&nbsp;</span></span>function&nbsp;<span style="font-family: courier; font-size: medium;">setPassiveGoals</span><span style="font-size: medium;">&nbsp;</span>to<span style="font-size: medium;">&nbsp;</span><span style="font-family: courier; font-size: medium;">setDailyGoals</span>. This change reinforces that Health Services only supports daily passive goals.We&#8217;ve also condensed multiple APIs for registering Passive Listeners into a single registration call. Clients can directly implement the desired overrides for only the data your app needs.</p> <div style="text-align: left;"> Additionally, the Passive Listener <span style="font-family: courier; font-size: medium;"><b>BroadcastReceiver</b></span> was replaced by the <span style="font-family: courier; font-size: medium;"><b>PassiveListenerService</b></span>, which offers stronger typing, along with better reliability and performance. Clients can now register both a service and a callback simultaneously with different requests, making it easier to register a callback for UI updates while reserving the background request for database updates. </div> <p style="text-align: left;"><br/></p><h2 style="text-align: left;">Build for even more devices on Wear OS 3</h2> <p> Health Services is only available for Wear OS 3. The Wear OS 3 ecosystem now includes even more devices, which means your apps can reach even more users. Montblanc, Samsung, and Fossil are just a few of the OEMs that have recently released new devices running Wear OS 3 (with more coming later this year!). The newly released <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/the-new-google-pixel-watch-is-here.html" target="_blank">Pixel Watch</a> also features Fitbit health tracking powered by Health Services. </p> <p> If you haven&#8217;t used Health Services before, now is the time to try it out! And if your app is still using Health Services Alpha, here is why you should consider migrating: </p> <ul> <li>Ongoing Health Services Development: Since Health Services Beta is the newest version, bug fixes and feature improvements are likely to be prioritized over older versions. </li><li>Prepares your app infrastructure for when Health Services goes to stable release </li><li>Improvements to type safety - less chance of error in code! </li><li>Adds additional functionality to make it easier to work with Health Services data </li> </ul> <p> You can view the full list of changes and updated documentation at <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/health#health-services-client-1.0.0-beta01" target="_blank">developer.android.com</a>. </p><p><br/></p> </noscript> </div> </div> <div class="share"> <span class="twitter-custom social-wrapper" data-href="http://twitter.com/share?text=Android Developers Blog:Power your Wear OS fitness app with the latest version of Health Services&amp;url=https://android-developers.googleblog.com/2022/11/power-your-wear-os-fitness-app-with-health-services-latest-version.html&amp;via=google"> <img alt="Share on Twitter" height="24" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png" width="24"/> </span> <span class="fb-custom social-wrapper" data-href="https://www.facebook.com/sharer.php?u=https://android-developers.googleblog.com/2022/11/power-your-wear-os-fitness-app-with-health-services-latest-version.html"> <img alt="Share on Facebook" height="24" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png" width="24"/> </span> <span class="email-custom social-wrapper" data-href="mailto:?body=https://android-developers.googleblog.com/2022/11/power-your-wear-os-fitness-app-with-health-services-latest-version.html"> <img alt="Share by email" height="24" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAMFBMVEVMaXEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACbsZG/AAAAEHRSTlMAILDy/y78bg9wzE/jjwyQQ3EYrQAAAMpJREFUeAHN0yHIwmAQxvHng2/IypDZw9AOyuziDiemgb2tCgsGO4LY7cneg73Yl+0dsy8PcuzdsTaGf7D4e3cbbIdh+ptKq0VBmIhpSZhZWBNSCxmhFFv/sDmh0aVUyOF1VNj6cFZYIYEW478BWYVvYaogDmRXgAV38eAhhxdc0VueHkS1+wPggSZwRALEHOkBRmlWhe6HFvA0rzLg5vM+FqKaT2YBQQEL2u+DebUKuQ83Bf/zuZYKtn6he3G6V21vYU4I7DqPMUgfpvKO2fcD4TMAAAAASUVORK5CYII=" target="_blank" width="24"/> </span> </div> <div class="post-footer"> <div class="cmt_iframe_holder" data-href="https://android-developers.googleblog.com/2022/11/power-your-wear-os-fitness-app-with-health-services-latest-version.html" data-viewtype="FILTERED_POSTMOD"></div> <a href="https://web.archive.org/web/20230213032228/https://plus.google.com/112374322230920073195" rel="author" style="display: none"> Google </a> <div class="label-footer"> <span class="labels-caption"> Labels: </span> <span class="labels"> <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Health%20Services" rel="tag"> Health Services </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack" rel="tag"> Jetpack </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Wear%20OS" rel="tag"> Wear OS </a> </span> </div> </div> </div> <div class="post" data-id="1233487935615753230" itemscope="" itemtype="http://schema.org/BlogPosting"> <h2 class="title" itemprop="name"> <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-ads-22.html" itemprop="url" title="Android Dev Summit ‘22: What’s new in Jetpack "> Android Dev Summit &#8216;22: What&#8217;s new in Jetpack </a> </h2> <div class="post-header"> <div class="published"> <span class="publishdate" itemprop="datePublished"> 24 October 2022 </span> </div> </div> <div class="post-body"> <div class="post-content" itemprop="articleBody"> <script type="text/template"> <meta content="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKuG3pZKvsbM-kUpZA9fJF5BTED9YxJuHWfcUONNrA9hYvp0UeTjvfYlSvjANimdnsgZ0NeP5namasvB8ifJzHFFMVn3LoVjDWq-Ui0ZIOX0tdP4hd1vgS-Ls2Z7C36le-lTwlpXYknLZcmEHzpSoNW9mL1-u5Y3ycsDCeRnWbJA6go1js5NQaCWGc/s1600/ADS22%20-%20What_s%20new%20in%20Jetpack%20-%20App%20Architecture,%20Performance%20Libraries_1024x512.png" name="twitter:image"></meta> <img src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKuG3pZKvsbM-kUpZA9fJF5BTED9YxJuHWfcUONNrA9hYvp0UeTjvfYlSvjANimdnsgZ0NeP5namasvB8ifJzHFFMVn3LoVjDWq-Ui0ZIOX0tdP4hd1vgS-Ls2Z7C36le-lTwlpXYknLZcmEHzpSoNW9mL1-u5Y3ycsDCeRnWbJA6go1js5NQaCWGc/s1600/ADS22%20-%20What_s%20new%20in%20Jetpack%20-%20App%20Architecture,%20Performance%20Libraries_1024x512.png" style="display: none;" /> <p><em>Posted by Amanda Alexander, Product Manager</em></p><p> <a href="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCzC-3CS0NeTAR1ZOCHwJ6IvdNBCdIey2290oWnQWNAn9Db1lJroBP4uDSYYDbEhRf-G1nCy1uc8yII1xNPL2stcvf_S9mx1fZqRXOWAeZGwpqjw-sVQ10-OZDxy6yDuH3U64851_AxHPytlrANamHZtr9MIp4nH3E-W7MDkO2QZS65s8cuyqeAJMJ/s1600/ADS22%20-%20What_s%20new%20in%20Jetpack%20-%20App%20Architecture,%20Performance%20Libraries_4209x1253.png"><img border="0" data-original-height="800" data-original-width="1058" height="199" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCzC-3CS0NeTAR1ZOCHwJ6IvdNBCdIey2290oWnQWNAn9Db1lJroBP4uDSYYDbEhRf-G1nCy1uc8yII1xNPL2stcvf_S9mx1fZqRXOWAeZGwpqjw-sVQ10-OZDxy6yDuH3U64851_AxHPytlrANamHZtr9MIp4nH3E-W7MDkO2QZS65s8cuyqeAJMJ/w667-h199/ADS22%20-%20What_s%20new%20in%20Jetpack%20-%20App%20Architecture,%20Performance%20Libraries_4209x1253.png" width="667" /></a> </p><p><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack" target="_blank">Android Jetpack</a> is a key component of&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/modern-android-development" target="_blank">Modern Android Development</a>. It is a suite of over 100 libraries, tools and guidance to help developers follow best practices, reduce boilerplate code, and write code that works consistently across Android versions and devices. By using Android Jetpack to improve your productivity, you can focus on building unique features for your app.&nbsp;</p> <p>Most apps on Google Play use Jetpack as a key component of their app architecture, in fact&nbsp;over 90% of the top 1000 apps use Android Jetpack.</p> <p>At ADS this week we released updates to three major areas of Jetpack:</p> <p style="text-align: left;"></p><ol style="text-align: left;"> <li><a href="#Architecture Libraries and Guidance">Architecture Libraries and Guidance</a></li> <li><a href="#Application Performance">Application Performance</a></li> <li><a href="#User Interface Libraries and Guidance">User Interface Libraries and Guidance</a></li></ol><p></p><ol> </ol> <p>We&#8217;ll take a moment to expand on each of these areas and then conclude with some additional updates that we also shipped.</p> <p>Let&#8217;s go&#8230;</p> <p><br /></p> <u><h2 id="Architecture Libraries and Guidance">Architecture Libraries and Guidance</h2></u> <p>App architecture libraries and components ensure that apps are robust, testable, and maintainable.</p> <h3 style="text-align: left;">Managing tasks with WorkManager</h3> <p>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/work" target="_blank">WorkManager</a> library makes it easy to schedule deferrable, asynchronous tasks that must be run reliably for instance uploading backups or analytics. These APIs let you create a task and hand it off to WorkManager to run when the work constraints are met.</p> <p>WorkManager&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/work#2.8.0-alpha04" target="_blank">2.8.0-alpha04</a> has been updated with the ability to&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/work/WorkManager#updateWork(androidx.work.WorkRequest)" target="_blank">update WorkRequests</a> in a non-intrusive way, preserving original enqueue time, chaining and more. It makes changes to Worker&#8217;s constraints much easier, e.g. if constraints need to be changed from one version of an application to another or via configuration set by server-side. Previously, it was possible to achieve only by canceling already scheduled workers and rescheduling them again. However, this approach was very disruptive: already running workers could have been canceled, cadence of periodic workers could have been broken, and the whole chains of workers required reconstruction if one of them needed an update. Now using the&nbsp;<span id="docs-internal-guid-380c1e82-7fff-b2b7-bdb6-b5ba0ee13a28"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">update</span></span></span>&nbsp;method or&nbsp;<span style="font-family: courier;"><span id="docs-internal-guid-bfb26fcb-7fff-0b56-022c-0f624dc661ec"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">ExistingPeriodicWorkPolicy</span><span style="background-color: #f0f0f0; color: #880000; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">.UPDATE</span></span>&nbsp;</span>developers don&#8217;t have to worry about any of these issues.&nbsp;</p> <p><br /></p> <h3>Data Persistence</h3> <p>Most applications need to persist local state - whether it be caching results, managing local lists of user enter data, or powering data returned in the UI. &nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/room" target="_blank">Room</a> is the recommended data persistence layer which provides an abstraction layer over SQLite, allowing for increased usability and safety over the platform.</p> <p>In Room 2.5.0-alpha03, we added a new shortcut annotation,&nbsp;<span id="docs-internal-guid-a991f455-7fff-60dd-b36c-ced41af9f1c9"><span style="background-color: #f0f0f0; color: #1f7199; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">@Upsert</span></span></span>, which attempts to insert an entity when there is no uniqueness conflict or update the entity if there is a conflict. Moreover, all of Room runtime APIs along with androidx.sqlite have been converted to Kotlin, creating a better experience for Kotlin users such as strict nullability and opening the door to support other Kotlin language features.&nbsp;</p><p><br /></p> <h3>Android 13 Activity APIs, now backward compatible</h3> <p>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/activity#version_160_3" target="_blank">Activity</a> library includes the&nbsp;<span id="docs-internal-guid-e2e64e2d-7fff-2b6e-03d8-d2f2e0a1de53"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">ComponentActivity</span></span></span>&nbsp;class, a base class built on top of the Android framework&#8217;s Activity class which provides APIs that enable Jetpack Compose, other Architecture Components, and support for backporting new features introduced in Android 13 with Activity 1.6.1.</p> <p>By using&nbsp;<span style="background-color: #f0f0f0; color: #444444; font-family: courier; white-space: pre-wrap;">ComponentActivity</span>&nbsp;directly, or either of its subclasses of&nbsp;<span id="docs-internal-guid-069da072-7fff-c3ae-b426-f0e89d352433"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">FragmentActivity</span></span></span>&nbsp;or&nbsp;<span id="docs-internal-guid-0476ed4e-7fff-132f-d061-f58a1c841e1d"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">AppCompatActivity</span></span></span>, you can use a single API to pick images via the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/training/data-storage/shared/photopicker" target="_blank">Photo Picker</a> when it is available with an automatic fallback to the Storage Access Framework to allow support back to Android 4.4 (API 19).</p> <p>You&#8217;ll also be set up for the future with support for the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/guide/navigation/predictive-back-gesture" target="_blank">Predictive back gesture</a> introduced in Android 13 simply by upgrading to Activity 1.6.1. The Activity APIs provide a single API for&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/guide/navigation/navigation-custom-back" target="_blank">custom back navigation</a> that works back to API 14 and is fully compatible with opting in to predictive back gestures.</p><p><br /></p> <h3>Testing Pagination with the Paging Testing library</h3> <p>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/paging/v3-overview" target="_blank">Paging</a> library provides support for loading very large data sets. To get the most of Paging, the integration includes multiple layers of your application - your repository layer, ViewModel layer, and your UI.</p> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Paged data flows from the PagingSource or RemoteMediator components in the repository layer to the Pager component in the ViewModel layer. Then the Pager component exposes a Flow of PagingData to the PagingDataAdapter in the UI layer." border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPc6SyA5vcme5S79W3_N3t3XbjN--CpcvyEiJZ7ndLT6DxDn2fNMRznhL8j2mxEL_TtK98bwJgj-1KCNEdbiGny8TEpxQqu6VYnduU7BolEEoRviviqRTGkKY4guDD-JT-CbobkX1Sr_ibztjfBPvYCgPguOfRC9bB__GKcJpVh_0wrsTzSM5fK8Ha/s1600/image4.png" style="width: 100%;" td="" /></center></td></tr><tr><td class="tr-caption" style="text-align: center;"></td></tr></tbody></table></div> <div style="text-align: left;">To make it easier to test that integration,&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/paging#3.2.0-alpha03" target="_blank">Paging 3.2.0-alpha03</a> introduces a new paging-testing artifact with test specific APIs to make it possible to test each layer in isolation. This first release focuses on the repository layer and specifically around testing a custom PagingSource via the new TestPager APIs to ensure you can test your paging sources in different scenarios that are harder to reproduce with integration tests.</div><p style="text-align: left;"><br /></p> <h3>New Architecture Documentation</h3> <p>Investing in Architecture is important to improve the quality of your app by making it more robust, testable, maintainable, and scalable. That's why our recommendations on Architecture keep growing! In fact, they've grown so much we released a&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/recommendations" target="_blank">new Architecture recommendations page</a> that consolidates and centralizes important best practices you can find in our docs.</p> <p>The team recently released new guidance on&nbsp;modularization. The guide is split into two parts:&nbsp;</p> <p style="text-align: left;"></p><ul style="text-align: left;"><li>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/modularization" target="_blank">overview page</a> gives you a high level, theoretical overview of the matter.&nbsp;</li><li>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/modularization/patterns" target="_blank">common modularization patterns page</a> dives deep into practical examples in the context of the modern Android architecture.</li></ul><p></p><ul> </ul> <p>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer" target="_blank">UI layer docs</a> got two new pages:</p> <p style="text-align: left;"></p><ul style="text-align: left;"><li>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer/stateholders" target="_blank">state holders and UI state page</a> explains the different types of state holders you can find in the UI layer and which implementation you should use depending on the type of logic to perform.&nbsp;</li><li>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer/state-production" target="_blank">state production page</a> that shows best practices about how to model and expose UI state depending on the sources of state change.&nbsp;</li></ul><p></p><ul> </ul> <p>Due to popular demand, the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer/events" target="_blank">UI events page</a> has been updated with examples of&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer/events#navigation-events" target="_blank">Navigation UI events</a>. We also released new Navigation guidance about&nbsp;<a href="https://web.archive.org/web/20230213032228/http://goo.gle/navigation-type-safety" target="_blank">providing runtime type safety to the Kotlin DSL and Navigation Compose</a>.</p> <p>Lastly, if you need to make your app work offline, we got you covered. The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/data-layer/offline-first" target="_blank">build an offline-first app guide</a> helps you design your app to properly handle reads and writes, and deal with sync and conflict resolution in a device with no Internet connectivity.</p><p style="text-align: left;"><br /></p> <h3>New ViewModel Documentation</h3> <p></p><ul style="text-align: left;"><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/viewmodel" target="_blank">ViewModels</a> provide an implementation of the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer/stateholders" target="_blank">state holder pattern</a> as part of your UI layer. We&#8217;ve recently updated the documentation to provide:</li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/viewmodel" target="_blank">An overview</a> of how ViewModels work and the scope they provide.</li><li>How to&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-factories" target="_blank">Create ViewModels with dependencies</a> when not using the prebuilt support for&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/training/dependency-injection/hilt-jetpack#viewmodels" target="_blank">Injecting ViewModel objects with Hilt</a>.</li><li>Details on the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-apis" target="_blank">available scopes for a ViewModel</a> and the APIs that enable that support.</li><li>How ViewModels can save their state across process death and recreation via the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-savedstate" target="_blank">Saved State module for ViewModel</a>.</li></ul><p></p> <ul> </ul> <p>This updated guidance is designed to make it easier to understand when ViewModels are the right tool to reach for when building your UI layer.</p> <p><br /></p> <u><h2 id="Application Performance">Application Performance</h2></u> <p>Using performance libraries allows you to build performant apps and identify optimizations to maintain high performance, resulting in better end-user experiences.&nbsp;</p> <p style="text-align: left;"><br /></p> <h3>Improving Start-up Times</h3> <p>App speed can have a big impact on a user&#8217;s experience, particularly when using apps right after installation. To improve that first time experience, we are continuing to enhance&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/studio/profile/baselineprofiles" target="_blank">Baseline Profiles</a>. Baseline Profiles allow apps and libraries to provide the Android run-time with metadata about code path usage, which it uses to prioritize ahead-of-time compilation. This profile data is aggregated across libraries and lands in an app&#8217;s APK as a baseline.prof file, which is then used at install time to partially pre-compile the app and its statically-linked library code. This can make your apps load faster and reduce dropped frames the first time a user interacts with an app.&nbsp;</p> <p>With AGP 7.3, baseline profile tooling is fully stable, so you don't need alpha dependencies to get a 30%+ performance boost to your app's initial launch and scroll after each app update.&nbsp;</p> <p>In profileinstaller:1.3.0-alpha01,&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/profileinstaller/ProfileVerifier" target="_blank">ProfileVerifier</a> allows you to inspect profile compilation in the field, and starting in Android Studio Flamingo Canary 6, the Studio APK Inspector now shows the contents of your APK's baseline profiles.</p><p><br /></p> <h3>Accurate reporting of startup metrics</h3> <p>Startup metrics are an important part of measuring your app&#8217;s performance, but the system (and the Benchmark libraries!) need a signal that marks the completion of the startup phase. That signal is the Activity&#8217;s call to&nbsp;<span id="docs-internal-guid-50759439-7fff-3ef0-11b9-a28f866059ae"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">reportFullyDrawn()</span></span></span>.&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/activity" target="_blank">Activity</a> 1.7.0-alpha01 added new APIs in the form of the FullyDrawnReporter APIs that allows multiple components to report when they are ready for interaction. ComponentActivity will wait for all components to complete before calling&nbsp;<span style="background-color: #f0f0f0; color: #444444; font-family: courier; white-space: pre-wrap;">reportFullyDrawn()</span>&nbsp;on your behalf.</p> <p>These APIs are encouraged to enable:</p> <p style="text-align: left;"></p><ul style="text-align: left;"><li>Signaling the Android Runtime when startup completes, to ensure all of the code run during a multi-frame startup sequence is included and prioritized for background compilation.</li><li>Signaling Macrobenchmark and Play Vitals when your application should be considered fully drawn for startup metrics, so you can track performance.</li></ul><p></p><ul> </ul> <p>Two Activity Compose APIs,&nbsp;<span id="docs-internal-guid-1ea01b8e-7fff-d61b-9ea9-3fbfeba2ba1a"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">ReportDrawnWhen</span></span></span>&nbsp;and&nbsp;<span id="docs-internal-guid-655e3243-7fff-047a-ce29-142be775abf1"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">ReportDrawnAfter</span></span></span>, have been added to make it more convenient to use the&nbsp;<span id="docs-internal-guid-33b888ef-7fff-3929-6b37-d4d3096b2ac8"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">FullyDrawnReporter</span></span></span>&nbsp;from individual composables.</p><p><br /></p> <h3>Recomposition Tracing</h3> <div style="text-align: left;">We recently launched the first alpha of Jetpack Compose Composition Tracing, a tool that allows you to see composable functions in the Android Studio system trace profiler. This feature combines the benefits of low intrusiveness from system tracing with method tracing levels of detail in your compositions. By adding a dependency on Compose Runtime Tracing, you will be able to see traces of your recomposition call stack in Android Studio Flamingo Canary 5 system traces and click on them to navigate straight to the code! You can read more about the feature and how to set it up in your project&nbsp;<a href="https://web.archive.org/web/20230213032228/https://medium.com/androiddevelopers/jetpack-compose-composition-tracing-9ec2b3aea535" target="_blank">here</a>.</div> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="UI screenshot of composables in the system trace" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivoJUJchRtmFJ4yYz9sNPdVtLr6VfjJ7yAPekv068YGiUdZiitEn3bhSeX9IuBxkkrxhS0pQ_KtHYxrw3rj95YGPLyli4MF2WSBzQDPSgCM7SOWA7ZQQf_l6kThd2YqNwf4BJOZAJRqFAYlSyNB8GjobfrlpAwpnh7AYatjFLA7YP_gd84UqCclYTV/s1600/image2.png" style="width: 100%;" td="" /></center></td></tr><tr><td class="tr-caption" style="text-align: center;"><i>Composables in the system trace</i></td></tr></tbody></table></div> <h2 id="User Interface Libraries and Guidance"><u>User Interface Libraries and Guidance</u></h2> <p><br /></p> <h3>Jetpack Compose</h3> <div style="text-align: left;">Jetpack Compose, Android&#8217;s modern toolkit for building native UI, has launched the Compose October &#8216;22 release which includes many performance improvements and adds support for staggered grids, drawing text directly to canvas, and pull to refresh. We also published our first Bill of Materials (BOM) to simplify the process of adding Compose library versions to your Gradle dependencies. Check out the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-compose.html" target="_blank">What&#8217;s New in Jetpack Compose</a> blog post to learn more.</div> <p><br /></p> <h3>Wear Tiles Material Library</h3> <p>Tiles for Wear OS give users glanceable access to information and actions. To help you create tiles, we launched the Tiles Material library, which includes built-in support for Material Design for Wear OS.</p> <p>The included components are:</p> <p style="text-align: left;"></p><ul style="text-align: left;"><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/Button" target="_blank">Button</a> - a clickable, circular-shaped object, with either icon, text or image with 3 predefined sizes.</li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/Chip" target="_blank">Chip</a> - a clickable, stadium-shaped object that can contain an icon, primary and secondary labels, and has fixed height and customizable width.</li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/CompactChip" target="_blank">CompactChip</a> &amp;&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/TitleChip" target="_blank">TitleChip</a> - two variations of the standard Chip that have smaller and larger heights, respectively, and can contain one line of text.</li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/CircularProgressIndicator" target="_blank">CircularProgressIndicator</a> - a colored arc around the edge of the screen with the given start and end angles, which can describe a full or partial circle with the full progress arc behind it.</li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/Text" target="_blank">Text</a> - a text element which uses the recommended Wear Material typography styles.</li></ul><p></p><div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="common tile components. a round icon with a pencil labelled &#39;button&#39;. a full width rectangle with rounded corners and text labelled &#39;chip&#39;. similar components, one larger and one smaller, labelled &#39;title chip&#39; and &#39;compact chip&#39; respectively. a circle path filled 75% clockwise labelled &#39;circular progress indicator&#39; and finally text labelled &#39;text with recommended typography pre-set&#39;" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-gKpK28YmUhVJ7UMldu2hdOmIQysunbkBlGCGChfkzV2PDz0M_lxQBU-xxyw-g17hIcXak_JrFW206C7xDERI7fOxvBPt6AD3ym1kFow6NQsxwZ3g8RiIw7JH4EWMLP4_LbgZPAT2lCSkdYkN8YP2dNpIVnJ0rBATT4VsZLoyo7AwDTC-lM0_c9E3/s1600/image3.png" style="width: 100%;" td="" /></center></td></tr><tr><td class="tr-caption" style="text-align: center;"><i></i></td></tr></tbody></table></div> <p>In addition to components, there are several recommended tile layouts within Material guidelines.&nbsp;Read more about Wear OS Tiles Material Library in this&nbsp;<a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/08/wear-os-tiles-material-library-build-tiles-fast.html" target="_blank">blog</a>.</p> <p>&nbsp;</p> <h3>Add Splash Screen to more devices</h3> <p>The core&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/core#core-splashscreen-1.0.0" target="_blank">SplashScreen</a> library brings the new Android 12 splash screen to all devices from API 23. Using the splash screen library, your application doesn't need any custom SplashScreen Activity and leverages the right APIs for a fast launch of your application. To use it, simply follow the steps outlined in our&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/guide/topics/ui/splash-screen/migrate#migrate_your_splash_screen_implementation" target="_blank">guide</a>. For more information about the Android 12 splash screen, visit the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/guide/topics/ui/splash-screen" target="_blank">official documentation</a>.</p> <p>&nbsp;</p> <h2><u>Other key updates</u></h2> <p style="text-align: left;"><br /></p><h3>Camera&nbsp;</h3> <p>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/camera" target="_blank">CameraX</a> library makes it easier to add camera capabilities to your app. In 1.2.0-beta01, a&nbsp;new library camera-mlkit-vision was added. It enables an easy integration of CameraX with many MLKit features, including barcode scanning, face detection, text detection, etc. You can find the sample code&nbsp;<a href="https://web.archive.org/web/20230213032228/https://github.com/androidx/androidx/blob/androidx-main/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MlKitFragment.kt" target="_blank">here</a>. We also added a new experimental&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/camera/core/ImageCapture#CAPTURE_MODE_ZERO_SHUTTER_LAG" target="_blank">Zero-Shutter Lag API</a> which optimizes capture pipeline to have better latency while keeping good image quality.&nbsp;</p> <p><br /></p> <h3>Annotation</h3> <div style="text-align: left;">The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/annotation" target="_blank">Annotation</a> library exposes metadata that helps tools and other developers understand your app's code. It provides familiar annotations like @NonNull that pair with lint checks to improve the correctness and usability of your code.</div> <p>Annotation 1.5 stable release has been fully migrated to Kotlin sources, resulting in support for Kotlin-specific target use sites and other Kotlin-compatible annotation features.</p> <p><br /></p> <h3>Kotlin Multiplatform</h3> <p>We have been experimenting with Kotlin Multiplatform Mobile from Jetbrains to enable code sharing across platforms. We have experimental previews of the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/collection" target="_blank">Collections</a> and&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/datastore" target="_blank">DataStore</a> libraries for apps targeting Android and iOS and we would like your feedback! Read more&nbsp;<a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/announcing-experimental-preview-of-jetpack-multiplatform-libraries.html" target="_blank">here</a>.&nbsp;</p> <h2> <hr /> </h2> <p><br /></p> <p>This was a brief tour of all the key changes in Jetpack over the past few months. For more details on each Jetpack library, check out the AndroidX&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/versions" target="_blank">release notes</a>, quickly find relevant libraries with the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/explorer?case=popular" target="_blank">API picker</a> and watch the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://goo.gle/ADS-MAD" target="_blank">Google ADS talks</a> for additional highlights.</p> <p></p> </script> <noscript> <meta content="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKuG3pZKvsbM-kUpZA9fJF5BTED9YxJuHWfcUONNrA9hYvp0UeTjvfYlSvjANimdnsgZ0NeP5namasvB8ifJzHFFMVn3LoVjDWq-Ui0ZIOX0tdP4hd1vgS-Ls2Z7C36le-lTwlpXYknLZcmEHzpSoNW9mL1-u5Y3ycsDCeRnWbJA6go1js5NQaCWGc/s1600/ADS22%20-%20What_s%20new%20in%20Jetpack%20-%20App%20Architecture,%20Performance%20Libraries_1024x512.png" name="twitter:image"></meta> <img src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKuG3pZKvsbM-kUpZA9fJF5BTED9YxJuHWfcUONNrA9hYvp0UeTjvfYlSvjANimdnsgZ0NeP5namasvB8ifJzHFFMVn3LoVjDWq-Ui0ZIOX0tdP4hd1vgS-Ls2Z7C36le-lTwlpXYknLZcmEHzpSoNW9mL1-u5Y3ycsDCeRnWbJA6go1js5NQaCWGc/s1600/ADS22%20-%20What_s%20new%20in%20Jetpack%20-%20App%20Architecture,%20Performance%20Libraries_1024x512.png" style="display: none;"/> <p><em>Posted by Amanda Alexander, Product Manager</em></p><p> <a href="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCzC-3CS0NeTAR1ZOCHwJ6IvdNBCdIey2290oWnQWNAn9Db1lJroBP4uDSYYDbEhRf-G1nCy1uc8yII1xNPL2stcvf_S9mx1fZqRXOWAeZGwpqjw-sVQ10-OZDxy6yDuH3U64851_AxHPytlrANamHZtr9MIp4nH3E-W7MDkO2QZS65s8cuyqeAJMJ/s1600/ADS22%20-%20What_s%20new%20in%20Jetpack%20-%20App%20Architecture,%20Performance%20Libraries_4209x1253.png"><img border="0" data-original-height="800" data-original-width="1058" height="199" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCzC-3CS0NeTAR1ZOCHwJ6IvdNBCdIey2290oWnQWNAn9Db1lJroBP4uDSYYDbEhRf-G1nCy1uc8yII1xNPL2stcvf_S9mx1fZqRXOWAeZGwpqjw-sVQ10-OZDxy6yDuH3U64851_AxHPytlrANamHZtr9MIp4nH3E-W7MDkO2QZS65s8cuyqeAJMJ/w667-h199/ADS22%20-%20What_s%20new%20in%20Jetpack%20-%20App%20Architecture,%20Performance%20Libraries_4209x1253.png" width="667"/></a> </p><p><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack" target="_blank">Android Jetpack</a> is a key component of&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/modern-android-development" target="_blank">Modern Android Development</a>. It is a suite of over 100 libraries, tools and guidance to help developers follow best practices, reduce boilerplate code, and write code that works consistently across Android versions and devices. By using Android Jetpack to improve your productivity, you can focus on building unique features for your app.&nbsp;</p> <p>Most apps on Google Play use Jetpack as a key component of their app architecture, in fact&nbsp;over 90% of the top 1000 apps use Android Jetpack.</p> <p>At ADS this week we released updates to three major areas of Jetpack:</p> <p style="text-align: left;"></p><ol style="text-align: left;"> <li><a href="#Architecture Libraries and Guidance">Architecture Libraries and Guidance</a></li> <li><a href="#Application Performance">Application Performance</a></li> <li><a href="#User Interface Libraries and Guidance">User Interface Libraries and Guidance</a></li></ol><p></p><ol> </ol> <p>We&#8217;ll take a moment to expand on each of these areas and then conclude with some additional updates that we also shipped.</p> <p>Let&#8217;s go&#8230;</p> <p><br/></p> <u><h2 id="Architecture Libraries and Guidance">Architecture Libraries and Guidance</h2></u> <p>App architecture libraries and components ensure that apps are robust, testable, and maintainable.</p> <h3 style="text-align: left;">Managing tasks with WorkManager</h3> <p>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/work" target="_blank">WorkManager</a> library makes it easy to schedule deferrable, asynchronous tasks that must be run reliably for instance uploading backups or analytics. These APIs let you create a task and hand it off to WorkManager to run when the work constraints are met.</p> <p>WorkManager&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/work#2.8.0-alpha04" target="_blank">2.8.0-alpha04</a> has been updated with the ability to&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/work/WorkManager#updateWork(androidx.work.WorkRequest)" target="_blank">update WorkRequests</a> in a non-intrusive way, preserving original enqueue time, chaining and more. It makes changes to Worker&#8217;s constraints much easier, e.g. if constraints need to be changed from one version of an application to another or via configuration set by server-side. Previously, it was possible to achieve only by canceling already scheduled workers and rescheduling them again. However, this approach was very disruptive: already running workers could have been canceled, cadence of periodic workers could have been broken, and the whole chains of workers required reconstruction if one of them needed an update. Now using the&nbsp;<span id="docs-internal-guid-380c1e82-7fff-b2b7-bdb6-b5ba0ee13a28"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">update</span></span></span>&nbsp;method or&nbsp;<span style="font-family: courier;"><span id="docs-internal-guid-bfb26fcb-7fff-0b56-022c-0f624dc661ec"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">ExistingPeriodicWorkPolicy</span><span style="background-color: #f0f0f0; color: #880000; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">.UPDATE</span></span>&nbsp;</span>developers don&#8217;t have to worry about any of these issues.&nbsp;</p> <p><br/></p> <h3>Data Persistence</h3> <p>Most applications need to persist local state - whether it be caching results, managing local lists of user enter data, or powering data returned in the UI. &nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/room" target="_blank">Room</a> is the recommended data persistence layer which provides an abstraction layer over SQLite, allowing for increased usability and safety over the platform.</p> <p>In Room 2.5.0-alpha03, we added a new shortcut annotation,&nbsp;<span id="docs-internal-guid-a991f455-7fff-60dd-b36c-ced41af9f1c9"><span style="background-color: #f0f0f0; color: #1f7199; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">@Upsert</span></span></span>, which attempts to insert an entity when there is no uniqueness conflict or update the entity if there is a conflict. Moreover, all of Room runtime APIs along with androidx.sqlite have been converted to Kotlin, creating a better experience for Kotlin users such as strict nullability and opening the door to support other Kotlin language features.&nbsp;</p><p><br/></p> <h3>Android 13 Activity APIs, now backward compatible</h3> <p>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/activity#version_160_3" target="_blank">Activity</a> library includes the&nbsp;<span id="docs-internal-guid-e2e64e2d-7fff-2b6e-03d8-d2f2e0a1de53"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">ComponentActivity</span></span></span>&nbsp;class, a base class built on top of the Android framework&#8217;s Activity class which provides APIs that enable Jetpack Compose, other Architecture Components, and support for backporting new features introduced in Android 13 with Activity 1.6.1.</p> <p>By using&nbsp;<span style="background-color: #f0f0f0; color: #444444; font-family: courier; white-space: pre-wrap;">ComponentActivity</span>&nbsp;directly, or either of its subclasses of&nbsp;<span id="docs-internal-guid-069da072-7fff-c3ae-b426-f0e89d352433"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">FragmentActivity</span></span></span>&nbsp;or&nbsp;<span id="docs-internal-guid-0476ed4e-7fff-132f-d061-f58a1c841e1d"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">AppCompatActivity</span></span></span>, you can use a single API to pick images via the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/training/data-storage/shared/photopicker" target="_blank">Photo Picker</a> when it is available with an automatic fallback to the Storage Access Framework to allow support back to Android 4.4 (API 19).</p> <p>You&#8217;ll also be set up for the future with support for the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/guide/navigation/predictive-back-gesture" target="_blank">Predictive back gesture</a> introduced in Android 13 simply by upgrading to Activity 1.6.1. The Activity APIs provide a single API for&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/guide/navigation/navigation-custom-back" target="_blank">custom back navigation</a> that works back to API 14 and is fully compatible with opting in to predictive back gestures.</p><p><br/></p> <h3>Testing Pagination with the Paging Testing library</h3> <p>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/paging/v3-overview" target="_blank">Paging</a> library provides support for loading very large data sets. To get the most of Paging, the integration includes multiple layers of your application - your repository layer, ViewModel layer, and your UI.</p> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="Paged data flows from the PagingSource or RemoteMediator components in the repository layer to the Pager component in the ViewModel layer. Then the Pager component exposes a Flow of PagingData to the PagingDataAdapter in the UI layer." border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPc6SyA5vcme5S79W3_N3t3XbjN--CpcvyEiJZ7ndLT6DxDn2fNMRznhL8j2mxEL_TtK98bwJgj-1KCNEdbiGny8TEpxQqu6VYnduU7BolEEoRviviqRTGkKY4guDD-JT-CbobkX1Sr_ibztjfBPvYCgPguOfRC9bB__GKcJpVh_0wrsTzSM5fK8Ha/s1600/image4.png" style="width: 100%;" td=""/></center></td></tr><tr><td class="tr-caption" style="text-align: center;"></td></tr></tbody></table></div> <div style="text-align: left;">To make it easier to test that integration,&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/paging#3.2.0-alpha03" target="_blank">Paging 3.2.0-alpha03</a> introduces a new paging-testing artifact with test specific APIs to make it possible to test each layer in isolation. This first release focuses on the repository layer and specifically around testing a custom PagingSource via the new TestPager APIs to ensure you can test your paging sources in different scenarios that are harder to reproduce with integration tests.</div><p style="text-align: left;"><br/></p> <h3>New Architecture Documentation</h3> <p>Investing in Architecture is important to improve the quality of your app by making it more robust, testable, maintainable, and scalable. That's why our recommendations on Architecture keep growing! In fact, they've grown so much we released a&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/recommendations" target="_blank">new Architecture recommendations page</a> that consolidates and centralizes important best practices you can find in our docs.</p> <p>The team recently released new guidance on&nbsp;modularization. The guide is split into two parts:&nbsp;</p> <p style="text-align: left;"></p><ul style="text-align: left;"><li>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/modularization" target="_blank">overview page</a> gives you a high level, theoretical overview of the matter.&nbsp;</li><li>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/modularization/patterns" target="_blank">common modularization patterns page</a> dives deep into practical examples in the context of the modern Android architecture.</li></ul><p></p><ul> </ul> <p>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer" target="_blank">UI layer docs</a> got two new pages:</p> <p style="text-align: left;"></p><ul style="text-align: left;"><li>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer/stateholders" target="_blank">state holders and UI state page</a> explains the different types of state holders you can find in the UI layer and which implementation you should use depending on the type of logic to perform.&nbsp;</li><li>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer/state-production" target="_blank">state production page</a> that shows best practices about how to model and expose UI state depending on the sources of state change.&nbsp;</li></ul><p></p><ul> </ul> <p>Due to popular demand, the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer/events" target="_blank">UI events page</a> has been updated with examples of&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer/events#navigation-events" target="_blank">Navigation UI events</a>. We also released new Navigation guidance about&nbsp;<a href="https://web.archive.org/web/20230213032228/http://goo.gle/navigation-type-safety" target="_blank">providing runtime type safety to the Kotlin DSL and Navigation Compose</a>.</p> <p>Lastly, if you need to make your app work offline, we got you covered. The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/data-layer/offline-first" target="_blank">build an offline-first app guide</a> helps you design your app to properly handle reads and writes, and deal with sync and conflict resolution in a device with no Internet connectivity.</p><p style="text-align: left;"><br/></p> <h3>New ViewModel Documentation</h3> <p></p><ul style="text-align: left;"><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/viewmodel" target="_blank">ViewModels</a> provide an implementation of the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/architecture/ui-layer/stateholders" target="_blank">state holder pattern</a> as part of your UI layer. We&#8217;ve recently updated the documentation to provide:</li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/viewmodel" target="_blank">An overview</a> of how ViewModels work and the scope they provide.</li><li>How to&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-factories" target="_blank">Create ViewModels with dependencies</a> when not using the prebuilt support for&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/training/dependency-injection/hilt-jetpack#viewmodels" target="_blank">Injecting ViewModel objects with Hilt</a>.</li><li>Details on the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-apis" target="_blank">available scopes for a ViewModel</a> and the APIs that enable that support.</li><li>How ViewModels can save their state across process death and recreation via the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-savedstate" target="_blank">Saved State module for ViewModel</a>.</li></ul><p></p> <ul> </ul> <p>This updated guidance is designed to make it easier to understand when ViewModels are the right tool to reach for when building your UI layer.</p> <p><br/></p> <u><h2 id="Application Performance">Application Performance</h2></u> <p>Using performance libraries allows you to build performant apps and identify optimizations to maintain high performance, resulting in better end-user experiences.&nbsp;</p> <p style="text-align: left;"><br/></p> <h3>Improving Start-up Times</h3> <p>App speed can have a big impact on a user&#8217;s experience, particularly when using apps right after installation. To improve that first time experience, we are continuing to enhance&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/studio/profile/baselineprofiles" target="_blank">Baseline Profiles</a>. Baseline Profiles allow apps and libraries to provide the Android run-time with metadata about code path usage, which it uses to prioritize ahead-of-time compilation. This profile data is aggregated across libraries and lands in an app&#8217;s APK as a baseline.prof file, which is then used at install time to partially pre-compile the app and its statically-linked library code. This can make your apps load faster and reduce dropped frames the first time a user interacts with an app.&nbsp;</p> <p>With AGP 7.3, baseline profile tooling is fully stable, so you don't need alpha dependencies to get a 30%+ performance boost to your app's initial launch and scroll after each app update.&nbsp;</p> <p>In profileinstaller:1.3.0-alpha01,&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/profileinstaller/ProfileVerifier" target="_blank">ProfileVerifier</a> allows you to inspect profile compilation in the field, and starting in Android Studio Flamingo Canary 6, the Studio APK Inspector now shows the contents of your APK's baseline profiles.</p><p><br/></p> <h3>Accurate reporting of startup metrics</h3> <p>Startup metrics are an important part of measuring your app&#8217;s performance, but the system (and the Benchmark libraries!) need a signal that marks the completion of the startup phase. That signal is the Activity&#8217;s call to&nbsp;<span id="docs-internal-guid-50759439-7fff-3ef0-11b9-a28f866059ae"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">reportFullyDrawn()</span></span></span>.&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/activity" target="_blank">Activity</a> 1.7.0-alpha01 added new APIs in the form of the FullyDrawnReporter APIs that allows multiple components to report when they are ready for interaction. ComponentActivity will wait for all components to complete before calling&nbsp;<span style="background-color: #f0f0f0; color: #444444; font-family: courier; white-space: pre-wrap;">reportFullyDrawn()</span>&nbsp;on your behalf.</p> <p>These APIs are encouraged to enable:</p> <p style="text-align: left;"></p><ul style="text-align: left;"><li>Signaling the Android Runtime when startup completes, to ensure all of the code run during a multi-frame startup sequence is included and prioritized for background compilation.</li><li>Signaling Macrobenchmark and Play Vitals when your application should be considered fully drawn for startup metrics, so you can track performance.</li></ul><p></p><ul> </ul> <p>Two Activity Compose APIs,&nbsp;<span id="docs-internal-guid-1ea01b8e-7fff-d61b-9ea9-3fbfeba2ba1a"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">ReportDrawnWhen</span></span></span>&nbsp;and&nbsp;<span id="docs-internal-guid-655e3243-7fff-047a-ce29-142be775abf1"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">ReportDrawnAfter</span></span></span>, have been added to make it more convenient to use the&nbsp;<span id="docs-internal-guid-33b888ef-7fff-3929-6b37-d4d3096b2ac8"><span style="background-color: #f0f0f0; color: #444444; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: courier;">FullyDrawnReporter</span></span></span>&nbsp;from individual composables.</p><p><br/></p> <h3>Recomposition Tracing</h3> <div style="text-align: left;">We recently launched the first alpha of Jetpack Compose Composition Tracing, a tool that allows you to see composable functions in the Android Studio system trace profiler. This feature combines the benefits of low intrusiveness from system tracing with method tracing levels of detail in your compositions. By adding a dependency on Compose Runtime Tracing, you will be able to see traces of your recomposition call stack in Android Studio Flamingo Canary 5 system traces and click on them to navigate straight to the code! You can read more about the feature and how to set it up in your project&nbsp;<a href="https://web.archive.org/web/20230213032228/https://medium.com/androiddevelopers/jetpack-compose-composition-tracing-9ec2b3aea535" target="_blank">here</a>.</div> <div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="UI screenshot of composables in the system trace" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivoJUJchRtmFJ4yYz9sNPdVtLr6VfjJ7yAPekv068YGiUdZiitEn3bhSeX9IuBxkkrxhS0pQ_KtHYxrw3rj95YGPLyli4MF2WSBzQDPSgCM7SOWA7ZQQf_l6kThd2YqNwf4BJOZAJRqFAYlSyNB8GjobfrlpAwpnh7AYatjFLA7YP_gd84UqCclYTV/s1600/image2.png" style="width: 100%;" td=""/></center></td></tr><tr><td class="tr-caption" style="text-align: center;"><i>Composables in the system trace</i></td></tr></tbody></table></div> <h2 id="User Interface Libraries and Guidance"><u>User Interface Libraries and Guidance</u></h2> <p><br/></p> <h3>Jetpack Compose</h3> <div style="text-align: left;">Jetpack Compose, Android&#8217;s modern toolkit for building native UI, has launched the Compose October &#8216;22 release which includes many performance improvements and adds support for staggered grids, drawing text directly to canvas, and pull to refresh. We also published our first Bill of Materials (BOM) to simplify the process of adding Compose library versions to your Gradle dependencies. Check out the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-compose.html" target="_blank">What&#8217;s New in Jetpack Compose</a> blog post to learn more.</div> <p><br/></p> <h3>Wear Tiles Material Library</h3> <p>Tiles for Wear OS give users glanceable access to information and actions. To help you create tiles, we launched the Tiles Material library, which includes built-in support for Material Design for Wear OS.</p> <p>The included components are:</p> <p style="text-align: left;"></p><ul style="text-align: left;"><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/Button" target="_blank">Button</a> - a clickable, circular-shaped object, with either icon, text or image with 3 predefined sizes.</li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/Chip" target="_blank">Chip</a> - a clickable, stadium-shaped object that can contain an icon, primary and secondary labels, and has fixed height and customizable width.</li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/CompactChip" target="_blank">CompactChip</a> &amp;&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/TitleChip" target="_blank">TitleChip</a> - two variations of the standard Chip that have smaller and larger heights, respectively, and can contain one line of text.</li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/CircularProgressIndicator" target="_blank">CircularProgressIndicator</a> - a colored arc around the edge of the screen with the given start and end angles, which can describe a full or partial circle with the full progress arc behind it.</li><li><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/wear/tiles/material/Text" target="_blank">Text</a> - a text element which uses the recommended Wear Material typography styles.</li></ul><p></p><div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><center><img alt="common tile components. a round icon with a pencil labelled 'button'. a full width rectangle with rounded corners and text labelled 'chip'. similar components, one larger and one smaller, labelled 'title chip' and 'compact chip' respectively. a circle path filled 75% clockwise labelled 'circular progress indicator' and finally text labelled 'text with recommended typography pre-set'" border="0" data-original-height="1504" data-original-width="720" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-gKpK28YmUhVJ7UMldu2hdOmIQysunbkBlGCGChfkzV2PDz0M_lxQBU-xxyw-g17hIcXak_JrFW206C7xDERI7fOxvBPt6AD3ym1kFow6NQsxwZ3g8RiIw7JH4EWMLP4_LbgZPAT2lCSkdYkN8YP2dNpIVnJ0rBATT4VsZLoyo7AwDTC-lM0_c9E3/s1600/image3.png" style="width: 100%;" td=""/></center></td></tr><tr><td class="tr-caption" style="text-align: center;"><i></i></td></tr></tbody></table></div> <p>In addition to components, there are several recommended tile layouts within Material guidelines.&nbsp;Read more about Wear OS Tiles Material Library in this&nbsp;<a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/08/wear-os-tiles-material-library-build-tiles-fast.html" target="_blank">blog</a>.</p> <p>&nbsp;</p> <h3>Add Splash Screen to more devices</h3> <p>The core&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/core#core-splashscreen-1.0.0" target="_blank">SplashScreen</a> library brings the new Android 12 splash screen to all devices from API 23. Using the splash screen library, your application doesn't need any custom SplashScreen Activity and leverages the right APIs for a fast launch of your application. To use it, simply follow the steps outlined in our&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/guide/topics/ui/splash-screen/migrate#migrate_your_splash_screen_implementation" target="_blank">guide</a>. For more information about the Android 12 splash screen, visit the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/guide/topics/ui/splash-screen" target="_blank">official documentation</a>.</p> <p>&nbsp;</p> <h2><u>Other key updates</u></h2> <p style="text-align: left;"><br/></p><h3>Camera&nbsp;</h3> <p>The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/camera" target="_blank">CameraX</a> library makes it easier to add camera capabilities to your app. In 1.2.0-beta01, a&nbsp;new library camera-mlkit-vision was added. It enables an easy integration of CameraX with many MLKit features, including barcode scanning, face detection, text detection, etc. You can find the sample code&nbsp;<a href="https://web.archive.org/web/20230213032228/https://github.com/androidx/androidx/blob/androidx-main/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MlKitFragment.kt" target="_blank">here</a>. We also added a new experimental&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/androidx/camera/core/ImageCapture#CAPTURE_MODE_ZERO_SHUTTER_LAG" target="_blank">Zero-Shutter Lag API</a> which optimizes capture pipeline to have better latency while keeping good image quality.&nbsp;</p> <p><br/></p> <h3>Annotation</h3> <div style="text-align: left;">The&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/annotation" target="_blank">Annotation</a> library exposes metadata that helps tools and other developers understand your app's code. It provides familiar annotations like @NonNull that pair with lint checks to improve the correctness and usability of your code.</div> <p>Annotation 1.5 stable release has been fully migrated to Kotlin sources, resulting in support for Kotlin-specific target use sites and other Kotlin-compatible annotation features.</p> <p><br/></p> <h3>Kotlin Multiplatform</h3> <p>We have been experimenting with Kotlin Multiplatform Mobile from Jetbrains to enable code sharing across platforms. We have experimental previews of the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/collection" target="_blank">Collections</a> and&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/topic/libraries/architecture/datastore" target="_blank">DataStore</a> libraries for apps targeting Android and iOS and we would like your feedback! Read more&nbsp;<a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/announcing-experimental-preview-of-jetpack-multiplatform-libraries.html" target="_blank">here</a>.&nbsp;</p> <h2> <hr/> </h2> <p><br/></p> <p>This was a brief tour of all the key changes in Jetpack over the past few months. For more details on each Jetpack library, check out the AndroidX&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/versions" target="_blank">release notes</a>, quickly find relevant libraries with the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/explorer?case=popular" target="_blank">API picker</a> and watch the&nbsp;<a href="https://web.archive.org/web/20230213032228/https://goo.gle/ADS-MAD" target="_blank">Google ADS talks</a> for additional highlights.</p> <p></p> </noscript> </div> </div> <div class="share"> <span class="twitter-custom social-wrapper" data-href="http://twitter.com/share?text=Android Developers Blog:Android Dev Summit ‘22: What’s new in Jetpack &amp;url=https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-ads-22.html&amp;via=google"> <img alt="Share on Twitter" height="24" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png" width="24"/> </span> <span class="fb-custom social-wrapper" data-href="https://www.facebook.com/sharer.php?u=https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-ads-22.html"> <img alt="Share on Facebook" height="24" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png" width="24"/> </span> <span class="email-custom social-wrapper" data-href="mailto:?body=https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-ads-22.html"> <img alt="Share by email" height="24" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAMFBMVEVMaXEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACbsZG/AAAAEHRSTlMAILDy/y78bg9wzE/jjwyQQ3EYrQAAAMpJREFUeAHN0yHIwmAQxvHng2/IypDZw9AOyuziDiemgb2tCgsGO4LY7cneg73Yl+0dsy8PcuzdsTaGf7D4e3cbbIdh+ptKq0VBmIhpSZhZWBNSCxmhFFv/sDmh0aVUyOF1VNj6cFZYIYEW478BWYVvYaogDmRXgAV38eAhhxdc0VueHkS1+wPggSZwRALEHOkBRmlWhe6HFvA0rzLg5vM+FqKaT2YBQQEL2u+DebUKuQ83Bf/zuZYKtn6he3G6V21vYU4I7DqPMUgfpvKO2fcD4TMAAAAASUVORK5CYII=" target="_blank" width="24"/> </span> </div> <div class="post-footer"> <div class="cmt_iframe_holder" data-href="https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-ads-22.html" data-viewtype="FILTERED_POSTMOD"></div> <a href="https://web.archive.org/web/20230213032228/https://plus.google.com/112374322230920073195" rel="author" style="display: none"> Google </a> <div class="label-footer"> <span class="labels-caption"> Labels: </span> <span class="labels"> <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/%23androiddevsummit" rel="tag"> #androiddevsummit </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack" rel="tag"> Jetpack </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Modern%20Android%20Development" rel="tag"> Modern Android Development </a> </span> </div> </div> </div> <div class="post" data-id="4805062828559402052" itemscope="" itemtype="http://schema.org/BlogPosting"> <h2 class="title" itemprop="name"> <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-compose.html" itemprop="url" title="What’s new in Jetpack Compose"> What&#8217;s new in Jetpack Compose </a> </h2> <div class="post-header"> <div class="published"> <span class="publishdate" itemprop="datePublished"> 24 October 2022 </span> </div> </div> <div class="post-body"> <div class="post-content" itemprop="articleBody"> <script type="text/template"> <meta content="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD57qOWeCNuSagChKRfqBBNn3IN1j7SEbohUhWXXzEwtsM6m9uHkzv_N5yCsBcLDhJhDV1J-7Omwf9vFe7DP31UNxnFzooj8JWgL9DRNvgHuoF7BtnKl38yqoZ5NGR0AnkXxeOO1-XCm8jzwtqbHhooKkqLZg28idBo1QHhGWXaAk3vOtHpAzQjhUw/s1600/ADS_22%20-%20What_s%20new%20in%20Jetpack%20Compose%20ADS22-SOCIAL.png" name="twitter:image"> <img src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD57qOWeCNuSagChKRfqBBNn3IN1j7SEbohUhWXXzEwtsM6m9uHkzv_N5yCsBcLDhJhDV1J-7Omwf9vFe7DP31UNxnFzooj8JWgL9DRNvgHuoF7BtnKl38yqoZ5NGR0AnkXxeOO1-XCm8jzwtqbHhooKkqLZg28idBo1QHhGWXaAk3vOtHpAzQjhUw/s1600/ADS_22%20-%20What_s%20new%20in%20Jetpack%20Compose%20ADS22-SOCIAL.png" style="display: none;"> <p><em>Posted by <a href="https://web.archive.org/web/20230213032228/https://twitter.com/lojanda" target="_blank">Jolanda Verhoef</a>, Android Developer Relations Engineer</em></p><p> <a href="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8DjU1NkAz6KDyTpTK2bNnJDk67OwdEQiaBN5d2pMmMCd0B77D2S-gCqrT6ZDvojuhSrj9KGSqip3o0DFCsGmtgv3DF2j1Nr1tsaglwnBwO4UIVl9LRHei4B6XVlHGMjoWwdm9NPm5U7W_g7oXsg0hL49cwU51VbyKwUyLfEIE0zYFbYGIXM9zGypI/s1600/ADS_22%20-%20What_s%20new%20in%20Jetpack%20Compose%20ADS22.png"><img border="0" data-original-height="800" data-original-width="1058" height="204" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8DjU1NkAz6KDyTpTK2bNnJDk67OwdEQiaBN5d2pMmMCd0B77D2S-gCqrT6ZDvojuhSrj9KGSqip3o0DFCsGmtgv3DF2j1Nr1tsaglwnBwO4UIVl9LRHei4B6XVlHGMjoWwdm9NPm5U7W_g7oXsg0hL49cwU51VbyKwUyLfEIE0zYFbYGIXM9zGypI/w687-h204/ADS_22%20-%20What_s%20new%20in%20Jetpack%20Compose%20ADS22.png" width="687"></a> </p><p> We launched <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose" target="_blank">Jetpack Compose</a> over a year ago, and have been busy improving it ever since. We&#8217;ve added new features and invented powerful tools to make your experience developing Android UI as productive, intuitive and fun as possible. So, if you&#39;re starting a new app, it&#39;s time to write it with Compose! With Material Design 3 support, new Bill Of Materials, <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/07/compose-for-wear-os-10-stable.html" target="_blank">Compose for Wear OS Stable</a> and Android TV (alpha), Compose Camp, and many other pieces of news&#8230; It&#39;s an exciting release! </p> <h2>Compose in the Community</h2> <div style="text-align: left;"> In the last year, we&#8217;ve seen many companies <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose/adopt#what-developers-are-saying" target="_blank">developing with Compose at scale</a>, implementing new features and migrating screens from views to Compose. For example, we talked to the engineers at Lyft, who told us that<strong> over 90% of their new feature code is written in Compose, and <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/lyft-reduced-their-code-for-ui-components-using-jetpack-compose.html" target="_blank">moving to Compose made their code much simpler and easier to maintain</a></strong>. They also shared <em>&#8220;We rewrote the button component in our app using Compose. Before it required 800 lines of code across three files plus 17 different XML files, and it is now down to a single Kotlin file with 300 lines of code. This is a 60% reduction in the Kotlin code alone&#8220;.</em> The team at Zepeto has also been implementing Compose across many features, and are enjoying the experience, as <em>&#8220;Compose simplified our UI layer logic, making it <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/zepeto-plans-to-migrate-80-percent-of-apps-ui-to-jetpack-compose.html">easier to understand code written by my colleagues</a>.&#8221;</em></div><div style="text-align: left;"><iframe allowfullscreen="" class="BLOG_video_class" height="315" src="https://web.archive.org/web/20230213032228/https://www.youtube.com/embed/QO6Cg9MSpE8" width="800" youtube-src-id="QO6Cg9MSpE8"></iframe></div><div style="text-align: left;">It&#8217;s great to see how these teams experience faster development cycles, and also feel their UI code is more concise and readable. And they&#8217;re not the only ones. Since this year&#8217;s Google I/O, the number of top 1000 apps on Google Play using Compose has increased by 50%! To help your team follow in the footsteps of the teams at Lyft, Zepeto, and others, we published a guide on <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose/adopt/for-large-teams" target="_blank">How to Adopt Compose for your Team</a>. It outlines how and where to start, and shows the areas of development where Compose can bring huge added value.</div><p><br></p> <h2>Compose, October &#8216;22 release</h2> <p> Today we&#8217;re releasing a new stable version of Compose, with some exciting features and news. </p> <p> First of all, we&#8217;ve heard from you how it can be daunting to track versions across different artifacts that might go on different release schedules, so we&#8217;re now publishing, together with every Stable release of any of the Compose artifacts, a Bill of Materials, or BOM, to make your life easier. </p> <p> Our first BOM release, Compose October &#8216;22, brings support for Staggered Grids, drawing Text directly to Canvas, Pull to Refresh, as well as performance improvements and bug fixes. </p><p><br></p> <h3 style="text-align: left;">Compose Bill of Materials</h3> <p> A BOM is a Maven module that declares a set of libraries with their versions. It will greatly simplify the way you define Compose library versions in your Gradle dependencies block, especially now that we <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/06/independent-versioning-of-Jetpack-Compose-libraries.html" target="_blank">moved the various Jetpack Compose libraries to independent versioning schemes</a>. Instead of defining each version separately, which can become cumbersome and prone to errors when library versions start to differ, you now only need to define one BOM version and all Compose library versions will be extracted from that. We will publish a new version of the BOM every time a Compose artifact has a new stable release, so moving from stable release to stable release is going to be much simpler. </p><p><span id="docs-internal-guid-50e8e6d1-7fff-02b4-b0d7-3d3c98d386b3"></span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="719"></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f0f0f0; border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: courier;"><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">dependencies {</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; </span><span style="background-color: #f0f0f0; color: #888888; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">// Import the Compose BOM</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">implementation</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> platform(</span><span style="background-color: #f0f0f0; color: #880000; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#39;androidx.compose:compose-bom:2022.10.00&#39;</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">)</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; </span><span style="background-color: #f0f0f0; color: #888888; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">// Declare dependencies for the desired Compose libraries without versions</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">implementation</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: #f0f0f0; color: #880000; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#39;androidx.compose.foundation:foundation&#39;</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; androidTestImplementation </span><span style="background-color: #f0f0f0; color: #880000; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#39;androidx.compose.ui:ui-test-junit4&#39;</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; ...</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></span></p></td></tr></tbody></table><br></div>We&#8217;ve added the instructions on how to add the Compose BOM to our <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose/setup" target="_blank">Quick start guide</a>. Note that you can still choose to define your dependencies using hard-coded versions. The BOM is added as a useful way to simplify dependencies and make upgrades easier.<div><span>&#160;&#160; &#160;</span></div><div><br> <h3 style="text-align: left;">Modifiers on overdrive</h3> <p> Behind the scenes, we&#8217;re always working on improving Compose performance. The October &#8216;22 release includes a major refactor of how Modifiers work under the hood. While you will not notice anything changing in the APIs, this refactor paves the way for greatly improving Modifier performance. Learn more about the rationale behind the changes, and what&#8217;s planned for the near future in the ADS talk <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/events/dev-summit/technical-talks" target="_blank">Compose Modifiers deep dive</a>.<iframe allowfullscreen="" class="BLOG_video_class" height="312" src="https://web.archive.org/web/20230213032228/https://www.youtube.com/embed/BjGX2RftXsU" width="800" youtube-src-id="BjGX2RftXsU"></iframe></p><p></p><div style="text-align: left;"><br></div><h3 style="text-align: left;">Popup &amp; Dialog elevation change</h3><p></p> <p> Accessibility is always a first-class citizen for Compose, and this release contains a behavior change that helps fix an Accessibility bug with Popups and Dialogs: their maximum elevation is decreased from 30dp to 8dp. Your app will be impacted only if it uses a custom dialog or popup implementation with an elevation higher than 8dp. The <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/compose-ui" target="_blank">release notes</a> contain more information about the change, including a way to override the new behavior as an interim solution (keep in mind that we always recommend using 8dp maximum when customizing popups or dialogs). </p><p><br></p> <h3 style="text-align: left;">New features</h3> <div><p> We added a lot of new functionality to Compose. Here are some highlights: </p><ul style="text-align: left;"><li> Implement staggered grids using the new <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/staggeredgrid/package-summary#LazyHorizontalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)" target="_blank"><span style="font-family: courier; font-size: medium;">LazyHorizontalStaggeredGrid</span></a></code> and <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/staggeredgrid/package-summary#LazyVerticalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)" target="_blank"><span style="font-family: courier; font-size: medium;">LazyVerticalStaggeredGrid</span></a></code></li><li>Draw text directly to Canvas using <code><span style="font-family: courier; font-size: medium;"><a href="https://web.archive.org/web/20230213032228/http://drawscope.drawtext" target="_blank">DrawScope.drawText</a></span></code></li><li>You can add a <a href="https://web.archive.org/web/20230213032228/https://fonts.google.com/knowledge/topics/variable_fonts" target="_blank">variable font</a> to your app and change its properties using the <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/ui/text/font/FontVariation" target="_blank"><span style="font-family: courier; font-size: medium;">FontVariation</span></a></code> object.</li><li>Add a <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/ui/text/UrlAnnotation" target="_blank"><span style="font-family: courier; font-size: medium;">UrlAnnotation</span></a><span style="font-family: courier;">&#160;</span>to your annotated string to improve Accessibility services interacting with the text.</li><li>Add hyphenation to your text using the new&#160;<span style="font-family: courier;"><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/ui/text/style/LineBreak" target="_blank"><span style="font-size: medium;">LineBreak</span></a> </span>API.</li><li><a href="https://web.archive.org/web/20230213032228/https://material.io/design/platform-guidance/android-swipe-to-refresh" target="_blank">Swipe to refresh</a> with the new <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/material/pullrefresh/package-summary#(androidx.compose.ui.Modifier).pullRefresh(androidx.compose.material.pullrefresh.PullRefreshState,kotlin.Boolean)" target="_blank"><span style="font-family: courier; font-size: medium;">pullRefresh</span></a></code> modifier.</li><li>Add snapping behavior to your lazy lists using <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior" target="_blank">SnapFlingBehavior</a>.</li><li><code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/ui/layout/package-summary#LookaheadLayout(kotlin.Function1,androidx.compose.ui.Modifier,androidx.compose.ui.layout.MeasurePolicy)" target="_blank"><span style="font-family: courier; font-size: medium;">LookAheadLayout</span></a></code> is a new type of layout that provides information about final measurement and placement of its children so you can decide on intermediate layouts.</li></ul></div> <div><h2>Compose Material 3 stable</h2></div> <a href="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBITldrbxnjPjiWhqzeO4ftBFoVNxXHLpqmk7OpJMfsCbNd1s6H4NHEKn1k1MNCFHumWnBIDKx3DJKd3IL_fV9oftKghZp3-fNe4ReDb6MiLQbmhBPtgC2pPA8qGtvR_66cTImgqHnHRXhtmph98ILXH62b_QB409tisYkKZpoMsL4exIkVixvQHB-/s1600/image2.png" style="text-align: center;"><img border="0" data-original-height="800" data-original-width="1058" height="357" src="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBITldrbxnjPjiWhqzeO4ftBFoVNxXHLpqmk7OpJMfsCbNd1s6H4NHEKn1k1MNCFHumWnBIDKx3DJKd3IL_fV9oftKghZp3-fNe4ReDb6MiLQbmhBPtgC2pPA8qGtvR_66cTImgqHnHRXhtmph98ILXH62b_QB409tisYkKZpoMsL4exIkVixvQHB-/w640-h357/image2.png" width="640"></a> <div><p>Today we also announce the first stable release of the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/compose-material3" target="_blank">Compose Material 3 library</a>! You can build an app using Compose and theme it according to <a href="https://web.archive.org/web/20230213032228/https://m3.material.io/" target="_blank">Material Design 3</a>, our latest iteration of Material Design. Use Material Design 3 to further customize your app&#8217;s colors, typography and shapes to make your brand stand out! The library contains fresh and updated versions of many UI components, such as buttons, cards, checkboxes, switches, navigation bars, drawers, and many more, with support for others on its way. See a list of all the supported components in the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#overview" target="_blank">documentation</a>&#160;and learn more in <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/material-design-3-compose-stable.html">this blog post</a>.</p></div> <p> To help you adopt Material 3 check out our new <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose/themes/material2-material3" target="_blank">migration guide</a> with clear guidance on how Material 2 concepts translate to Material 3. The default template in Android Studio Flamingo now uses Material 3, to get you up and running in no time. We&#8217;ve also updated many of our sample apps, tutorials, templates, and codelabs to use Material 3 so you can learn as you go! </p> <h2 style="text-align: left;">New tools</h2> <p> Developing your app using Jetpack Compose is much easier with the new and improved tools around it. We&#8217;ve added tons of new features to Android Studio to improve your workflow and efficiency: </p> <p> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/studio/releases#new_in_compose" target="_blank">Android Studio Dolphin</a> is the latest <strong>stable release</strong>, bringing you: </p> <ul> <li>Animation Coordination </li><li>Multipreview annotations </li><li>Recomposition counts in Layout Inspector </li> </ul> <p> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/studio/preview/features#2022.1.1" target="_blank">Android Studio Electric Eel</a> contains <strong>beta features</strong>, like: </p> <ul> <li>Live Edit (experimental) </li><li>Composition rendering highlighting </li><li>Configuring Preview devices </li><li>Live updates in Previews </li> </ul> <p> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/studio/preview/features#2022.2.1" target="_blank">Android Studio Flamingo</a> contains <strong>canary features</strong> such as: </p> <ul> <li>New project templates use Compose and Material 3 by default </li><li>Live Edit turned on by default </li><li>Improved composition tracing to help you better inspect performance issues.</li></ul><br><ul> </ul> <h3 style="text-align: left;"><strong>Relay</strong></h3><p> Today we also launch the first alpha version of <a href="https://web.archive.org/web/20230213032228/https://relay.material.io/" target="_blank">Relay</a>, a design-to-code solution for improving designer-developer collaboration. Designers create UI components using the <a href="https://web.archive.org/web/20230213032228/https://www.figma.com/community/plugin/1041056822461507786" target="_blank">Figma plugin</a>, and developers use the <a href="https://web.archive.org/web/20230213032228/https://plugins.jetbrains.com/plugin/19721-relay-for-android-studio/" target="_blank">Android Studio plugin</a> to automatically use these components in their apps. The generated components are composable functions and can be integrated directly into your Compose app. Learn more about Relay in the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose/tooling/relay" target="_blank">documentation</a>. </p><p><br></p> <h2 style="text-align: left;">Compose on Wear OS and Large Screens</h2> <p> In July we released the first Stable version of <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/07/compose-for-wear-os-10-stable.html" target="_blank">Compose for Wear OS</a>, ready to build production apps. <strong>Compose for Wear OS is our recommended approach for building UIs for Wear OS apps</strong>. We&#8217;ve included over twenty Compose UI components that were designed specifically for Wearables, like <code><span style="font-family: courier; font-size: medium;">TimeText</span></code>, <code><span style="font-family: courier; font-size: medium;">PositionIndicator</span></code>, and <code><span style="font-family: courier; font-size: medium;">ScalingLazyColumn</span></code>. </p> <p> We&#8217;re also continuing to make it easier to design, develop, and test <strong>apps for large screens</strong> such as foldables, tablets, and Chrome OS. The <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/compose-material3" target="_blank"><span style="font-family: courier; font-size: medium;">material3-window-size-class</span></a></code> library graduated to Stable, giving you a set of opinionated viewport breakpoints to work with. Large screen designs often contain staggered grids, and the addition of <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/staggeredgrid/package-summary#LazyHorizontalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)" target="_blank"><span style="font-family: courier; font-size: medium;">LazyHorizontalStaggeredGrid</span></a></code> and&#160;<span style="font-family: courier; font-size: medium;"><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/staggeredgrid/package-summary#LazyVerticalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)" target="_blank">LazyVerticalStaggeredGrid</a></span><span style="font-size: medium;">&#160;</span>will help implement these.</p><span></span></div> </script> <noscript> <meta content="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD57qOWeCNuSagChKRfqBBNn3IN1j7SEbohUhWXXzEwtsM6m9uHkzv_N5yCsBcLDhJhDV1J-7Omwf9vFe7DP31UNxnFzooj8JWgL9DRNvgHuoF7BtnKl38yqoZ5NGR0AnkXxeOO1-XCm8jzwtqbHhooKkqLZg28idBo1QHhGWXaAk3vOtHpAzQjhUw/s1600/ADS_22%20-%20What_s%20new%20in%20Jetpack%20Compose%20ADS22-SOCIAL.png" name="twitter:image"> <img src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD57qOWeCNuSagChKRfqBBNn3IN1j7SEbohUhWXXzEwtsM6m9uHkzv_N5yCsBcLDhJhDV1J-7Omwf9vFe7DP31UNxnFzooj8JWgL9DRNvgHuoF7BtnKl38yqoZ5NGR0AnkXxeOO1-XCm8jzwtqbHhooKkqLZg28idBo1QHhGWXaAk3vOtHpAzQjhUw/s1600/ADS_22%20-%20What_s%20new%20in%20Jetpack%20Compose%20ADS22-SOCIAL.png" style="display: none;"> <p><em>Posted by <a href="https://web.archive.org/web/20230213032228/https://twitter.com/lojanda" target="_blank">Jolanda Verhoef</a>, Android Developer Relations Engineer</em></p><p> <a href="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8DjU1NkAz6KDyTpTK2bNnJDk67OwdEQiaBN5d2pMmMCd0B77D2S-gCqrT6ZDvojuhSrj9KGSqip3o0DFCsGmtgv3DF2j1Nr1tsaglwnBwO4UIVl9LRHei4B6XVlHGMjoWwdm9NPm5U7W_g7oXsg0hL49cwU51VbyKwUyLfEIE0zYFbYGIXM9zGypI/s1600/ADS_22%20-%20What_s%20new%20in%20Jetpack%20Compose%20ADS22.png"><img border="0" data-original-height="800" data-original-width="1058" height="204" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8DjU1NkAz6KDyTpTK2bNnJDk67OwdEQiaBN5d2pMmMCd0B77D2S-gCqrT6ZDvojuhSrj9KGSqip3o0DFCsGmtgv3DF2j1Nr1tsaglwnBwO4UIVl9LRHei4B6XVlHGMjoWwdm9NPm5U7W_g7oXsg0hL49cwU51VbyKwUyLfEIE0zYFbYGIXM9zGypI/w687-h204/ADS_22%20-%20What_s%20new%20in%20Jetpack%20Compose%20ADS22.png" width="687"></a> </p><p> We launched <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose" target="_blank">Jetpack Compose</a> over a year ago, and have been busy improving it ever since. We&#8217;ve added new features and invented powerful tools to make your experience developing Android UI as productive, intuitive and fun as possible. So, if you&#39;re starting a new app, it&#39;s time to write it with Compose! With Material Design 3 support, new Bill Of Materials, <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/07/compose-for-wear-os-10-stable.html" target="_blank">Compose for Wear OS Stable</a> and Android TV (alpha), Compose Camp, and many other pieces of news&#8230; It&#39;s an exciting release! </p> <h2>Compose in the Community</h2> <div style="text-align: left;"> In the last year, we&#8217;ve seen many companies <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose/adopt#what-developers-are-saying" target="_blank">developing with Compose at scale</a>, implementing new features and migrating screens from views to Compose. For example, we talked to the engineers at Lyft, who told us that<strong> over 90% of their new feature code is written in Compose, and <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/lyft-reduced-their-code-for-ui-components-using-jetpack-compose.html" target="_blank">moving to Compose made their code much simpler and easier to maintain</a></strong>. They also shared <em>&#8220;We rewrote the button component in our app using Compose. Before it required 800 lines of code across three files plus 17 different XML files, and it is now down to a single Kotlin file with 300 lines of code. This is a 60% reduction in the Kotlin code alone&#8220;.</em> The team at Zepeto has also been implementing Compose across many features, and are enjoying the experience, as <em>&#8220;Compose simplified our UI layer logic, making it <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/zepeto-plans-to-migrate-80-percent-of-apps-ui-to-jetpack-compose.html">easier to understand code written by my colleagues</a>.&#8221;</em></div><div style="text-align: left;"><iframe allowfullscreen="" class="BLOG_video_class" height="315" src="https://web.archive.org/web/20230213032228if_/https://www.youtube.com/embed/QO6Cg9MSpE8" width="800" youtube-src-id="QO6Cg9MSpE8"></iframe></div><div style="text-align: left;">It&#8217;s great to see how these teams experience faster development cycles, and also feel their UI code is more concise and readable. And they&#8217;re not the only ones. Since this year&#8217;s Google I/O, the number of top 1000 apps on Google Play using Compose has increased by 50%! To help your team follow in the footsteps of the teams at Lyft, Zepeto, and others, we published a guide on <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose/adopt/for-large-teams" target="_blank">How to Adopt Compose for your Team</a>. It outlines how and where to start, and shows the areas of development where Compose can bring huge added value.</div><p><br></p> <h2>Compose, October &#8216;22 release</h2> <p> Today we&#8217;re releasing a new stable version of Compose, with some exciting features and news. </p> <p> First of all, we&#8217;ve heard from you how it can be daunting to track versions across different artifacts that might go on different release schedules, so we&#8217;re now publishing, together with every Stable release of any of the Compose artifacts, a Bill of Materials, or BOM, to make your life easier. </p> <p> Our first BOM release, Compose October &#8216;22, brings support for Staggered Grids, drawing Text directly to Canvas, Pull to Refresh, as well as performance improvements and bug fixes. </p><p><br></p> <h3 style="text-align: left;">Compose Bill of Materials</h3> <p> A BOM is a Maven module that declares a set of libraries with their versions. It will greatly simplify the way you define Compose library versions in your Gradle dependencies block, especially now that we <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/06/independent-versioning-of-Jetpack-Compose-libraries.html" target="_blank">moved the various Jetpack Compose libraries to independent versioning schemes</a>. Instead of defining each version separately, which can become cumbersome and prone to errors when library versions start to differ, you now only need to define one BOM version and all Compose library versions will be extracted from that. We will publish a new version of the BOM every time a Compose artifact has a new stable release, so moving from stable release to stable release is going to be much simpler. </p><p><span id="docs-internal-guid-50e8e6d1-7fff-02b4-b0d7-3d3c98d386b3"></span></p><div align="left" dir="ltr" style="margin-left: 0pt;"><table style="border-collapse: collapse; border: none;"><colgroup><col width="719"></colgroup><tbody><tr style="height: 0pt;"><td style="background-color: #f0f0f0; border-bottom: solid #000000 1pt; border-color: rgb(0, 0, 0); border-left: solid #000000 1pt; border-right: solid #000000 1pt; border-style: solid; border-top: solid #000000 1pt; border-width: 1pt; overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: courier;"><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">dependencies {</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; </span><span style="background-color: #f0f0f0; color: #888888; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">// Import the Compose BOM</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">implementation</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> platform(</span><span style="background-color: #f0f0f0; color: #880000; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#39;androidx.compose:compose-bom:2022.10.00&#39;</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">)</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; </span><span style="background-color: #f0f0f0; color: #888888; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">// Declare dependencies for the desired Compose libraries without versions</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; </span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">implementation</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: #f0f0f0; color: #880000; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#39;androidx.compose.foundation:foundation&#39;</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; androidTestImplementation </span><span style="background-color: #f0f0f0; color: #880000; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#39;androidx.compose.ui:ui-test-junit4&#39;</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&#160; &#160; ...</span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br></span><span style="background-color: #f0f0f0; color: #444444; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">}</span></span></p></td></tr></tbody></table><br></div>We&#8217;ve added the instructions on how to add the Compose BOM to our <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose/setup" target="_blank">Quick start guide</a>. Note that you can still choose to define your dependencies using hard-coded versions. The BOM is added as a useful way to simplify dependencies and make upgrades easier.<div><span>&#160;&#160; &#160;</span></div><div><br> <h3 style="text-align: left;">Modifiers on overdrive</h3> <p> Behind the scenes, we&#8217;re always working on improving Compose performance. The October &#8216;22 release includes a major refactor of how Modifiers work under the hood. While you will not notice anything changing in the APIs, this refactor paves the way for greatly improving Modifier performance. Learn more about the rationale behind the changes, and what&#8217;s planned for the near future in the ADS talk <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/events/dev-summit/technical-talks" target="_blank">Compose Modifiers deep dive</a>.<iframe allowfullscreen="" class="BLOG_video_class" height="312" src="https://web.archive.org/web/20230213032228if_/https://www.youtube.com/embed/BjGX2RftXsU" width="800" youtube-src-id="BjGX2RftXsU"></iframe></p><p></p><div style="text-align: left;"><br></div><h3 style="text-align: left;">Popup &amp; Dialog elevation change</h3><p></p> <p> Accessibility is always a first-class citizen for Compose, and this release contains a behavior change that helps fix an Accessibility bug with Popups and Dialogs: their maximum elevation is decreased from 30dp to 8dp. Your app will be impacted only if it uses a custom dialog or popup implementation with an elevation higher than 8dp. The <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/compose-ui" target="_blank">release notes</a> contain more information about the change, including a way to override the new behavior as an interim solution (keep in mind that we always recommend using 8dp maximum when customizing popups or dialogs). </p><p><br></p> <h3 style="text-align: left;">New features</h3> <div><p> We added a lot of new functionality to Compose. Here are some highlights: </p><ul style="text-align: left;"><li> Implement staggered grids using the new <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/staggeredgrid/package-summary#LazyHorizontalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)" target="_blank"><span style="font-family: courier; font-size: medium;">LazyHorizontalStaggeredGrid</span></a></code> and <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/staggeredgrid/package-summary#LazyVerticalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)" target="_blank"><span style="font-family: courier; font-size: medium;">LazyVerticalStaggeredGrid</span></a></code></li><li>Draw text directly to Canvas using <code><span style="font-family: courier; font-size: medium;"><a href="https://web.archive.org/web/20230213032228/http://drawscope.drawtext/" target="_blank">DrawScope.drawText</a></span></code></li><li>You can add a <a href="https://web.archive.org/web/20230213032228/https://fonts.google.com/knowledge/topics/variable_fonts" target="_blank">variable font</a> to your app and change its properties using the <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/ui/text/font/FontVariation" target="_blank"><span style="font-family: courier; font-size: medium;">FontVariation</span></a></code> object.</li><li>Add a <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/ui/text/UrlAnnotation" target="_blank"><span style="font-family: courier; font-size: medium;">UrlAnnotation</span></a><span style="font-family: courier;">&#160;</span>to your annotated string to improve Accessibility services interacting with the text.</li><li>Add hyphenation to your text using the new&#160;<span style="font-family: courier;"><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/ui/text/style/LineBreak" target="_blank"><span style="font-size: medium;">LineBreak</span></a> </span>API.</li><li><a href="https://web.archive.org/web/20230213032228/https://material.io/design/platform-guidance/android-swipe-to-refresh" target="_blank">Swipe to refresh</a> with the new <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/material/pullrefresh/package-summary#(androidx.compose.ui.Modifier).pullRefresh(androidx.compose.material.pullrefresh.PullRefreshState,kotlin.Boolean)" target="_blank"><span style="font-family: courier; font-size: medium;">pullRefresh</span></a></code> modifier.</li><li>Add snapping behavior to your lazy lists using <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior" target="_blank">SnapFlingBehavior</a>.</li><li><code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/ui/layout/package-summary#LookaheadLayout(kotlin.Function1,androidx.compose.ui.Modifier,androidx.compose.ui.layout.MeasurePolicy)" target="_blank"><span style="font-family: courier; font-size: medium;">LookAheadLayout</span></a></code> is a new type of layout that provides information about final measurement and placement of its children so you can decide on intermediate layouts.</li></ul></div> <div><h2>Compose Material 3 stable</h2></div> <a href="https://web.archive.org/web/20230213032228/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBITldrbxnjPjiWhqzeO4ftBFoVNxXHLpqmk7OpJMfsCbNd1s6H4NHEKn1k1MNCFHumWnBIDKx3DJKd3IL_fV9oftKghZp3-fNe4ReDb6MiLQbmhBPtgC2pPA8qGtvR_66cTImgqHnHRXhtmph98ILXH62b_QB409tisYkKZpoMsL4exIkVixvQHB-/s1600/image2.png" style="text-align: center;"><img border="0" data-original-height="800" data-original-width="1058" height="357" src="https://web.archive.org/web/20230213032228im_/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBITldrbxnjPjiWhqzeO4ftBFoVNxXHLpqmk7OpJMfsCbNd1s6H4NHEKn1k1MNCFHumWnBIDKx3DJKd3IL_fV9oftKghZp3-fNe4ReDb6MiLQbmhBPtgC2pPA8qGtvR_66cTImgqHnHRXhtmph98ILXH62b_QB409tisYkKZpoMsL4exIkVixvQHB-/w640-h357/image2.png" width="640"></a> <div><p>Today we also announce the first stable release of the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/compose-material3" target="_blank">Compose Material 3 library</a>! You can build an app using Compose and theme it according to <a href="https://web.archive.org/web/20230213032228/https://m3.material.io/" target="_blank">Material Design 3</a>, our latest iteration of Material Design. Use Material Design 3 to further customize your app&#8217;s colors, typography and shapes to make your brand stand out! The library contains fresh and updated versions of many UI components, such as buttons, cards, checkboxes, switches, navigation bars, drawers, and many more, with support for others on its way. See a list of all the supported components in the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#overview" target="_blank">documentation</a>&#160;and learn more in <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/material-design-3-compose-stable.html">this blog post</a>.</p></div> <p> To help you adopt Material 3 check out our new <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose/themes/material2-material3" target="_blank">migration guide</a> with clear guidance on how Material 2 concepts translate to Material 3. The default template in Android Studio Flamingo now uses Material 3, to get you up and running in no time. We&#8217;ve also updated many of our sample apps, tutorials, templates, and codelabs to use Material 3 so you can learn as you go! </p> <h2 style="text-align: left;">New tools</h2> <p> Developing your app using Jetpack Compose is much easier with the new and improved tools around it. We&#8217;ve added tons of new features to Android Studio to improve your workflow and efficiency: </p> <p> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/studio/releases#new_in_compose" target="_blank">Android Studio Dolphin</a> is the latest <strong>stable release</strong>, bringing you: </p> <ul> <li>Animation Coordination </li><li>Multipreview annotations </li><li>Recomposition counts in Layout Inspector </li> </ul> <p> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/studio/preview/features#2022.1.1" target="_blank">Android Studio Electric Eel</a> contains <strong>beta features</strong>, like: </p> <ul> <li>Live Edit (experimental) </li><li>Composition rendering highlighting </li><li>Configuring Preview devices </li><li>Live updates in Previews </li> </ul> <p> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/studio/preview/features#2022.2.1" target="_blank">Android Studio Flamingo</a> contains <strong>canary features</strong> such as: </p> <ul> <li>New project templates use Compose and Material 3 by default </li><li>Live Edit turned on by default </li><li>Improved composition tracing to help you better inspect performance issues.</li></ul><br><ul> </ul> <h3 style="text-align: left;"><strong>Relay</strong></h3><p> Today we also launch the first alpha version of <a href="https://web.archive.org/web/20230213032228/https://relay.material.io/" target="_blank">Relay</a>, a design-to-code solution for improving designer-developer collaboration. Designers create UI components using the <a href="https://web.archive.org/web/20230213032228/https://www.figma.com/community/plugin/1041056822461507786" target="_blank">Figma plugin</a>, and developers use the <a href="https://web.archive.org/web/20230213032228/https://plugins.jetbrains.com/plugin/19721-relay-for-android-studio/" target="_blank">Android Studio plugin</a> to automatically use these components in their apps. The generated components are composable functions and can be integrated directly into your Compose app. Learn more about Relay in the <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/compose/tooling/relay" target="_blank">documentation</a>. </p><p><br></p> <h2 style="text-align: left;">Compose on Wear OS and Large Screens</h2> <p> In July we released the first Stable version of <a href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/07/compose-for-wear-os-10-stable.html" target="_blank">Compose for Wear OS</a>, ready to build production apps. <strong>Compose for Wear OS is our recommended approach for building UIs for Wear OS apps</strong>. We&#8217;ve included over twenty Compose UI components that were designed specifically for Wearables, like <code><span style="font-family: courier; font-size: medium;">TimeText</span></code>, <code><span style="font-family: courier; font-size: medium;">PositionIndicator</span></code>, and <code><span style="font-family: courier; font-size: medium;">ScalingLazyColumn</span></code>. </p> <p> We&#8217;re also continuing to make it easier to design, develop, and test <strong>apps for large screens</strong> such as foldables, tablets, and Chrome OS. The <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/jetpack/androidx/releases/compose-material3" target="_blank"><span style="font-family: courier; font-size: medium;">material3-window-size-class</span></a></code> library graduated to Stable, giving you a set of opinionated viewport breakpoints to work with. Large screen designs often contain staggered grids, and the addition of <code><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/staggeredgrid/package-summary#LazyHorizontalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)" target="_blank"><span style="font-family: courier; font-size: medium;">LazyHorizontalStaggeredGrid</span></a></code> and&#160;<span style="font-family: courier; font-size: medium;"><a href="https://web.archive.org/web/20230213032228/https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/staggeredgrid/package-summary#LazyVerticalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells,androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)" target="_blank">LazyVerticalStaggeredGrid</a></span><span style="font-size: medium;">&#160;</span>will help implement these.</p><span></span></div> </noscript> </div> </div> <div class="jump-link"> <a class="maia-button maia-button-secondary" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-compose.html#more" title="What’s new in Jetpack Compose"> Read more &#187; </a> </div> <div class="share"> <span class="twitter-custom social-wrapper" data-href="http://twitter.com/share?text=Android Developers Blog:What’s new in Jetpack Compose&amp;url=https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-compose.html&amp;via=google"> <img alt="Share on Twitter" height="24" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png" width="24"/> </span> <span class="fb-custom social-wrapper" data-href="https://www.facebook.com/sharer.php?u=https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-compose.html"> <img alt="Share on Facebook" height="24" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png" width="24"/> </span> <span class="email-custom social-wrapper" data-href="mailto:?body=https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-compose.html"> <img alt="Share by email" height="24" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAMFBMVEVMaXEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACbsZG/AAAAEHRSTlMAILDy/y78bg9wzE/jjwyQQ3EYrQAAAMpJREFUeAHN0yHIwmAQxvHng2/IypDZw9AOyuziDiemgb2tCgsGO4LY7cneg73Yl+0dsy8PcuzdsTaGf7D4e3cbbIdh+ptKq0VBmIhpSZhZWBNSCxmhFFv/sDmh0aVUyOF1VNj6cFZYIYEW478BWYVvYaogDmRXgAV38eAhhxdc0VueHkS1+wPggSZwRALEHOkBRmlWhe6HFvA0rzLg5vM+FqKaT2YBQQEL2u+DebUKuQ83Bf/zuZYKtn6he3G6V21vYU4I7DqPMUgfpvKO2fcD4TMAAAAASUVORK5CYII=" target="_blank" width="24"/> </span> </div> <div class="post-footer"> <div class="cmt_iframe_holder" data-href="https://android-developers.googleblog.com/2022/10/whats-new-in-jetpack-compose.html" data-viewtype="FILTERED_POSTMOD"></div> <a href="https://web.archive.org/web/20230213032228/https://plus.google.com/112374322230920073195" rel="author" style="display: none"> Google </a> <div class="label-footer"> <span class="labels-caption"> Labels: </span> <span class="labels"> <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Android" rel="tag"> Android </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Android%20Jetpack" rel="tag"> Android Jetpack </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Android%20Studio" rel="tag"> Android Studio </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Compose" rel="tag"> Compose </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Featured" rel="tag"> Featured </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack" rel="tag"> Jetpack </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack%20Compose" rel="tag"> Jetpack Compose </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack%20Compose%201.3" rel="tag"> Jetpack Compose 1.3 </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/latest" rel="tag"> latest </a> , <a class="label" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Platform_Update" rel="tag"> Platform_Update </a> </span> </div> </div> </div> <div class="blog-pager" id="blog-pager"> <a class="home-link" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/"> <i class="material-icons"> &#59530; </i> </a> <i class="material-icons disabled"> &#58820; </i> <span id="blog-pager-older-link"> <a class="blog-pager-older-link" href="https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack?updated-max=2022-10-24T09:02:00-07:00&amp;max-results=20&amp;start=4&amp;by-date=false" id="Blog1_blog-pager-older-link" title="Older Posts"> <i class="material-icons"> &#58824; </i> </a> </span> </div> <div class="clear"></div> </div></div> </div> </div> </div> <!-- Other blogs --> <aside class="adb-footer-section"> <div class="adb-footer-columns"> <div class="adb-footer-column"> <h2 class="adb-footer-title">Google developers blog</h2> <a href="https://web.archive.org/web/20230213032228/https://developers.googleblog.com/" target="_blank">Google Developers Blog</a> </div> <div class="adb-footer-column"> <h2 class="adb-footer-title">Connect</h2> <span class="adb-footer-social-label"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/" title="Android Developers Site"> <img alt="Android Developers Site" class="sidebar-icon android-logo" src="https://web.archive.org/web/20230213032228im_/https://developer.android.com/static/images/logos/android.svg"/> </a>Android Developers</span> <div class="adb-footer-social-links" style="display: flex"> <a href="https://web.archive.org/web/20230213032228/https://www.youtube.com/user/androiddevelopers" title="Android Developers on YouTube"> <img alt="Android Developers on YouTube" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/video_youtube_grey600_24dp.png"/> </a> <a href="https://web.archive.org/web/20230213032228/https://www.linkedin.com/showcase/androiddev/" title="Android Developers on LinkedIn"> <img alt="Android Developers on LinkedIn" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://www.pagetraffic.com/blog/wp-content/uploads/2022/09/linkedin-black-white-logo.png"/> </a> <a href="https://web.archive.org/web/20230213032228/https://medium.com/androiddevelopers" title="Android Developers on Medium"> <img alt="Android Developers on Medium" class="sidebar-icon" src="data:image/png;base64,R0lGODlhMAAwAPcAAHJxcHFwb4iGhYODgre2tnNxcLi4t+jo6LCvr3BubW1ram5tbLOzsm1sa3BvbmtqaWppaG5ta2poZ2dmZoB/fmhnZ3Jxb3Jwb5CPjqqpqWtram9ubWxras7OzmloaGpoaHh3d8XExPj4+GhnZmhoZ/39/fv7+2pqaWtqaObm5r69vK2trIWEg+np6X59fIOCgYiHhoaFg93d3ba1tJKRj66trdDQz2lnZrm5uIKCge7t7WpqarW1tKampW1ra9ra2t3d3PPy8uvr67W1s8LCwZ2cm5WUk2ppafX19auqqpKRkff393RzcXRycbSzso+OjbW0s8vLyqalpODf3/f39srKymdnZ+Xl5IKCgLa2tXRzcnZ0c/7+/ru7upuamrS0s7y8u5GPjpuamNPS0nZ1dJqZmLu6ufHx8X18e+zs7O7u7fDw8Obl5dXU1JaVlLSzs52dm9DOzpOSkWtoZ2dnZoCAf+jn525sa9bW1r28vOfn556enbKysamop9nZ2Hl3dnFvbuXl5cTEw399fMHAv6Cenb6+vayrqsrKya+urdHR0fr5+W1tbHp5eNfW1uno6ODg4OLi4qqpqPr6+omIhn59fXh3dmZmZv///3FwbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMTEgNzkuMTU4MzI1LCAyMDE1LzA5LzEwLTAxOjEwOjIwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCQzNFMDRFOTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCQzNFMDRFQTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkJDM0UwNEU3OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkJDM0UwNEU4OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAAAAAAAsAAAAADAAMAAACP8AKVgaSLCgwYMIEyq0RCGTw4cQI0qcSLHiQ0sWM2rcOHCjx48QO4IcqVEkyZMTTaJc6VAlS5QuX5J0CcDBpZs4cY4AYBFAgpxAM/EMiRHiAiYYBChdqrRJg6ETN6BgwXQphgILIrp80EANpq9gv465FIFigEsCwoYNBOGB1qIPz7JQG/bFJagQAV0KQRdTGhAeArydeElOX0xVLiWYWCHAJLoyGF0qIDFmgJ+HDlu6JBgigEtS+rqYnBIuxAANLnXoa+iSg4gcLl0RfYliTIefV/TlssVK50wXLj05PKB26YqXCBwmYNzhz9V9i9s2HTE5JjYl1C5p4OEhHTKYRAj/oSv9OEXrPWzQ7XPJQiYLlxJh6hKFfPPB55UboURXxwS3D1yyBiaDtGHfdMgpl8ElSNAlxmSXYIAJEJekcKB5hCnHwyVD0BXJJRpcoggmZVzSwoWVUQeRdW9c0khfYVzyByYmQDDBASjilyEmDNwEXVh+XOIEJipcwgGOapWXYoI83hQDXSWAcGIdlzSAZFhK6iiRdT1GcMkPdD2CCSSXjKDAlWBlSRSTPX5Gw2GFXDLHmTmumV+TmcRmoVpULECCBHQmeZ+dhGWBCR+1fbYHXYTUBiiaX6l5kYoOFXCJbgtSdgIJZ6gVA2eP1jnpRAB8cIkjmOBxyQcAwCdJWFNc06JAJhI8oAddWAw6akSoXSIIWCGQBQAEEAQBFhx3BXDDJXbQpcVdGDp02SVJqLWgA8Eh8JUIC1RwwQaXeGECXVCAiFdL1J2lRF92FdDYIpjEoZhNRRzW5ALnujRBDvZWUkEmlxCBiRuuXfKFvV8hQpaWmZwAAw4IRCwxAmDAcEQAGqBRQwI+bLDDDCoYIPLII5uRxwKzElopBEDlhAJlBUxwyR0FBGBpyy0DkC+lMn10W88e/Qx0STwPnZHQRleEdNLRMk200z5DDZJAC1VttdUUBAQAOw=="/> </a> <a href="https://web.archive.org/web/20230213032228/https://twitter.com/androiddev" title="Follow Android Developers on Twitter"> <img alt="Follow Android Developers on Twitter" class="sidebar-icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABgUExURUxpcWdnZ2ZmZmNjY2dnZ2ZmZmZmZmdnZ2dnZ2ZmZmZmZmZmZmZmZmZmZmdnZ2VlZWdnZ2ZmZmdnZ2ZmZmdnZ2ZmZmdnZ2dnZ2dnZ2ZmZmZmZmZmZmdnZ2dnZ2dnZ2dnZ/SZ370AAAAfdFJOUwCRmwjzDdwc+rM+LaykVRXqZsVfhs8mc9RLvIA1i0NM81npAAABVklEQVRIx+1VyZaDIBAEBBXjEnVwQ9P//5ejGNQgKLnOm7ooUAXd1SwI/XlEfSvrMfClJxUoxBNbO8pcferIzs9gA03mdi5gXPoZVFZ+BQfQKaUAcl0HIL2eXyNDaEmHzL/Did+f+RIxXs9D6dLozDzEiR9OFYSBFgCUH/wH2KD4KqQFojkIahtfrO62WwffJT8WPtHVjPe+KmdOQRxZDYy5TALl9QmbIKLGCA0HYVmhePMDhyUmKNMRYTRSD0GnI5rrMHQegvTKQRva66KekW9F4l78vQyo8RLw6618kYI6vLF/Fd5owxsB+dz9L5Z3vilrZ6+jwsaBxDcBPQvzzN+kUJ4uiYb61kCDVd8EpOD0KX44b9/Gmkrivq7xV/yCWON3xNPU3NOfggwEZ8KRbm2Z++WuGXa8VRF+2i5eydzuBL2hCeXr/sFMyinjPCUyHyP0jyN+Abu6gf9XJNj2AAAAAElFTkSuQmCC"/> </a> </div> <span class="adb-footer-social-label"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/distribute/" title="Google Play Site"> <img alt="Google Play Site" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://developer.android.com/static/images/logos/google-play.svg"/> </a>Google Play</span> <div class="adb-footer-social-links" style="display: flex"> <a href="https://web.archive.org/web/20230213032228/https://www.linkedin.com/company/googleplaybiz/" title="Google Play Apps &amp; Games on LinkedIn"> <img alt="Google Play Apps &amp; Games on LinkedIn" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://www.pagetraffic.com/blog/wp-content/uploads/2022/09/linkedin-black-white-logo.png"/> </a> <a href="https://web.archive.org/web/20230213032228/https://medium.com/googleplaydev" title="Google Play Apps &amp; Games on Medium"> <img alt="Google Play Apps &amp; Games on Medium" class="sidebar-icon" src="data:image/png;base64,R0lGODlhMAAwAPcAAHJxcHFwb4iGhYODgre2tnNxcLi4t+jo6LCvr3BubW1ram5tbLOzsm1sa3BvbmtqaWppaG5ta2poZ2dmZoB/fmhnZ3Jxb3Jwb5CPjqqpqWtram9ubWxras7OzmloaGpoaHh3d8XExPj4+GhnZmhoZ/39/fv7+2pqaWtqaObm5r69vK2trIWEg+np6X59fIOCgYiHhoaFg93d3ba1tJKRj66trdDQz2lnZrm5uIKCge7t7WpqarW1tKampW1ra9ra2t3d3PPy8uvr67W1s8LCwZ2cm5WUk2ppafX19auqqpKRkff393RzcXRycbSzso+OjbW0s8vLyqalpODf3/f39srKymdnZ+Xl5IKCgLa2tXRzcnZ0c/7+/ru7upuamrS0s7y8u5GPjpuamNPS0nZ1dJqZmLu6ufHx8X18e+zs7O7u7fDw8Obl5dXU1JaVlLSzs52dm9DOzpOSkWtoZ2dnZoCAf+jn525sa9bW1r28vOfn556enbKysamop9nZ2Hl3dnFvbuXl5cTEw399fMHAv6Cenb6+vayrqsrKya+urdHR0fr5+W1tbHp5eNfW1uno6ODg4OLi4qqpqPr6+omIhn59fXh3dmZmZv///3FwbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMTEgNzkuMTU4MzI1LCAyMDE1LzA5LzEwLTAxOjEwOjIwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCQzNFMDRFOTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCQzNFMDRFQTlEMzgxMUU3OTk1N0ZEREQyNENCOTcxNCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkJDM0UwNEU3OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkJDM0UwNEU4OUQzODExRTc5OTU3RkRERDI0Q0I5NzE0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAAAAAAAsAAAAADAAMAAACP8AKVgaSLCgwYMIEyq0RCGTw4cQI0qcSLHiQ0sWM2rcOHCjx48QO4IcqVEkyZMTTaJc6VAlS5QuX5J0CcDBpZs4cY4AYBFAgpxAM/EMiRHiAiYYBChdqrRJg6ETN6BgwXQphgILIrp80EANpq9gv465FIFigEsCwoYNBOGB1qIPz7JQG/bFJagQAV0KQRdTGhAeArydeElOX0xVLiWYWCHAJLoyGF0qIDFmgJ+HDlu6JBgigEtS+rqYnBIuxAANLnXoa+iSg4gcLl0RfYliTIefV/TlssVK50wXLj05PKB26YqXCBwmYNzhz9V9i9s2HTE5JjYl1C5p4OEhHTKYRAj/oSv9OEXrPWzQ7XPJQiYLlxJh6hKFfPPB55UboURXxwS3D1yyBiaDtGHfdMgpl8ElSNAlxmSXYIAJEJekcKB5hCnHwyVD0BXJJRpcoggmZVzSwoWVUQeRdW9c0khfYVzyByYmQDDBASjilyEmDNwEXVh+XOIEJipcwgGOapWXYoI83hQDXSWAcGIdlzSAZFhK6iiRdT1GcMkPdD2CCSSXjKDAlWBlSRSTPX5Gw2GFXDLHmTmumV+TmcRmoVpULECCBHQmeZ+dhGWBCR+1fbYHXYTUBiiaX6l5kYoOFXCJbgtSdgIJZ6gVA2eP1jnpRAB8cIkjmOBxyQcAwCdJWFNc06JAJhI8oAddWAw6akSoXSIIWCGQBQAEEAQBFhx3BXDDJXbQpcVdGDp02SVJqLWgA8Eh8JUIC1RwwQaXeGECXVCAiFdL1J2lRF92FdDYIpjEoZhNRRzW5ALnujRBDvZWUkEmlxCBiRuuXfKFvV8hQpaWmZwAAw4IRCwxAmDAcEQAGqBRQwI+bLDDDCoYIPLII5uRxwKzElopBEDlhAJlBUxwyR0FBGBpyy0DkC+lMn10W88e/Qx0STwPnZHQRleEdNLRMk200z5DDZJAC1VttdUUBAQAOw=="/> </a> <a href="https://web.archive.org/web/20230213032228/https://twitter.com/GooglePlayBiz" title="Follow GooglePlaydBiz on Twitter"> <img alt="Follow GooglePlayBiz on Twitter" class="sidebar-icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABgUExURUxpcWdnZ2ZmZmNjY2dnZ2ZmZmZmZmdnZ2dnZ2ZmZmZmZmZmZmZmZmZmZmdnZ2VlZWdnZ2ZmZmdnZ2ZmZmdnZ2ZmZmdnZ2dnZ2dnZ2ZmZmZmZmZmZmdnZ2dnZ2dnZ2dnZ/SZ370AAAAfdFJOUwCRmwjzDdwc+rM+LaykVRXqZsVfhs8mc9RLvIA1i0NM81npAAABVklEQVRIx+1VyZaDIBAEBBXjEnVwQ9P//5ejGNQgKLnOm7ooUAXd1SwI/XlEfSvrMfClJxUoxBNbO8pcferIzs9gA03mdi5gXPoZVFZ+BQfQKaUAcl0HIL2eXyNDaEmHzL/Did+f+RIxXs9D6dLozDzEiR9OFYSBFgCUH/wH2KD4KqQFojkIahtfrO62WwffJT8WPtHVjPe+KmdOQRxZDYy5TALl9QmbIKLGCA0HYVmhePMDhyUmKNMRYTRSD0GnI5rrMHQegvTKQRva66KekW9F4l78vQyo8RLw6618kYI6vLF/Fd5owxsB+dz9L5Z3vilrZ6+jwsaBxDcBPQvzzN+kUJ4uiYb61kCDVd8EpOD0KX44b9/Gmkrivq7xV/yCWON3xNPU3NOfggwEZ8KRbm2Z++WuGXa8VRF+2i5eydzuBL2hCeXr/sFMyinjPCUyHyP0jyN+Abu6gf9XJNj2AAAAAElFTkSuQmCC"/> </a> </div> </div> <div class="adb-footer-column"> <h2 class="adb-footer-title">Subscribe</h2> <div class="section" id="footer-subscribe"><div class="widget HTML" data-version="1" id="HTML1"> <a href="https://web.archive.org/web/20230213032228/https://android-developers.blogspot.com/atom.xml"> <div class="tab"> <img alt="" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/1x/rss_feed_grey600_24dp.png"/> <h2>Feed</h2> </div> </a> </div><div class="widget HTML" data-version="1" id="HTML2"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/newsletter/index.html"> <div class="tab"> <img alt="" class="sidebar-icon" src="https://web.archive.org/web/20230213032228im_/https://www.gstatic.com/images/icons/material/system/2x/news_grey600_24dp.png"/> <h2>Newsletter</h2> </div> </a> </div></div> </div> </div> </aside> <!-- Footer --> <footer class="adb-footer-btm"> <ul class="adb-footer-info"> <a href="https://web.archive.org/web/20230213032228/https://policies.google.com/privacy"><li class="footer-list">Privacy</li></a> | <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/license"><li class="footer-list">License</li></a> | <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/distribute/marketing-tools/brand-guidelines"><li class="footer-list">Brand guidelines</li></a> </ul> <div class="footer-newsletter"> <a href="https://web.archive.org/web/20230213032228/https://developer.android.com/newsletter/#subscribe"><button class="subscribe-btn">Get news and tips by email</button></a> </div> </footer> <script type="text/javascript"> //<![CDATA[ (function () { "use strict"; // Social sharing popups. var postEl = document.getElementsByClassName("social-wrapper"); var postCount = postEl.length; for (var i = 0; i < postCount; i++) { postEl[i].addEventListener("click", function (event) { var postUrl = this.getAttribute("data-href"); window.open( postUrl, "popUpWindow", "height=500,width=500,left=10,top=10,resizable=yes,scrollbars=yes,toolbar=yes,menubar=no,location=no,directories=no,status=yes" ); }); } var BreakpointHandler = function () { this.initted = false; this.isHomePage = false; this.isMobile = false; }; BreakpointHandler.prototype.finalizeSummary = function ( summaryHtml, lastNode ) { // Use $.trim for IE8 compatibility summaryHtml = $.trim(summaryHtml).replace(/(<br>|\s+)*$/, ""); if (lastNode.nodeType == 3) { var lastChar = summaryHtml.slice(-1); if (!lastChar.match(/[.”"?]/)) { if (!lastChar.match(/[A-Za-z]/)) { summaryHtml = summaryHtml.slice(0, -1); } summaryHtml += " ..."; } } else if ( lastNode.nodeType == 1 && (lastNode.nodeName == "I" || lastNode.nodeName == "A") ) { summaryHtml += " ..."; } return summaryHtml; }; BreakpointHandler.prototype.generateSummaryFromContent = function ( content, numWords ) { var seenWords = 0; var summaryHtml = ""; for (var i = 0; i < content.childNodes.length; i++) { var node = content.childNodes[i]; var nodeText; if (node.nodeType == 1) { if (node.hasAttribute("data-about-pullquote")) { continue; } nodeText = node.textContent; if (nodeText === undefined) { // innerText for IE8 nodeText = node.innerText; } if (node.nodeName == "DIV" || node.nodeName == "B") { // Don't end early if we haven't seen enough words. if (seenWords < 10) { continue; } if (i > 0) { summaryHtml = this.finalizeSummary( summaryHtml, content.childNodes[i - 1] ); } break; } summaryHtml += node.outerHTML; } else if (node.nodeType == 3) { nodeText = node.nodeValue; summaryHtml += nodeText + " "; } var words = nodeText.match(/\S+\s*/g); if (!words) { continue; } var remain = numWords - seenWords; if (words.length >= remain) { summaryHtml = this.finalizeSummary(summaryHtml, node); break; } seenWords += words.length; } return summaryHtml; }; BreakpointHandler.prototype.detect = function () { var match, pl = /\+/g, search = /([^&=]+)=?([^&]*)/g, decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, query = window.location.search.substring(1); var urlParams = {}; while ((match = search.exec(query))) urlParams[decode(match[1])] = decode(match[2]); this.isListPage = $("html").hasClass("list-page"); this.isMobile = urlParams["m"] === "1"; this.isHomePage = window.location.pathname == "/"; }; BreakpointHandler.prototype.initContent = function () { var self = this; $(".post").each(function (index) { var body = $(this).children(".post-body")[0]; var content = $(body).children(".post-content")[0]; $(content).addClass("post-original"); var data = $(content).children("script").html(); data = self.rewriteForSSL(data); // If exists, extract specified editor's preview. var match = data.match( /([\s\S]+?)<div data-is-preview.+?>([\s\S]+)<\/div>/m ); if (match) { data = match[1]; } // Prevent big images from loading when they aren't needed. // This must be done as a pre-injection step, since image loading can't be // canceled once embedded into the DOM. if (self.isListPage && self.isMobile) { data = data.replace(/<(img|iframe) .+?>/g, ""); } // Insert template to be rendered as nodes. content.innerHTML = data; if (self.isListPage) { var summary = document.createElement("div"); $(summary).addClass("post-content"); $(summary).addClass("post-summary"); body.insertBefore(summary, content); if (match) { // Use provided summary. summary.innerHTML = match[2]; } else { // Generate a summary. // Summary generation relies on DOM, so it must occur after content is // inserted into the page. summary.innerHTML = self.generateSummaryFromContent( content, 30 ); } // Add read more link to summary. var titleAnchor = $(this).find(".title a")[0]; var link = titleAnchor.cloneNode(true); link.innerHTML = "Read More"; $(link).addClass("read-more"); summary.appendChild(link); } }); // Firefox does not allow for proper styling of BR. if (navigator.userAgent.indexOf("Firefox") > -1) { $(".post-content br").replaceWith('<span class="space"></span>'); } $(".loading").removeClass("loading"); }; BreakpointHandler.prototype.process = function () { if (!this.initted) { var makeInsecureImageRegex = function (hosts) { var whitelist = hosts.join("|").replace(/\./g, "\\."); // Normal image tags, plus input images (yes, this is possible!) return new RegExp( "(<(img|input)[^>]+?src=(\"|'))http:\/\/(" + whitelist + ")", "g" ); }; this.sslImageRegex = makeInsecureImageRegex( BreakpointHandler.KNOWN_HTTPS_HOSTS ); this.sslImageCurrentDomainRegex = makeInsecureImageRegex([ window.location.hostname, ]); this.detect(); this.initContent(); this.initted = true; } }; BreakpointHandler.KNOWN_HTTPS_HOSTS = [ "www.google.org", "www.google.com", "services.google.com", "blogger.com", "draft.blogger.com", "www.blogger.com", "photos1.blogger.com", "photos2.blogger.com", "photos3.blogger.com", "blogblog.com", "img1.blogblog.com", "img2.blogblog.com", "www.blogblog.com", "www1.blogblog.com", "www2.blogblog.com", "0.bp.blogspot.com", "1.bp.blogspot.com", "2.bp.blogspot.com", "3.bp.blogspot.com", "4.bp.blogspot.com", "lh3.googleusercontent.com", "lh4.googleusercontent.com", "lh5.googleusercontent.com", "lh6.googleusercontent.com", "themes.googleusercontent.com", ]; BreakpointHandler.prototype.rewriteForSSL = function (html) { // Handle HTTP -> HTTPS source replacement of images, movies, and other embedded content. return ( html .replace(this.sslImageRegex, "$1https://$4") .replace(this.sslImageCurrentDomainRegex, "$1//$4") .replace( /(<(embed|iframe)[^>]+?src=("|'))http:\/\/([^"']*?(youtube|picasaweb\.google)\.com)/g, "$1https://$4" ) // Slideshow SWF takes a image host, so we need to rewrite that parameter. .replace(/(<embed[^>]+?feed=http(?=[^s]))/g, "$1s") ); }; $(document).ready(function () { var handler = new BreakpointHandler(); handler.process(); // Top-level navigation. $(".BlogArchive .tab").click(function (ev) { ev.preventDefault(); $(this).parent().toggleClass("active"); $(this).siblings().slideToggle(300); }); $(".Label .tab").click(function (ev) { ev.preventDefault(); $(this).parent().toggleClass("active"); $(this).siblings().slideToggle(300); }); // Blog archive year expansion. $(".BlogArchive .intervalToggle").click(function (ev) { ev.preventDefault(); if ($(this).parent().hasClass("collapsed")) { $(this).parent().removeClass("collapsed"); $(this).parent().addClass("expanded"); } else { $(this).parent().removeClass("expanded"); $(this).parent().addClass("collapsed"); } }); // Reverse order of months. $(".BlogArchive .intervalToggle + div").each(function (_, items) { var year = $(this); year.children().each(function (_, month) { year.prepend(month); }); }); // Set anchors to open in new tab. $(".post-content img") .parent() .each(function (_, node) { if (node.nodeName == "A") { $(this).attr("target", "_blank"); } }); // Process search requests. $(".searchBox input").on("keypress", function (ev) { if (ev.which == 13) { window.location.href = "https://web.archive.org/web/20230213032228/https://www.google.com/search?q=site%3A" + window.location.hostname + "%20" + encodeURIComponent($(this).val()); } }); }); $.fn.labelWidgetShowMore = function () { if (!this.length) return; // Fetch feed containing labels var promise = $.getJSON( "/feeds/posts/summary?alt=json&max-results=0" ); // For each label widget this.each(function () { var $widget = $(this); var open = false; var $viewMoreContent = $widget.find(".adb-label-view-more-content"); var $viewMoreToggle = $widget.find(".adb-label-view-more-toggle"); var $list = $viewMoreContent.find("ul"); // Function to set new open/closed state function setOpenClosed(_open) { open = _open; var text = $viewMoreToggle.data(open ? "open" : "closed"); $viewMoreContent.toggle(open); $viewMoreToggle.text(text).toggleClass("active", open); } // Toggle handler $viewMoreToggle.on("click", function () { setOpenClosed(!open); }); // Load data promise.then(function (data) { $.each(data.feed.category, function (index, category) { $( '<li><a href="/search/label/' + category.term + '">' + category.term + "</a></li>" ).appendTo($list); }); // Set toggle text setOpenClosed(false); }); }); }; $.fn.fixedScroll = function (scrollBounds) { if (!this.length) return; var $elements = this; $(window).on("scroll", function () { var page = document.documentElement; var body = document.body; // How much the page or body can scroll var scrollHeight = page.scrollHeight || body.scrollHeight; // How much the page or body has scrolled var scrollTop = page.scrollTop || body.scrollTop; // Whether the page has scrolled past the top bound var scrollStart = scrollTop > scrollBounds.top; $elements.each(function () { var $element = $(this); var elementHeight = $element.height(); // Whether the fixed element has scrolled past the bottom bound var scrollEnd = scrollHeight - scrollTop < scrollBounds.bottom + elementHeight; $element.toggleClass("scroll-start", scrollStart); $element.toggleClass("scroll-end", scrollEnd); }); }); }; $(function () { // Enable show more link for Labels widget $(".widget.Label").labelWidgetShowMore(); // Enable fixed sidebar $(".col-right").fixedScroll({ top: 227, bottom: 643 }); // Enable pretty printed code snippets $("pre:not(.no-pretty-print)").addClass("prettyprint"); PR.prettyPrint(); }); })(); var $popoutNavBtn = $(".adb-burger-box"); var $popoutNav = $(".popout-nav"); var $popoutOverlay = $(".popout-overlay"); var $closePopoutBtn = $("#close-popout"); var $popoutSearchBtn = $(".adb-search-box"); var $popoutSearch = $(".popout-search"); var $popoutSearchOverlay = $(".popout-search-overlay"); var $closeSearchPopoutBtn = $("#close-search-popout"); var $moreDropdownBtn = $(".more-dropdown"); var $dropdownIcon = $(".dropdown-icon"); var $dropdownMenu = $(".dropdown-nav"); function moreDropdown() { if ($moreDropdownBtn.attr("status") === "inactive") { $moreDropdownBtn.attr("status", "active"); $dropdownIcon.css({ transform: "rotate(" + -180 + "deg)" }); $dropdownMenu.css({ display: "block" }); } else if ($moreDropdownBtn.attr("status") === "active") { $moreDropdownBtn.attr("status", "inactive"); $dropdownIcon.css({ transform: "rotate(" + 0 + "deg)" }); $dropdownMenu.css({ display: "none" }); } } function openSidenav() { $popoutNav.css({ left: 0 }); $popoutOverlay .css({ opacity: 0.4, }) .show(); } function closeSidenav() { $popoutNav.css({ left: -280 }); $popoutOverlay.css({ opacity: 0 }); setTimeout(function () { $popoutOverlay.hide(); }, 200); } function openSideSearch() { $popoutSearch.css({ right: 0 }); $popoutSearchOverlay .css({ opacity: 0.4, }) .show(); } function closeSideSearch() { $popoutSearch.css({ right: -280 }); $popoutSearchOverlay.css({ opacity: 0 }); setTimeout(function () { $popoutSearchOverlay.hide(); }, 200); } $popoutNavBtn.click(openSidenav); $closePopoutBtn.click(closeSidenav); $popoutOverlay.click(closeSidenav); $popoutSearchBtn.click(openSideSearch); $closeSearchPopoutBtn.click(closeSideSearch); $popoutSearchOverlay.click(closeSideSearch); $moreDropdownBtn.mouseenter(moreDropdown); $dropdownMenu.mouseleave(moreDropdown); //]]> </script> <script type="text/javascript" src="https://web.archive.org/web/20230213032228js_/https://www.blogger.com/static/v1/widgets/4056224860-widgets.js"></script> <script type="text/javascript"> window['__wavt'] = 'AOuZoY4hi6IR0z2McSXsK577s-Nw16wayA:1676124975736';_WidgetManager._Init('//web.archive.org/web/20230213032228/https://www.blogger.com/rearrange?blogID\x3d6755709643044947179','//web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack','6755709643044947179'); _WidgetManager._SetDataContext([{'name': 'blog', 'data': {'blogId': '6755709643044947179', 'title': 'Android Developers Blog', 'url': 'https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack', 'canonicalUrl': 'https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack', 'homepageUrl': 'https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/', 'searchUrl': 'https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search', 'canonicalHomepageUrl': 'https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/', 'blogspotFaviconUrl': 'https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/favicon.ico', 'bloggerUrl': 'https://web.archive.org/web/20230213032228/https://www.blogger.com', 'hasCustomDomain': true, 'httpsEnabled': true, 'enabledCommentProfileImages': true, 'gPlusViewType': 'FILTERED_POSTMOD', 'adultContent': false, 'analyticsAccountNumber': 'UA-5831155-1', 'encoding': 'UTF-8', 'locale': 'en', 'localeUnderscoreDelimited': 'en', 'languageDirection': 'ltr', 'isPrivate': false, 'isMobile': false, 'isMobileRequest': false, 'mobileClass': '', 'isPrivateBlog': false, 'isDynamicViewsAvailable': true, 'feedLinks': '\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Android Developers Blog - Atom\x22 href\x3d\x22https://android-developers.googleblog.com/feeds/posts/default\x22 /\x3e\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/rss+xml\x22 title\x3d\x22Android Developers Blog - RSS\x22 href\x3d\x22https://android-developers.googleblog.com/feeds/posts/default?alt\x3drss\x22 /\x3e\n\x3clink rel\x3d\x22service.post\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Android Developers Blog - Atom\x22 href\x3d\x22https://www.blogger.com/feeds/6755709643044947179/posts/default\x22 /\x3e\n', 'meTag': '', 'adsenseHostId': 'ca-host-pub-1556223355139109', 'adsenseHasAds': false, 'adsenseAutoAds': false, 'boqCommentIframeForm': true, 'loginRedirectParam': '', 'view': '', 'dynamicViewsCommentsSrc': '//web.archive.org/web/20230213032228/https://www.blogblog.com/dynamicviews/4224c15c4e7c9321/js/comments.js', 'dynamicViewsScriptSrc': '//web.archive.org/web/20230213032228/https://www.blogblog.com/dynamicviews/7f6a87a4a5e22766', 'plusOneApiSrc': 'https://web.archive.org/web/20230213032228/https://apis.google.com/js/platform.js', 'disableGComments': true, 'sharing': {'platforms': [{'name': 'Get link', 'key': 'link', 'shareMessage': 'Get link', 'target': ''}, {'name': 'Facebook', 'key': 'facebook', 'shareMessage': 'Share to Facebook', 'target': 'facebook'}, {'name': 'BlogThis!', 'key': 'blogThis', 'shareMessage': 'BlogThis!', 'target': 'blog'}, {'name': 'Twitter', 'key': 'twitter', 'shareMessage': 'Share to Twitter', 'target': 'twitter'}, {'name': 'Pinterest', 'key': 'pinterest', 'shareMessage': 'Share to Pinterest', 'target': 'pinterest'}, {'name': 'Email', 'key': 'email', 'shareMessage': 'Email', 'target': 'email'}], 'disableGooglePlus': true, 'googlePlusShareButtonWidth': 0, 'googlePlusBootstrap': '\x3cscript type\x3d\x22text/javascript\x22\x3ewindow.___gcfg \x3d {\x27lang\x27: \x27en\x27};\x3c/script\x3e'}, 'hasCustomJumpLinkMessage': false, 'jumpLinkMessage': 'Read more', 'pageType': 'index', 'searchLabel': 'Jetpack', 'pageName': 'Jetpack', 'pageTitle': 'Android Developers Blog: Jetpack', 'metaDescription': 'News and insights on the Android platform, developer tools, and events.'}}, {'name': 'features', 'data': {'sharing_get_link_dialog': 'true', 'sharing_native': 'false'}}, {'name': 'messages', 'data': {'edit': 'Edit', 'linkCopiedToClipboard': 'Link copied to clipboard!', 'ok': 'Ok', 'postLink': 'Post Link'}}, {'name': 'template', 'data': {'name': 'custom', 'localizedName': 'Custom', 'isResponsive': false, 'isAlternateRendering': false, 'isCustom': true}}, {'name': 'view', 'data': {'classic': {'name': 'classic', 'url': '?view\x3dclassic'}, 'flipcard': {'name': 'flipcard', 'url': '?view\x3dflipcard'}, 'magazine': {'name': 'magazine', 'url': '?view\x3dmagazine'}, 'mosaic': {'name': 'mosaic', 'url': '?view\x3dmosaic'}, 'sidebar': {'name': 'sidebar', 'url': '?view\x3dsidebar'}, 'snapshot': {'name': 'snapshot', 'url': '?view\x3dsnapshot'}, 'timeslide': {'name': 'timeslide', 'url': '?view\x3dtimeslide'}, 'isMobile': false, 'title': 'Android Developers Blog', 'description': 'News and insights on the Android platform, developer tools, and events.', 'url': 'https://web.archive.org/web/20230213032228/https://android-developers.googleblog.com/search/label/Jetpack', 'type': 'feed', 'isSingleItem': false, 'isMultipleItems': true, 'isError': false, 'isPage': false, 'isPost': false, 'isHomepage': false, 'isArchive': false, 'isSearch': true, 'isLabelSearch': true, 'search': {'label': 'Jetpack', 'resultsMessage': 'Showing posts with the label Jetpack', 'resultsMessageHtml': 'Showing posts with the label \x3cspan class\x3d\x27search-label\x27\x3eJetpack\x3c/span\x3e'}}}]); _WidgetManager._RegisterWidget('_BlogSearchView', new _WidgetInfo('BlogSearch2', 'header-popout-search', document.getElementById('BlogSearch2'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogSearchView', new _WidgetInfo('BlogSearch1', 'header-search', document.getElementById('BlogSearch1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogView', new _WidgetInfo('Blog1', 'main', document.getElementById('Blog1'), {'cmtInteractionsEnabled': false, 'navMessage': 'Showing posts with label \x3cb\x3eJetpack\x3c/b\x3e. \x3ca href\x3d\x22https://android-developers.googleblog.com/\x22\x3eShow all posts\x3c/a\x3e', 'lightboxEnabled': true, 'lightboxModuleUrl': 'https://web.archive.org/web/20230213032228/https://www.blogger.com/static/v1/jsbin/3480701712-lbx.js', 'lightboxCssUrl': 'https://web.archive.org/web/20230213032228/https://www.blogger.com/static/v1/v-css/2678123480-lightbox_bundle.css'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HTMLView', new _WidgetInfo('HTML1', 'footer-subscribe', document.getElementById('HTML1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HTMLView', new _WidgetInfo('HTML2', 'footer-subscribe', document.getElementById('HTML2'), {}, 'displayModeFull')); </script> </body> </html><!-- FILE ARCHIVED ON 03:22:28 Feb 13, 2023 AND RETRIEVED FROM THE INTERNET ARCHIVE ON 20:29:21 Dec 04, 2024. JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. SECTION 108(a)(3)). --> <!-- playback timings (ms): captures_list: 0.683 exclusion.robots: 0.031 exclusion.robots.policy: 0.018 esindex: 0.013 cdx.remote: 8.083 LoadShardBlock: 133.586 (3) PetaboxLoader3.datanode: 122.179 (4) load_resource: 92.097 PetaboxLoader3.resolve: 43.887 -->

Pages: 1 2 3 4 5 6 7 8 9 10