CINXE.COM
<!doctype html><html lang="en"><head><title data-rh="true">Migrating the AOSP QuickSearchBox App to Kotlin | by Android Developers | Android Developers | Medium</title><meta data-rh="true" charset="utf-8"/><meta data-rh="true" name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1,maximum-scale=1"/><meta data-rh="true" name="theme-color" content="#000000"/><meta data-rh="true" name="twitter:app:name:iphone" content="Medium"/><meta data-rh="true" name="twitter:app:id:iphone" content="828256236"/><meta data-rh="true" property="al:ios:app_name" content="Medium"/><meta data-rh="true" property="al:ios:app_store_id" content="828256236"/><meta data-rh="true" property="al:android:package" content="com.medium.reader"/><meta data-rh="true" property="fb:app_id" content="542599432471018"/><meta data-rh="true" property="og:site_name" content="Medium"/><meta data-rh="true" property="og:type" content="article"/><meta data-rh="true" property="article:published_time" content="2022-09-28T11:16:45.866Z"/><meta data-rh="true" name="title" content="Migrating the AOSP QuickSearchBox App to Kotlin | by Android Developers | Android Developers | Medium"/><meta data-rh="true" property="og:title" content="Migrating the AOSP QuickSearchBox App to Kotlin"/><meta data-rh="true" property="al:android:url" content="medium://p/1264346619ec"/><meta data-rh="true" property="al:ios:url" content="medium://p/1264346619ec"/><meta data-rh="true" property="al:android:app_name" content="Medium"/><meta data-rh="true" name="description" content="For the past three years, the Android Open Source Project (AOSP) applications team has taken on the task of converting AOSP apps from Java to Kotlin. This pursuit was begun as part of Android’s…"/><meta data-rh="true" property="og:description" content="Over the course of 6 weeks, over 11,000 lines of Java code within the QuickSearchBox app were converted to Kotlin to showcase best…"/><meta data-rh="true" property="og:url" content="https://medium.com/androiddevelopers/migrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec"/><meta data-rh="true" property="al:web:url" content="https://medium.com/androiddevelopers/migrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec"/><meta data-rh="true" property="og:image" content="https://miro.medium.com/v2/resize:fit:720/1*cWnPe-kD4hAVuH3IIcNUcA.png"/><meta data-rh="true" property="article:author" content="https://medium.com/@AndroidDev"/><meta data-rh="true" name="author" content="Android Developers"/><meta data-rh="true" name="robots" content="index,noarchive,follow,max-image-preview:large"/><meta data-rh="true" name="referrer" content="unsafe-url"/><meta data-rh="true" property="twitter:title" content="Migrating the AOSP QuickSearchBox App to Kotlin"/><meta data-rh="true" name="twitter:site" content="@androiddev"/><meta data-rh="true" name="twitter:app:url:iphone" content="medium://p/1264346619ec"/><meta data-rh="true" property="twitter:description" content="Over the course of 6 weeks, over 11,000 lines of Java code within the QuickSearchBox app were converted to Kotlin to showcase best…"/><meta data-rh="true" name="twitter:image:src" content="https://miro.medium.com/v2/resize:fit:720/1*cWnPe-kD4hAVuH3IIcNUcA.png"/><meta data-rh="true" name="twitter:card" content="summary_large_image"/><meta data-rh="true" name="twitter:creator" content="@AndroidDev"/><meta data-rh="true" name="twitter:label1" content="Reading time"/><meta data-rh="true" name="twitter:data1" content="8 min read"/><link data-rh="true" rel="icon" href="https://miro.medium.com/v2/5d8de952517e8160e40ef9841c781cdc14a5db313057fa3c3de41c6f5b494b19"/><link data-rh="true" rel="search" type="application/opensearchdescription+xml" title="Medium" href="/osd.xml"/><link data-rh="true" rel="apple-touch-icon" sizes="152x152" href="https://miro.medium.com/v2/resize:fill:304:304/10fd5c419ac61637245384e7099e131627900034828f4f386bdaa47a74eae156"/><link data-rh="true" rel="apple-touch-icon" sizes="120x120" href="https://miro.medium.com/v2/resize:fill:240:240/10fd5c419ac61637245384e7099e131627900034828f4f386bdaa47a74eae156"/><link data-rh="true" rel="apple-touch-icon" sizes="76x76" href="https://miro.medium.com/v2/resize:fill:152:152/10fd5c419ac61637245384e7099e131627900034828f4f386bdaa47a74eae156"/><link data-rh="true" rel="apple-touch-icon" sizes="60x60" href="https://miro.medium.com/v2/resize:fill:120:120/10fd5c419ac61637245384e7099e131627900034828f4f386bdaa47a74eae156"/><link data-rh="true" rel="mask-icon" href="https://miro.medium.com/v2/resize:fill:1000:1000/7*GAOKVe--MXbEJmV9230oOQ.png" color="#171717"/><link data-rh="true" rel="preconnect" href="https://glyph.medium.com" crossOrigin=""/><link data-rh="true" id="glyph_preload_link" rel="preload" as="style" type="text/css" href="https://glyph.medium.com/css/unbound.css"/><link data-rh="true" id="glyph_link" rel="stylesheet" type="text/css" href="https://glyph.medium.com/css/unbound.css"/><link data-rh="true" rel="author" href="https://medium.com/@AndroidDev"/><link data-rh="true" rel="canonical" href="https://medium.com/androiddevelopers/migrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec"/><link data-rh="true" rel="alternate" href="android-app://com.medium.reader/https/medium.com/p/1264346619ec"/><script data-rh="true" type="application/ld+json">{"@context":"http:\u002F\u002Fschema.org","@type":"NewsArticle","image":["https:\u002F\u002Fmiro.medium.com\u002Fv2\u002Fresize:fit:1200\u002F1*cWnPe-kD4hAVuH3IIcNUcA.png"],"url":"https:\u002F\u002Fmedium.com\u002Fandroiddevelopers\u002Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec","dateCreated":"2022-09-23T20:00:28.638Z","datePublished":"2022-09-23T20:00:28.638Z","dateModified":"2022-09-28T11:16:55.634Z","headline":"Migrating the AOSP QuickSearchBox App to Kotlin - Android Developers - Medium","name":"Migrating the AOSP QuickSearchBox App to Kotlin - Android Developers - Medium","description":"For the past three years, the Android Open Source Project (AOSP) applications team has taken on the task of converting AOSP apps from Java to Kotlin. This pursuit was begun as part of Android’s…","identifier":"1264346619ec","author":{"@type":"Person","name":"Android Developers","url":"https:\u002F\u002Fmedium.com\u002F@AndroidDev"},"creator":["Android Developers"],"publisher":{"@type":"Organization","name":"Android Developers","url":"https:\u002F\u002Fmedium.com\u002Fandroiddevelopers","logo":{"@type":"ImageObject","width":500,"height":60,"url":"https:\u002F\u002Fmiro.medium.com\u002Fv2\u002Fresize:fit:1000\u002F1*5pMw_nx55x_66tk77kutPQ.png"}},"mainEntityOfPage":"https:\u002F\u002Fmedium.com\u002Fandroiddevelopers\u002Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec"}</script><style type="text/css" data-fela-rehydration="566" data-fela-type="STATIC">html{box-sizing:border-box;-webkit-text-size-adjust:100%}*, *:before, *:after{box-sizing:inherit}body{margin:0;padding:0;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;color:rgba(0,0,0,0.8);position:relative;min-height:100vh}h1, h2, h3, h4, h5, h6, dl, dd, ol, ul, menu, figure, blockquote, p, pre, form{margin:0}menu, ol, ul{padding:0;list-style:none;list-style-image:none}main{display:block}a{color:inherit;text-decoration:none}a, button, input{-webkit-tap-highlight-color:transparent}img, svg{vertical-align:middle}button{background:transparent;overflow:visible}button, input, optgroup, select, textarea{margin:0}:root{--reach-tabs:1;--reach-menu-button:1}#speechify-root{font-family:Sohne, sans-serif}div[data-popper-reference-hidden="true"]{visibility:hidden;pointer-events:none}.grecaptcha-badge{visibility:hidden} /*XCode style (c) Angel Garcia <angelgarcia.mail@gmail.com>*/.hljs {background: #fff;color: black; }/* Gray DOCTYPE selectors like WebKit */ .xml .hljs-meta {color: #c0c0c0; }.hljs-comment, .hljs-quote {color: #007400; }.hljs-tag, .hljs-attribute, .hljs-keyword, .hljs-selector-tag, .hljs-literal, .hljs-name {color: #aa0d91; }.hljs-variable, .hljs-template-variable {color: #3F6E74; }.hljs-code, .hljs-string, .hljs-meta .hljs-string {color: #c41a16; }.hljs-regexp, .hljs-link {color: #0E0EFF; }.hljs-title, .hljs-symbol, .hljs-bullet, .hljs-number {color: #1c00cf; }.hljs-section, .hljs-meta {color: #643820; }.hljs-title.class_, .hljs-class .hljs-title, .hljs-type, .hljs-built_in, .hljs-params {color: #5c2699; }.hljs-attr {color: #836C28; }.hljs-subst {color: #000; }.hljs-formula {background-color: #eee;font-style: italic; }.hljs-addition {background-color: #baeeba; }.hljs-deletion {background-color: #ffc8bd; }.hljs-selector-id, .hljs-selector-class {color: #9b703f; }.hljs-doctag, .hljs-strong {font-weight: bold; }.hljs-emphasis {font-style: italic; } </style><style type="text/css" data-fela-rehydration="566" data-fela-type="KEYFRAME">@-webkit-keyframes k1{0%{opacity:0.8}50%{opacity:0.5}100%{opacity:0.8}}@-moz-keyframes k1{0%{opacity:0.8}50%{opacity:0.5}100%{opacity:0.8}}@keyframes k1{0%{opacity:0.8}50%{opacity:0.5}100%{opacity:0.8}}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE">.a{font-family:medium-content-sans-serif-font, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif}.b{font-weight:400}.c{background-color:rgba(255, 255, 255, 1)}.l{display:block}.m{position:sticky}.n{top:0}.o{z-index:500}.p{padding:0 24px}.q{align-items:center}.r{border-bottom:solid 1px #F2F2F2}.y{height:41px}.z{line-height:20px}.ab{display:flex}.ac{height:57px}.ae{flex:1 0 auto}.af{color:inherit}.ag{fill:inherit}.ah{font-size:inherit}.ai{border:inherit}.aj{font-family:inherit}.ak{letter-spacing:inherit}.al{font-weight:inherit}.am{padding:0}.an{margin:0}.ao{cursor:pointer}.ap:disabled{cursor:not-allowed}.aq:disabled{color:#6B6B6B}.ar:disabled{fill:#6B6B6B}.au{width:auto}.av path{fill:#242424}.aw{height:25px}.ax{margin-left:16px}.ay{border:none}.az{border-radius:20px}.ba{width:240px}.bb{background:#F9F9F9}.bc path{fill:#6B6B6B}.be{outline:none}.bf{font-family:sohne, "Helvetica Neue", Helvetica, Arial, sans-serif}.bg{font-size:14px}.bh{width:100%}.bi{padding:10px 20px 10px 0}.bj{background-color:transparent}.bk{color:#242424}.bl::placeholder{color:#6B6B6B}.bm{display:inline-block}.bn{margin-left:12px}.bo{margin-right:12px}.bp{border-radius:4px}.bq{margin-left:24px}.br{height:24px}.bx{background-color:#F9F9F9}.by{border-radius:50%}.bz{height:32px}.ca{width:32px}.cb{justify-content:center}.ch{max-width:680px}.ci{min-width:0}.cj{animation:k1 1.2s ease-in-out infinite}.ck{height:100vh}.cl{margin-bottom:16px}.cm{margin-top:48px}.cn{align-items:flex-start}.co{flex-direction:column}.cp{justify-content:space-between}.cq{margin-bottom:24px}.cw{width:80%}.cx{background-color:#F2F2F2}.dd{height:44px}.de{width:44px}.df{margin:auto 0}.dg{margin-bottom:4px}.dh{height:16px}.di{width:120px}.dj{width:80px}.dp{margin-bottom:8px}.dq{width:96%}.dr{width:98%}.ds{width:81%}.dt{margin-left:8px}.du{color:#6B6B6B}.dv{font-size:13px}.dw{height:100%}.ep{color:#FFFFFF}.eq{fill:#FFFFFF}.er{background:rgba(132, 132, 136, 1)}.es{border-color:rgba(132, 132, 136, 1)}.ew:disabled{cursor:inherit !important}.ex:disabled{opacity:0.3}.ey:disabled:hover{background:rgba(132, 132, 136, 1)}.ez:disabled:hover{border-color:rgba(132, 132, 136, 1)}.fa{border-radius:99em}.fb{border-width:1px}.fc{border-style:solid}.fd{box-sizing:border-box}.fe{text-decoration:none}.ff{text-align:center}.fi{margin-right:32px}.fj{position:relative}.fk{fill:#6B6B6B}.fn{background:transparent}.fo svg{margin-left:4px}.fp svg{fill:#6B6B6B}.fr{box-shadow:inset 0 0 0 1px rgba(0, 0, 0, 0.05)}.fs{position:absolute}.fz{margin:0 24px}.gd{background:rgba(255, 255, 255, 1)}.ge{border:1px solid #F2F2F2}.gf{box-shadow:0 1px 4px #F2F2F2}.gg{max-height:100vh}.gh{overflow-y:auto}.gi{left:0}.gj{top:calc(100vh + 100px)}.gk{bottom:calc(100vh + 100px)}.gl{width:10px}.gm{pointer-events:none}.gn{word-break:break-word}.go{word-wrap:break-word}.gp:after{display:block}.gq:after{content:""}.gr:after{clear:both}.gs{line-height:1.23}.gt{letter-spacing:0}.gu{font-style:normal}.gv{font-weight:700}.hv{font-style:inherit}.ib{align-items:baseline}.ic{width:48px}.id{height:48px}.ie{border:2px solid rgba(255, 255, 255, 1)}.if{z-index:0}.ig{box-shadow:none}.ih{border:1px solid rgba(0, 0, 0, 0.05)}.ii{margin-left:-12px}.ij{width:28px}.ik{height:28px}.il{z-index:1}.im{width:24px}.in{margin-bottom:2px}.io{flex-wrap:nowrap}.ip{font-size:16px}.iq{line-height:24px}.is{margin:0 8px}.it{display:inline}.iu{color:rgba(132, 132, 136, 1)}.iv{fill:rgba(132, 132, 136, 1)}.iy{flex:0 0 auto}.jb{flex-wrap:wrap}.je{white-space:pre-wrap}.jf{margin-right:4px}.jg{overflow:hidden}.jh{max-height:20px}.ji{text-overflow:ellipsis}.jj{display:-webkit-box}.jk{-webkit-line-clamp:1}.jl{-webkit-box-orient:vertical}.jm{word-break:break-all}.jo{padding-left:8px}.jp{padding-right:8px}.kq> *{flex-shrink:0}.kr{overflow-x:scroll}.ks::-webkit-scrollbar{display:none}.kt{scrollbar-width:none}.ku{-ms-overflow-style:none}.kv{width:74px}.kw{flex-direction:row}.kx{z-index:2}.la{-webkit-user-select:none}.lb{border:0}.lc{fill:rgba(117, 117, 117, 1)}.lf{outline:0}.lg{user-select:none}.lh> svg{pointer-events:none}.lq{cursor:progress}.lr{margin-left:4px}.ls{margin-top:0px}.lt{opacity:1}.lu{padding:4px 0}.lx{width:16px}.lz{display:inline-flex}.mf{max-width:100%}.mg{padding:8px 2px}.mh svg{color:#6B6B6B}.my{line-height:1.58}.mz{letter-spacing:-0.004em}.na{font-family:source-serif-pro, Georgia, Cambria, "Times New Roman", Times, serif}.nv{margin-bottom:-0.46em}.nw{font-style:italic}.nx{margin-left:auto}.ny{margin-right:auto}.nz{max-width:720px}.of{clear:both}.oh{cursor:zoom-in}.oi{z-index:auto}.ok{height:auto}.ol{line-height:1.12}.om{letter-spacing:-0.022em}.on{font-weight:600}.pi{margin-bottom:-0.28em}.po{text-decoration:underline}.pp{list-style-type:disc}.pq{margin-left:30px}.pr{padding-left:0px}.px{list-style-type:decimal}.py{padding:2px 4px}.pz{font-size:75%}.qa> strong{font-family:inherit}.qb{font-family:source-code-pro, Menlo, Monaco, "Courier New", Courier, monospace}.qc{margin:auto}.qd{padding-bottom:100%}.qe{height:0}.qf{max-width:987px}.qg{max-width:1970px}.qh{max-width:1972px}.qi{margin-top:32px}.qj{margin-bottom:14px}.qk{padding-top:24px}.ql{padding-bottom:10px}.qm{background-color:#000000}.qn{height:3px}.qo{width:3px}.qp{margin-right:20px}.qq{margin-bottom:26px}.qr{margin-top:6px}.qs{margin-top:8px}.qt{margin-right:8px}.qu{padding:8px 16px}.qv{border-radius:100px}.qw{transition:background 300ms ease}.qy{white-space:nowrap}.qz{border-top:none}.ra{height:52px}.rb{max-height:52px}.rc{box-sizing:content-box}.rd{position:static}.rf{max-width:155px}.rq{height:0px}.rr{margin-bottom:40px}.rs{margin-bottom:48px}.sg{border-radius:2px}.si{height:64px}.sj{width:64px}.sk{align-self:flex-end}.sl{flex:1 1 auto}.sr{padding-right:4px}.ss{font-weight:500}.tf{margin-top:16px}.tg{color:rgba(255, 255, 255, 1)}.th{fill:rgba(255, 255, 255, 1)}.ti{background:rgba(25, 25, 25, 1)}.tj{border-color:rgba(25, 25, 25, 1)}.tm:disabled{opacity:0.1}.tn:disabled:hover{background:rgba(25, 25, 25, 1)}.to:disabled:hover{border-color:rgba(25, 25, 25, 1)}.tx{gap:18px}.ty{fill:rgba(61, 61, 61, 1)}.ua{fill:#242424}.ub{background:0}.uc{border-color:#242424}.ud:disabled:hover{color:#242424}.ue:disabled:hover{fill:#242424}.uf:disabled:hover{border-color:#242424}.uq{border-bottom:solid 1px #E5E5E5}.ur{margin-top:72px}.us{padding:24px 0}.ut{margin-bottom:0px}.uu{margin-right:16px}.as:hover:not(:disabled){color:rgba(25, 25, 25, 1)}.at:hover:not(:disabled){fill:rgba(25, 25, 25, 1)}.et:hover{background:rgba(113, 114, 117, 1)}.eu:hover{border-color:rgba(113, 114, 117, 1)}.ev:hover{cursor:pointer}.fl:hover{color:#242424}.fm:hover{fill:#242424}.fq:hover svg{fill:#242424}.ft:hover{background-color:rgba(0, 0, 0, 0.1)}.ir:hover{text-decoration:underline}.iw:hover:not(:disabled){color:rgba(113, 114, 117, 1)}.ix:hover:not(:disabled){fill:rgba(113, 114, 117, 1)}.le:hover{fill:rgba(8, 8, 8, 1)}.lv:hover{fill:#000000}.lw:hover p{color:#000000}.ly:hover{color:#000000}.mi:hover svg{color:#000000}.qx:hover{background-color:#F2F2F2}.sh:hover{background-color:none}.tk:hover{background:#000000}.tl:hover{border-color:#242424}.tz:hover{fill:rgba(25, 25, 25, 1)}.bd:focus-within path{fill:#242424}.ld:focus{fill:rgba(8, 8, 8, 1)}.mj:focus svg{color:#000000}.oj:focus{transform:scale(1.01)}.li:active{border-style:none}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE" media="all and (min-width: 1080px)">.d{display:none}.bw{width:64px}.cg{margin:0 64px}.cv{height:48px}.dc{margin-bottom:52px}.do{margin-bottom:48px}.ef{font-size:14px}.eg{line-height:20px}.em{font-size:13px}.eo{padding:5px 12px}.fh{display:flex}.fy{margin-bottom:68px}.gc{max-width:680px}.hq{font-size:42px}.hr{margin-top:1.19em}.hs{margin-bottom:32px}.ht{line-height:52px}.hu{letter-spacing:-0.011em}.ia{align-items:center}.kc{border-top:solid 1px #F2F2F2}.kd{border-bottom:solid 1px #F2F2F2}.ke{margin:32px 0 0}.kf{padding:3px 8px}.ko> *{margin-right:24px}.kp> :last-child{margin-right:0}.lp{margin-top:0px}.me{margin:0}.nr{font-size:20px}.ns{margin-top:2.14em}.nt{line-height:32px}.nu{letter-spacing:-0.003em}.oe{margin-top:56px}.pe{font-size:24px}.pf{margin-top:1.95em}.pg{line-height:30px}.ph{letter-spacing:-0.016em}.pn{margin-top:0.94em}.pw{margin-top:1.14em}.rk{display:inline-block}.rp{margin-bottom:104px}.rt{flex-direction:row}.rw{margin-bottom:0}.rx{margin-right:20px}.sm{max-width:500px}.td{line-height:24px}.te{letter-spacing:0}.tt{margin-bottom:88px}.tw{margin-bottom:72px}.uk{width:min-width}.up{padding-top:72px}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE" media="all and (max-width: 1079.98px)">.e{display:none}.lo{margin-top:0px}.rj{display:inline-block}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE" media="all and (max-width: 903.98px)">.f{display:none}.ln{margin-top:0px}.ri{display:inline-block}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE" media="all and (max-width: 727.98px)">.g{display:none}.ll{margin-top:0px}.lm{margin-right:0px}.rh{display:inline-block}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE" media="all and (max-width: 551.98px)">.h{display:none}.s{display:flex}.t{justify-content:space-between}.bs{width:24px}.cc{margin:0 24px}.cr{height:40px}.cy{margin-bottom:44px}.dk{margin-bottom:32px}.dx{font-size:13px}.dy{line-height:20px}.eh{padding:0px 8px 1px}.fu{margin-bottom:4px}.gw{font-size:32px}.gx{margin-top:1.01em}.gy{margin-bottom:24px}.gz{line-height:38px}.ha{letter-spacing:-0.014em}.hw{align-items:flex-start}.iz{flex-direction:column}.jc{margin-bottom:2px}.jq{margin:24px -24px 0}.jr{padding:0}.kg> *{margin-right:8px}.kh> :last-child{margin-right:24px}.ky{margin-left:0px}.lj{margin-top:0px}.lk{margin-right:0px}.ma{margin:0}.mk{border:1px solid #F2F2F2}.ml{border-radius:99em}.mm{padding:0px 16px 0px 12px}.mn{height:38px}.mo{align-items:center}.mq svg{margin-right:8px}.nb{font-size:18px}.nc{margin-top:1.56em}.nd{line-height:28px}.ne{letter-spacing:-0.003em}.oa{margin-top:40px}.oo{font-size:20px}.op{margin-top:1.2em}.oq{line-height:24px}.or{letter-spacing:0}.pj{margin-top:0.67em}.ps{margin-top:1.34em}.rg{display:inline-block}.rl{margin-bottom:96px}.se{margin-bottom:20px}.sf{margin-right:0}.sq{max-width:100%}.st{font-size:24px}.su{line-height:30px}.sv{letter-spacing:-0.016em}.tp{margin-bottom:64px}.ug{width:100%}.ul{padding-top:48px}.mp:hover{border-color:#E5E5E5}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE" media="all and (min-width: 904px) and (max-width: 1079.98px)">.i{display:none}.bv{width:64px}.cf{margin:0 64px}.cu{height:48px}.db{margin-bottom:52px}.dn{margin-bottom:48px}.ed{font-size:14px}.ee{line-height:20px}.ek{font-size:13px}.el{padding:5px 12px}.fg{display:flex}.fx{margin-bottom:68px}.gb{max-width:680px}.hl{font-size:42px}.hm{margin-top:1.19em}.hn{margin-bottom:32px}.ho{line-height:52px}.hp{letter-spacing:-0.011em}.hz{align-items:center}.jy{border-top:solid 1px #F2F2F2}.jz{border-bottom:solid 1px #F2F2F2}.ka{margin:32px 0 0}.kb{padding:3px 8px}.km> *{margin-right:24px}.kn> :last-child{margin-right:0}.md{margin:0}.nn{font-size:20px}.no{margin-top:2.14em}.np{line-height:32px}.nq{letter-spacing:-0.003em}.od{margin-top:56px}.pa{font-size:24px}.pb{margin-top:1.95em}.pc{line-height:30px}.pd{letter-spacing:-0.016em}.pm{margin-top:0.94em}.pv{margin-top:1.14em}.ro{margin-bottom:104px}.ru{flex-direction:row}.ry{margin-bottom:0}.rz{margin-right:20px}.sn{max-width:500px}.tb{line-height:24px}.tc{letter-spacing:0}.ts{margin-bottom:88px}.tv{margin-bottom:72px}.uj{width:min-width}.uo{padding-top:72px}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE" media="all and (min-width: 728px) and (max-width: 903.98px)">.j{display:none}.w{display:flex}.x{justify-content:space-between}.bu{width:64px}.ce{margin:0 48px}.ct{height:48px}.da{margin-bottom:52px}.dm{margin-bottom:48px}.eb{font-size:13px}.ec{line-height:20px}.ej{padding:0px 8px 1px}.fw{margin-bottom:68px}.ga{max-width:680px}.hg{font-size:42px}.hh{margin-top:1.19em}.hi{margin-bottom:32px}.hj{line-height:52px}.hk{letter-spacing:-0.011em}.hy{align-items:center}.ju{border-top:solid 1px #F2F2F2}.jv{border-bottom:solid 1px #F2F2F2}.jw{margin:32px 0 0}.jx{padding:3px 8px}.kk> *{margin-right:24px}.kl> :last-child{margin-right:0}.mc{margin:0}.nj{font-size:20px}.nk{margin-top:2.14em}.nl{line-height:32px}.nm{letter-spacing:-0.003em}.oc{margin-top:56px}.ow{font-size:24px}.ox{margin-top:1.95em}.oy{line-height:30px}.oz{letter-spacing:-0.016em}.pl{margin-top:0.94em}.pu{margin-top:1.14em}.rn{margin-bottom:104px}.rv{flex-direction:row}.sa{margin-bottom:0}.sb{margin-right:20px}.so{max-width:500px}.sz{line-height:24px}.ta{letter-spacing:0}.tr{margin-bottom:88px}.tu{margin-bottom:72px}.ui{width:min-width}.un{padding-top:72px}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE" media="all and (min-width: 552px) and (max-width: 727.98px)">.k{display:none}.u{display:flex}.v{justify-content:space-between}.bt{width:24px}.cd{margin:0 24px}.cs{height:40px}.cz{margin-bottom:44px}.dl{margin-bottom:32px}.dz{font-size:13px}.ea{line-height:20px}.ei{padding:0px 8px 1px}.fv{margin-bottom:4px}.hb{font-size:32px}.hc{margin-top:1.01em}.hd{margin-bottom:24px}.he{line-height:38px}.hf{letter-spacing:-0.014em}.hx{align-items:flex-start}.ja{flex-direction:column}.jd{margin-bottom:2px}.js{margin:24px 0 0}.jt{padding:0}.ki> *{margin-right:8px}.kj> :last-child{margin-right:8px}.kz{margin-left:0px}.mb{margin:0}.mr{border:1px solid #F2F2F2}.ms{border-radius:99em}.mt{padding:0px 16px 0px 12px}.mu{height:38px}.mv{align-items:center}.mx svg{margin-right:8px}.nf{font-size:18px}.ng{margin-top:1.56em}.nh{line-height:28px}.ni{letter-spacing:-0.003em}.ob{margin-top:40px}.os{font-size:20px}.ot{margin-top:1.2em}.ou{line-height:24px}.ov{letter-spacing:0}.pk{margin-top:0.67em}.pt{margin-top:1.34em}.rm{margin-bottom:96px}.sc{margin-bottom:20px}.sd{margin-right:0}.sp{max-width:100%}.sw{font-size:24px}.sx{line-height:30px}.sy{letter-spacing:-0.016em}.tq{margin-bottom:64px}.uh{width:100%}.um{padding-top:48px}.mw:hover{border-color:#E5E5E5}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE" media="print">.re{display:none}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE" media="(orientation: landscape) and (max-width: 903.98px)">.jn{max-height:none}</style><style type="text/css" data-fela-rehydration="566" data-fela-type="RULE" media="(prefers-reduced-motion: no-preference)">.og{transition:transform 300ms cubic-bezier(0.2, 0, 0.2, 1)}</style></head><body><div id="root"><div class="a b c"><div class="d e f g h i j k"></div><script>document.domain = document.domain;</script><div class="l c"><div class="l m n o c"><div class="p q r s t u v w x i d y z"><a class="du ag dv bf ak b am an ao ap aq ar as at s u w i d q dw z" href="https://rsci.app.link/?%24canonical_url=https%3A%2F%2Fmedium.com%2Fp%2F1264346619ec&%7Efeature=LoOpenInAppButton&%7Echannel=ShowPostUnderCollection&source=---top_nav_layout_nav----------------------------------" rel="noopener follow">Open in app<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" fill="none" viewBox="0 0 10 10" class="dt"><path fill="currentColor" d="M.985 8.485a.375.375 0 1 0 .53.53zM8.75 1.25h.375A.375.375 0 0 0 8.75.875zM8.375 6.5a.375.375 0 1 0 .75 0zM3.5.875a.375.375 0 1 0 0 .75zm-1.985 8.14 7.5-7.5-.53-.53-7.5 7.5zm6.86-7.765V6.5h.75V1.25zM3.5 1.625h5.25v-.75H3.5z"></path></svg></a><div class="ab q"><p class="bf b dx dy dz ea eb ec ed ee ef eg du"><span><button class="bf b dx dy eh dz ea ei eb ec ej ek ee el em eg eo ep eq er es et eu ev ew ex ey ez fa fb fc fd bm fe ff" data-testid="headerSignUpButton">Sign up</button></span></p><div class="ax l"><p class="bf b dx dy dz ea eb ec ed ee ef eg du"><span><a class="af ag ah ai aj ak al am an ao ap aq ar as at" data-testid="headerSignInButton" rel="noopener follow" href="/m/signin?operation=login&redirect=https%3A%2F%2Fmedium.com%2Fandroiddevelopers%2Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec&source=post_page---top_nav_layout_nav-----------------------global_nav-----------">Sign in</a></span></p></div></div></div><div class="p q r ab ac"><div class="ab q ae"><a class="af ag ah ai aj ak al am an ao ap aq ar as at ab" aria-label="Homepage" data-testid="headerMediumLogo" rel="noopener follow" href="/?source=---top_nav_layout_nav----------------------------------"><svg xmlns="http://www.w3.org/2000/svg" width="719" height="160" fill="none" viewBox="0 0 719 160" class="au av aw"><path fill="#242424" d="m174.104 9.734.215-.047V8.02H130.39L89.6 103.89 48.81 8.021H1.472v1.666l.212.047c8.018 1.81 12.09 4.509 12.09 14.242V137.93c0 9.734-4.087 12.433-12.106 14.243l-.212.047v1.671h32.118v-1.665l-.213-.048c-8.018-1.809-12.089-4.509-12.089-14.242V30.586l52.399 123.305h2.972l53.925-126.743V140.75c-.687 7.688-4.721 10.062-11.982 11.701l-.215.05v1.652h55.948v-1.652l-.215-.05c-7.269-1.639-11.4-4.013-12.087-11.701l-.037-116.774h.037c0-9.733 4.071-12.432 12.087-14.242m25.555 75.488c.915-20.474 8.268-35.252 20.606-35.507 3.806.063 6.998 1.312 9.479 3.714 5.272 5.118 7.751 15.812 7.368 31.793zm-.553 5.77h65.573v-.275c-.186-15.656-4.721-27.834-13.466-36.196-7.559-7.227-18.751-11.203-30.507-11.203h-.263c-6.101 0-13.584 1.48-18.909 4.16-6.061 2.807-11.407 7.003-15.855 12.511-7.161 8.874-11.499 20.866-12.554 34.343q-.05.606-.092 1.212a50 50 0 0 0-.065 1.151 85.807 85.807 0 0 0-.094 5.689c.71 30.524 17.198 54.917 46.483 54.917 25.705 0 40.675-18.791 44.407-44.013l-1.886-.664c-6.557 13.556-18.334 21.771-31.738 20.769-18.297-1.369-32.314-19.922-31.042-42.395m139.722 41.359c-2.151 5.101-6.639 7.908-12.653 7.908s-11.513-4.129-15.418-11.63c-4.197-8.053-6.405-19.436-6.405-32.92 0-28.067 8.729-46.22 22.24-46.22 5.657 0 10.111 2.807 12.236 7.704zm43.499 20.008c-8.019-1.897-12.089-4.722-12.089-14.951V1.309l-48.716 14.353v1.757l.299-.024c6.72-.543 11.278.386 13.925 2.83 2.072 1.915 3.082 4.853 3.082 8.987v18.66c-4.803-3.067-10.516-4.56-17.448-4.56-14.059 0-26.909 5.92-36.176 16.672-9.66 11.205-14.767 26.518-14.767 44.278-.003 31.72 15.612 53.039 38.851 53.039 13.595 0 24.533-7.449 29.54-20.013v16.865h43.711v-1.746zM424.1 19.819c0-9.904-7.468-17.374-17.375-17.374-9.859 0-17.573 7.632-17.573 17.374s7.721 17.374 17.573 17.374c9.907 0 17.375-7.47 17.375-17.374m11.499 132.546c-8.019-1.897-12.089-4.722-12.089-14.951h-.035V43.635l-43.714 12.551v1.705l.263.024c9.458.842 12.047 4.1 12.047 15.152v81.086h43.751v-1.746zm112.013 0c-8.018-1.897-12.089-4.722-12.089-14.951V43.635l-41.621 12.137v1.71l.246.026c7.733.813 9.967 4.257 9.967 15.36v59.279c-2.578 5.102-7.415 8.131-13.274 8.336-9.503 0-14.736-6.419-14.736-18.073V43.638l-43.714 12.55v1.703l.262.024c9.459.84 12.05 4.097 12.05 15.152v50.17a56.3 56.3 0 0 0 .91 10.444l.787 3.423c3.701 13.262 13.398 20.197 28.59 20.197 12.868 0 24.147-7.966 29.115-20.43v17.311h43.714v-1.747zm169.818 1.788v-1.749l-.213-.05c-8.7-2.006-12.089-5.789-12.089-13.49v-63.79c0-19.89-11.171-31.761-29.883-31.761-13.64 0-25.141 7.882-29.569 20.16-3.517-13.01-13.639-20.16-28.606-20.16-13.146 0-23.449 6.938-27.869 18.657V43.643L545.487 55.68v1.715l.263.024c9.345.829 12.047 4.181 12.047 14.95v81.784h40.787v-1.746l-.215-.053c-6.941-1.631-9.181-4.606-9.181-12.239V66.998c1.836-4.289 5.537-9.37 12.853-9.37 9.086 0 13.692 6.296 13.692 18.697v77.828h40.797v-1.746l-.215-.053c-6.94-1.631-9.18-4.606-9.18-12.239V75.066a42 42 0 0 0-.578-7.26c1.947-4.661 5.86-10.177 13.475-10.177 9.214 0 13.691 6.114 13.691 18.696v77.828z"></path></svg></a><div class="ax h"><div class="ab ay az ba bb q bc bd"><div class="bm" aria-hidden="false" aria-describedby="searchResults" aria-labelledby="searchResults"></div><div class="bn bo ab"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M4.092 11.06a6.95 6.95 0 1 1 13.9 0 6.95 6.95 0 0 1-13.9 0m6.95-8.05a8.05 8.05 0 1 0 5.13 14.26l3.75 3.75a.56.56 0 1 0 .79-.79l-3.73-3.73A8.05 8.05 0 0 0 11.042 3z" clip-rule="evenodd"></path></svg></div><input role="combobox" aria-controls="searchResults" aria-expanded="false" aria-label="search" data-testid="headerSearchInput" tabindex="0" class="ay be bf bg z bh bi bj bk bl" placeholder="Search" value=""/></div></div></div><div class="h k w fg fh"><div class="fi ab"><span><a class="af ag ah ai aj ak al am an ao ap aq ar as at" data-testid="headerWriteButton" rel="noopener follow" href="/m/signin?operation=register&redirect=https%3A%2F%2Fmedium.com%2Fnew-story&source=---top_nav_layout_nav-----------------------new_post_topnav-----------"><div class="bf b bg z du fj fk ab q fl fm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24" aria-label="Write"><path fill="currentColor" d="M14 4a.5.5 0 0 0 0-1zm7 6a.5.5 0 0 0-1 0zm-7-7H4v1h10zM3 4v16h1V4zm1 17h16v-1H4zm17-1V10h-1v10zm-1 1a1 1 0 0 0 1-1h-1zM3 20a1 1 0 0 0 1 1v-1zM4 3a1 1 0 0 0-1 1h1z"></path><path stroke="currentColor" d="m17.5 4.5-8.458 8.458a.25.25 0 0 0-.06.098l-.824 2.47a.25.25 0 0 0 .316.316l2.47-.823a.25.25 0 0 0 .098-.06L19.5 6.5m-2-2 2.323-2.323a.25.25 0 0 1 .354 0l1.646 1.646a.25.25 0 0 1 0 .354L19.5 6.5m-2-2 2 2"></path></svg><div class="dt l">Write</div></div></a></span></div></div><div class="k j i d"><div class="fi ab"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" data-testid="headerSearchButton" rel="noopener follow" href="/search?source=---top_nav_layout_nav----------------------------------"><div class="bf b bg z du fj fk ab q fl fm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24" aria-label="Search"><path fill="currentColor" fill-rule="evenodd" d="M4.092 11.06a6.95 6.95 0 1 1 13.9 0 6.95 6.95 0 0 1-13.9 0m6.95-8.05a8.05 8.05 0 1 0 5.13 14.26l3.75 3.75a.56.56 0 1 0 .79-.79l-3.73-3.73A8.05 8.05 0 0 0 11.042 3z" clip-rule="evenodd"></path></svg></div></a></div></div><div class="fi h k j"><div class="ab q"><p class="bf b dx dy dz ea eb ec ed ee ef eg du"><span><button class="bf b dx dy eh dz ea ei eb ec ej ek ee el em eg eo ep eq er es et eu ev ew ex ey ez fa fb fc fd bm fe ff" data-testid="headerSignUpButton">Sign up</button></span></p><div class="ax l"><p class="bf b dx dy dz ea eb ec ed ee ef eg du"><span><a class="af ag ah ai aj ak al am an ao ap aq ar as at" data-testid="headerSignInButton" rel="noopener follow" href="/m/signin?operation=login&redirect=https%3A%2F%2Fmedium.com%2Fandroiddevelopers%2Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec&source=post_page---top_nav_layout_nav-----------------------global_nav-----------">Sign in</a></span></p></div></div></div><div class="l" aria-hidden="false"><button class="ay fn am ab q ao fo fp fq" aria-label="user options menu" data-testid="headerUserIcon"><div class="l fj"><img alt="" class="l fd by bz ca cx" src="https://miro.medium.com/v2/resize:fill:64:64/1*dmbNkD5D-u45r44go_cf0g.png" width="32" height="32" loading="lazy" role="presentation"/><div class="fr by l bz ca fs n ay ft"></div></div></button></div></div></div><div class="l"><div class="fu fv fw fx fy l"><div class="ab cb"><div class="ci bh fz ga gb gc"></div></div><article><div class="l"><div class="l"><span class="l"></span><section><div><div class="fs gi gj gk gl gm"></div><div class="gn go gp gq gr"><div class="ab cb"><div class="ci bh fz ga gb gc"><div><h1 id="1896" class="pw-post-title gs gt gu bf gv gw gx gy gz ha hb hc hd he hf hg hh hi hj hk hl hm hn ho hp hq hr hs ht hu bk" data-testid="storyTitle"><em class="hv">Migrating the AOSP QuickSearchBox App to Kotlin</em></h1><div><div class="speechify-ignore ab cp"><div class="speechify-ignore bh l"><div class="hw hx hy hz ia ab"><div><div class="ab ib"><div><div class="bm" aria-hidden="false"><a rel="noopener follow" href="/@AndroidDev?source=post_page---byline--1264346619ec--------------------------------"><div class="l ic id by ie if"><div class="l fj"><img alt="Android Developers" class="l fd by dd de cx" src="https://miro.medium.com/v2/resize:fill:88:88/1*VglQS9HKgUvUuAX36Np5qQ.png" width="44" height="44" loading="lazy" data-testid="authorPhoto"/><div class="ig by l dd de fs n ih ft"></div></div></div></a></div></div><div class="ii ab fj"><div><div class="bm" aria-hidden="false"><a href="https://medium.com/androiddevelopers?source=post_page---byline--1264346619ec--------------------------------" rel="noopener follow"><div class="l ij ik by ie il"><div class="l fj"><img alt="Android Developers" class="l fd by br im cx" src="https://miro.medium.com/v2/resize:fill:48:48/1*4Tg6pPzer7cIarYaszIKaQ.png" width="24" height="24" loading="lazy" data-testid="publicationPhoto"/><div class="ig by l br im fs n ih ft"></div></div></div></a></div></div></div></div></div><div class="bn bh l"><div class="ab"><div style="flex:1"><span class="bf b bg z bk"><div class="in ab q"><div class="ab q io"><div class="ab q"><div><div class="bm" aria-hidden="false"><p class="bf b ip iq bk"><a class="af ag ah ai aj ak al am an ao ap aq ar ir" data-testid="authorName" rel="noopener follow" href="/@AndroidDev?source=post_page---byline--1264346619ec--------------------------------">Android Developers</a></p></div></div></div><span class="is it" aria-hidden="true"><span class="bf b bg z du">·</span></span><p class="bf b ip iq du"><span><a class="iu iv ah ai aj ak al am an ao ap aq ar ex iw ix" rel="noopener follow" href="/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fsubscribe%2Fuser%2Fe1f26db83092&operation=register&redirect=https%3A%2F%2Fmedium.com%2Fandroiddevelopers%2Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec&user=Android+Developers&userId=e1f26db83092&source=post_page-e1f26db83092--byline--1264346619ec---------------------post_header-----------">Follow</a></span></p></div></div></span></div></div><div class="l iy"><span class="bf b bg z du"><div class="ab cn iz ja jb"><div class="jc jd ab"><div class="bf b bg z du ab je"><span class="jf l iy">Published in</span><div><div class="l" aria-hidden="false"><a class="af ag ah ai aj ak al am an ao ap aq ar ir ab q" data-testid="publicationName" href="https://medium.com/androiddevelopers?source=post_page---byline--1264346619ec--------------------------------" rel="noopener follow"><p class="bf b bg z jg jh ji jj jk jl jm jn bk">Android Developers</p></a></div></div></div><div class="h k"><span class="is it" aria-hidden="true"><span class="bf b bg z du">·</span></span></div></div><span class="bf b bg z du"><div class="ab ae"><span data-testid="storyReadTime">8 min read</span><div class="jo jp l" aria-hidden="true"><span class="l" aria-hidden="true"><span class="bf b bg z du">·</span></span></div><span data-testid="storyPublishDate">Sep 23, 2022</span></div></span></div></span></div></div></div><div class="ab cp jq jr js jt ju jv jw jx jy jz ka kb kc kd ke kf"><div class="h k w fg fh q"><div class="kv l"><div class="ab q kw kx"><div class="pw-multi-vote-icon fj jf ky kz la"><span><a class="af ag ah ai aj ak al am an ao ap aq ar as at" data-testid="headerClapButton" rel="noopener follow" href="/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fandroiddevelopers%2F1264346619ec&operation=register&redirect=https%3A%2F%2Fmedium.com%2Fandroiddevelopers%2Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec&user=Android+Developers&userId=e1f26db83092&source=---header_actions--1264346619ec---------------------clap_footer-----------"><div><div class="bm" aria-hidden="false"><div class="lb ao lc ld le lf am lg lh li la"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-label="clap"><path fill-rule="evenodd" d="M11.37.828 12 3.282l.63-2.454zM13.916 3.953l1.523-2.112-1.184-.39zM8.589 1.84l1.522 2.112-.337-2.501zM18.523 18.92c-.86.86-1.75 1.246-2.62 1.33a6 6 0 0 0 .407-.372c2.388-2.389 2.86-4.951 1.399-7.623l-.912-1.603-.79-1.672c-.26-.56-.194-.98.203-1.288a.7.7 0 0 1 .546-.132c.283.046.546.231.728.5l2.363 4.157c.976 1.624 1.141 4.237-1.324 6.702m-10.999-.438L3.37 14.328a.828.828 0 0 1 .585-1.408.83.83 0 0 1 .585.242l2.158 2.157a.365.365 0 0 0 .516-.516l-2.157-2.158-1.449-1.449a.826.826 0 0 1 1.167-1.17l3.438 3.44a.363.363 0 0 0 .516 0 .364.364 0 0 0 0-.516L5.293 9.513l-.97-.97a.826.826 0 0 1 0-1.166.84.84 0 0 1 1.167 0l.97.968 3.437 3.436a.36.36 0 0 0 .517 0 .366.366 0 0 0 0-.516L6.977 7.83a.82.82 0 0 1-.241-.584.82.82 0 0 1 .824-.826c.219 0 .43.087.584.242l5.787 5.787a.366.366 0 0 0 .587-.415l-1.117-2.363c-.26-.56-.194-.98.204-1.289a.7.7 0 0 1 .546-.132c.283.046.545.232.727.501l2.193 3.86c1.302 2.38.883 4.59-1.277 6.75-1.156 1.156-2.602 1.627-4.19 1.367-1.418-.236-2.866-1.033-4.079-2.246M10.75 5.971l2.12 2.12c-.41.502-.465 1.17-.128 1.89l.22.465-3.523-3.523a.8.8 0 0 1-.097-.368c0-.22.086-.428.241-.584a.847.847 0 0 1 1.167 0m7.355 1.705c-.31-.461-.746-.758-1.23-.837a1.44 1.44 0 0 0-1.11.275c-.312.24-.505.543-.59.881a1.74 1.74 0 0 0-.906-.465 1.47 1.47 0 0 0-.82.106l-2.182-2.182a1.56 1.56 0 0 0-2.2 0 1.54 1.54 0 0 0-.396.701 1.56 1.56 0 0 0-2.21-.01 1.55 1.55 0 0 0-.416.753c-.624-.624-1.649-.624-2.237-.037a1.557 1.557 0 0 0 0 2.2c-.239.1-.501.238-.715.453a1.56 1.56 0 0 0 0 2.2l.516.515a1.556 1.556 0 0 0-.753 2.615L7.01 19c1.32 1.319 2.909 2.189 4.475 2.449q.482.08.971.08c.85 0 1.653-.198 2.393-.579.231.033.46.054.686.054 1.266 0 2.457-.52 3.505-1.567 2.763-2.763 2.552-5.734 1.439-7.586z" clip-rule="evenodd"></path></svg></div></div></div></a></span></div><div class="pw-multi-vote-count l lj lk ll lm ln lo lp"><p class="bf b dv z du"><span class="lq">--</span></p></div></div></div><div><div class="bm" aria-hidden="false"><button class="ao lb lt lu ab q fk lv lw" aria-label="responses"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="ls"><path d="M18.006 16.803c1.533-1.456 2.234-3.325 2.234-5.321C20.24 7.357 16.709 4 12.191 4S4 7.357 4 11.482c0 4.126 3.674 7.482 8.191 7.482.817 0 1.622-.111 2.393-.327.231.2.48.391.744.559 1.06.693 2.203 1.044 3.399 1.044.224-.008.4-.112.486-.287a.49.49 0 0 0-.042-.518c-.495-.67-.845-1.364-1.04-2.057a4 4 0 0 1-.125-.598zm-3.122 1.055-.067-.223-.315.096a8 8 0 0 1-2.311.338c-4.023 0-7.292-2.955-7.292-6.587 0-3.633 3.269-6.588 7.292-6.588 4.014 0 7.112 2.958 7.112 6.593 0 1.794-.608 3.469-2.027 4.72l-.195.168v.255c0 .056 0 .151.016.295.025.231.081.478.154.733.154.558.398 1.117.722 1.659a5.3 5.3 0 0 1-2.165-.845c-.276-.176-.714-.383-.941-.59z"></path></svg><p class="bf b dv z du"><span class="pw-responses-count lr ls">6</span></p></button></div></div></div><div class="ab q kg kh ki kj kk kl km kn ko kp kq kr ks kt ku"><div class="lx k j i d"></div><div class="h k"><div><div class="bm" aria-hidden="false"><span><a class="af ag ah ai aj ak al am an ao ap aq ar as at" data-testid="headerBookmarkButton" rel="noopener follow" href="/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fbookmark%2Fp%2F1264346619ec&operation=register&redirect=https%3A%2F%2Fmedium.com%2Fandroiddevelopers%2Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec&source=---header_actions--1264346619ec---------------------bookmark_footer-----------"><svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="none" viewBox="0 0 25 25" class="du ly" aria-label="Add to list bookmark button"><path fill="currentColor" d="M18 2.5a.5.5 0 0 1 1 0V5h2.5a.5.5 0 0 1 0 1H19v2.5a.5.5 0 1 1-1 0V6h-2.5a.5.5 0 0 1 0-1H18zM7 7a1 1 0 0 1 1-1h3.5a.5.5 0 0 0 0-1H8a2 2 0 0 0-2 2v14a.5.5 0 0 0 .805.396L12.5 17l5.695 4.396A.5.5 0 0 0 19 21v-8.5a.5.5 0 0 0-1 0v7.485l-5.195-4.012a.5.5 0 0 0-.61 0L7 19.985z"></path></svg></a></span></div></div></div><div class="fd lz cn"><div class="l ae"><div class="ab cb"><div class="ma mb mc md me mf ci bh"><div class="ab"><div class="bm bh" aria-hidden="false"><div><div class="bm" aria-hidden="false"><button aria-label="Listen" data-testid="audioPlayButton" class="af fk ah ai aj ak al mg an ao ap ex mh mi lw mj mk ml mm mn s mo mp mq mr ms mt mu u mv mw mx"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M3 12a9 9 0 1 1 18 0 9 9 0 0 1-18 0m9-10C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2m3.376 10.416-4.599 3.066a.5.5 0 0 1-.777-.416V8.934a.5.5 0 0 1 .777-.416l4.599 3.066a.5.5 0 0 1 0 .832" clip-rule="evenodd"></path></svg><div class="j i d"><p class="bf b bg z du">Listen</p></div></button></div></div></div></div></div></div></div></div><div class="bm" aria-hidden="false" aria-describedby="postFooterSocialMenu" aria-labelledby="postFooterSocialMenu"><div><div class="bm" aria-hidden="false"><button aria-controls="postFooterSocialMenu" aria-expanded="false" aria-label="Share Post" data-testid="headerSocialShareButton" class="af fk ah ai aj ak al mg an ao ap ex mh mi lw mj mk ml mm mn s mo mp mq mr ms mt mu u mv mw mx"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M15.218 4.931a.4.4 0 0 1-.118.132l.012.006a.45.45 0 0 1-.292.074.5.5 0 0 1-.3-.13l-2.02-2.02v7.07c0 .28-.23.5-.5.5s-.5-.22-.5-.5v-7.04l-2 2a.45.45 0 0 1-.57.04h-.02a.4.4 0 0 1-.16-.3.4.4 0 0 1 .1-.32l2.8-2.8a.5.5 0 0 1 .7 0l2.8 2.79a.42.42 0 0 1 .068.498m-.106.138.008.004v-.01zM16 7.063h1.5a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-11c-1.1 0-2-.9-2-2v-10a2 2 0 0 1 2-2H8a.5.5 0 0 1 .35.15.5.5 0 0 1 .15.35.5.5 0 0 1-.15.35.5.5 0 0 1-.35.15H6.4c-.5 0-.9.4-.9.9v10.2a.9.9 0 0 0 .9.9h11.2c.5 0 .9-.4.9-.9v-10.2c0-.5-.4-.9-.9-.9H16a.5.5 0 0 1 0-1" clip-rule="evenodd"></path></svg><div class="j i d"><p class="bf b bg z du">Share</p></div></button></div></div></div></div></div></div></div></div></div><p id="2bdc" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">submitted by <em class="nw">Ryan O’Leary, Android Intern</em></p><figure class="oa ob oc od oe of nx ny paragraph-image"><div role="button" tabindex="0" class="og oh fj oi bh oj"><div class="nx ny nz"><picture><source srcSet="https://miro.medium.com/v2/resize:fit:640/format:webp/1*cWnPe-kD4hAVuH3IIcNUcA.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*cWnPe-kD4hAVuH3IIcNUcA.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*cWnPe-kD4hAVuH3IIcNUcA.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*cWnPe-kD4hAVuH3IIcNUcA.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*cWnPe-kD4hAVuH3IIcNUcA.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*cWnPe-kD4hAVuH3IIcNUcA.png 1100w, https://miro.medium.com/v2/resize:fit:1400/format:webp/1*cWnPe-kD4hAVuH3IIcNUcA.png 1400w" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" type="image/webp"/><source data-testid="og" srcSet="https://miro.medium.com/v2/resize:fit:640/1*cWnPe-kD4hAVuH3IIcNUcA.png 640w, https://miro.medium.com/v2/resize:fit:720/1*cWnPe-kD4hAVuH3IIcNUcA.png 720w, https://miro.medium.com/v2/resize:fit:750/1*cWnPe-kD4hAVuH3IIcNUcA.png 750w, https://miro.medium.com/v2/resize:fit:786/1*cWnPe-kD4hAVuH3IIcNUcA.png 786w, https://miro.medium.com/v2/resize:fit:828/1*cWnPe-kD4hAVuH3IIcNUcA.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*cWnPe-kD4hAVuH3IIcNUcA.png 1100w, https://miro.medium.com/v2/resize:fit:1400/1*cWnPe-kD4hAVuH3IIcNUcA.png 1400w" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px"/><img alt="" class="bh mf ok c" width="700" height="209" loading="eager" role="presentation"/></picture></div></div></figure><h1 id="a67c" class="ol om gu bf on oo op oq or os ot ou ov ow ox oy oz pa pb pc pd pe pf pg ph pi bk">Overview</h1><p id="f17a" class="pw-post-body-paragraph my mz gu na b nb pj nd ne nf pk nh ni nj pl nl nm nn pm np nq nr pn nt nu nv gn bk">For the past three years, the <a class="af po" href="https://android.googlesource.com/platform/packages/apps/" rel="noopener ugc nofollow" target="_blank">Android Open Source Project (AOSP) applications</a> team has taken on the task of converting AOSP apps from Java to Kotlin. This pursuit was begun as part of Android’s commitment to increasingly develop with <a class="af po" href="https://developer.android.com/kotlin/first" rel="noopener ugc nofollow" target="_blank"><strong class="na gv">Kotlin-first</strong></a>. Kotlin is a safe, pragmatic, and concise language, with a number of language-specific advantages. Useful Kotlin language features include:</p><ul class=""><li id="51b5" class="my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv pp pq pr bk"><strong class="na gv">Null Safety</strong>: Kotlin types are non-nullable unless explicitly specified. This is incredibly useful for developers in avoiding difficult to trace null-pointer exceptions.</li><li id="a333" class="my mz gu na b nb ps nd ne nf pt nh ni nj pu nl nm nn pv np nq nr pw nt nu nv pp pq pr bk"><strong class="na gv">Conciseness</strong>: Kotlin allows developers to reduce the amount of boilerplate code, expressing more with less lines of code compared to Java.</li><li id="a75d" class="my mz gu na b nb ps nd ne nf pt nh ni nj pu nl nm nn pv np nq nr pw nt nu nv pp pq pr bk"><strong class="na gv">Java Interoperability</strong>: Interoperability between Kotlin and the Java programming language proved very helpful for this project as the migration could be done incrementally, and is useful for all developers working in mixed Java and Kotlin projects.</li><li id="3b09" class="my mz gu na b nb ps nd ne nf pt nh ni nj pu nl nm nn pv np nq nr pw nt nu nv pp pq pr bk"><strong class="na gv">Jetpack Libraries and Coroutines</strong>: Kotlin provides in-language support for lightweight, structured concurrency through coroutines. Additionally, Android development content is Kotlin-first and provides access to useful Jetpack libraries.</li></ul><p id="fd6c" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">AOSP interns converted the AOSP <a class="af po" rel="noopener" href="/androiddevelopers/re-writing-the-aosp-deskclock-app-in-kotlin-76c836370cb">Deskclock</a> app in 2019 and <a class="af po" rel="noopener" href="/androiddevelopers/re-writing-the-aosp-calendar-app-in-kotlin-48ceb0e3a65c">Calendar</a> app in 2020, detailing the migration process in similar articles. This year the AOSP team set out to fully convert the QuickSearchBox app as part of a summer 2022 intern project. Introduced in 2009 through the Android 1.6 release, the QuickSearchBox app allows users to search both their device and the web directly from their home screen, providing suggestions based on downloaded content, contacts, apps, and browser history. Over the course of 6 weeks, over 11,000 lines of Java code within the QuickSearchBox app were converted to Kotlin to showcase best practices in Android development and provide the functionality of the QuickSearchBox app with Kotlin-first in mind.</p><h1 id="9d75" class="ol om gu bf on oo op oq or os ot ou ov ow ox oy oz pa pb pc pd pe pf pg ph pi bk">Automatic Conversion and Bug Fixing</h1><p id="71b4" class="pw-post-body-paragraph my mz gu na b nb pj nd ne nf pk nh ni nj pl nl nm nn pm np nq nr pn nt nu nv gn bk">To migrate the QuickSearchBox app to Kotlin, we utilized the <a class="af po" href="https://developer.android.com/kotlin/add-kotlin#convert" rel="noopener ugc nofollow" target="_blank">Kotlin conversion tool</a> that is included in Android Studio. Our process followed five steps:</p><ol class=""><li id="83a3" class="my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv px pq pr bk">Copy the <code class="cx py pz qa qb b">.java</code> file to a <code class="cx py pz qa qb b">.kt</code> file of the same name using the command: <code class="cx py pz qa qb b">cp ExampleFile.java ExampleFile.kt</code></li><li id="a042" class="my mz gu na b nb ps nd ne nf pt nh ni nj pu nl nm nn pv np nq nr pw nt nu nv px pq pr bk">Utilize the provided conversion tool to convert the Java code to Kotlin</li><li id="90f1" class="my mz gu na b nb ps nd ne nf pt nh ni nj pu nl nm nn pv np nq nr pw nt nu nv px pq pr bk">Add the <code class="cx py pz qa qb b">.java</code> file to an <code class="cx py pz qa qb b">exclude_srcs</code> property in the <code class="cx py pz qa qb b">Android.bp</code> file so that the conversion could occur incrementally, one file at a time</li><li id="13b1" class="my mz gu na b nb ps nd ne nf pt nh ni nj pu nl nm nn pv np nq nr pw nt nu nv px pq pr bk">Resolve compilation errors and any other bugs introduced by conversion</li><li id="5e78" class="my mz gu na b nb ps nd ne nf pt nh ni nj pu nl nm nn pv np nq nr pw nt nu nv px pq pr bk">Run and pass unit tests to check for correctness of the Kotlin based implementation, and run manual tests to check feature parity between the Kotlin and the legacy Java based app.</li></ol><p id="8780" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">The git history of each of these steps was maintained to showcase the process of migration to outside developers, and can be viewed for each file in the master branch of <a class="af po" href="https://cs.android.com/android/_/android/platform/packages/apps/QuickSearchBox/+/master:src/com/android/quicksearchbox/" rel="noopener ugc nofollow" target="_blank">AOSP QuickSearchBox</a>. The bug-fixing step in this process was necessary due to several common issues found within Android Studio’s Java-to-Kotlin conversion tool, which occurred in the majority of AOSP QuickSearchBox files that were migrated.</p><p id="94d6" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">These c<strong class="na gv">ommon problems and their solutions</strong> are as follows:</p><p id="1e9a" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Common Problem: </strong>Required Import statements would frequently be removed in the converted Kotlin file</p><p id="b0e8" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Solution: </strong>Manually convert in the needed import statements</p><p id="5396" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Common Problem: </strong>The <code class="cx py pz qa qb b">override</code> keyword was often not added to methods and variables tagged <code class="cx py pz qa qb b">@Override</code></p><p id="73fc" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Solution: </strong>Manually add in <code class="cx py pz qa qb b">override</code> modifier</p><p id="ba08" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Common Problem:</strong> One larger problem encountered was the conversion of nullable variables to non-nullable variables in the Kotlin code. This caused a variety of errors, from type mismatches to large sections of QuickSearchBox code not being executed since logic relying on null checking was now unused. Converting one file at a time also meant that the use of inherently nullable types in the unconverted Java files would create issues when attempting to assign, override, or return those inherited variables in the Kotlin code.</p><p id="35bc" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Solution: </strong>In most cases, these properties actually needed to be nullable in order to achieve the desired functionality. The solution here was simply to use nullable types by adding <code class="cx py pz qa qb b">?</code> to the type declaration. This required adding in safe calls (<code class="cx py pz qa qb b">?.</code>) where appropriate and changing the expected types where necessary to avoid mismatch errors. While this may seem counterintuitive to the null safe nature of Kotlin, it was necessary for some properties to remain nullable since they were set asynchronously, and all variables that could be made non-nullable were.</p><p id="8684" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Common Problem:</strong> Functions beginning with <code class="cx py pz qa qb b">get</code> and <code class="cx py pz qa qb b">set</code> were changed by the converter to variables with explicitly defined getters/setters, but many usages were unchanged and left undefined</p><p id="9c06" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Solution: </strong>Manually change calls to the converted functions to the variable name; eg. <code class="cx py pz qa qb b">getSuggestions()</code> -> <code class="cx py pz qa qb b">suggestions</code></p><p id="3134" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Common Problem:</strong> Usages of <code class="cx py pz qa qb b">getClass()</code> were unchanged by the converter in the Kotlin code. Unlike Java, Kotlin does not support calling <code class="cx py pz qa qb b">getClass()</code> on objects to retrieve a <code class="cx py pz qa qb b">Class</code> type token.</p><p id="4174" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Solution: </strong>Utilize Kotlin’s class reference syntax, changing usages of <code class="cx py pz qa qb b">getClass()</code> to <code class="cx py pz qa qb b">::class</code> to return the <code class="cx py pz qa qb b">KClass</code> token.</p><p id="fb62" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Common Problem:</strong> The QuickSearchBox project was built with the <code class="cx py pz qa qb b">-Werror</code> flag turned on, and a common source of errors was the use of <code class="cx py pz qa qb b"><a class="af po" href="https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html" rel="noopener ugc nofollow" target="_blank">java.util.Collection</a></code> in a Kotlin class. When migrating an app to Kotlin, it’s recommended for developers to utilize libraries built for Kotlin in order to maximize language-specific benefits.</p><p id="59aa" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><strong class="na gv">Solution: </strong>Switch to using the Kotlin equivalent of the required class. This has the benefit of increased safety, as the class can be specified as mutable or immutable when necessary.</p><p id="8467" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">i.e. <code class="cx py pz qa qb b">java.util.Collection</code> -> <code class="cx py pz qa qb b">kotlin.collections.Collection</code> or <code class="cx py pz qa qb b">kotlin.collections.MutableCollection</code></p><p id="13de" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">As the migration took place, the API level of the QuickSearchBox app was also updated to the latest version. This involved replacing several deprecated function calls with the new versions recommended by Android. An example of this was the deprecated use of <code class="cx py pz qa qb b">AsyncTask</code> within <code class="cx py pz qa qb b">SearchBaseUrlHelper</code>.</p><figure class="oa ob oc od oe of"><div class="qc jg l fj"><div class="qd qe l"></div></div></figure><p id="f099" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><code class="cx py pz qa qb b">AsyncTask</code> was deprecated in API level 30, with <a class="af po" href="https://developer.android.com/reference/android/os/AsyncTask" rel="noopener ugc nofollow" target="_blank">Android documentation</a> recommending the use of <code class="cx py pz qa qb b"><a class="af po" href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html" rel="noopener ugc nofollow" target="_blank">java.util.concurrent</a></code> or <a class="af po" href="https://developer.android.com/topic/libraries/architecture/coroutines" rel="noopener ugc nofollow" target="_blank">Kotlin concurrency utilities</a>. Since this project is geared towards showcasing the benefits of developing in Kotlin, we replaced the use of <code class="cx py pz qa qb b">AsyncTask</code> with a Kotlin coroutine scope and <code class="cx py pz qa qb b">async</code> block to asynchronously issue network requests. While, like Java, Kotlin supports the creation of threads, coroutines are non-blocking and stackless, allowing for lower memory usage.</p><figure class="oa ob oc od oe of"><div class="qc jg l fj"><div class="qd qe l"></div></div></figure><p id="9257" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">Finally, one subtle source of bugs that developers should watch out for comes from the Kotlin converter feature that changes methods whose names begin with <code class="cx py pz qa qb b">get</code> and <code class="cx py pz qa qb b">set</code> to variables with explicitly defined getters and setters. When this change takes place, the converter will rename the migrated function to a variable of the same name, except without the preceding <code class="cx py pz qa qb b">get</code> or <code class="cx py pz qa qb b">set</code>. However, the converter fails to check for existing usages of the same name, potentially introducing devastating bugs into the converted code. In large files with many usages of these variables, these bugs can be incredibly hard to catch. This was seen in the file <code class="cx py pz qa qb b">DelayingSuggestionsAdapter</code>, where the line:</p><figure class="oa ob oc od oe of"><div class="qc jg l fj"><div class="qd qe l"></div></div></figure><p id="7265" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">Was changed in the Kotlin code to:</p><figure class="oa ob oc od oe of"><div class="qc jg l fj"><div class="qd qe l"></div></div></figure><p id="7d17" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">This introduced a subtle runtime bug where an object would be released too early, generating an exception in a completely different file. While the fix was a simple one line change, tracking down all improper usages caused by the conversion can prove time consuming for developers, and ideally the converter would utilize more comprehensive code checking before creating new variables.</p><h1 id="a5f9" class="ol om gu bf on oo op oq or os ot ou ov ow ox oy oz pa pb pc pd pe pf pg ph pi bk">Performance Analysis</h1><p id="e3cb" class="pw-post-body-paragraph my mz gu na b nb pj nd ne nf pk nh ni nj pl nl nm nn pm np nq nr pn nt nu nv gn bk">After completing the conversion, we ran some benchmark tests to analyze the changes that occurred within the AOSP QuickSearchBox app when migrating to Kotlin. Some interesting performance metrics we can use to compare the Java and Kotlin versions of the fully functioning QuickSearchBox app include:</p><figure class="oa ob oc od oe of nx ny paragraph-image"><div role="button" tabindex="0" class="og oh fj oi bh oj"><div class="nx ny qf"><picture><source srcSet="https://miro.medium.com/v2/resize:fit:640/format:webp/1*a6CTIV_4uQytd8hv7uwtXw.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*a6CTIV_4uQytd8hv7uwtXw.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*a6CTIV_4uQytd8hv7uwtXw.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*a6CTIV_4uQytd8hv7uwtXw.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*a6CTIV_4uQytd8hv7uwtXw.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*a6CTIV_4uQytd8hv7uwtXw.png 1100w, https://miro.medium.com/v2/resize:fit:1400/format:webp/1*a6CTIV_4uQytd8hv7uwtXw.png 1400w" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" type="image/webp"/><source data-testid="og" srcSet="https://miro.medium.com/v2/resize:fit:640/1*a6CTIV_4uQytd8hv7uwtXw.png 640w, https://miro.medium.com/v2/resize:fit:720/1*a6CTIV_4uQytd8hv7uwtXw.png 720w, https://miro.medium.com/v2/resize:fit:750/1*a6CTIV_4uQytd8hv7uwtXw.png 750w, https://miro.medium.com/v2/resize:fit:786/1*a6CTIV_4uQytd8hv7uwtXw.png 786w, https://miro.medium.com/v2/resize:fit:828/1*a6CTIV_4uQytd8hv7uwtXw.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*a6CTIV_4uQytd8hv7uwtXw.png 1100w, https://miro.medium.com/v2/resize:fit:1400/1*a6CTIV_4uQytd8hv7uwtXw.png 1400w" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px"/><img alt="" class="bh mf ok c" width="700" height="129" loading="lazy" role="presentation"/></picture></div></div></figure><p id="f192" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">After the migration was completed, we saw a sizable reduction of about <strong class="na gv">11.5%</strong> in the total # LOC (lines of code). It’s also worthwhile to note that the migration of the QuickSearchBox app focused on maintaining the existing functionality of the app when converting to Kotlin, without majorly refactoring the structure of the code. If we were to rewrite the entire QuickSearchBox app starting from Kotlin, we believe it would be possible to achieve an even greater reduction in size by better utilizing the concise nature of Kotlin.</p><figure class="oa ob oc od oe of nx ny paragraph-image"><div role="button" tabindex="0" class="og oh fj oi bh oj"><div class="nx ny qg"><picture><source srcSet="https://miro.medium.com/v2/resize:fit:640/format:webp/1*i6iPeWl4WMLcBnDf4sOKdA.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*i6iPeWl4WMLcBnDf4sOKdA.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*i6iPeWl4WMLcBnDf4sOKdA.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*i6iPeWl4WMLcBnDf4sOKdA.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*i6iPeWl4WMLcBnDf4sOKdA.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*i6iPeWl4WMLcBnDf4sOKdA.png 1100w, https://miro.medium.com/v2/resize:fit:1400/format:webp/1*i6iPeWl4WMLcBnDf4sOKdA.png 1400w" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" type="image/webp"/><source data-testid="og" srcSet="https://miro.medium.com/v2/resize:fit:640/1*i6iPeWl4WMLcBnDf4sOKdA.png 640w, https://miro.medium.com/v2/resize:fit:720/1*i6iPeWl4WMLcBnDf4sOKdA.png 720w, https://miro.medium.com/v2/resize:fit:750/1*i6iPeWl4WMLcBnDf4sOKdA.png 750w, https://miro.medium.com/v2/resize:fit:786/1*i6iPeWl4WMLcBnDf4sOKdA.png 786w, https://miro.medium.com/v2/resize:fit:828/1*i6iPeWl4WMLcBnDf4sOKdA.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*i6iPeWl4WMLcBnDf4sOKdA.png 1100w, https://miro.medium.com/v2/resize:fit:1400/1*i6iPeWl4WMLcBnDf4sOKdA.png 1400w" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px"/><img alt="" class="bh mf ok c" width="700" height="125" loading="lazy" role="presentation"/></picture></div></div></figure><p id="7600" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">We did see a rather significant increase in the APK size, however this increase can be attributed to the new inclusion of the <code class="cx py pz qa qb b"><a class="af po" href="https://github.com/Kotlin/kotlinx.coroutines" rel="noopener ugc nofollow" target="_blank">kotlinx-coroutines</a></code> and <code class="cx py pz qa qb b"><a class="af po" href="https://developer.android.com/jetpack/androidx/releases/core" rel="noopener ugc nofollow" target="_blank">androidx.core</a></code> libraries in this project. The <code class="cx py pz qa qb b">androidx.core</code> library was added in order to substitute several deprecated function calls with the recommended replacements in order for QuickSearchBox to target the latest API level, and would have occurred regardless of the Kotlin migration. Since the overall size of the QuickSearchBox APK was still quite small (less than a MB total) we believe this increase is justifiable.</p><p id="1c7a" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">Another consequence of the small app size from the outset is that the relative code size increase seems rather large, however that increase doesn’t scale with APK size and remains mostly constant in terms of the KB added no matter how big the app. That is, for a large app it will be a fraction of a percent instead.</p><figure class="oa ob oc od oe of nx ny paragraph-image"><div role="button" tabindex="0" class="og oh fj oi bh oj"><div class="nx ny qh"><picture><source srcSet="https://miro.medium.com/v2/resize:fit:640/format:webp/1*CinyhwFGuID57XbV3yxgkw.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*CinyhwFGuID57XbV3yxgkw.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*CinyhwFGuID57XbV3yxgkw.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*CinyhwFGuID57XbV3yxgkw.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*CinyhwFGuID57XbV3yxgkw.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*CinyhwFGuID57XbV3yxgkw.png 1100w, https://miro.medium.com/v2/resize:fit:1400/format:webp/1*CinyhwFGuID57XbV3yxgkw.png 1400w" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" type="image/webp"/><source data-testid="og" srcSet="https://miro.medium.com/v2/resize:fit:640/1*CinyhwFGuID57XbV3yxgkw.png 640w, https://miro.medium.com/v2/resize:fit:720/1*CinyhwFGuID57XbV3yxgkw.png 720w, https://miro.medium.com/v2/resize:fit:750/1*CinyhwFGuID57XbV3yxgkw.png 750w, https://miro.medium.com/v2/resize:fit:786/1*CinyhwFGuID57XbV3yxgkw.png 786w, https://miro.medium.com/v2/resize:fit:828/1*CinyhwFGuID57XbV3yxgkw.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*CinyhwFGuID57XbV3yxgkw.png 1100w, https://miro.medium.com/v2/resize:fit:1400/1*CinyhwFGuID57XbV3yxgkw.png 1400w" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px"/><img alt="" class="bh mf ok c" width="700" height="126" loading="lazy" role="presentation"/></picture></div></div></figure><p id="1465" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk">Finally, the clean build time for the code was averaged over 10 trials for each language. These trials, and the previous benchmark tests, were all taken on a machine with 96 cores and 180 GB of RAM. We actually saw a slight decrease in total build time in the Kotlin version of the app, potentially due to the decrease in # LOC, switch to several updated API calls, and use of Kotlin’s coroutines.</p><h1 id="d9f5" class="ol om gu bf on oo op oq or os ot ou ov ow ox oy oz pa pb pc pd pe pf pg ph pi bk">Conclusion</h1><p id="a7a5" class="pw-post-body-paragraph my mz gu na b nb pj nd ne nf pk nh ni nj pl nl nm nn pm np nq nr pn nt nu nv gn bk">In total, the migration of the AOSP QuickSearchBox app took approximately 6 weeks, with 1 intern working on the project. The rate of conversion greatly increased with familiarity of Git/Repo, Kotlin, and common errors found in Android Studio Kotlin tool. By the midpoint of the project, the process of converting the file, fixing errors, formatting, and maintaining the Git history was incredibly efficient, with up to 10 files being migrated per day. Ultimately, this project helped prove the benefits of converting an app to Kotlin, from more concise syntax to providing access to useful libraries and lightweight concurrency, and can act as an example for other developers seeking to migrate their own apps.</p></div></div></div><div class="ab cb qi qj qk ql" role="separator"><span class="qm by bm qn qo qp"></span><span class="qm by bm qn qo qp"></span><span class="qm by bm qn qo"></span></div><div class="gn go gp gq gr"><div class="ab cb"><div class="ci bh fz ga gb gc"><p id="879c" class="pw-post-body-paragraph my mz gu na b nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv gn bk"><em class="nw">Java is a trademark or registered trademark of Oracle and/or its affiliates.</em></p></div></div></div></div></section></div></div></article></div><div class="ab cb"><div class="ci bh fz ga gb gc"><div class="qq qr ab jb"><div class="qs ab"><a class="qt ay am ao" rel="noopener follow" href="/tag/aosp?source=post_page-----1264346619ec--------------------------------"><div class="qu fj cx qv ge qw qx bf b bg z bk qy">Aosp</div></a></div><div class="qs ab"><a class="qt ay am ao" rel="noopener follow" href="/tag/kotlin?source=post_page-----1264346619ec--------------------------------"><div class="qu fj cx qv ge qw qx bf b bg z bk qy">Kotlin</div></a></div><div class="qs ab"><a class="qt ay am ao" rel="noopener follow" href="/tag/performance-analysis?source=post_page-----1264346619ec--------------------------------"><div class="qu fj cx qv ge qw qx bf b bg z bk qy">Performance Analysis</div></a></div><div class="qs ab"><a class="qt ay am ao" rel="noopener follow" href="/tag/android-studio?source=post_page-----1264346619ec--------------------------------"><div class="qu fj cx qv ge qw qx bf b bg z bk qy">Android Studio</div></a></div><div class="qs ab"><a class="qt ay am ao" rel="noopener follow" href="/tag/best-practices?source=post_page-----1264346619ec--------------------------------"><div class="qu fj cx qv ge qw qx bf b bg z bk qy">Best Practices</div></a></div></div></div></div><div class="l"></div><footer class="qz qj ra rb rc ab q rd il c"><div class="l ae"><div class="ab cb"><div class="ci bh fz ga gb gc"><div class="ab cp re"><div class="ab q kw"><div class="rf l"><span class="l rg rh ri e d"><div class="ab q kw kx"><div class="pw-multi-vote-icon fj jf ky kz la"><span><a class="af ag ah ai aj ak al am an ao ap aq ar as at" data-testid="footerClapButton" rel="noopener follow" href="/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fandroiddevelopers%2F1264346619ec&operation=register&redirect=https%3A%2F%2Fmedium.com%2Fandroiddevelopers%2Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec&user=Android+Developers&userId=e1f26db83092&source=---footer_actions--1264346619ec---------------------clap_footer-----------"><div><div class="bm" aria-hidden="false"><div class="lb ao lc ld le lf am lg lh li la"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-label="clap"><path fill-rule="evenodd" d="M11.37.828 12 3.282l.63-2.454zM13.916 3.953l1.523-2.112-1.184-.39zM8.589 1.84l1.522 2.112-.337-2.501zM18.523 18.92c-.86.86-1.75 1.246-2.62 1.33a6 6 0 0 0 .407-.372c2.388-2.389 2.86-4.951 1.399-7.623l-.912-1.603-.79-1.672c-.26-.56-.194-.98.203-1.288a.7.7 0 0 1 .546-.132c.283.046.546.231.728.5l2.363 4.157c.976 1.624 1.141 4.237-1.324 6.702m-10.999-.438L3.37 14.328a.828.828 0 0 1 .585-1.408.83.83 0 0 1 .585.242l2.158 2.157a.365.365 0 0 0 .516-.516l-2.157-2.158-1.449-1.449a.826.826 0 0 1 1.167-1.17l3.438 3.44a.363.363 0 0 0 .516 0 .364.364 0 0 0 0-.516L5.293 9.513l-.97-.97a.826.826 0 0 1 0-1.166.84.84 0 0 1 1.167 0l.97.968 3.437 3.436a.36.36 0 0 0 .517 0 .366.366 0 0 0 0-.516L6.977 7.83a.82.82 0 0 1-.241-.584.82.82 0 0 1 .824-.826c.219 0 .43.087.584.242l5.787 5.787a.366.366 0 0 0 .587-.415l-1.117-2.363c-.26-.56-.194-.98.204-1.289a.7.7 0 0 1 .546-.132c.283.046.545.232.727.501l2.193 3.86c1.302 2.38.883 4.59-1.277 6.75-1.156 1.156-2.602 1.627-4.19 1.367-1.418-.236-2.866-1.033-4.079-2.246M10.75 5.971l2.12 2.12c-.41.502-.465 1.17-.128 1.89l.22.465-3.523-3.523a.8.8 0 0 1-.097-.368c0-.22.086-.428.241-.584a.847.847 0 0 1 1.167 0m7.355 1.705c-.31-.461-.746-.758-1.23-.837a1.44 1.44 0 0 0-1.11.275c-.312.24-.505.543-.59.881a1.74 1.74 0 0 0-.906-.465 1.47 1.47 0 0 0-.82.106l-2.182-2.182a1.56 1.56 0 0 0-2.2 0 1.54 1.54 0 0 0-.396.701 1.56 1.56 0 0 0-2.21-.01 1.55 1.55 0 0 0-.416.753c-.624-.624-1.649-.624-2.237-.037a1.557 1.557 0 0 0 0 2.2c-.239.1-.501.238-.715.453a1.56 1.56 0 0 0 0 2.2l.516.515a1.556 1.556 0 0 0-.753 2.615L7.01 19c1.32 1.319 2.909 2.189 4.475 2.449q.482.08.971.08c.85 0 1.653-.198 2.393-.579.231.033.46.054.686.054 1.266 0 2.457-.52 3.505-1.567 2.763-2.763 2.552-5.734 1.439-7.586z" clip-rule="evenodd"></path></svg></div></div></div></a></span></div><div class="pw-multi-vote-count l lj lk ll lm ln lo lp"><p class="bf b dv z du"><span class="lq">--</span></p></div></div></span><span class="l h g f rj rk"><div class="ab q kw kx"><div class="pw-multi-vote-icon fj jf ky kz la"><span><a class="af ag ah ai aj ak al am an ao ap aq ar as at" data-testid="footerClapButton" rel="noopener follow" href="/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fandroiddevelopers%2F1264346619ec&operation=register&redirect=https%3A%2F%2Fmedium.com%2Fandroiddevelopers%2Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec&user=Android+Developers&userId=e1f26db83092&source=---footer_actions--1264346619ec---------------------clap_footer-----------"><div><div class="bm" aria-hidden="false"><div class="lb ao lc ld le lf am lg lh li la"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-label="clap"><path fill-rule="evenodd" d="M11.37.828 12 3.282l.63-2.454zM13.916 3.953l1.523-2.112-1.184-.39zM8.589 1.84l1.522 2.112-.337-2.501zM18.523 18.92c-.86.86-1.75 1.246-2.62 1.33a6 6 0 0 0 .407-.372c2.388-2.389 2.86-4.951 1.399-7.623l-.912-1.603-.79-1.672c-.26-.56-.194-.98.203-1.288a.7.7 0 0 1 .546-.132c.283.046.546.231.728.5l2.363 4.157c.976 1.624 1.141 4.237-1.324 6.702m-10.999-.438L3.37 14.328a.828.828 0 0 1 .585-1.408.83.83 0 0 1 .585.242l2.158 2.157a.365.365 0 0 0 .516-.516l-2.157-2.158-1.449-1.449a.826.826 0 0 1 1.167-1.17l3.438 3.44a.363.363 0 0 0 .516 0 .364.364 0 0 0 0-.516L5.293 9.513l-.97-.97a.826.826 0 0 1 0-1.166.84.84 0 0 1 1.167 0l.97.968 3.437 3.436a.36.36 0 0 0 .517 0 .366.366 0 0 0 0-.516L6.977 7.83a.82.82 0 0 1-.241-.584.82.82 0 0 1 .824-.826c.219 0 .43.087.584.242l5.787 5.787a.366.366 0 0 0 .587-.415l-1.117-2.363c-.26-.56-.194-.98.204-1.289a.7.7 0 0 1 .546-.132c.283.046.545.232.727.501l2.193 3.86c1.302 2.38.883 4.59-1.277 6.75-1.156 1.156-2.602 1.627-4.19 1.367-1.418-.236-2.866-1.033-4.079-2.246M10.75 5.971l2.12 2.12c-.41.502-.465 1.17-.128 1.89l.22.465-3.523-3.523a.8.8 0 0 1-.097-.368c0-.22.086-.428.241-.584a.847.847 0 0 1 1.167 0m7.355 1.705c-.31-.461-.746-.758-1.23-.837a1.44 1.44 0 0 0-1.11.275c-.312.24-.505.543-.59.881a1.74 1.74 0 0 0-.906-.465 1.47 1.47 0 0 0-.82.106l-2.182-2.182a1.56 1.56 0 0 0-2.2 0 1.54 1.54 0 0 0-.396.701 1.56 1.56 0 0 0-2.21-.01 1.55 1.55 0 0 0-.416.753c-.624-.624-1.649-.624-2.237-.037a1.557 1.557 0 0 0 0 2.2c-.239.1-.501.238-.715.453a1.56 1.56 0 0 0 0 2.2l.516.515a1.556 1.556 0 0 0-.753 2.615L7.01 19c1.32 1.319 2.909 2.189 4.475 2.449q.482.08.971.08c.85 0 1.653-.198 2.393-.579.231.033.46.054.686.054 1.266 0 2.457-.52 3.505-1.567 2.763-2.763 2.552-5.734 1.439-7.586z" clip-rule="evenodd"></path></svg></div></div></div></a></span></div><div class="pw-multi-vote-count l lj lk ll lm ln lo lp"><p class="bf b dv z du"><span class="lq">--</span></p></div></div></span></div><div class="bq ab"><div><div class="bm" aria-hidden="false"><button class="ao lb lt lu ab q fk lv lw" aria-label="responses"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="ls"><path d="M18.006 16.803c1.533-1.456 2.234-3.325 2.234-5.321C20.24 7.357 16.709 4 12.191 4S4 7.357 4 11.482c0 4.126 3.674 7.482 8.191 7.482.817 0 1.622-.111 2.393-.327.231.2.48.391.744.559 1.06.693 2.203 1.044 3.399 1.044.224-.008.4-.112.486-.287a.49.49 0 0 0-.042-.518c-.495-.67-.845-1.364-1.04-2.057a4 4 0 0 1-.125-.598zm-3.122 1.055-.067-.223-.315.096a8 8 0 0 1-2.311.338c-4.023 0-7.292-2.955-7.292-6.587 0-3.633 3.269-6.588 7.292-6.588 4.014 0 7.112 2.958 7.112 6.593 0 1.794-.608 3.469-2.027 4.72l-.195.168v.255c0 .056 0 .151.016.295.025.231.081.478.154.733.154.558.398 1.117.722 1.659a5.3 5.3 0 0 1-2.165-.845c-.276-.176-.714-.383-.941-.59z"></path></svg><p class="bf b bg z du"><span class="pw-responses-count lr ls">6</span></p></button></div></div></div></div><div class="ab q"><div class="qp l iy"><div><div class="bm" aria-hidden="false"><span><a class="af ag ah ai aj ak al am an ao ap aq ar as at" data-testid="footerBookmarkButton" rel="noopener follow" href="/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fbookmark%2Fp%2F1264346619ec&operation=register&redirect=https%3A%2F%2Fmedium.com%2Fandroiddevelopers%2Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec&source=---footer_actions--1264346619ec---------------------bookmark_footer-----------"><svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="none" viewBox="0 0 25 25" class="du ly" aria-label="Add to list bookmark button"><path fill="currentColor" d="M18 2.5a.5.5 0 0 1 1 0V5h2.5a.5.5 0 0 1 0 1H19v2.5a.5.5 0 1 1-1 0V6h-2.5a.5.5 0 0 1 0-1H18zM7 7a1 1 0 0 1 1-1h3.5a.5.5 0 0 0 0-1H8a2 2 0 0 0-2 2v14a.5.5 0 0 0 .805.396L12.5 17l5.695 4.396A.5.5 0 0 0 19 21v-8.5a.5.5 0 0 0-1 0v7.485l-5.195-4.012a.5.5 0 0 0-.61 0L7 19.985z"></path></svg></a></span></div></div></div><div class="qp l iy"><div class="bm" aria-hidden="false" aria-describedby="postFooterSocialMenu" aria-labelledby="postFooterSocialMenu"><div><div class="bm" aria-hidden="false"><button aria-controls="postFooterSocialMenu" aria-expanded="false" aria-label="Share Post" data-testid="footerSocialShareButton" class="af fk ah ai aj ak al mg an ao ap ex mh mi lw mj"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M15.218 4.931a.4.4 0 0 1-.118.132l.012.006a.45.45 0 0 1-.292.074.5.5 0 0 1-.3-.13l-2.02-2.02v7.07c0 .28-.23.5-.5.5s-.5-.22-.5-.5v-7.04l-2 2a.45.45 0 0 1-.57.04h-.02a.4.4 0 0 1-.16-.3.4.4 0 0 1 .1-.32l2.8-2.8a.5.5 0 0 1 .7 0l2.8 2.79a.42.42 0 0 1 .068.498m-.106.138.008.004v-.01zM16 7.063h1.5a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-11c-1.1 0-2-.9-2-2v-10a2 2 0 0 1 2-2H8a.5.5 0 0 1 .35.15.5.5 0 0 1 .15.35.5.5 0 0 1-.15.35.5.5 0 0 1-.35.15H6.4c-.5 0-.9.4-.9.9v10.2a.9.9 0 0 0 .9.9h11.2c.5 0 .9-.4.9-.9v-10.2c0-.5-.4-.9-.9-.9H16a.5.5 0 0 1 0-1" clip-rule="evenodd"></path></svg></button></div></div></div></div></div></div></div></div></div></footer><div class="rl rm rn ro rp l"><div class="ab cb"><div class="ci bh fz ga gb gc"><div class="rq bh r rr"></div><div class="rs l"><div class="ab rt ru rv ja iz"><div class="rw rx ry rz sa sb sc sd se sf ab cp"><div class="h k"><a href="https://medium.com/androiddevelopers?source=post_page---post_publication_info--1264346619ec--------------------------------" rel="noopener follow"><div class="fj ab"><img alt="Android Developers" class="sg ic id cx" src="https://miro.medium.com/v2/resize:fill:96:96/1*4Tg6pPzer7cIarYaszIKaQ.png" width="48" height="48" loading="lazy"/><div class="sg l id ic fs n fr sh"></div></div></a></div><div class="j i d"><a href="https://medium.com/androiddevelopers?source=post_page---post_publication_info--1264346619ec--------------------------------" rel="noopener follow"><div class="fj ab"><img alt="Android Developers" class="sg sj si cx" src="https://miro.medium.com/v2/resize:fill:128:128/1*4Tg6pPzer7cIarYaszIKaQ.png" width="64" height="64" loading="lazy"/><div class="sg l si sj fs n fr sh"></div></div></a></div><div class="j i d sk iy"><div class="ab"></div></div></div><div class="ab co sl"><div class="sm sn so sp sq l"><a class="af ag ah aj ak al am an ao ap aq ar as at ab q" href="https://medium.com/androiddevelopers?source=post_page---post_publication_info--1264346619ec--------------------------------" rel="noopener follow"><h2 class="pw-author-name bf ss st su sv sw sx sy nj sz ta nn tb tc nr td te bk"><span class="gn sr">Published in <!-- -->Android Developers</span></h2></a><div class="qs ab ib"><div class="l iy"><span class="pw-follower-count bf b bg z du"><a class="af ag ah ai aj ak al am an ao ap aq ar ir" rel="noopener follow" href="/androiddevelopers/followers?source=post_page---post_publication_info--1264346619ec--------------------------------">52K Followers</a></span></div><div class="bf b bg z du ab je"><span class="is l" aria-hidden="true"><span class="bf b bg z du">·</span></span><a class="af ag ah ai aj ak al am an ao ap aq ar ir" rel="noopener follow" href="/androiddevelopers/test-smarter-not-harder-new-testing-strategies-documentation-255973814ae3?source=post_page---post_publication_info--1264346619ec--------------------------------">Last published <!-- -->4 days ago</a></div></div><div class="tf l"><p class="bf b bg z bk"><span class="gn">Articles on modern tools and resources to help you build experiences that people love, faster and easier, across every Android device.</span></p></div></div></div><div class="h k"><div class="ab"></div></div></div></div><div class="ab rt ru rv ja iz"><div class="rw rx ry rz sa sb sc sd se sf ab cp"><div class="h k"><a tabindex="0" rel="noopener follow" href="/@AndroidDev?source=post_page---post_author_info--1264346619ec--------------------------------"><div class="l fj"><img alt="Android Developers" class="l fd by id ic cx" src="https://miro.medium.com/v2/resize:fill:96:96/1*VglQS9HKgUvUuAX36Np5qQ.png" width="48" height="48" loading="lazy"/><div class="fr by l id ic fs n ay sh"></div></div></a></div><div class="j i d"><a tabindex="0" rel="noopener follow" href="/@AndroidDev?source=post_page---post_author_info--1264346619ec--------------------------------"><div class="l fj"><img alt="Android Developers" class="l fd by si sj cx" src="https://miro.medium.com/v2/resize:fill:128:128/1*VglQS9HKgUvUuAX36Np5qQ.png" width="64" height="64" loading="lazy"/><div class="fr by l si sj fs n ay sh"></div></div></a></div><div class="j i d sk iy"><div class="ab"><span><button class="bf b bg z tg qu th ti tj tk tl ev ew tm tn to fa fb fc fd bm fe ff">Follow</button></span></div></div></div><div class="ab co sl"><div class="sm sn so sp sq l"><a class="af ag ah aj ak al am an ao ap aq ar as at ab q" rel="noopener follow" href="/@AndroidDev?source=post_page---post_author_info--1264346619ec--------------------------------"><h2 class="pw-author-name bf ss st su sv sw sx sy nj sz ta nn tb tc nr td te bk"><span class="gn sr">Written by <!-- -->Android Developers</span></h2></a><div class="qs ab ib"><div class="l iy"><span class="pw-follower-count bf b bg z du"><a class="af ag ah ai aj ak al am an ao ap aq ar ir" rel="noopener follow" href="/@AndroidDev/followers?source=post_page---post_author_info--1264346619ec--------------------------------">72K Followers</a></span></div><div class="bf b bg z du ab je"><span class="is l" aria-hidden="true"><span class="bf b bg z du">·</span></span><a class="af ag ah ai aj ak al am an ao ap aq ar ir" rel="noopener follow" href="/@AndroidDev/following?source=post_page---post_author_info--1264346619ec--------------------------------">60 Following</a></div></div><div class="tf l"><p class="bf b bg z bk"><span class="gn">News and announcements for developers from the Android team.</span></p></div></div></div><div class="h k"><div class="ab"><span><button class="bf b bg z tg qu th ti tj tk tl ev ew tm tn to fa fb fc fd bm fe ff">Follow</button></span></div></div></div></div></div></div><div class="tp tq tr ts tt l"><div class="rq bh r tp tq tu tv tw"></div><div class="ab cb"><div class="ci bh fz ga gb gc"><div class="ab q cp"><h2 class="bf ss oo oq or os ou ov ow oy oz pa pc pd pe pg ph bk">Responses (<!-- -->6<!-- -->)</h2><div class="ab tx"><div><div class="bm" aria-hidden="false"><a class="ty tz" href="https://policy.medium.com/medium-rules-30e5502c4eb4?source=post_page---post_responses--1264346619ec--------------------------------" rel="noopener follow" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 25 25"><path fill-rule="evenodd" d="M11.987 5.036a.754.754 0 0 1 .914-.01c.972.721 1.767 1.218 2.6 1.543.828.322 1.719.485 2.887.505a.755.755 0 0 1 .741.757c-.018 3.623-.43 6.256-1.449 8.21-1.034 1.984-2.662 3.209-4.966 4.083a.75.75 0 0 1-.537-.003c-2.243-.874-3.858-2.095-4.897-4.074-1.024-1.951-1.457-4.583-1.476-8.216a.755.755 0 0 1 .741-.757c1.195-.02 2.1-.182 2.923-.503.827-.322 1.6-.815 2.519-1.535m.468.903c-.897.69-1.717 1.21-2.623 1.564-.898.35-1.856.527-3.026.565.037 3.45.469 5.817 1.36 7.515.884 1.684 2.25 2.762 4.284 3.571 2.092-.81 3.465-1.89 4.344-3.575.886-1.698 1.299-4.065 1.334-7.512-1.149-.039-2.091-.217-2.99-.567-.906-.353-1.745-.873-2.683-1.561m-.009 9.155a2.672 2.672 0 1 0 0-5.344 2.672 2.672 0 0 0 0 5.344m0 1a3.672 3.672 0 1 0 0-7.344 3.672 3.672 0 0 0 0 7.344m-1.813-3.777.525-.526.916.917 1.623-1.625.526.526-2.149 2.152z" clip-rule="evenodd"></path></svg></a></div></div></div></div><div class="qi l"><button class="bf b bg z bk qu ua ub uc ly lv tl ev ew ex ud ue uf fa ug uh ui uj uk fb fc fd bm fe ff">See all responses</button></div></div></div></div><div class="ul um un uo up l bx"><div class="h k j"><div class="rq bh uq ur"></div><div class="ab cb"><div class="ci bh fz ga gb gc"><div class="us ab kw jb"><div class="ut uu l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" href="https://help.medium.com/hc/en-us?source=post_page-----1264346619ec--------------------------------" rel="noopener follow"><p class="bf b dv z du">Help</p></a></div><div class="ut uu l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" href="https://medium.statuspage.io/?source=post_page-----1264346619ec--------------------------------" rel="noopener follow"><p class="bf b dv z du">Status</p></a></div><div class="ut uu l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="/about?autoplay=1&source=post_page-----1264346619ec--------------------------------"><p class="bf b dv z du">About</p></a></div><div class="ut uu l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="/jobs-at-medium/work-at-medium-959d1a85284e?source=post_page-----1264346619ec--------------------------------"><p class="bf b dv z du">Careers</p></a></div><div class="ut uu l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" href="pressinquiries@medium.com?source=post_page-----1264346619ec--------------------------------" rel="noopener follow"><p class="bf b dv z du">Press</p></a></div><div class="ut uu l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" href="https://blog.medium.com/?source=post_page-----1264346619ec--------------------------------" rel="noopener follow"><p class="bf b dv z du">Blog</p></a></div><div class="ut uu l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" href="https://policy.medium.com/medium-privacy-policy-f03bf92035c9?source=post_page-----1264346619ec--------------------------------" rel="noopener follow"><p class="bf b dv z du">Privacy</p></a></div><div class="ut uu l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" href="https://policy.medium.com/medium-terms-of-service-9db0094a1e0f?source=post_page-----1264346619ec--------------------------------" rel="noopener follow"><p class="bf b dv z du">Terms</p></a></div><div class="ut uu l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" href="https://speechify.com/medium?source=post_page-----1264346619ec--------------------------------" rel="noopener follow"><p class="bf b dv z du">Text to speech</p></a></div><div class="ut l"><a class="af ag ah ai aj ak al am an ao ap aq ar as at" rel="noopener follow" href="/business?source=post_page-----1264346619ec--------------------------------"><p class="bf b dv z du">Teams</p></a></div></div></div></div></div></div></div></div></div></div><script>window.__BUILD_ID__="main-20241125-190609-795bd7e319"</script><script>window.__GRAPHQL_URI__ = "https://medium.com/_/graphql"</script><script>window.__PRELOADED_STATE__ = {"algolia":{"queries":{}},"cache":{"experimentGroupSet":true,"reason":"","group":"enabled","tags":["group-edgeCachePosts","post-1264346619ec","user-e1f26db83092","collection-95b274b437c2"],"serverVariantState":"44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","middlewareEnabled":true,"cacheStatus":"DYNAMIC","shouldUseCache":true,"vary":[],"lohpSummerUpsellEnabled":false,"publicationHierarchyEnabledWeb":false,"postBottomResponsesEnabled":false},"client":{"hydrated":false,"isUs":false,"isNativeMedium":false,"isSafariMobile":false,"isSafari":false,"isFirefox":false,"routingEntity":{"type":"DEFAULT","explicit":false},"viewerIsBot":false},"debug":{"requestId":"944e71f4-a27e-43ed-9b43-073f4e409fbc","hybridDevServices":[],"originalSpanCarrier":{"traceparent":"00-32095256155c6c6b022f751a3878417b-b6450453df22e64e-01"}},"multiVote":{"clapsPerPost":{}},"navigation":{"branch":{"show":null,"hasRendered":null,"blockedByCTA":false},"hideGoogleOneTap":false,"hasRenderedAlternateUserBanner":null,"currentLocation":"https:\u002F\u002Fmedium.com\u002Fandroiddevelopers\u002Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec","host":"medium.com","hostname":"medium.com","referrer":"","hasSetReferrer":false,"susiModal":{"step":null,"operation":"register"},"postRead":false,"partnerProgram":{"selectedCountryCode":null},"queryString":"","currentHash":""},"config":{"nodeEnv":"production","version":"main-20241125-190609-795bd7e319","target":"production","productName":"Medium","publicUrl":"https:\u002F\u002Fcdn-client.medium.com\u002Flite","authDomain":"medium.com","authGoogleClientId":"216296035834-k1k6qe060s2tp2a2jam4ljdcms00sttg.apps.googleusercontent.com","favicon":"production","glyphUrl":"https:\u002F\u002Fglyph.medium.com","branchKey":"key_live_ofxXr2qTrrU9NqURK8ZwEhknBxiI6KBm","algolia":{"appId":"MQ57UUUQZ2","apiKeySearch":"394474ced050e3911ae2249ecc774921","indexPrefix":"medium_","host":"-dsn.algolia.net"},"recaptchaKey":"6Lfc37IUAAAAAKGGtC6rLS13R1Hrw_BqADfS1LRk","recaptcha3Key":"6Lf8R9wUAAAAABMI_85Wb8melS7Zj6ziuf99Yot5","recaptchaEnterpriseKeyId":"6Le-uGgpAAAAAPprRaokM8AKthQ9KNGdoxaGUvVp","datadog":{"applicationId":"6702d87d-a7e0-42fe-bbcb-95b469547ea0","clientToken":"pub853ea8d17ad6821d9f8f11861d23dfed","rumToken":"pubf9cc52896502b9413b68ba36fc0c7162","context":{"deployment":{"target":"production","tag":"main-20241125-190609-795bd7e319","commit":"795bd7e3199e13224bc0a42c1be5cd7062eab1ad"}},"datacenter":"us"},"googleAnalyticsCode":"G-7JY7T788PK","googlePay":{"apiVersion":"2","apiVersionMinor":"0","merchantId":"BCR2DN6TV7EMTGBM","merchantName":"Medium","instanceMerchantId":"13685562959212738550"},"applePay":{"version":3},"signInWallCustomDomainCollectionIds":["3a8144eabfe3","336d898217ee","61061eb0c96b","138adf9c44c","819cc2aaeee0"],"mediumMastodonDomainName":"me.dm","mediumOwnedAndOperatedCollectionIds":["8a9336e5bb4","b7e45b22fec3","193b68bd4fba","8d6b8a439e32","54c98c43354d","3f6ecf56618","d944778ce714","92d2092dc598","ae2a65f35510","1285ba81cada","544c7006046e","fc8964313712","40187e704f1c","88d9857e584e","7b6769f2748b","bcc38c8f6edf","cef6983b292","cb8577c9149e","444d13b52878","713d7dbc99b0","ef8e90590e66","191186aaafa0","55760f21cdc5","9dc80918cc93","bdc4052bbdba","8ccfed20cbb2"],"tierOneDomains":["medium.com","thebolditalic.com","arcdigital.media","towardsdatascience.com","uxdesign.cc","codeburst.io","psiloveyou.xyz","writingcooperative.com","entrepreneurshandbook.co","prototypr.io","betterhumans.coach.me","theascent.pub"],"topicsToFollow":["d61cf867d93f","8a146bc21b28","1eca0103fff3","4d562ee63426","aef1078a3ef5","e15e46793f8d","6158eb913466","55f1c20aba7a","3d18b94f6858","4861fee224fd","63c6f1f93ee","1d98b3a9a871","decb52b64abf","ae5d4995e225","830cded25262"],"topicToTagMappings":{"accessibility":"accessibility","addiction":"addiction","android-development":"android-development","art":"art","artificial-intelligence":"artificial-intelligence","astrology":"astrology","basic-income":"basic-income","beauty":"beauty","biotech":"biotech","blockchain":"blockchain","books":"books","business":"business","cannabis":"cannabis","cities":"cities","climate-change":"climate-change","comics":"comics","coronavirus":"coronavirus","creativity":"creativity","cryptocurrency":"cryptocurrency","culture":"culture","cybersecurity":"cybersecurity","data-science":"data-science","design":"design","digital-life":"digital-life","disability":"disability","economy":"economy","education":"education","equality":"equality","family":"family","feminism":"feminism","fiction":"fiction","film":"film","fitness":"fitness","food":"food","freelancing":"freelancing","future":"future","gadgets":"gadgets","gaming":"gaming","gun-control":"gun-control","health":"health","history":"history","humor":"humor","immigration":"immigration","ios-development":"ios-development","javascript":"javascript","justice":"justice","language":"language","leadership":"leadership","lgbtqia":"lgbtqia","lifestyle":"lifestyle","machine-learning":"machine-learning","makers":"makers","marketing":"marketing","math":"math","media":"media","mental-health":"mental-health","mindfulness":"mindfulness","money":"money","music":"music","neuroscience":"neuroscience","nonfiction":"nonfiction","outdoors":"outdoors","parenting":"parenting","pets":"pets","philosophy":"philosophy","photography":"photography","podcasts":"podcast","poetry":"poetry","politics":"politics","privacy":"privacy","product-management":"product-management","productivity":"productivity","programming":"programming","psychedelics":"psychedelics","psychology":"psychology","race":"race","relationships":"relationships","religion":"religion","remote-work":"remote-work","san-francisco":"san-francisco","science":"science","self":"self","self-driving-cars":"self-driving-cars","sexuality":"sexuality","social-media":"social-media","society":"society","software-engineering":"software-engineering","space":"space","spirituality":"spirituality","sports":"sports","startups":"startup","style":"style","technology":"technology","transportation":"transportation","travel":"travel","true-crime":"true-crime","tv":"tv","ux":"ux","venture-capital":"venture-capital","visual-design":"visual-design","work":"work","world":"world","writing":"writing"},"defaultImages":{"avatar":{"imageId":"1*dmbNkD5D-u45r44go_cf0g.png","height":150,"width":150},"orgLogo":{"imageId":"7*V1_7XP4snlmqrc_0Njontw.png","height":110,"width":500},"postLogo":{"imageId":"bd978bb536350a710e8efb012513429cabdc4c28700604261aeda246d0f980b7","height":810,"width":1440},"postPreviewImage":{"imageId":"1*hn4v1tCaJy7cWMyb0bpNpQ.png","height":386,"width":579}},"collectionStructuredData":{"8d6b8a439e32":{"name":"Elemental","data":{"@type":"NewsMediaOrganization","ethicsPolicy":"https:\u002F\u002Fhelp.medium.com\u002Fhc\u002Fen-us\u002Farticles\u002F360043290473","logo":{"@type":"ImageObject","url":"https:\u002F\u002Fcdn-images-1.medium.com\u002Fmax\u002F980\u002F1*9ygdqoKprhwuTVKUM0DLPA@2x.png","width":980,"height":159}}},"3f6ecf56618":{"name":"Forge","data":{"@type":"NewsMediaOrganization","ethicsPolicy":"https:\u002F\u002Fhelp.medium.com\u002Fhc\u002Fen-us\u002Farticles\u002F360043290473","logo":{"@type":"ImageObject","url":"https:\u002F\u002Fcdn-images-1.medium.com\u002Fmax\u002F596\u002F1*uULpIlImcO5TDuBZ6lm7Lg@2x.png","width":596,"height":183}}},"ae2a65f35510":{"name":"GEN","data":{"@type":"NewsMediaOrganization","ethicsPolicy":"https:\u002F\u002Fhelp.medium.com\u002Fhc\u002Fen-us\u002Farticles\u002F360043290473","logo":{"@type":"ImageObject","url":"https:\u002F\u002Fmiro.medium.com\u002Fmax\u002F264\u002F1*RdVZMdvfV3YiZTw6mX7yWA.png","width":264,"height":140}}},"88d9857e584e":{"name":"LEVEL","data":{"@type":"NewsMediaOrganization","ethicsPolicy":"https:\u002F\u002Fhelp.medium.com\u002Fhc\u002Fen-us\u002Farticles\u002F360043290473","logo":{"@type":"ImageObject","url":"https:\u002F\u002Fmiro.medium.com\u002Fmax\u002F540\u002F1*JqYMhNX6KNNb2UlqGqO2WQ.png","width":540,"height":108}}},"7b6769f2748b":{"name":"Marker","data":{"@type":"NewsMediaOrganization","ethicsPolicy":"https:\u002F\u002Fhelp.medium.com\u002Fhc\u002Fen-us\u002Farticles\u002F360043290473","logo":{"@type":"ImageObject","url":"https:\u002F\u002Fcdn-images-1.medium.com\u002Fmax\u002F383\u002F1*haCUs0wF6TgOOvfoY-jEoQ@2x.png","width":383,"height":92}}},"444d13b52878":{"name":"OneZero","data":{"@type":"NewsMediaOrganization","ethicsPolicy":"https:\u002F\u002Fhelp.medium.com\u002Fhc\u002Fen-us\u002Farticles\u002F360043290473","logo":{"@type":"ImageObject","url":"https:\u002F\u002Fmiro.medium.com\u002Fmax\u002F540\u002F1*cw32fIqCbRWzwJaoQw6BUg.png","width":540,"height":123}}},"8ccfed20cbb2":{"name":"Zora","data":{"@type":"NewsMediaOrganization","ethicsPolicy":"https:\u002F\u002Fhelp.medium.com\u002Fhc\u002Fen-us\u002Farticles\u002F360043290473","logo":{"@type":"ImageObject","url":"https:\u002F\u002Fmiro.medium.com\u002Fmax\u002F540\u002F1*tZUQqRcCCZDXjjiZ4bDvgQ.png","width":540,"height":106}}}},"embeddedPostIds":{"coronavirus":"cd3010f9d81f"},"sharedCdcMessaging":{"COVID_APPLICABLE_TAG_SLUGS":[],"COVID_APPLICABLE_TOPIC_NAMES":[],"COVID_APPLICABLE_TOPIC_NAMES_FOR_TOPIC_PAGE":[],"COVID_MESSAGES":{"tierA":{"text":"For more information on the novel coronavirus and Covid-19, visit cdc.gov.","markups":[{"start":66,"end":73,"href":"https:\u002F\u002Fwww.cdc.gov\u002Fcoronavirus\u002F2019-nCoV"}]},"tierB":{"text":"Anyone can publish on Medium per our Policies, but we don’t fact-check every story. For more info about the coronavirus, see cdc.gov.","markups":[{"start":37,"end":45,"href":"https:\u002F\u002Fhelp.medium.com\u002Fhc\u002Fen-us\u002Fcategories\u002F201931128-Policies-Safety"},{"start":125,"end":132,"href":"https:\u002F\u002Fwww.cdc.gov\u002Fcoronavirus\u002F2019-nCoV"}]},"paywall":{"text":"This article has been made free for everyone, thanks to Medium Members. For more information on the novel coronavirus and Covid-19, visit cdc.gov.","markups":[{"start":56,"end":70,"href":"https:\u002F\u002Fmedium.com\u002Fmembership"},{"start":138,"end":145,"href":"https:\u002F\u002Fwww.cdc.gov\u002Fcoronavirus\u002F2019-nCoV"}]},"unbound":{"text":"This article is free for everyone, thanks to Medium Members. For more information on the novel coronavirus and Covid-19, visit cdc.gov.","markups":[{"start":45,"end":59,"href":"https:\u002F\u002Fmedium.com\u002Fmembership"},{"start":127,"end":134,"href":"https:\u002F\u002Fwww.cdc.gov\u002Fcoronavirus\u002F2019-nCoV"}]}},"COVID_BANNER_POST_ID_OVERRIDE_WHITELIST":["3b31a67bff4a"]},"sharedVoteMessaging":{"TAGS":["politics","election-2020","government","us-politics","election","2020-presidential-race","trump","donald-trump","democrats","republicans","congress","republican-party","democratic-party","biden","joe-biden","maga"],"TOPICS":["politics","election"],"MESSAGE":{"text":"Find out more about the U.S. election results here.","markups":[{"start":46,"end":50,"href":"https:\u002F\u002Fcookpolitical.com\u002F2020-national-popular-vote-tracker"}]},"EXCLUDE_POSTS":["397ef29e3ca5"]},"embedPostRules":[],"recircOptions":{"v1":{"limit":3},"v2":{"limit":8}},"braintreeClientKey":"production_zjkj96jm_m56f8fqpf7ngnrd4","braintree":{"enabled":true,"merchantId":"m56f8fqpf7ngnrd4","merchantAccountId":{"usd":"AMediumCorporation_instant","eur":"amediumcorporation_EUR","cad":"amediumcorporation_CAD"},"publicKey":"ds2nn34bg2z7j5gd","braintreeEnvironment":"production","dashboardUrl":"https:\u002F\u002Fwww.braintreegateway.com\u002Fmerchants","gracePeriodDurationInDays":14,"mediumMembershipPlanId":{"monthly":"ce105f8c57a3","monthlyV2":"e8a5e126-792b-4ee6-8fba-d574c1b02fc5","monthlyWithTrial":"d5ee3dbe3db8","monthlyPremium":"fa741a9b47a2","yearly":"a40ad4a43185","yearlyV2":"3815d7d6-b8ca-4224-9b8c-182f9047866e","yearlyStaff":"d74fb811198a","yearlyWithTrial":"b3bc7350e5c7","yearlyPremium":"e21bd2c12166","monthlyOneYearFree":"e6c0637a-2bad-4171-ab4f-3c268633d83c","monthly25PercentOffFirstYear":"235ecc62-0cdb-49ae-9378-726cd21c504b","monthly20PercentOffFirstYear":"ba518864-9c13-4a99-91ca-411bf0cac756","monthly15PercentOffFirstYear":"594c029b-9f89-43d5-88f8-8173af4e070e","monthly10PercentOffFirstYear":"c6c7bc9a-40f2-4b51-8126-e28511d5bdb0","monthlyForStudents":"629ebe51-da7d-41fd-8293-34cd2f2030a8","yearlyOneYearFree":"78ba7be9-0d9f-4ece-aa3e-b54b826f2bf1","yearly25PercentOffFirstYear":"2dbb010d-bb8f-4eeb-ad5c-a08509f42d34","yearly20PercentOffFirstYear":"47565488-435b-47f8-bf93-40d5fbe0ebc8","yearly15PercentOffFirstYear":"8259809b-0881-47d9-acf7-6c001c7f720f","yearly10PercentOffFirstYear":"9dd694fb-96e1-472c-8d9e-3c868d5c1506","yearlyForStudents":"e29345ef-ab1c-4234-95c5-70e50fe6bc23","monthlyCad":"p52orjkaceei","yearlyCad":"h4q9g2up9ktt"},"braintreeDiscountId":{"oneMonthFree":"MONTHS_FREE_01","threeMonthsFree":"MONTHS_FREE_03","sixMonthsFree":"MONTHS_FREE_06","fiftyPercentOffOneYear":"FIFTY_PERCENT_OFF_ONE_YEAR"},"3DSecureVersion":"2","defaultCurrency":"usd","providerPlanIdCurrency":{"4ycw":"usd","rz3b":"usd","3kqm":"usd","jzw6":"usd","c2q2":"usd","nnsw":"usd","q8qw":"usd","d9y6":"usd","fx7w":"cad","nwf2":"cad"}},"paypalClientId":"AXj1G4fotC2GE8KzWX9mSxCH1wmPE3nJglf4Z2ig_amnhvlMVX87otaq58niAg9iuLktVNF_1WCMnN7v","paypal":{"host":"https:\u002F\u002Fapi.paypal.com:443","clientMode":"production","serverMode":"live","webhookId":"4G466076A0294510S","monthlyPlan":{"planId":"P-9WR0658853113943TMU5FDQA","name":"Medium Membership (Monthly) with setup fee","description":"Unlimited access to the best and brightest stories on Medium. Membership billed monthly."},"yearlyPlan":{"planId":"P-7N8963881P8875835MU5JOPQ","name":"Medium Membership (Annual) with setup fee","description":"Unlimited access to the best and brightest stories on Medium. Membership billed annually."},"oneYearGift":{"name":"Medium Membership (1 Year, Digital Gift Code)","description":"Unlimited access to the best and brightest stories on Medium. Gift codes can be redeemed at medium.com\u002Fredeem.","price":"50.00","currency":"USD","sku":"membership-gift-1-yr"},"oldMonthlyPlan":{"planId":"P-96U02458LM656772MJZUVH2Y","name":"Medium Membership (Monthly)","description":"Unlimited access to the best and brightest stories on Medium. Membership billed monthly."},"oldYearlyPlan":{"planId":"P-59P80963JF186412JJZU3SMI","name":"Medium Membership (Annual)","description":"Unlimited access to the best and brightest stories on Medium. Membership billed annually."},"monthlyPlanWithTrial":{"planId":"P-66C21969LR178604GJPVKUKY","name":"Medium Membership (Monthly) with setup fee","description":"Unlimited access to the best and brightest stories on Medium. Membership billed monthly."},"yearlyPlanWithTrial":{"planId":"P-6XW32684EX226940VKCT2MFA","name":"Medium Membership (Annual) with setup fee","description":"Unlimited access to the best and brightest stories on Medium. Membership billed annually."},"oldMonthlyPlanNoSetupFee":{"planId":"P-4N046520HR188054PCJC7LJI","name":"Medium Membership (Monthly)","description":"Unlimited access to the best and brightest stories on Medium. Membership billed monthly."},"oldYearlyPlanNoSetupFee":{"planId":"P-7A4913502Y5181304CJEJMXQ","name":"Medium Membership (Annual)","description":"Unlimited access to the best and brightest stories on Medium. Membership billed annually."},"sdkUrl":"https:\u002F\u002Fwww.paypal.com\u002Fsdk\u002Fjs"},"stripePublishableKey":"pk_live_7FReX44VnNIInZwrIIx6ghjl","log":{"json":true,"level":"info"},"imageUploadMaxSizeMb":25,"staffPicks":{"title":"Staff Picks","catalogId":"c7bc6e1ee00f"}},"session":{"xsrf":""}}</script><script>window.__APOLLO_STATE__ = {"ROOT_QUERY":{"__typename":"Query","viewer":null,"collectionByDomainOrSlug({\"domainOrSlug\":\"androiddevelopers\"})":{"__ref":"Collection:95b274b437c2"},"postResult({\"id\":\"1264346619ec\"})":{"__ref":"Post:1264346619ec"}},"ImageMetadata:":{"__typename":"ImageMetadata","id":""},"Collection:95b274b437c2":{"__typename":"Collection","id":"95b274b437c2","favicon":{"__ref":"ImageMetadata:"},"customStyleSheet":null,"colorPalette":{"__typename":"ColorPalette","highlightSpectrum":{"__typename":"ColorSpectrum","backgroundColor":"#FFFFFFFF","colorPoints":[{"__typename":"ColorPoint","color":"#FFF4F2F3","point":0},{"__typename":"ColorPoint","color":"#FFF2F0F1","point":0.1},{"__typename":"ColorPoint","color":"#FFF0EEEF","point":0.2},{"__typename":"ColorPoint","color":"#FFEEECEE","point":0.3},{"__typename":"ColorPoint","color":"#FFECEAEC","point":0.4},{"__typename":"ColorPoint","color":"#FFEAE8EA","point":0.5},{"__typename":"ColorPoint","color":"#FFE8E7E9","point":0.6},{"__typename":"ColorPoint","color":"#FFE6E5E7","point":0.7},{"__typename":"ColorPoint","color":"#FFE4E3E5","point":0.8},{"__typename":"ColorPoint","color":"#FFE2E1E4","point":0.9},{"__typename":"ColorPoint","color":"#FFE0DFE2","point":1}]},"defaultBackgroundSpectrum":{"__typename":"ColorSpectrum","backgroundColor":"#FFFFFFFF","colorPoints":[{"__typename":"ColorPoint","color":"#FF848488","point":0},{"__typename":"ColorPoint","color":"#FF7B7B7E","point":0.1},{"__typename":"ColorPoint","color":"#FF717275","point":0.2},{"__typename":"ColorPoint","color":"#FF68686B","point":0.3},{"__typename":"ColorPoint","color":"#FF5E5E60","point":0.4},{"__typename":"ColorPoint","color":"#FF545456","point":0.5},{"__typename":"ColorPoint","color":"#FF4A494B","point":0.6},{"__typename":"ColorPoint","color":"#FF3F3E40","point":0.7},{"__typename":"ColorPoint","color":"#FF333334","point":0.8},{"__typename":"ColorPoint","color":"#FF272728","point":0.9},{"__typename":"ColorPoint","color":"#FF1A1A1A","point":1}]},"tintBackgroundSpectrum":{"__typename":"ColorSpectrum","backgroundColor":"#FF202124","colorPoints":[{"__typename":"ColorPoint","color":"#FF202124","point":0},{"__typename":"ColorPoint","color":"#FF3D3E41","point":0.1},{"__typename":"ColorPoint","color":"#FF58585B","point":0.2},{"__typename":"ColorPoint","color":"#FF707073","point":0.3},{"__typename":"ColorPoint","color":"#FF878689","point":0.4},{"__typename":"ColorPoint","color":"#FF9D9C9E","point":0.5},{"__typename":"ColorPoint","color":"#FFB2B1B2","point":0.6},{"__typename":"ColorPoint","color":"#FFC6C5C6","point":0.7},{"__typename":"ColorPoint","color":"#FFDAD8DA","point":0.8},{"__typename":"ColorPoint","color":"#FFEEEBEC","point":0.9},{"__typename":"ColorPoint","color":"#FFFFFEFF","point":1}]}},"domain":null,"slug":"androiddevelopers","googleAnalyticsId":null,"editors":[{"__typename":"CollectionMastheadUserItem","user":{"__ref":"User:e1f26db83092"}},{"__typename":"CollectionMastheadUserItem","user":{"__ref":"User:2e0fc9a4a8c2"}},{"__typename":"CollectionMastheadUserItem","user":{"__ref":"User:d5885adb1ddf"}}],"name":"Android Developers","avatar":{"__ref":"ImageMetadata:1*4Tg6pPzer7cIarYaszIKaQ.png"},"description":"Articles on modern tools and resources to help you build experiences that people love, faster and easier, across every Android device.","subscriberCount":52026,"latestPostsConnection({\"paging\":{\"limit\":1}})":{"__typename":"PostConnection","posts":[{"__ref":"Post:255973814ae3"}]},"viewerEdge":{"__ref":"CollectionViewerEdge:collectionId:95b274b437c2-viewerId:lo_4354d79c6a5e"},"twitterUsername":"androiddev","facebookPageId":null,"logo":{"__ref":"ImageMetadata:1*5pMw_nx55x_66tk77kutPQ.png"}},"User:e1f26db83092":{"__typename":"User","id":"e1f26db83092","name":"Android Developers","username":"AndroidDev","newsletterV3":{"__ref":"NewsletterV3:271a39694cd7"},"linkedAccounts":{"__ref":"LinkedAccounts:e1f26db83092"},"isSuspended":false,"imageId":"1*VglQS9HKgUvUuAX36Np5qQ.png","mediumMemberAt":0,"verifications":{"__typename":"VerifiedInfo","isBookAuthor":false},"socialStats":{"__typename":"SocialStats","followerCount":72720,"followingCount":58,"collectionFollowingCount":2},"customDomainState":null,"hasSubdomain":false,"bio":"News and announcements for developers from the Android team.","isPartnerProgramEnrolled":false,"viewerEdge":{"__ref":"UserViewerEdge:userId:e1f26db83092-viewerId:lo_4354d79c6a5e"},"viewerIsUser":false,"postSubscribeMembershipUpsellShownAt":0,"membership":null,"allowNotes":true,"twitterScreenName":"AndroidDev"},"User:2e0fc9a4a8c2":{"__typename":"User","id":"2e0fc9a4a8c2"},"User:d5885adb1ddf":{"__typename":"User","id":"d5885adb1ddf"},"ImageMetadata:1*4Tg6pPzer7cIarYaszIKaQ.png":{"__typename":"ImageMetadata","id":"1*4Tg6pPzer7cIarYaszIKaQ.png"},"User:e0a4c9469bb5":{"__typename":"User","id":"e0a4c9469bb5","customDomainState":null,"hasSubdomain":false,"username":"JoseAlcerreca"},"Post:255973814ae3":{"__typename":"Post","id":"255973814ae3","firstPublishedAt":1732208584164,"creator":{"__ref":"User:e0a4c9469bb5"},"collection":{"__ref":"Collection:95b274b437c2"},"isSeries":false,"mediumUrl":"https:\u002F\u002Fmedium.com\u002Fandroiddevelopers\u002Ftest-smarter-not-harder-new-testing-strategies-documentation-255973814ae3","sequence":null,"uniqueSlug":"test-smarter-not-harder-new-testing-strategies-documentation-255973814ae3"},"LinkedAccounts:e1f26db83092":{"__typename":"LinkedAccounts","mastodon":null,"id":"e1f26db83092"},"UserViewerEdge:userId:e1f26db83092-viewerId:lo_4354d79c6a5e":{"__typename":"UserViewerEdge","id":"userId:e1f26db83092-viewerId:lo_4354d79c6a5e","isFollowing":false,"isUser":false,"isMuting":false},"NewsletterV3:271a39694cd7":{"__typename":"NewsletterV3","id":"271a39694cd7","type":"NEWSLETTER_TYPE_AUTHOR","slug":"e1f26db83092","name":"e1f26db83092","collection":null,"user":{"__ref":"User:e1f26db83092"}},"Topic:64756708407c":{"__typename":"Topic","slug":"android-development","id":"64756708407c","name":"Android Dev"},"Paragraph:ef6d8411e287_0":{"__typename":"Paragraph","id":"ef6d8411e287_0","name":"1896","type":"H3","href":null,"layout":null,"metadata":null,"text":"Migrating the AOSP QuickSearchBox App to Kotlin","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"EM","start":0,"end":47,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_1":{"__typename":"Paragraph","id":"ef6d8411e287_1","name":"2bdc","type":"P","href":null,"layout":null,"metadata":null,"text":"submitted by Ryan O’Leary, Android Intern","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"EM","start":13,"end":41,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"ImageMetadata:1*cWnPe-kD4hAVuH3IIcNUcA.png":{"__typename":"ImageMetadata","id":"1*cWnPe-kD4hAVuH3IIcNUcA.png","originalHeight":214,"originalWidth":720,"focusPercentX":null,"focusPercentY":null,"alt":null},"Paragraph:ef6d8411e287_2":{"__typename":"Paragraph","id":"ef6d8411e287_2","name":"e30b","type":"IMG","href":null,"layout":"INSET_CENTER","metadata":{"__ref":"ImageMetadata:1*cWnPe-kD4hAVuH3IIcNUcA.png"},"text":"","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_3":{"__typename":"Paragraph","id":"ef6d8411e287_3","name":"a67c","type":"H3","href":null,"layout":null,"metadata":null,"text":"Overview","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_4":{"__typename":"Paragraph","id":"ef6d8411e287_4","name":"f17a","type":"P","href":null,"layout":null,"metadata":null,"text":"For the past three years, the Android Open Source Project (AOSP) applications team has taken on the task of converting AOSP apps from Java to Kotlin. This pursuit was begun as part of Android’s commitment to increasingly develop with Kotlin-first. Kotlin is a safe, pragmatic, and concise language, with a number of language-specific advantages. Useful Kotlin language features include:","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"A","start":30,"end":77,"href":"https:\u002F\u002Fandroid.googlesource.com\u002Fplatform\u002Fpackages\u002Fapps\u002F","anchorType":"LINK","userId":null,"linkMetadata":null},{"__typename":"Markup","type":"A","start":234,"end":246,"href":"https:\u002F\u002Fdeveloper.android.com\u002Fkotlin\u002Ffirst","anchorType":"LINK","userId":null,"linkMetadata":null},{"__typename":"Markup","type":"STRONG","start":234,"end":246,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_5":{"__typename":"Paragraph","id":"ef6d8411e287_5","name":"51b5","type":"ULI","href":null,"layout":null,"metadata":null,"text":"Null Safety: Kotlin types are non-nullable unless explicitly specified. This is incredibly useful for developers in avoiding difficult to trace null-pointer exceptions.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"STRONG","start":0,"end":11,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_6":{"__typename":"Paragraph","id":"ef6d8411e287_6","name":"a333","type":"ULI","href":null,"layout":null,"metadata":null,"text":"Conciseness: Kotlin allows developers to reduce the amount of boilerplate code, expressing more with less lines of code compared to Java.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"STRONG","start":0,"end":11,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_7":{"__typename":"Paragraph","id":"ef6d8411e287_7","name":"a75d","type":"ULI","href":null,"layout":null,"metadata":null,"text":"Java Interoperability: Interoperability between Kotlin and the Java programming language proved very helpful for this project as the migration could be done incrementally, and is useful for all developers working in mixed Java and Kotlin projects.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"STRONG","start":0,"end":21,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_8":{"__typename":"Paragraph","id":"ef6d8411e287_8","name":"3b09","type":"ULI","href":null,"layout":null,"metadata":null,"text":"Jetpack Libraries and Coroutines: Kotlin provides in-language support for lightweight, structured concurrency through coroutines. Additionally, Android development content is Kotlin-first and provides access to useful Jetpack libraries.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"STRONG","start":0,"end":32,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_9":{"__typename":"Paragraph","id":"ef6d8411e287_9","name":"fd6c","type":"P","href":null,"layout":null,"metadata":null,"text":"AOSP interns converted the AOSP Deskclock app in 2019 and Calendar app in 2020, detailing the migration process in similar articles. This year the AOSP team set out to fully convert the QuickSearchBox app as part of a summer 2022 intern project. Introduced in 2009 through the Android 1.6 release, the QuickSearchBox app allows users to search both their device and the web directly from their home screen, providing suggestions based on downloaded content, contacts, apps, and browser history. Over the course of 6 weeks, over 11,000 lines of Java code within the QuickSearchBox app were converted to Kotlin to showcase best practices in Android development and provide the functionality of the QuickSearchBox app with Kotlin-first in mind.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"A","start":32,"end":41,"href":"https:\u002F\u002Fmedium.com\u002Fandroiddevelopers\u002Fre-writing-the-aosp-deskclock-app-in-kotlin-76c836370cb","anchorType":"LINK","userId":null,"linkMetadata":null},{"__typename":"Markup","type":"A","start":58,"end":66,"href":"https:\u002F\u002Fmedium.com\u002Fandroiddevelopers\u002Fre-writing-the-aosp-calendar-app-in-kotlin-48ceb0e3a65c","anchorType":"LINK","userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_10":{"__typename":"Paragraph","id":"ef6d8411e287_10","name":"9d75","type":"H3","href":null,"layout":null,"metadata":null,"text":"Automatic Conversion and Bug Fixing","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_11":{"__typename":"Paragraph","id":"ef6d8411e287_11","name":"71b4","type":"P","href":null,"layout":null,"metadata":null,"text":"To migrate the QuickSearchBox app to Kotlin, we utilized the Kotlin conversion tool that is included in Android Studio. Our process followed five steps:","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"A","start":61,"end":83,"href":"https:\u002F\u002Fdeveloper.android.com\u002Fkotlin\u002Fadd-kotlin#convert","anchorType":"LINK","userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_12":{"__typename":"Paragraph","id":"ef6d8411e287_12","name":"83a3","type":"OLI","href":null,"layout":null,"metadata":null,"text":"Copy the .java file to a .kt file of the same name using the command: cp ExampleFile.java ExampleFile.kt","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":9,"end":14,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":25,"end":28,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":70,"end":104,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_13":{"__typename":"Paragraph","id":"ef6d8411e287_13","name":"a042","type":"OLI","href":null,"layout":null,"metadata":null,"text":"Utilize the provided conversion tool to convert the Java code to Kotlin","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_14":{"__typename":"Paragraph","id":"ef6d8411e287_14","name":"90f1","type":"OLI","href":null,"layout":null,"metadata":null,"text":"Add the .java file to an exclude_srcs property in the Android.bp file so that the conversion could occur incrementally, one file at a time","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":8,"end":13,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":25,"end":37,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":54,"end":64,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_15":{"__typename":"Paragraph","id":"ef6d8411e287_15","name":"13b1","type":"OLI","href":null,"layout":null,"metadata":null,"text":"Resolve compilation errors and any other bugs introduced by conversion","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_16":{"__typename":"Paragraph","id":"ef6d8411e287_16","name":"5e78","type":"OLI","href":null,"layout":null,"metadata":null,"text":"Run and pass unit tests to check for correctness of the Kotlin based implementation, and run manual tests to check feature parity between the Kotlin and the legacy Java based app.","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_17":{"__typename":"Paragraph","id":"ef6d8411e287_17","name":"8780","type":"P","href":null,"layout":null,"metadata":null,"text":"The git history of each of these steps was maintained to showcase the process of migration to outside developers, and can be viewed for each file in the master branch of AOSP QuickSearchBox. The bug-fixing step in this process was necessary due to several common issues found within Android Studio’s Java-to-Kotlin conversion tool, which occurred in the majority of AOSP QuickSearchBox files that were migrated.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"A","start":170,"end":189,"href":"https:\u002F\u002Fcs.android.com\u002Fandroid\u002F_\u002Fandroid\u002Fplatform\u002Fpackages\u002Fapps\u002FQuickSearchBox\u002F+\u002Fmaster:src\u002Fcom\u002Fandroid\u002Fquicksearchbox\u002F","anchorType":"LINK","userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_18":{"__typename":"Paragraph","id":"ef6d8411e287_18","name":"94d6","type":"P","href":null,"layout":null,"metadata":null,"text":"These common problems and their solutions are as follows:","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"STRONG","start":7,"end":41,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_19":{"__typename":"Paragraph","id":"ef6d8411e287_19","name":"1e9a","type":"P","href":null,"layout":null,"metadata":null,"text":"Common Problem: Required Import statements would frequently be removed in the converted Kotlin file","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"STRONG","start":0,"end":16,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_20":{"__typename":"Paragraph","id":"ef6d8411e287_20","name":"b0e8","type":"P","href":null,"layout":null,"metadata":null,"text":"Solution: Manually convert in the needed import statements","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"STRONG","start":0,"end":10,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_21":{"__typename":"Paragraph","id":"ef6d8411e287_21","name":"5396","type":"P","href":null,"layout":null,"metadata":null,"text":"Common Problem: The override keyword was often not added to methods and variables tagged @Override","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":20,"end":28,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":89,"end":98,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"STRONG","start":0,"end":16,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_22":{"__typename":"Paragraph","id":"ef6d8411e287_22","name":"73fc","type":"P","href":null,"layout":null,"metadata":null,"text":"Solution: Manually add in override modifier","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":26,"end":34,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"STRONG","start":0,"end":10,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_23":{"__typename":"Paragraph","id":"ef6d8411e287_23","name":"ba08","type":"P","href":null,"layout":null,"metadata":null,"text":"Common Problem: One larger problem encountered was the conversion of nullable variables to non-nullable variables in the Kotlin code. This caused a variety of errors, from type mismatches to large sections of QuickSearchBox code not being executed since logic relying on null checking was now unused. Converting one file at a time also meant that the use of inherently nullable types in the unconverted Java files would create issues when attempting to assign, override, or return those inherited variables in the Kotlin code.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"STRONG","start":0,"end":15,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_24":{"__typename":"Paragraph","id":"ef6d8411e287_24","name":"35bc","type":"P","href":null,"layout":null,"metadata":null,"text":"Solution: In most cases, these properties actually needed to be nullable in order to achieve the desired functionality. The solution here was simply to use nullable types by adding ? to the type declaration. This required adding in safe calls (?.) where appropriate and changing the expected types where necessary to avoid mismatch errors. While this may seem counterintuitive to the null safe nature of Kotlin, it was necessary for some properties to remain nullable since they were set asynchronously, and all variables that could be made non-nullable were.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":181,"end":182,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":244,"end":246,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"STRONG","start":0,"end":10,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_25":{"__typename":"Paragraph","id":"ef6d8411e287_25","name":"8684","type":"P","href":null,"layout":null,"metadata":null,"text":"Common Problem: Functions beginning with get and set were changed by the converter to variables with explicitly defined getters\u002Fsetters, but many usages were unchanged and left undefined","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":41,"end":44,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":49,"end":52,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"STRONG","start":0,"end":15,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_26":{"__typename":"Paragraph","id":"ef6d8411e287_26","name":"9c06","type":"P","href":null,"layout":null,"metadata":null,"text":"Solution: Manually change calls to the converted functions to the variable name; eg. getSuggestions() -\u003E suggestions","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":85,"end":101,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":105,"end":116,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"STRONG","start":0,"end":10,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_27":{"__typename":"Paragraph","id":"ef6d8411e287_27","name":"3134","type":"P","href":null,"layout":null,"metadata":null,"text":"Common Problem: Usages of getClass() were unchanged by the converter in the Kotlin code. Unlike Java, Kotlin does not support calling getClass() on objects to retrieve a Class type token.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":26,"end":36,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":134,"end":144,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":170,"end":175,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"STRONG","start":0,"end":15,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_28":{"__typename":"Paragraph","id":"ef6d8411e287_28","name":"4174","type":"P","href":null,"layout":null,"metadata":null,"text":"Solution: Utilize Kotlin’s class reference syntax, changing usages of getClass() to ::class to return the KClass token.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":70,"end":80,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":84,"end":91,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":106,"end":112,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"STRONG","start":0,"end":10,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_29":{"__typename":"Paragraph","id":"ef6d8411e287_29","name":"fb62","type":"P","href":null,"layout":null,"metadata":null,"text":"Common Problem: The QuickSearchBox project was built with the -Werror flag turned on, and a common source of errors was the use of java.util.Collection in a Kotlin class. When migrating an app to Kotlin, it’s recommended for developers to utilize libraries built for Kotlin in order to maximize language-specific benefits.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":62,"end":69,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":131,"end":151,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"A","start":131,"end":151,"href":"https:\u002F\u002Fdocs.oracle.com\u002Fjavase\u002F8\u002Fdocs\u002Fapi\u002Fjava\u002Futil\u002FCollection.html","anchorType":"LINK","userId":null,"linkMetadata":null},{"__typename":"Markup","type":"STRONG","start":0,"end":15,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_30":{"__typename":"Paragraph","id":"ef6d8411e287_30","name":"59aa","type":"P","href":null,"layout":null,"metadata":null,"text":"Solution: Switch to using the Kotlin equivalent of the required class. This has the benefit of increased safety, as the class can be specified as mutable or immutable when necessary.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"STRONG","start":0,"end":10,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_31":{"__typename":"Paragraph","id":"ef6d8411e287_31","name":"8467","type":"P","href":null,"layout":null,"metadata":null,"text":"i.e. java.util.Collection -\u003E kotlin.collections.Collection or kotlin.collections.MutableCollection","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":5,"end":25,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":29,"end":58,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":62,"end":98,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_32":{"__typename":"Paragraph","id":"ef6d8411e287_32","name":"13de","type":"P","href":null,"layout":null,"metadata":null,"text":"As the migration took place, the API level of the QuickSearchBox app was also updated to the latest version. This involved replacing several deprecated function calls with the new versions recommended by Android. An example of this was the deprecated use of AsyncTask within SearchBaseUrlHelper.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":258,"end":267,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":275,"end":294,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"MediaResource:3b77b64e9044744949e882bc2a966039":{"__typename":"MediaResource","id":"3b77b64e9044744949e882bc2a966039","iframeSrc":"","iframeHeight":0,"iframeWidth":0,"title":"https:\u002F\u002Fcs.android.com\u002Fandroid\u002F_\u002Fandroid\u002Fplatform\u002Fpackages\u002Fapps\u002FQuickSearchBox\u002F+\u002Fad91de857f9bb9c80497f544fdec32e477ec90b1:src\u002Fcom\u002Fandroid\u002Fquicksearchbox\u002Fgoogle\u002FSearchBaseUrlHelper.kt;dlc=511db7a2b7a9c33683ab5b4e1303a7c1faaa2ef5"},"Paragraph:ef6d8411e287_33":{"__typename":"Paragraph","id":"ef6d8411e287_33","name":"18da","type":"IFRAME","href":null,"layout":"INSET_CENTER","metadata":null,"text":"","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":{"__typename":"Iframe","mediaResource":{"__ref":"MediaResource:3b77b64e9044744949e882bc2a966039"}},"mixtapeMetadata":null},"Paragraph:ef6d8411e287_34":{"__typename":"Paragraph","id":"ef6d8411e287_34","name":"f099","type":"P","href":null,"layout":null,"metadata":null,"text":"AsyncTask was deprecated in API level 30, with Android documentation recommending the use of java.util.concurrent or Kotlin concurrency utilities. Since this project is geared towards showcasing the benefits of developing in Kotlin, we replaced the use of AsyncTask with a Kotlin coroutine scope and async block to asynchronously issue network requests. While, like Java, Kotlin supports the creation of threads, coroutines are non-blocking and stackless, allowing for lower memory usage.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":0,"end":9,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":93,"end":113,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":256,"end":265,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":300,"end":305,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"A","start":47,"end":68,"href":"https:\u002F\u002Fdeveloper.android.com\u002Freference\u002Fandroid\u002Fos\u002FAsyncTask","anchorType":"LINK","userId":null,"linkMetadata":null},{"__typename":"Markup","type":"A","start":93,"end":113,"href":"https:\u002F\u002Fdocs.oracle.com\u002Fjavase\u002F7\u002Fdocs\u002Fapi\u002Fjava\u002Futil\u002Fconcurrent\u002Fpackage-summary.html","anchorType":"LINK","userId":null,"linkMetadata":null},{"__typename":"Markup","type":"A","start":117,"end":145,"href":"https:\u002F\u002Fdeveloper.android.com\u002Ftopic\u002Flibraries\u002Farchitecture\u002Fcoroutines","anchorType":"LINK","userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"MediaResource:d55ba2fde1c03a48f94a391dab0fa2c3":{"__typename":"MediaResource","id":"d55ba2fde1c03a48f94a391dab0fa2c3","iframeSrc":"","iframeHeight":0,"iframeWidth":0,"title":"SearchBaseUrlHelper.kt"},"Paragraph:ef6d8411e287_35":{"__typename":"Paragraph","id":"ef6d8411e287_35","name":"11e4","type":"IFRAME","href":null,"layout":"INSET_CENTER","metadata":null,"text":"","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":{"__typename":"Iframe","mediaResource":{"__ref":"MediaResource:d55ba2fde1c03a48f94a391dab0fa2c3"}},"mixtapeMetadata":null},"Paragraph:ef6d8411e287_36":{"__typename":"Paragraph","id":"ef6d8411e287_36","name":"9257","type":"P","href":null,"layout":null,"metadata":null,"text":"Finally, one subtle source of bugs that developers should watch out for comes from the Kotlin converter feature that changes methods whose names begin with get and set to variables with explicitly defined getters and setters. When this change takes place, the converter will rename the migrated function to a variable of the same name, except without the preceding get or set. However, the converter fails to check for existing usages of the same name, potentially introducing devastating bugs into the converted code. In large files with many usages of these variables, these bugs can be incredibly hard to catch. This was seen in the file DelayingSuggestionsAdapter, where the line:","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":156,"end":159,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":164,"end":167,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":365,"end":368,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":372,"end":375,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":641,"end":667,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"MediaResource:8cb90e52012f33dcb9523af0597a8cb7":{"__typename":"MediaResource","id":"8cb90e52012f33dcb9523af0597a8cb7","iframeSrc":"","iframeHeight":0,"iframeWidth":0,"title":"https:\u002F\u002Fcs.android.com\u002Fandroid\u002F_\u002Fandroid\u002Fplatform\u002Fpackages\u002Fapps\u002FQuickSearchBox\u002F+\u002F136d79bd9516bc7cd7763e3589d3c520e1de04f9:src\u002Fcom\u002Fandroid\u002Fquicksearchbox\u002Fui\u002FDelayingSuggestionsAdapter.kt;dlc=431ea3b921f61457cd988f7ba0904171c25619f6"},"Paragraph:ef6d8411e287_37":{"__typename":"Paragraph","id":"ef6d8411e287_37","name":"6c3a","type":"IFRAME","href":null,"layout":"INSET_CENTER","metadata":null,"text":"","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":{"__typename":"Iframe","mediaResource":{"__ref":"MediaResource:8cb90e52012f33dcb9523af0597a8cb7"}},"mixtapeMetadata":null},"Paragraph:ef6d8411e287_38":{"__typename":"Paragraph","id":"ef6d8411e287_38","name":"7265","type":"P","href":null,"layout":null,"metadata":null,"text":"Was changed in the Kotlin code to:","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"MediaResource:880efd568418237a2b72cb208f7d410a":{"__typename":"MediaResource","id":"880efd568418237a2b72cb208f7d410a","iframeSrc":"","iframeHeight":0,"iframeWidth":0,"title":"DelayingSuggestionsAdapter.kt"},"Paragraph:ef6d8411e287_39":{"__typename":"Paragraph","id":"ef6d8411e287_39","name":"ba31","type":"IFRAME","href":null,"layout":"INSET_CENTER","metadata":null,"text":"","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":{"__typename":"Iframe","mediaResource":{"__ref":"MediaResource:880efd568418237a2b72cb208f7d410a"}},"mixtapeMetadata":null},"Paragraph:ef6d8411e287_40":{"__typename":"Paragraph","id":"ef6d8411e287_40","name":"7d17","type":"P","href":null,"layout":null,"metadata":null,"text":"This introduced a subtle runtime bug where an object would be released too early, generating an exception in a completely different file. While the fix was a simple one line change, tracking down all improper usages caused by the conversion can prove time consuming for developers, and ideally the converter would utilize more comprehensive code checking before creating new variables.","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_41":{"__typename":"Paragraph","id":"ef6d8411e287_41","name":"a5f9","type":"H3","href":null,"layout":null,"metadata":null,"text":"Performance Analysis","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_42":{"__typename":"Paragraph","id":"ef6d8411e287_42","name":"e3cb","type":"P","href":null,"layout":null,"metadata":null,"text":"After completing the conversion, we ran some benchmark tests to analyze the changes that occurred within the AOSP QuickSearchBox app when migrating to Kotlin. Some interesting performance metrics we can use to compare the Java and Kotlin versions of the fully functioning QuickSearchBox app include:","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"ImageMetadata:1*a6CTIV_4uQytd8hv7uwtXw.png":{"__typename":"ImageMetadata","id":"1*a6CTIV_4uQytd8hv7uwtXw.png","originalHeight":181,"originalWidth":987,"focusPercentX":null,"focusPercentY":null,"alt":null},"Paragraph:ef6d8411e287_43":{"__typename":"Paragraph","id":"ef6d8411e287_43","name":"2b94","type":"IMG","href":null,"layout":"INSET_CENTER","metadata":{"__ref":"ImageMetadata:1*a6CTIV_4uQytd8hv7uwtXw.png"},"text":"","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_44":{"__typename":"Paragraph","id":"ef6d8411e287_44","name":"f192","type":"P","href":null,"layout":null,"metadata":null,"text":"After the migration was completed, we saw a sizable reduction of about 11.5% in the total # LOC (lines of code). It’s also worthwhile to note that the migration of the QuickSearchBox app focused on maintaining the existing functionality of the app when converting to Kotlin, without majorly refactoring the structure of the code. If we were to rewrite the entire QuickSearchBox app starting from Kotlin, we believe it would be possible to achieve an even greater reduction in size by better utilizing the concise nature of Kotlin.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"STRONG","start":71,"end":76,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"ImageMetadata:1*i6iPeWl4WMLcBnDf4sOKdA.png":{"__typename":"ImageMetadata","id":"1*i6iPeWl4WMLcBnDf4sOKdA.png","originalHeight":350,"originalWidth":1970,"focusPercentX":null,"focusPercentY":null,"alt":null},"Paragraph:ef6d8411e287_45":{"__typename":"Paragraph","id":"ef6d8411e287_45","name":"d60c","type":"IMG","href":null,"layout":"INSET_CENTER","metadata":{"__ref":"ImageMetadata:1*i6iPeWl4WMLcBnDf4sOKdA.png"},"text":"","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_46":{"__typename":"Paragraph","id":"ef6d8411e287_46","name":"7600","type":"P","href":null,"layout":null,"metadata":null,"text":"We did see a rather significant increase in the APK size, however this increase can be attributed to the new inclusion of the kotlinx-coroutines and androidx.core libraries in this project. The androidx.core library was added in order to substitute several deprecated function calls with the recommended replacements in order for QuickSearchBox to target the latest API level, and would have occurred regardless of the Kotlin migration. Since the overall size of the QuickSearchBox APK was still quite small (less than a MB total) we believe this increase is justifiable.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"CODE","start":126,"end":144,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":149,"end":162,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"CODE","start":194,"end":207,"href":null,"anchorType":null,"userId":null,"linkMetadata":null},{"__typename":"Markup","type":"A","start":126,"end":144,"href":"https:\u002F\u002Fgithub.com\u002FKotlin\u002Fkotlinx.coroutines","anchorType":"LINK","userId":null,"linkMetadata":null},{"__typename":"Markup","type":"A","start":149,"end":162,"href":"https:\u002F\u002Fdeveloper.android.com\u002Fjetpack\u002Fandroidx\u002Freleases\u002Fcore","anchorType":"LINK","userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_47":{"__typename":"Paragraph","id":"ef6d8411e287_47","name":"1c7a","type":"P","href":null,"layout":null,"metadata":null,"text":"Another consequence of the small app size from the outset is that the relative code size increase seems rather large, however that increase doesn’t scale with APK size and remains mostly constant in terms of the KB added no matter how big the app. That is, for a large app it will be a fraction of a percent instead.","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"ImageMetadata:1*CinyhwFGuID57XbV3yxgkw.png":{"__typename":"ImageMetadata","id":"1*CinyhwFGuID57XbV3yxgkw.png","originalHeight":354,"originalWidth":1972,"focusPercentX":null,"focusPercentY":null,"alt":null},"Paragraph:ef6d8411e287_48":{"__typename":"Paragraph","id":"ef6d8411e287_48","name":"ecdf","type":"IMG","href":null,"layout":"INSET_CENTER","metadata":{"__ref":"ImageMetadata:1*CinyhwFGuID57XbV3yxgkw.png"},"text":"","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_49":{"__typename":"Paragraph","id":"ef6d8411e287_49","name":"1465","type":"P","href":null,"layout":null,"metadata":null,"text":"Finally, the clean build time for the code was averaged over 10 trials for each language. These trials, and the previous benchmark tests, were all taken on a machine with 96 cores and 180 GB of RAM. We actually saw a slight decrease in total build time in the Kotlin version of the app, potentially due to the decrease in # LOC, switch to several updated API calls, and use of Kotlin’s coroutines.","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_50":{"__typename":"Paragraph","id":"ef6d8411e287_50","name":"d9f5","type":"H3","href":null,"layout":null,"metadata":null,"text":"Conclusion","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_51":{"__typename":"Paragraph","id":"ef6d8411e287_51","name":"a7a5","type":"P","href":null,"layout":null,"metadata":null,"text":"In total, the migration of the AOSP QuickSearchBox app took approximately 6 weeks, with 1 intern working on the project. The rate of conversion greatly increased with familiarity of Git\u002FRepo, Kotlin, and common errors found in Android Studio Kotlin tool. By the midpoint of the project, the process of converting the file, fixing errors, formatting, and maintaining the Git history was incredibly efficient, with up to 10 files being migrated per day. Ultimately, this project helped prove the benefits of converting an app to Kotlin, from more concise syntax to providing access to useful libraries and lightweight concurrency, and can act as an example for other developers seeking to migrate their own apps.","hasDropCap":null,"dropCapImage":null,"markups":[],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"Paragraph:ef6d8411e287_52":{"__typename":"Paragraph","id":"ef6d8411e287_52","name":"879c","type":"P","href":null,"layout":null,"metadata":null,"text":"Java is a trademark or registered trademark of Oracle and\u002For its affiliates.","hasDropCap":null,"dropCapImage":null,"markups":[{"__typename":"Markup","type":"EM","start":0,"end":76,"href":null,"anchorType":null,"userId":null,"linkMetadata":null}],"codeBlockMetadata":null,"iframe":null,"mixtapeMetadata":null},"CollectionViewerEdge:collectionId:95b274b437c2-viewerId:lo_4354d79c6a5e":{"__typename":"CollectionViewerEdge","id":"collectionId:95b274b437c2-viewerId:lo_4354d79c6a5e","isEditor":false,"isMuting":false},"ImageMetadata:1*5pMw_nx55x_66tk77kutPQ.png":{"__typename":"ImageMetadata","id":"1*5pMw_nx55x_66tk77kutPQ.png","originalWidth":600,"originalHeight":72},"PostViewerEdge:postId:1264346619ec-viewerId:lo_4354d79c6a5e":{"__typename":"PostViewerEdge","shouldIndexPostForExternalSearch":true,"id":"postId:1264346619ec-viewerId:lo_4354d79c6a5e"},"Tag:aosp":{"__typename":"Tag","id":"aosp","displayTitle":"Aosp","normalizedTagSlug":"aosp"},"Tag:kotlin":{"__typename":"Tag","id":"kotlin","displayTitle":"Kotlin","normalizedTagSlug":"kotlin"},"Tag:performance-analysis":{"__typename":"Tag","id":"performance-analysis","displayTitle":"Performance Analysis","normalizedTagSlug":"performance-analysis"},"Tag:android-studio":{"__typename":"Tag","id":"android-studio","displayTitle":"Android Studio","normalizedTagSlug":"android-studio"},"Tag:best-practices":{"__typename":"Tag","id":"best-practices","displayTitle":"Best Practices","normalizedTagSlug":"best-practices"},"Post:1264346619ec":{"__typename":"Post","id":"1264346619ec","collection":{"__ref":"Collection:95b274b437c2"},"content({\"postMeteringOptions\":{}})":{"__typename":"PostContent","isLockedPreviewOnly":false,"bodyModel":{"__typename":"RichText","sections":[{"__typename":"Section","name":"fd1d","startIndex":0,"textLayout":null,"imageLayout":null,"backgroundImage":null,"videoLayout":null,"backgroundVideo":null},{"__typename":"Section","name":"b882","startIndex":52,"textLayout":null,"imageLayout":null,"backgroundImage":null,"videoLayout":null,"backgroundVideo":null}],"paragraphs":[{"__ref":"Paragraph:ef6d8411e287_0"},{"__ref":"Paragraph:ef6d8411e287_1"},{"__ref":"Paragraph:ef6d8411e287_2"},{"__ref":"Paragraph:ef6d8411e287_3"},{"__ref":"Paragraph:ef6d8411e287_4"},{"__ref":"Paragraph:ef6d8411e287_5"},{"__ref":"Paragraph:ef6d8411e287_6"},{"__ref":"Paragraph:ef6d8411e287_7"},{"__ref":"Paragraph:ef6d8411e287_8"},{"__ref":"Paragraph:ef6d8411e287_9"},{"__ref":"Paragraph:ef6d8411e287_10"},{"__ref":"Paragraph:ef6d8411e287_11"},{"__ref":"Paragraph:ef6d8411e287_12"},{"__ref":"Paragraph:ef6d8411e287_13"},{"__ref":"Paragraph:ef6d8411e287_14"},{"__ref":"Paragraph:ef6d8411e287_15"},{"__ref":"Paragraph:ef6d8411e287_16"},{"__ref":"Paragraph:ef6d8411e287_17"},{"__ref":"Paragraph:ef6d8411e287_18"},{"__ref":"Paragraph:ef6d8411e287_19"},{"__ref":"Paragraph:ef6d8411e287_20"},{"__ref":"Paragraph:ef6d8411e287_21"},{"__ref":"Paragraph:ef6d8411e287_22"},{"__ref":"Paragraph:ef6d8411e287_23"},{"__ref":"Paragraph:ef6d8411e287_24"},{"__ref":"Paragraph:ef6d8411e287_25"},{"__ref":"Paragraph:ef6d8411e287_26"},{"__ref":"Paragraph:ef6d8411e287_27"},{"__ref":"Paragraph:ef6d8411e287_28"},{"__ref":"Paragraph:ef6d8411e287_29"},{"__ref":"Paragraph:ef6d8411e287_30"},{"__ref":"Paragraph:ef6d8411e287_31"},{"__ref":"Paragraph:ef6d8411e287_32"},{"__ref":"Paragraph:ef6d8411e287_33"},{"__ref":"Paragraph:ef6d8411e287_34"},{"__ref":"Paragraph:ef6d8411e287_35"},{"__ref":"Paragraph:ef6d8411e287_36"},{"__ref":"Paragraph:ef6d8411e287_37"},{"__ref":"Paragraph:ef6d8411e287_38"},{"__ref":"Paragraph:ef6d8411e287_39"},{"__ref":"Paragraph:ef6d8411e287_40"},{"__ref":"Paragraph:ef6d8411e287_41"},{"__ref":"Paragraph:ef6d8411e287_42"},{"__ref":"Paragraph:ef6d8411e287_43"},{"__ref":"Paragraph:ef6d8411e287_44"},{"__ref":"Paragraph:ef6d8411e287_45"},{"__ref":"Paragraph:ef6d8411e287_46"},{"__ref":"Paragraph:ef6d8411e287_47"},{"__ref":"Paragraph:ef6d8411e287_48"},{"__ref":"Paragraph:ef6d8411e287_49"},{"__ref":"Paragraph:ef6d8411e287_50"},{"__ref":"Paragraph:ef6d8411e287_51"},{"__ref":"Paragraph:ef6d8411e287_52"}]},"validatedShareKey":"","shareKeyCreator":null},"creator":{"__ref":"User:e1f26db83092"},"inResponseToEntityType":null,"isLocked":false,"isMarkedPaywallOnly":false,"lockedSource":"LOCKED_POST_SOURCE_NONE","mediumUrl":"https:\u002F\u002Fmedium.com\u002Fandroiddevelopers\u002Fmigrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec","primaryTopic":{"__ref":"Topic:64756708407c"},"topics":[{"__typename":"Topic","slug":"android-development"}],"isPublished":true,"latestPublishedVersion":"ef6d8411e287","visibility":"PUBLIC","postResponses":{"__typename":"PostResponses","count":6},"clapCount":335,"allowResponses":true,"isLimitedState":false,"title":"Migrating the AOSP QuickSearchBox App to Kotlin","isSeries":false,"sequence":null,"uniqueSlug":"migrating-the-aosp-quicksearchbox-app-to-kotlin-1264346619ec","socialTitle":"","socialDek":"","canonicalUrl":"","metaDescription":"","latestPublishedAt":1664363805866,"readingTime":7.020754716981132,"previewContent":{"__typename":"PreviewContent","subtitle":"Over the course of 6 weeks, over 11,000 lines of Java code within the QuickSearchBox app were converted to Kotlin to showcase best…"},"previewImage":{"__ref":"ImageMetadata:1*cWnPe-kD4hAVuH3IIcNUcA.png"},"isShortform":false,"seoTitle":"","firstPublishedAt":1663963228638,"updatedAt":1664363815634,"shortformType":"SHORTFORM_TYPE_LINK","seoDescription":"","viewerEdge":{"__ref":"PostViewerEdge:postId:1264346619ec-viewerId:lo_4354d79c6a5e"},"isSuspended":false,"license":"ALL_RIGHTS_RESERVED","tags":[{"__ref":"Tag:aosp"},{"__ref":"Tag:kotlin"},{"__ref":"Tag:performance-analysis"},{"__ref":"Tag:android-studio"},{"__ref":"Tag:best-practices"}],"isNewsletter":false,"statusForCollection":"APPROVED","pendingCollection":null,"detectedLanguage":"en","wordCount":1675,"layerCake":3,"responsesLocked":false}}</script><script>window.__MIDDLEWARE_STATE__={"session":{"xsrf":""},"cache":{"cacheStatus":"HIT"}}</script><script src="https://cdn-client.medium.com/lite/static/js/manifest.de931270.js"></script><script src="https://cdn-client.medium.com/lite/static/js/9865.1496d74a.js"></script><script src="https://cdn-client.medium.com/lite/static/js/main.3947a59c.js"></script><script src="https://cdn-client.medium.com/lite/static/js/instrumentation.d9108df7.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/reporting.ff22a7a5.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/9120.5df29668.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/5049.d1ead72d.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/4810.6318add7.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/6618.db187378.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/2707.b0942613.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/9977.5b3eb23a.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/8599.1ab63137.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/5250.9f9e01d2.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/6349.b071a958.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/2648.26563adf.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/8393.826a25fb.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/7079.67349d50.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/3735.afb7e926.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/5642.a2d9f6a1.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/6546.cd03f950.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/6834.08de95de.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/7346.72622eb9.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/2420.2a5e2d95.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/839.ca7937c2.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/7975.d195c6f1.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/2106.21ff89d3.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/7394.3d049572.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/2961.00a48598.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/8204.c4082863.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/4391.59acaed3.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/PostPage.MainContent.c8a11795.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/8414.6565ad5f.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/3974.8d3e0217.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/2527.a0afad8a.chunk.js"></script> <script src="https://cdn-client.medium.com/lite/static/js/PostResponsesContent.36c2ecf4.chunk.js"></script><script>window.main();</script><script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'8e9005e5998e87b4',t:'MTczMjY4ODUzOC4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>